Gr2PICT.java |
// graphics.dclap/gr2pict.java // write java ip.graphics calls to outstream in PICT format (apple macintosh) // d.gilbert, dec. 1996 // based on PSgr by E.J. Friedman-Hill package graphics.dclap; // pack edu.indiana.bio.graphics.dclap; import java.awt.*; import java.awt.image.ImageObserver; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Gr2PICT is a Graphics subclass that draws to PICT format. * @version 1.0 * @author Don Gilbert */ public class Gr2PICT extends java.awt.Graphics { //uncomment for jdk 1.2 public void drawString(java.text.AttributedCharacterIterator aci, int a, int b) { }; public final static int CLONE = 49; protected final static int PAGEHEIGHT = 792; protected final static int PAGEWIDTH = 612; protected DataOutputStream os; protected Color clr = Color.black; protected Font font = new Font("Times", Font.PLAIN, 12); protected Rectangle clipr = new Rectangle(-30000, -30000, 60000, 60000); protected Point origin = new Point(0, 0); protected boolean trouble = false; protected Graphics g; public Rectangle getClipBounds() { Rectangle r = new Rectangle(); return r; } public void setClip(int a, int b, int c, int d) { return; } public void setClip(java.awt.Shape a) { return; } public java.awt.Shape getClip() { Rectangle s = new Rectangle(); return s; } public void drawPolyline(int[] a, int[] b, int c) { return; } public boolean drawImage(Image i, int a, int b, int c, int d, int e, int f, int g, int h, ImageObserver o) { return true; } public boolean drawImage(Image i, int a, int b, int c, int d, int e, int f, int g, int h, Color cl, ImageObserver o) { return true; } /** * Constructs a new Gr2PICT Object. Unlike regular Graphics objects, * Gr2PICT contexts can be created directly. * @param o Output stream for PostScript output * @see #create */ public Gr2PICT(OutputStream o, Graphics g) { os = new DataOutputStream(o); trouble = false; this.g = g; Rectangle r = g.getClipBounds(); if (r == null) r = new Rectangle(0, 0, PAGEWIDTH, PAGEHEIGHT); emitHeader(r.width, r.height); } public Gr2PICT(OutputStream o, Graphics g, int what) { os = new DataOutputStream(o); trouble = false; this.g = g; Rectangle r = g.getClipBounds(); if (r == null) r = new Rectangle(0, 0, PAGEWIDTH, PAGEHEIGHT); if (what != CLONE) emitHeader(r.width, r.height); } private int fAlign = 0; protected void emitbyte(int v) { try { os.writeByte(v); fAlign++; } catch (IOException ex) { trouble = true; } } protected void emitword(int v, DataOutputStream dos) { try { dos.writeShort(v); } catch (IOException ex) { trouble = true; } } protected void emitint(int v) { try { os.writeInt(v); } catch (IOException ex) { trouble = true; } } protected void emitstring(String s) { try { os.writeBytes(s); fAlign += s.length(); } catch (IOException ex) { trouble = true; } } protected final void emitop(int op) { if ((fAlign & 1) == 1) emitbyte(0); // pad to word size emitword(op,os); } protected final void emitcolor(Color c) { emitword(c.getRed() << 8,os); emitword(c.getGreen() << 8,os); emitword(c.getBlue() << 8,os); } protected final void emitrect(int x, int y, int width, int height) { emitword(y,os); emitword(x,os); emitword(y + height,os); emitword(x + width,os); } protected final void emitroundrect( int opcode, int x, int y, int width, int height, int arcWidth, int arcHeight) { emitop(QD.oOvSize); emitword(arcHeight,os); emitword(arcWidth,os); emitop(opcode); emitrect(x, y, width, height); } protected void emitpolygon(Polygon p) { int polysize = 2 + 8 + p.npoints * 4; emitword(polysize,os); Rectangle r = p.getBounds(); emitrect(r.x, r.y, r.width, r.height); for (int i = 0; i < p.npoints; i++) { emitword(p.ypoints[i],os); emitword(p.xpoints[i],os); } } protected void emitcomment(int kind, int datasize, String data) { if (datasize == 0) { emitop(QD.oShortComment); emitword(kind,os); } else { emitop(QD.oLongComment); emitword(kind,os); emitword(data.length(),os); emitstring(data); } } public final void beginPicGroup() { emitcomment(QD.picGrpBeg, 0, null); } public final void endPicGroup() { emitcomment(QD.picGrpEnd, 0, null); } public void laserLine(int num, int denom) { // set laserwriter line width // use num=1, denom=4 for 1/300 dpi line //emitcomment( QD.setLineWidth, sizeof(lineSize), lineSize); emitop(QD.oLongComment); emitword(QD.setLineWidth,os); emitword(4,os); emitword(num,os); emitword(denom,os); } /** Top of every PICT file */ protected void emitHeader(int picwidth, int picheight) { // 512 byte writeHeader try { int buflen = 512; byte[] buf = new byte[buflen]; // java zeros buf !? os.write(buf, 0, buflen); } catch (IOException ex) { trouble = true; } int picbytes = 0; // will zero here suffice !? seems to be working... emitword(picbytes,os); emitrect(0, 0, picwidth, picheight); emitop(QD.oVersion); emitword(QD.version2,os); // writeHeader from clarisdraw // C00 0x10 0xc00 : (skip 24) FF FF FF FF FF FF 0 0 FF FF emitop(QD.oHeaderOp); // reserved writeHeader opcode, followed by 24 byte writeHeader emitint(-1); // total size in bytes or -1 for (int i = 0; i < 4; i++) { emitword(-1,os); emitword(0,os); } // fixedpt bound box or -1 emitint(-1); // reserved or -1 //oDefHilite & clipRect of pict size as 1st op is usual but not required !? emitop(QD.oDefHilite); clipRect(clipr.x, clipr.y, clipr.width, clipr.height); beginPicGroup(); } ///////////// Graphics Public Interface //////////// /** * Creates a new Gr2PICT Object that is a copy of the original Gr2PICT Object. */ public Graphics create() { Gr2PICT grpict = new Gr2PICT(os, g, CLONE); grpict.font = font; grpict.clipr = clipr; grpict.clr = clr; return grpict; } /** * Creates a new Graphics Object with the specified parameters, * based on the original * Graphics Object. * This method translates the specified parameters, x and y, to * the proper origin coordinates and then clips the Graphics Object to the * area. * @param x the x coordinate * @param y the y coordinate * @param width the width of the area * @param height the height of the area * @see #translate */ public Graphics create(int x, int y, int width, int height) { Graphics g = create(); g.translate(x, y); g.clipRect(0, 0, width, height); return g; } /** * Translates the specified parameters into the origin of * the ip.graphics context. All subsequent * operations on this ip.graphics context will be relative to this origin. * @param x the x coordinate * @param y the y coordinate * @see #scale */ public void translate(int x, int y) { // ? do this internally, adjusting each emitted x,y? origin.x = x; origin.y = y; // or as emitted opcode ?? emitop(QD.oOrigin); emitword(-x,os); emitword(-y,os); } /** * Gets the current color. * @see #setColor */ public Color getColor() { return clr; } /** * Sets the current color to the specified color. All subsequent ip.graphics operations * will use this specified color. * @param c the color to be set * @see Color * @see #getColor */ public void setColor(Color c) { if (c != null) clr = c; emitop(QD.oRGBFgCol); emitcolor(clr); } /** * Sets the default paint mode to overwrite the destination with the * current color. */ public void setPaintMode() { emitop(QD.oPnMode); emitword(QD.patCopy,os); // or QD.patOr } /** * Sets the paint mode to alternate between the current color * and the new specified color. * @param c1 the second color */ public void setXORMode(Color c1) { emitop(QD.oPnMode); emitword(QD.patXor,os); if (c1 != null) { // set c1 as PICT HiliteColor & set HiliteMode !? emitop(QD.oHiliteMode); emitop(QD.oHiliteColor); emitcolor(c1); } } /** * Gets the current font. * @see #setFont */ public Font getFont() { return font; } /** * Sets the font for all subsequent text-drawing operations. * @param font the specified font * @see Font * @see #getFont * @see #drawString * @see #drawBytes * @see #drawChars */ public void setFont(Font f) { if (f != null) { this.font = f; // count # bytes in name + number + String fname = font.getName(); fname = "Times"; // HACK! // Make sure the font is really // Times, for the purpose of printing!! int qdnum = QD.getQuickDrawFontNum(fname); if (qdnum >= 0) { emitop(QD.oTxFont); emitword(qdnum,os); } else { emitop(QD.oFontName); int len = fname.length() + 1 + 2 + 2; // length-byte + fontnum + #bytes total emitword(len,os); emitword(QD.fontnum++,os); emitstring(fname); } int face = 0; int style = font.getStyle(); if ((style & Font.BOLD) != 0) face |= QD.bold; if ((style & Font.ITALIC) != 0) face |= QD.italic; emitop(QD.oTxFace); emitbyte(face); emitop(QD.oTxSize); emitword(font.getSize(),os); } } /** * Gets the current font metrics. * @see #getFont */ public FontMetrics getFontMetrics() { return getFontMetrics(getFont()); } /** * Gets the current font metrics for the specified font. * @param f the specified font * @see #getFont * @see #getFontMetrics */ public FontMetrics getFontMetrics(Font f) { return g.getFontMetrics(f); } /** * Returns the bounding rectangle of the current clipping area. * @see #clipRect */ public Rectangle getBounds() { return clipr; } /** * Clips to a rectangle. The resulting clipping area is the * intersection of the current clipping area and the specified * rectangle. Graphic operations have no effect outside of the * clipping area. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #getClipRect */ public void clipRect(int x, int y, int width, int height) { clipr = new Rectangle(x, y, width, height); emitop(QD.oClip); int rgnsize = 2 + 8; //size-word + sizeof(rect) + sizeof(region)?? emitword(rgnsize,os); emitrect(x, y, width, height); } /** * Copies an area of the screen. * @param x the x-coordinate of the source * @param y the y-coordinate of the source * @param width the width * @param height the height * @param dx the horizontal distance * @param dy the vertical distance */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { //throw new RuntimeException("copyArea not supported"); } /** * Draws a line between the coordinates (x1,y1) and (x2,y2). The line is drawn * below and to the left of the logical coordinates. * @param x1 the first point's x coordinate * @param y1 the first point's y coordinate * @param x2 the second point's x coordinate * @param y2 the second point's y coordinate */ public void drawLine(int x1, int y1, int x2, int y2) { emitop(QD.oLine); emitword(y1,os); emitword(x1,os); emitword(y2,os); emitword(x2,os); } /** * Fills the specified rectangle with the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #drawRect * @see #clearRect */ public void fillRect(int x, int y, int width, int height) { emitop(QD.opaintRect); emitrect(x, y, width, height); } /** * Draws the outline of the specified rectangle using the current color. * Use drawRect(x, y, width-1, height-1) to draw the outline inside the specified * rectangle. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #fillRect * @see #clearRect */ public void drawRect(int x, int y, int width, int height) { emitop(QD.oframeRect); emitrect(x, y, width, height); } /** * Clears the specified rectangle by filling it with the current background color * of the current drawing surface. * Which drawing surface it selects depends on how the ip.graphics context * was created. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #fillRect * @see #drawRect */ public void clearRect(int x, int y, int width, int height) { emitop(QD.oeraseRect); emitrect(x, y, width, height); } /** * Draws an outlined rounded corner rectangle using the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param arcWidth the diameter of the arc * @param arcHeight the radius of the arc * @see #fillRoundRect */ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { emitroundrect(QD.oframeRRect, x, y, width, height, arcWidth, arcHeight); } /** * Draws a rounded rectangle filled in with the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param arcWidth the diameter of the arc * @param arcHeight the radius of the arc * @see #drawRoundRect */ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { emitroundrect(QD.opaintRRect, x, y, width, height, arcWidth, arcHeight); } /** * Draws a highlighted 3-D rectangle. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param raised a boolean that states whether the rectangle is raised or not */ public void draw3DRect(int x, int y, int width, int height, boolean raised) { Color c = getColor(); Color brighter = c.brighter(); Color darker = c.darker(); setColor(raised ? brighter : darker); drawLine(x, y, x, y + height); drawLine(x + 1, y, x + width - 1, y); setColor(raised ? darker : brighter); drawLine(x + 1, y + height, x + width, y + height); drawLine(x + width, y, x + width, y + height); setColor(c); } /** * Paints a highlighted 3-D rectangle using the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param raised a boolean that states whether the rectangle is raised or not */ public void fill3DRect(int x, int y, int width, int height, boolean raised) { Color c = getColor(); Color brighter = c.brighter(); Color darker = c.darker(); if (!raised) setColor(darker); fillRect(x + 1, y + 1, width - 2, height - 2); setColor(raised ? brighter : darker); drawLine(x, y, x, y + height - 1); drawLine(x + 1, y, x + width - 2, y); setColor(raised ? darker : brighter); drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1); drawLine(x + width - 1, y, x + width - 1, y + height - 1); setColor(c); } /** * Draws an oval inside the specified rectangle using the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #fillOval */ public void drawOval(int x, int y, int width, int height) { emitop(QD.oframeOval); emitrect(x, y, width, height); } /** * Fills an oval inside the specified rectangle using the current color. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @see #drawOval */ public void fillOval(int x, int y, int width, int height) { emitop(QD.opaintOval); emitrect(x, y, width, height); } /** * Draws an arc bounded by the specified rectangle from startAngle to * endAngle. 0 degrees is at the 3-o'clock position.Positive arc * angles indicate counter-clockwise rotations, negative arc angles are * drawn clockwise. * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param startAngle the beginning angle * @param arcAngle the angle of the arc (relative to startAngle). * @see #fillArc */ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { emitop(QD.oframeArc); emitrect(x, y, width, height); emitword(startAngle + 90,os); emitword(arcAngle,os); } /** * Fills an arc using the current color. This generates a pie shape. * * @param x the x coordinate * @param y the y coordinate * @param width the width of the arc * @param height the height of the arc * @param startAngle the beginning angle * @param arcAngle the angle of the arc (relative to startAngle). * @see #drawArc */ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { emitop(QD.opaintArc); emitrect(x, y, width, height); emitword(startAngle,os); emitword(arcAngle,os); } /** * Draws a polygon defined by an array of x points and y points. * @param xPoints an array of x points * @param yPoints an array of y points * @param nPoints the total number of points * @see #fillPolygon */ public void drawPolygon(int xPoints[], int yPoints[], int nPoints) { drawPolygon(new Polygon(xPoints, yPoints, nPoints)); } /** * Draws a polygon defined by the specified point. * @param p the specified polygon * @see #fillPolygon */ public void drawPolygon(Polygon p) { emitop(QD.oframePoly); emitpolygon(p); } /** * Fills a polygon with the current color. * @param xPoints an array of x points * @param yPoints an array of y points * @param nPoints the total number of points * @see #drawPolygon */ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { fillPolygon(new Polygon(xPoints, yPoints, nPoints)); } /** * Fills the specified polygon with the current color. * @param p the polygon * @see #drawPolygon */ public void fillPolygon(Polygon p) { emitop(QD.opaintPoly); emitpolygon(p); } /** * Draws the specified String using the current font and color. * The x,y position is the starting point of the baseline of the String. * @param str the String to be drawn * @param x the x coordinate * @param y the y coordinate * @see #drawChars * @see #drawBytes */ public void drawString(String str, int x, int y) { emitop(QD.oLongText); emitword(y,os); emitword(x,os); emitbyte(str.length()); emitstring(str); } /** * Draws the specified characters using the current font and color. * @param data the array of characters to be drawn * @param offset the start offset in the data * @param length the number of characters to be drawn * @param x the x coordinate * @param y the y coordinate * @see #drawString * @see #drawBytes */ public void drawChars(char data[], int offset, int length, int x, int y) { drawString(new String(data, offset, length), x, y); } /** * Draws the specified bytes using the current font and color. * @param data the data to be drawn * @param offset the start offset in the data * @param length the number of bytes that are drawn * @param x the x coordinate * @param y the y coordinate * @see #drawString * @see #drawChars */ public void drawBytes(byte data[], int offset, int length, int x, int y) { // deprecated: //drawString(new String(data, 0, offset, length), x, y); drawString(new String(data, offset, length), x, y); } /****** // possible data for imaging // hexadecimal digits protected final static char hd[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; // number of chars in a full row of pixel data protected final static int charsPerRow = 12*6; *******/ public boolean doImage(Image img, int x, int y, int width, int height, ImageObserver observer, Color bgcolor) { /****** // This class fetches the pixels in its constructor. PixelConsumer pc = new PixelConsumer(img); y = transformY(y); os.println("gsave"); os.println("% build a temporary dictionary"); os.println("20 dict begin"); emitColorImageProlog(pc.xdim); os.println("% lower left corner"); os.print(x); os.print(" "); os.print(y); os.println(" translate"); // compute image size. First of all, if width or height is 0, image is 1:1. if (height == 0 || width == 0) { height = pc.ydim; width = pc.xdim; } os.println("% size of image"); os.print(width); os.print(" "); os.print(height); os.println(" scale"); os.print(pc.xdim); os.print(" "); os.print(pc.ydim); os.println(" 8"); os.print("["); os.print(pc.xdim); os.print(" 0 0 -"); os.print(pc.ydim); os.print(" 0 "); os.print(0); os.println("]"); os.println("{currentfile pix readhexstring pop}"); os.println("false 3 colorimage"); os.println(""); int offset, sleepyet=0;; // array to hold a line of pixel data char[] sb = new char[charsPerRow + 1]; for (int i=0; i<pc.ydim; i++) { offset = 0; ++sleepyet; if (bgcolor == null) { // real color image. We're deliberately duplicating code here // in the interest of speed - we don't want to check bgcolor // on every iteration. for (int j=0; j<pc.xdim; j++) { int n = pc.pix[j][i]; // put hex chars into string // flip red for blue, to make postscript happy. sb[offset++] = hd[(n & 0xF0) >> 4]; sb[offset++] = hd[(n & 0xF) ]; sb[offset++] = hd[(n & 0xF000) >> 12]; sb[offset++] = hd[(n & 0xF00) >> 8]; sb[offset++] = hd[(n & 0xF00000) >> 20]; sb[offset++] = hd[(n & 0xF0000) >> 16]; if (offset >= charsPerRow) { String s = String.copyValueOf(sb, 0, offset); os.println(s); if (sleepyet > 5) { try { // let the screen update occasionally! Thread.sleep(15); } catch (java.lang.InterruptedException ex) { // yeah, so? } sleepyet = 0; } offset = 0; } } } else { os.println("%FalseColor"); // was System.out.println // false color image. for (int j=0; j<pc.xdim; j++) { int bg = bgcolor.getGreen() << 16 + bgcolor.getBlue() << 8 + bgcolor.getRed(); int fg = clr.getGreen() << 16 + clr.getBlue() << 8 + clr.getRed(); int n = (pc.pix[j][i] == 1 ? fg : bg); // put hex chars into string sb[offset++] = hd[(n & 0xF0) ]; sb[offset++] = hd[(n & 0xF) ]; sb[offset++] = hd[(n & 0xF000) ]; sb[offset++] = hd[(n & 0xF00) ]; sb[offset++] = hd[(n & 0xF00000)]; sb[offset++] = hd[(n & 0xF0000) ]; if (offset >= charsPerRow) { String s = String.copyValueOf(sb, 0, offset); os.println(s); if (sleepyet > 5) { try { // let the screen update occasionally! Thread.sleep(15); } catch (java.lang.InterruptedException ex) { // yeah, so? } sleepyet = 0; } offset = 0; } } } // print partial rows if (offset != 0) { String s = String.copyValueOf(sb, 0, offset); os.println(s); } } os.println(""); os.println("end"); os.println("grestore"); ************/ return true; } /** * Draws the specified image at the specified coordinate (x, y). If the image is * incomplete the image observer will be notified later. * @param img the specified image to be drawn * @param x the x coordinate * @param y the y coordinate * @param observer notifies if the image is complete or not * @see Image * @see ImageObserver */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return doImage(img, x, y, 0, 0, observer, null); } /** * Draws the specified image inside the specified rectangle. The image is * scaled if necessary. If the image is incomplete the image observer will be * notified later. * @param img the specified image to be drawn * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param observer notifies if the image is complete or not * @see Image * @see ImageObserver */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { return doImage(img, x, y, width, height, observer, null); } /** * Draws the specified image at the specified coordinate (x, y). If the image is * incomplete the image observer will be notified later. * @param img the specified image to be drawn * @param x the x coordinate * @param y the y coordinate * @param bgcolor the background color * @param observer notifies if the image is complete or not * @see Image * @see ImageObserver */ public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { return doImage(img, x, y, 0, 0, observer, bgcolor); } /** * Draws the specified image inside the specified rectangle. The image is * scaled if necessary. If the image is incomplete the image observer will be * notified later. * @param img the specified image to be drawn * @param x the x coordinate * @param y the y coordinate * @param width the width of the rectangle * @param height the height of the rectangle * @param bgcolor the background color * @param observer notifies if the image is complete or not * @see Image * @see ImageObserver * */ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { return doImage(img, x, y, width, height, observer, bgcolor); } /** * Disposes of this ip.graphics context. The Graphics context cannot be used after * being disposed of. * @see #finalize */ public void dispose() { endPicGroup(); emitop(QD.oopEndPic); try { os.flush(); } catch (IOException ex) { trouble = true; } } /** * Disposes of this ip.graphics context once it is no longer referenced. * @see #dispose */ public void finalize() { super.finalize(); dispose(); } /** * Returns a String object representing this Graphic's value. */ public String toString() { return getClass().getName() + "[font=" + getFont() + ",color=" + getColor() + "]"; } public boolean checkError() { return trouble; } }