package net.bioclipse.spectrum.graph2d;
import java.awt.*;
import java.util.*;
import java.lang.*;
import java.awt.image.*;
/*
**************************************************************************
**
** Class RTextLine
**
**************************************************************************
** Copyright (C) 1996 Leigh Brookshaw
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**************************************************************************
**
** This class is an extension of TextLine that allows text to be rotated.
** Currently only multiples of 90 degrees is allowed but the Image filter
** could easily be extended to allow arbitrary rotation.
**
*************************************************************************/
/**
*
* This class is an extension of the TextLine Class
* that allows text to be rotated. To rotate the text the Component.createImage
* method is used. This means that this class needs to know
* the component that will receive the text. If the component is not
* known the text cannot be rotated and the class will fall back to the
* TextLine.draw method.<P>
*
* The text is rotated by running the image through an ImageFilter. This filter
* can easily be modified to rotate text through an arbitrary angle but
* unless the text is large the integer mapping will blur the text.
* Also positioning of arbitrarily rotated text becomes problematic.
*
* @version $Revision: 1.5 $, $Date: 1996/10/23 03:13:45 $
* @author Leigh Brookshaw
*/
public class RTextLine extends TextLine {
/*
*************
**
** Constants
**
************/
/*
**********************
**
** Protected Variables
**
*********************/
/**
* Rotation Angle of text in degrees
*/
protected int angle = 0;
private double cos = 1.0;
private double sin = 0.0;
private Component component = null;
/*
**********************
**
** Constructors
**
*********************/
/**
* Instantiate the class
*/
public RTextLine() { }
/**
* Instantiate the class.
* @param s String to parse.
*/
public RTextLine(String s) {
super(s);
}
/**
* Instantiate the class.
* @param c the Component the text will be drawn into.
*/
public RTextLine(Component c) {
setDrawingComponent(c);
}
/**
* Instantiate the class
* @param s String to parse.
* @param f Font to use.
*/
public RTextLine(String s, Font f) {
super(s,f);
}
/**
* Instantiate the class
* @param s String to parse.
* @param f Font to use.
* @param c Color to use
* @param j Justification
*/
public RTextLine(String s, Font f, Color c, int j) {
super(s,f,c,j);
}
/**
* Instantiate the class
* @param s String to parse.
* @param c Color to use
*/
public RTextLine(String s, Color c) {
super(s,c);
}
/**
* Instantiate the class
* @param f Font to use.
* @param c Color to use
* @param j Justification
* @param a Rotation angle in degrees
*/
public RTextLine(Font f, Color c, int j, int a) {
super(f,c,j);
setRotation(a);
}
/**
* Instantiate the class
* @param f Font to use.
* @param c Color to use
* @param j Justification
*/
public RTextLine(Font f, Color c, int j) {
super(f,c,j);
}
/*
*****************
**
** Public Methods
**
*****************/
/**
* Copy the state of the parsed Textline into the existing
* object.
* @param t The TextLine to get the state information from.
*/
public void copyState(RTextLine t) {
if(t==null) return;
font = t.getFont();
color = t.getColor();
justification = t.getJustification();
setRotation(t.getRotation(),t.getComponent() );
if(font == null) return;
fontname = font.getName();
fontstyle = font.getStyle();
fontsize = font.getSize();
parse = true;
}
/**
* Set the text rotation angle. Only multiples of 90 degrees
* are accepted
* @param angle The angle to rotate the text
*/
public void setRotation(int angle) {
this.angle = ((angle%360)/90)*90;
cos = Math.cos(angle*Math.PI/180.0);
sin = Math.sin(angle*Math.PI/180.0);
}
/**
* Set the Component the text will be drawn into. This is required to rotate
* the text. if it is not set the text will not be rotated.
* @param c The drawing component
*/
public void setDrawingComponent( Component c ) {
component = c;
}
/**
* Set the Rotation and the Component that will be drawn into
* @param angle The angle to rotate the text
* @param c The drawing component
*/
public void setRotation(int angle, Component c) {
setRotation(angle);
setDrawingComponent(c);
}
/**
* @return the Rotation angle in degrees.
*/
public int getRotation() {
return angle;
}
/**
* @return the Component that will receive the text.
*/
public Component getComponent() {
return component;
}
/**
* The width of the text after it has been rotated.
* @param g Graphics context.
* @return the width of the parsed text after it has been rotated.
*/
public int getRWidth(Graphics g) {
parseText(g);
return (int)(Math.abs(cos*width+sin*height) + 0.5);
}
/**
* The height of the text after it has been rotated.
* @param g Graphics context.
* @return the height of the parsed text after it has been rotated.
*/
public int getRHeight(Graphics g) {
parseText(g);
return (int)(Math.abs(-sin*width+cos*height) + 0.5);
}
/**
* Return the left edge of the rotated text.
* This is dependent on the justification of the text.
* @param g Graphics context.
* @return the Left edge of the rotated text
*/
public int getLeftEdge(Graphics g) {
return getLeftEdge(g,justification);
}
/**
* Return the right edge of the rotated text.
* This is dependent on the justification of the text.
* @param g Graphics context.
* @return the Right edge of the rotated text
*/
public int getRightEdge(Graphics g) {
return getRightEdge(g,justification);
}
/**
* Return the top edge of the rotated text.
* This is dependent on the justification of the text.
* @param g Graphics context.
* @return the Top edge of the rotated text
*/
public int getTopEdge(Graphics g) {
return getTopEdge(g,justification);
}
/**
* Return the bottom edge of the rotated text.
* This is dependent on the justification of the text.
* @param g Graphics context.
* @return the Bottom edge of the rotated text
*/
public int getBottomEdge(Graphics g) {
return getBottomEdge(g,justification);
}
/**
* Return the left edge of the rotated text.
* @param g Graphics context.
* @param j Text justification
* @return the Left edge of the rotated text
*/
public int getLeftEdge(Graphics g, int j) {
parseText(g);
switch (angle) {
case 90: case -270:
return -ascent;
case 180: case -180:
if(j == CENTER ) return -width/2;
else
if(j == RIGHT ) return 0;
else return -width;
case 270: case -90:
return -descent-leading;
default:
if(j == CENTER ) return -width/2;
else
if(j == RIGHT ) return -width;
else return 0;
}
}
/**
* Return the right edge of the rotated text.
* @param g Graphics context.
* @param j Text justification
* @return the Right edge of the rotated text
*/
public int getRightEdge(Graphics g, int j) {
parseText(g);
switch (angle) {
case 90: case -270:
return descent+leading;
case 180: case -180:
if(j == CENTER ) return width/2;
else
if(j == RIGHT ) return width;
else return 0;
case 270: case -90:
return ascent;
default:
if(j == CENTER ) return width/2;
else
if(j == RIGHT ) return 0;
else return width;
}
}
/**
* Return the top edge of the rotated text.
* @param g Graphics context.
* @param j Text justification
* @return the Top edge of the rotated text
*/
public int getTopEdge(Graphics g, int j) {
parseText(g);
switch (angle) {
case 90: case -270:
if(j == CENTER ) return width/2;
else
if(j == RIGHT ) return 0;
else return width;
case 180: case -180:
return descent+leading;
case 270: case -90:
if(j == CENTER ) return width/2;
else
if(j == RIGHT ) return width;
else return 0;
default:
return ascent;
}
}
/**
* Return the bottom edge of the rotated text.
* @param g Graphics context.
* @param j Text justification
* @return the Bottom edge of the rotated text
*/
public int getBottomEdge(Graphics g, int j) {
parseText(g);
switch (angle) {
case 90: case -270:
if(j == CENTER ) return -width/2;
else
if(j == RIGHT ) return -width;
else return 0;
case 180: case -180:
return -ascent;
case 270: case -90:
if(j == CENTER ) return -width/2;
else
if(j == RIGHT ) return 0;
else return -width;
default:
return -descent-leading;
}
}
/**
* Parse the text then draw it.
* @param g Graphics context
* @param x pixel position of the text
* @param y pixel position of the text
*/
public void draw(Graphics g, int x, int y) {
if( g == null ) return;
if(component == null ) angle = 0;
if(angle == 0 ) super.draw(g,x,y);
else draw(component,g,x,y);
}
/**
* Parse the text then draw it.
* @param g Graphics context
* @param x pixel position of the text
* @param y pixel position of the text
* @param j justification of the text
*/
public void draw(Graphics g, int x, int y, int j) {
justification = j;
if( g == null ) return;
if(component == null ) angle = 0;
if(angle == 0 ) super.draw(g,x,y);
else draw(component,g,x,y);
}
/**
* Parse the text, rotate it then draw it to the screen.
* @param g Graphics context
* @param x pixel position of the text
* @param y pixel position of the text
*/
public synchronized void draw(Component comp, Graphics g, int x, int y) {
// TextState ts;
// int xoffset = 0;
// int yoffset = 0;
// Image offsI = null;
// Graphics offsG = null;
// Image rotatedImage = null;
// int maxHeight = 0;
//
//
// if(text == null || comp == null) return;
//
//
// parseText(g);
//
// maxHeight = maxAscent + maxDescent;
//
// /*
// ** Calculate the offset of the rotated image so that it
// ** will be positioned correctly. Remeber the image is calculated
// ** on the Maximum Ascent and descent so that no character
// ** is truncated
// */
// switch (angle) {
//
// case 90: case -270:
// xoffset = -maxAscent;
// if(justification == CENTER ) yoffset = - width/2;
// else
// if(justification == RIGHT ) yoffset = 0;
// else yoffset = - width;
// break;
// case 180: case -180:
// yoffset = -maxDescent;
// if(justification == CENTER ) xoffset = - width/2;
// else
// if(justification == RIGHT ) xoffset = 0;
// else xoffset = - width;
// break;
// case 270: case -90:
// xoffset = -maxDescent;
// if(justification == CENTER ) yoffset = - width/2;
// else
// if(justification == RIGHT ) yoffset = - width;
// else yoffset = 0;
// break;
// default:
// xoffset = 0;
// yoffset = 0;
// break;
// }
// /*
// ** Create the offscreen image that the text will be written into
// */
// offsI = comp.createImage(width,maxHeight);
// offsG = offsI.getGraphics();
// /*
// ** Color the image with the background color
// */
// if(background != null) {
// offsG.setColor(background);
// } else {
// offsG.setColor(comp.getBackground());
// }
//
// offsG.fillRect(0,0,width,maxHeight);
// /*
// ** Set the image font and color
// */
// offsG.setFont(g.getFont());
// offsG.setColor(g.getColor());
//
// if(font != null) offsG.setFont(font);
// if(color != null) offsG.setColor(color);
//
// /*
// ** Write to the offscreen image
// */
// for(int i=0; i<list.size(); i++) {
// ts = ((TextState)(list.elementAt(i)));
// if(ts.f != null) offsG.setFont(ts.f);
// if(ts.s != null)
// offsG.drawString(ts.toString(),ts.x,ts.y+maxAscent );
// }
// /*
// ** Rotate the en image
// */
//
// RotateTextFilter f = new RotateTextFilter(angle);
// ImageProducer producer = new FilteredImageSource(offsI.getSource(),f);
//
// rotatedImage = comp.createImage(producer);
// /*
// ** Draw the rotated image to the Component. Do not notify any
// ** image consumer especially the component, otherwise we will get a
// ** feedback loop starting up,
// ** as this method is normally called from a paint method.
// */
// g.drawImage(rotatedImage,x+xoffset,y+yoffset,null);
//
//
}
}
/**
* This is an extension to the ImageFilter class that will rotate an image
* a multiple of 90 degrees. This filter is easily extended to allow arbitrary
* rotations
*/
class RotateTextFilter extends ImageFilter {
/*
** The angle to rotate the image in degrees
*/
private int angle = 0;
/*
** The minimum and maximum points on the image after rotation.
** More important if arbitrary rotation was allowed
*/
private int xmin = 0;
private int xmax = 0;
private int ymin = 0;
private int ymax = 0;
/*
** The width and height of the New rotated image
*/
private int width;
private int height;
/*
** the rotation cosines of the angle
*/
private double cos = 1.0;
private double sin = 0.0;
/*
** The arrays that will store the new image. Only one is used depending
** on the data type of the original image.
*/
private int ipixels[];
private byte bpixels[];
/*
** the color model of the original image
*/
private ColorModel colorModel;
/*
*****************
** Constructors
****************/
/**
* Instantiate the class
* @param angle the angle to rotate the image. Only multiples of 90 degrees
* are allowed
*/
public RotateTextFilter(int angle) {
this.angle = ((angle%360)/90)*90;
cos = Math.cos( angle*Math.PI/180.0 );
sin = Math.sin( angle*Math.PI/180.0 );
}
/**
* Add to the properties table of the image that it has been rotated
* @param props The property table of the original image
*/
public void setProperties(Hashtable props) {
props = (Hashtable) props.clone();
props.put("rotAngle", new Integer(angle) );
super.setProperties(props);
}
/**
* Find the dimensions of the original image and pass onto
* the image consumer the dimensions of the new roated image
* @param w width of the original image
* @param h height of the original image
*/
public void setDimensions(int w, int h) {
int x[] = {0,w-1,w-1,0};
int y[] = {0,0,h-1,h-1};
int xx;
int yy;
for( int i=0; i<4; i++ ) {
xx = (int)Math.round( x[i]*cos + y[i]*sin);
yy = (int)Math.round(-x[i]*sin + y[i]*cos);
xmin = Math.min(xmin,xx);
xmax = Math.max(xmax,xx);
ymin = Math.min(ymin,yy);
ymax = Math.max(ymax,yy);
}
width = xmax-xmin+1;
height = ymax-ymin+1;
consumer.setDimensions(width, height);
}
/**
* As the pixels of the original image are sent store them in memory
* as the rotated image.
*
* This method is called with a subset rectangle of the original image.
*
* @param x position of Left column in original image of this rectangle
* @param y poisition of Top row in original image of this rectangle
* @param w width of this rectangle
* @param h height of this rectangle
* @param model Colormodel associated with original image
* @param pixels Array containing the image or part of it
* @param off The offset into the pixels array where the parsed
* rectangle starts
* @param scan The actual width of the image.
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, byte pixels[], int off,
int scan) {
int i,j,k;
int ir,jr;
/*
** If the byte array is null create it. Also remember the color
** model so that we can pass it onto the image consumer
*/
if(bpixels == null) {
colorModel = model;
bpixels = new byte[width*height];
}
/*
** place the rotated image into memory one pixel at a time
*/
j = y;
for(int n=0; n<h; n++, j++) {
i = x;
for(int m=0; m<w; m++, i++) {
ir = (int)Math.round( i*cos + j*sin) - xmin;
jr = (int)Math.round(-i*sin + j*cos) - ymin;
k = ir+jr*width;
bpixels[k] = pixels[ (j-y)*scan+(i-x)+off ];
}
}
}
/**
* As the pixels of the original image are sent store them in memory
* as the rotated image.
*
* This method is called with a subset rectangle of the original image.
*
* @param x position of Left column in original image of this rectangle
* @param y poisition of Top row in original image of this rectangle
* @param w width of this rectangle
* @param h height of this rectangle
* @param model Colormodel associated with original image
* @param pixels Array containing the image or part of it
* @param off The offset into the pixels array where the parsed
* rectangle starts
* @param scan The actual width of the image.
*/
public void setPixels(int x, int y, int w, int h,
ColorModel model, int pixels[], int off,
int scan) {
int i,j,k;
int ir,jr;
/*
** If the integer array is null create it. Also remember the color
** model so that we can pass it onto the image consumer
*/
if(ipixels == null) {
colorModel = model;
ipixels = new int[width*height];
}
/*
** place the rotated image into memory one pixel at a time
*/
j = y;
for(int n=0; n<h; n++, j++) {
i = x;
for(int m=0; m<w; m++, i++) {
ir = (int)Math.round( i*cos + j*sin) - xmin;
jr = (int)Math.round(-i*sin + j*cos) - ymin;
k = ir+jr*width;
ipixels[k] = pixels[ (j-y)*scan+(i-x)+off ];
}
}
}
/**
* Called when the image is complete.
* When this is called by the image producer, we can then pass the rotated
* image onto the image consumer.
*
* @param status Status of the original image from the image producer.
*/
public void imageComplete(int status) {
if(status == ImageConsumer.IMAGEABORTED ||
status == ImageConsumer.IMAGEERROR ) {
consumer.imageComplete(status);
ipixels = null;
bpixels = null;
return;
}
/*
** Send the rotated image to the image consumer. Not forgetting to tell
** it when the image is complete
*/
if(bpixels != null) {
for(int j=0; j<height; j++) consumer.setPixels(0, j, width, 1,
colorModel, bpixels, j*width, width);
consumer.imageComplete(status);
} else
if(ipixels != null) {
for(int j=0; j<height; j++) consumer.setPixels(0, j, width, 1,
colorModel, ipixels, j*width, width);
consumer.imageComplete(status);
} else
consumer.imageComplete(ImageConsumer.IMAGEABORTED);
ipixels = null;
bpixels = null;
}
}