0001: /*
0002: * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.print;
0027:
0028: import java.lang.ref.SoftReference;
0029: import java.util.Hashtable;
0030: import sun.font.CharToGlyphMapper;
0031: import sun.font.CompositeFont;
0032: import sun.font.Font2D;
0033: import sun.font.Font2DHandle;
0034: import sun.font.FontManager;
0035:
0036: import java.awt.Color;
0037: import java.awt.Font;
0038: import java.awt.Graphics2D;
0039: import java.awt.Image;
0040: import java.awt.Paint;
0041: import java.awt.Polygon;
0042: import java.awt.Shape;
0043:
0044: import java.text.AttributedCharacterIterator;
0045:
0046: import java.awt.font.FontRenderContext;
0047: import java.awt.font.GlyphVector;
0048: import java.awt.font.TextAttribute;
0049: import java.awt.font.TextLayout;
0050:
0051: import java.awt.geom.AffineTransform;
0052: import java.awt.geom.Arc2D;
0053: import java.awt.geom.Ellipse2D;
0054: import java.awt.geom.Line2D;
0055: import java.awt.geom.Point2D;
0056: import java.awt.geom.Rectangle2D;
0057: import java.awt.geom.RoundRectangle2D;
0058: import java.awt.geom.PathIterator;
0059:
0060: import java.awt.image.BufferedImage;
0061: import java.awt.image.BufferedImageOp;
0062: import java.awt.image.ColorModel;
0063: import java.awt.image.DataBuffer;
0064: import java.awt.image.DataBufferInt;
0065: import java.awt.image.ImageObserver;
0066: import java.awt.image.IndexColorModel;
0067: import java.awt.image.Raster;
0068: import java.awt.image.RenderedImage;
0069: import java.awt.image.SampleModel;
0070: import java.awt.image.SinglePixelPackedSampleModel;
0071: import java.awt.image.VolatileImage;
0072: import sun.awt.image.ByteComponentRaster;
0073: import sun.awt.image.ToolkitImage;
0074: import sun.awt.image.SunWritableRaster;
0075:
0076: import java.awt.print.PageFormat;
0077: import java.awt.print.Printable;
0078: import java.awt.print.PrinterException;
0079: import java.awt.print.PrinterGraphics;
0080: import java.awt.print.PrinterJob;
0081:
0082: import java.util.Map;
0083:
0084: public abstract class PathGraphics extends ProxyGraphics2D {
0085:
0086: private Printable mPainter;
0087: private PageFormat mPageFormat;
0088: private int mPageIndex;
0089: private boolean mCanRedraw;
0090: protected boolean printingGlyphVector;
0091:
0092: protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,
0093: Printable painter, PageFormat pageFormat, int pageIndex,
0094: boolean canRedraw) {
0095: super (graphics, printerJob);
0096:
0097: mPainter = painter;
0098: mPageFormat = pageFormat;
0099: mPageIndex = pageIndex;
0100: mCanRedraw = canRedraw;
0101: }
0102:
0103: /**
0104: * Return the Printable instance responsible for drawing
0105: * into this Graphics.
0106: */
0107: protected Printable getPrintable() {
0108: return mPainter;
0109: }
0110:
0111: /**
0112: * Return the PageFormat associated with this page of
0113: * Graphics.
0114: */
0115: protected PageFormat getPageFormat() {
0116: return mPageFormat;
0117: }
0118:
0119: /**
0120: * Return the page index associated with this Graphics.
0121: */
0122: protected int getPageIndex() {
0123: return mPageIndex;
0124: }
0125:
0126: /**
0127: * Return true if we are allowed to ask the application
0128: * to redraw portions of the page. In general, with the
0129: * PrinterJob API, the application can be asked to do a
0130: * redraw. When PrinterJob is emulating PrintJob then we
0131: * can not.
0132: */
0133: public boolean canDoRedraws() {
0134: return mCanRedraw;
0135: }
0136:
0137: /**
0138: * Redraw a rectanglular area using a proxy graphics
0139: */
0140: public abstract void redrawRegion(Rectangle2D region,
0141: double scaleX, double scaleY, Shape clip,
0142: AffineTransform devTransform)
0143:
0144: throws PrinterException;
0145:
0146: /**
0147: * Draws a line, using the current color, between the points
0148: * <code>(x1, y1)</code> and <code>(x2, y2)</code>
0149: * in this graphics context's coordinate system.
0150: * @param x1 the first point's <i>x</i> coordinate.
0151: * @param y1 the first point's <i>y</i> coordinate.
0152: * @param x2 the second point's <i>x</i> coordinate.
0153: * @param y2 the second point's <i>y</i> coordinate.
0154: */
0155: public void drawLine(int x1, int y1, int x2, int y2) {
0156:
0157: Paint paint = getPaint();
0158:
0159: try {
0160: AffineTransform deviceTransform = getTransform();
0161: if (getClip() != null) {
0162: deviceClip(getClip().getPathIterator(deviceTransform));
0163: }
0164:
0165: deviceDrawLine(x1, y1, x2, y2, (Color) paint);
0166:
0167: } catch (ClassCastException e) {
0168: throw new IllegalArgumentException(
0169: "Expected a Color instance");
0170: }
0171: }
0172:
0173: /**
0174: * Draws the outline of the specified rectangle.
0175: * The left and right edges of the rectangle are at
0176: * <code>x</code> and <code>x + width</code>.
0177: * The top and bottom edges are at
0178: * <code>y</code> and <code>y + height</code>.
0179: * The rectangle is drawn using the graphics context's current color.
0180: * @param x the <i>x</i> coordinate
0181: * of the rectangle to be drawn.
0182: * @param y the <i>y</i> coordinate
0183: * of the rectangle to be drawn.
0184: * @param width the width of the rectangle to be drawn.
0185: * @param height the height of the rectangle to be drawn.
0186: * @see java.awt.Graphics#fillRect
0187: * @see java.awt.Graphics#clearRect
0188: */
0189: public void drawRect(int x, int y, int width, int height) {
0190:
0191: Paint paint = getPaint();
0192:
0193: try {
0194: AffineTransform deviceTransform = getTransform();
0195: if (getClip() != null) {
0196: deviceClip(getClip().getPathIterator(deviceTransform));
0197: }
0198:
0199: deviceFrameRect(x, y, width, height, (Color) paint);
0200:
0201: } catch (ClassCastException e) {
0202: throw new IllegalArgumentException(
0203: "Expected a Color instance");
0204: }
0205:
0206: }
0207:
0208: /**
0209: * Fills the specified rectangle.
0210: * The left and right edges of the rectangle are at
0211: * <code>x</code> and <code>x + width - 1</code>.
0212: * The top and bottom edges are at
0213: * <code>y</code> and <code>y + height - 1</code>.
0214: * The resulting rectangle covers an area
0215: * <code>width</code> pixels wide by
0216: * <code>height</code> pixels tall.
0217: * The rectangle is filled using the graphics context's current color.
0218: * @param x the <i>x</i> coordinate
0219: * of the rectangle to be filled.
0220: * @param y the <i>y</i> coordinate
0221: * of the rectangle to be filled.
0222: * @param width the width of the rectangle to be filled.
0223: * @param height the height of the rectangle to be filled.
0224: * @see java.awt.Graphics#clearRect
0225: * @see java.awt.Graphics#drawRect
0226: */
0227: public void fillRect(int x, int y, int width, int height) {
0228:
0229: Paint paint = getPaint();
0230:
0231: try {
0232: AffineTransform deviceTransform = getTransform();
0233: if (getClip() != null) {
0234: deviceClip(getClip().getPathIterator(deviceTransform));
0235: }
0236:
0237: deviceFillRect(x, y, width, height, (Color) paint);
0238:
0239: } catch (ClassCastException e) {
0240: throw new IllegalArgumentException(
0241: "Expected a Color instance");
0242: }
0243: }
0244:
0245: /**
0246: * Clears the specified rectangle by filling it with the background
0247: * color of the current drawing surface. This operation does not
0248: * use the current paint mode.
0249: * <p>
0250: * Beginning with Java 1.1, the background color
0251: * of offscreen images may be system dependent. Applications should
0252: * use <code>setColor</code> followed by <code>fillRect</code> to
0253: * ensure that an offscreen image is cleared to a specific color.
0254: * @param x the <i>x</i> coordinate of the rectangle to clear.
0255: * @param y the <i>y</i> coordinate of the rectangle to clear.
0256: * @param width the width of the rectangle to clear.
0257: * @param height the height of the rectangle to clear.
0258: * @see java.awt.Graphics#fillRect(int, int, int, int)
0259: * @see java.awt.Graphics#drawRect
0260: * @see java.awt.Graphics#setColor(java.awt.Color)
0261: * @see java.awt.Graphics#setPaintMode
0262: * @see java.awt.Graphics#setXORMode(java.awt.Color)
0263: */
0264: public void clearRect(int x, int y, int width, int height) {
0265:
0266: fill(new Rectangle2D.Float(x, y, width, height),
0267: getBackground());
0268: }
0269:
0270: /**
0271: * Draws an outlined round-cornered rectangle using this graphics
0272: * context's current color. The left and right edges of the rectangle
0273: * are at <code>x</code> and <code>x + width</code>,
0274: * respectively. The top and bottom edges of the rectangle are at
0275: * <code>y</code> and <code>y + height</code>.
0276: * @param x the <i>x</i> coordinate of the rectangle to be drawn.
0277: * @param y the <i>y</i> coordinate of the rectangle to be drawn.
0278: * @param width the width of the rectangle to be drawn.
0279: * @param height the height of the rectangle to be drawn.
0280: * @param arcWidth the horizontal diameter of the arc
0281: * at the four corners.
0282: * @param arcHeight the vertical diameter of the arc
0283: * at the four corners.
0284: * @see java.awt.Graphics#fillRoundRect
0285: */
0286: public void drawRoundRect(int x, int y, int width, int height,
0287: int arcWidth, int arcHeight) {
0288:
0289: draw(new RoundRectangle2D.Float(x, y, width, height, arcWidth,
0290: arcHeight));
0291: }
0292:
0293: /**
0294: * Fills the specified rounded corner rectangle with the current color.
0295: * The left and right edges of the rectangle
0296: * are at <code>x</code> and <code>x + width - 1</code>,
0297: * respectively. The top and bottom edges of the rectangle are at
0298: * <code>y</code> and <code>y + height - 1</code>.
0299: * @param x the <i>x</i> coordinate of the rectangle to be filled.
0300: * @param y the <i>y</i> coordinate of the rectangle to be filled.
0301: * @param width the width of the rectangle to be filled.
0302: * @param height the height of the rectangle to be filled.
0303: * @param arcWidth the horizontal diameter
0304: * of the arc at the four corners.
0305: * @param arcHeight the vertical diameter
0306: * of the arc at the four corners.
0307: * @see java.awt.Graphics#drawRoundRect
0308: */
0309: public void fillRoundRect(int x, int y, int width, int height,
0310: int arcWidth, int arcHeight) {
0311:
0312: fill(new RoundRectangle2D.Float(x, y, width, height, arcWidth,
0313: arcHeight));
0314: }
0315:
0316: /**
0317: * Draws the outline of an oval.
0318: * The result is a circle or ellipse that fits within the
0319: * rectangle specified by the <code>x</code>, <code>y</code>,
0320: * <code>width</code>, and <code>height</code> arguments.
0321: * <p>
0322: * The oval covers an area that is
0323: * <code>width + 1</code> pixels wide
0324: * and <code>height + 1</code> pixels tall.
0325: * @param x the <i>x</i> coordinate of the upper left
0326: * corner of the oval to be drawn.
0327: * @param y the <i>y</i> coordinate of the upper left
0328: * corner of the oval to be drawn.
0329: * @param width the width of the oval to be drawn.
0330: * @param height the height of the oval to be drawn.
0331: * @see java.awt.Graphics#fillOval
0332: * @since JDK1.0
0333: */
0334: public void drawOval(int x, int y, int width, int height) {
0335: draw(new Ellipse2D.Float(x, y, width, height));
0336: }
0337:
0338: /**
0339: * Fills an oval bounded by the specified rectangle with the
0340: * current color.
0341: * @param x the <i>x</i> coordinate of the upper left corner
0342: * of the oval to be filled.
0343: * @param y the <i>y</i> coordinate of the upper left corner
0344: * of the oval to be filled.
0345: * @param width the width of the oval to be filled.
0346: * @param height the height of the oval to be filled.
0347: * @see java.awt.Graphics#drawOval
0348: */
0349: public void fillOval(int x, int y, int width, int height) {
0350:
0351: fill(new Ellipse2D.Float(x, y, width, height));
0352: }
0353:
0354: /**
0355: * Draws the outline of a circular or elliptical arc
0356: * covering the specified rectangle.
0357: * <p>
0358: * The resulting arc begins at <code>startAngle</code> and extends
0359: * for <code>arcAngle</code> degrees, using the current color.
0360: * Angles are interpreted such that 0 degrees
0361: * is at the 3 o'clock position.
0362: * A positive value indicates a counter-clockwise rotation
0363: * while a negative value indicates a clockwise rotation.
0364: * <p>
0365: * The center of the arc is the center of the rectangle whose origin
0366: * is (<i>x</i>, <i>y</i>) and whose size is specified by the
0367: * <code>width</code> and <code>height</code> arguments.
0368: * <p>
0369: * The resulting arc covers an area
0370: * <code>width + 1</code> pixels wide
0371: * by <code>height + 1</code> pixels tall.
0372: * <p>
0373: * The angles are specified relative to the non-square extents of
0374: * the bounding rectangle such that 45 degrees always falls on the
0375: * line from the center of the ellipse to the upper right corner of
0376: * the bounding rectangle. As a result, if the bounding rectangle is
0377: * noticeably longer in one axis than the other, the angles to the
0378: * start and end of the arc segment will be skewed farther along the
0379: * longer axis of the bounds.
0380: * @param x the <i>x</i> coordinate of the
0381: * upper-left corner of the arc to be drawn.
0382: * @param y the <i>y</i> coordinate of the
0383: * upper-left corner of the arc to be drawn.
0384: * @param width the width of the arc to be drawn.
0385: * @param height the height of the arc to be drawn.
0386: * @param startAngle the beginning angle.
0387: * @param arcAngle the angular extent of the arc,
0388: * relative to the start angle.
0389: * @see java.awt.Graphics#fillArc
0390: */
0391: public void drawArc(int x, int y, int width, int height,
0392: int startAngle, int arcAngle) {
0393: draw(new Arc2D.Float(x, y, width, height, startAngle, arcAngle,
0394: Arc2D.OPEN));
0395: }
0396:
0397: /**
0398: * Fills a circular or elliptical arc covering the specified rectangle.
0399: * <p>
0400: * The resulting arc begins at <code>startAngle</code> and extends
0401: * for <code>arcAngle</code> degrees.
0402: * Angles are interpreted such that 0 degrees
0403: * is at the 3 o'clock position.
0404: * A positive value indicates a counter-clockwise rotation
0405: * while a negative value indicates a clockwise rotation.
0406: * <p>
0407: * The center of the arc is the center of the rectangle whose origin
0408: * is (<i>x</i>, <i>y</i>) and whose size is specified by the
0409: * <code>width</code> and <code>height</code> arguments.
0410: * <p>
0411: * The resulting arc covers an area
0412: * <code>width + 1</code> pixels wide
0413: * by <code>height + 1</code> pixels tall.
0414: * <p>
0415: * The angles are specified relative to the non-square extents of
0416: * the bounding rectangle such that 45 degrees always falls on the
0417: * line from the center of the ellipse to the upper right corner of
0418: * the bounding rectangle. As a result, if the bounding rectangle is
0419: * noticeably longer in one axis than the other, the angles to the
0420: * start and end of the arc segment will be skewed farther along the
0421: * longer axis of the bounds.
0422: * @param x the <i>x</i> coordinate of the
0423: * upper-left corner of the arc to be filled.
0424: * @param y the <i>y</i> coordinate of the
0425: * upper-left corner of the arc to be filled.
0426: * @param width the width of the arc to be filled.
0427: * @param height the height of the arc to be filled.
0428: * @param startAngle the beginning angle.
0429: * @param arcAngle the angular extent of the arc,
0430: * relative to the start angle.
0431: * @see java.awt.Graphics#drawArc
0432: */
0433: public void fillArc(int x, int y, int width, int height,
0434: int startAngle, int arcAngle) {
0435:
0436: fill(new Arc2D.Float(x, y, width, height, startAngle, arcAngle,
0437: Arc2D.PIE));
0438: }
0439:
0440: /**
0441: * Draws a sequence of connected lines defined by
0442: * arrays of <i>x</i> and <i>y</i> coordinates.
0443: * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.
0444: * The figure is not closed if the first point
0445: * differs from the last point.
0446: * @param xPoints an array of <i>x</i> points
0447: * @param yPoints an array of <i>y</i> points
0448: * @param nPoints the total number of points
0449: * @see java.awt.Graphics#drawPolygon(int[], int[], int)
0450: * @since JDK1.1
0451: */
0452: public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
0453: float fromX;
0454: float fromY;
0455: float toX;
0456: float toY;
0457:
0458: if (nPoints > 0) {
0459: fromX = xPoints[0];
0460: fromY = yPoints[0];
0461: for (int i = 1; i < nPoints; i++) {
0462: toX = xPoints[i];
0463: toY = yPoints[i];
0464: draw(new Line2D.Float(fromX, fromY, toX, toY));
0465: fromX = toX;
0466: fromY = toY;
0467: }
0468: }
0469:
0470: }
0471:
0472: /**
0473: * Draws a closed polygon defined by
0474: * arrays of <i>x</i> and <i>y</i> coordinates.
0475: * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.
0476: * <p>
0477: * This method draws the polygon defined by <code>nPoint</code> line
0478: * segments, where the first <code>nPoint - 1</code>
0479: * line segments are line segments from
0480: * <code>(xPoints[i - 1], yPoints[i - 1])</code>
0481: * to <code>(xPoints[i], yPoints[i])</code>, for
0482: * 1 ≤ <i>i</i> ≤ <code>nPoints</code>.
0483: * The figure is automatically closed by drawing a line connecting
0484: * the final point to the first point, if those points are different.
0485: * @param xPoints a an array of <code>x</code> coordinates.
0486: * @param yPoints a an array of <code>y</code> coordinates.
0487: * @param nPoints a the total number of points.
0488: * @see java.awt.Graphics#fillPolygon
0489: * @see java.awt.Graphics#drawPolyline
0490: */
0491: public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
0492:
0493: draw(new Polygon(xPoints, yPoints, nPoints));
0494: }
0495:
0496: /**
0497: * Draws the outline of a polygon defined by the specified
0498: * <code>Polygon</code> object.
0499: * @param p the polygon to draw.
0500: * @see java.awt.Graphics#fillPolygon
0501: * @see java.awt.Graphics#drawPolyline
0502: */
0503: public void drawPolygon(Polygon p) {
0504: draw(p);
0505: }
0506:
0507: /**
0508: * Fills a closed polygon defined by
0509: * arrays of <i>x</i> and <i>y</i> coordinates.
0510: * <p>
0511: * This method draws the polygon defined by <code>nPoint</code> line
0512: * segments, where the first <code>nPoint - 1</code>
0513: * line segments are line segments from
0514: * <code>(xPoints[i - 1], yPoints[i - 1])</code>
0515: * to <code>(xPoints[i], yPoints[i])</code>, for
0516: * 1 ≤ <i>i</i> ≤ <code>nPoints</code>.
0517: * The figure is automatically closed by drawing a line connecting
0518: * the final point to the first point, if those points are different.
0519: * <p>
0520: * The area inside the polygon is defined using an
0521: * even-odd fill rule, also known as the alternating rule.
0522: * @param xPoints a an array of <code>x</code> coordinates.
0523: * @param yPoints a an array of <code>y</code> coordinates.
0524: * @param nPoints a the total number of points.
0525: * @see java.awt.Graphics#drawPolygon(int[], int[], int)
0526: */
0527: public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
0528:
0529: fill(new Polygon(xPoints, yPoints, nPoints));
0530: }
0531:
0532: /**
0533: * Fills the polygon defined by the specified Polygon object with
0534: * the graphics context's current color.
0535: * <p>
0536: * The area inside the polygon is defined using an
0537: * even-odd fill rule, also known as the alternating rule.
0538: * @param p the polygon to fill.
0539: * @see java.awt.Graphics#drawPolygon(int[], int[], int)
0540: */
0541: public void fillPolygon(Polygon p) {
0542:
0543: fill(p);
0544: }
0545:
0546: /**
0547: * Draws the text given by the specified string, using this
0548: * graphics context's current font and color. The baseline of the
0549: * first character is at position (<i>x</i>, <i>y</i>) in this
0550: * graphics context's coordinate system.
0551: * @param str the string to be drawn.
0552: * @param x the <i>x</i> coordinate.
0553: * @param y the <i>y</i> coordinate.
0554: * @see java.awt.Graphics#drawBytes
0555: * @see java.awt.Graphics#drawChars
0556: * @since JDK1.0
0557: */
0558: public void drawString(String str, int x, int y) {
0559: drawString(str, (float) x, (float) y);
0560: }
0561:
0562: public void drawString(String str, float x, float y) {
0563: if (str.length() == 0) {
0564: return;
0565: }
0566: TextLayout layout = new TextLayout(str, getFont(),
0567: getFontRenderContext());
0568: layout.draw(this , x, y);
0569: }
0570:
0571: protected void drawString(String str, float x, float y, Font font,
0572: FontRenderContext frc, float w) {
0573: TextLayout layout = new TextLayout(str, font, frc);
0574: Shape textShape = layout.getOutline(AffineTransform
0575: .getTranslateInstance(x, y));
0576: fill(textShape);
0577: }
0578:
0579: /**
0580: * Draws the text given by the specified iterator, using this
0581: * graphics context's current color. The iterator has to specify a font
0582: * for each character. The baseline of the
0583: * first character is at position (<i>x</i>, <i>y</i>) in this
0584: * graphics context's coordinate system.
0585: * @param iterator the iterator whose text is to be drawn
0586: * @param x the <i>x</i> coordinate.
0587: * @param y the <i>y</i> coordinate.
0588: * @see java.awt.Graphics#drawBytes
0589: * @see java.awt.Graphics#drawChars
0590: */
0591: public void drawString(AttributedCharacterIterator iterator, int x,
0592: int y) {
0593: drawString(iterator, (float) x, (float) y);
0594: }
0595:
0596: public void drawString(AttributedCharacterIterator iterator,
0597: float x, float y) {
0598: if (iterator == null) {
0599: throw new NullPointerException(
0600: "attributedcharacteriterator is null");
0601: }
0602: TextLayout layout = new TextLayout(iterator,
0603: getFontRenderContext());
0604: layout.draw(this , x, y);
0605: }
0606:
0607: /**
0608: * Draws a GlyphVector.
0609: * The rendering attributes applied include the clip, transform,
0610: * paint or color, and composite attributes. The GlyphVector specifies
0611: * individual glyphs from a Font.
0612: * @param g The GlyphVector to be drawn.
0613: * @param x,y The coordinates where the glyphs should be drawn.
0614: * @see #setPaint
0615: * @see java.awt.Graphics#setColor
0616: * @see #transform
0617: * @see #setTransform
0618: * @see #setComposite
0619: * @see #clip
0620: * @see #setClip
0621: */
0622: public void drawGlyphVector(GlyphVector g, float x, float y) {
0623:
0624: /* We should not reach here if printingGlyphVector is already true.
0625: * Add an assert so this can be tested if need be.
0626: * But also ensure that we do at least render properly by filling
0627: * the outline.
0628: */
0629: if (printingGlyphVector) {
0630: assert !printingGlyphVector; // ie false.
0631: fill(g.getOutline(x, y));
0632: return;
0633: }
0634:
0635: try {
0636: printingGlyphVector = true;
0637: if (RasterPrinterJob.shapeTextProp
0638: || !printedSimpleGlyphVector(g, x, y)) {
0639: fill(g.getOutline(x, y));
0640: }
0641: } finally {
0642: printingGlyphVector = false;
0643: }
0644: }
0645:
0646: protected static SoftReference<Hashtable<Font2DHandle, Object>> fontMapRef = new SoftReference<Hashtable<Font2DHandle, Object>>(
0647: null);
0648:
0649: protected int platformFontCount(Font font, String str) {
0650: return 0;
0651: }
0652:
0653: /**
0654: * Default implementation returns false.
0655: * Callers of this method must always be prepared for this,
0656: * and delegate to outlines or some other solution.
0657: */
0658: protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
0659: return false;
0660: }
0661:
0662: /* GlyphVectors are usually encountered because TextLayout is in use.
0663: * Some times TextLayout is needed to handle complex text or some
0664: * rendering attributes trigger it.
0665: * We try to print GlyphVectors by reconstituting into a String,
0666: * as that is most recoverable for applications that export to formats
0667: * such as Postscript or PDF. In some cases (eg where its not complex
0668: * text and its just that positions aren't what we'd expect) we print
0669: * one character at a time. positioning individually.
0670: * Failing that, if we can directly send glyph codes to the printer
0671: * then we do that (printGlyphVector).
0672: * As a last resort we return false and let the caller print as filled
0673: * shapes.
0674: */
0675: boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {
0676:
0677: int flags = g.getLayoutFlags();
0678:
0679: /* We can't handle RTL, re-ordering, complex glyphs etc by
0680: * reconstituting glyphs into a String. So if any flags besides
0681: * position adjustments are set, see if we can directly
0682: * print the GlyphVector as glyph codes, using the positions
0683: * layout has assigned. If that fails return false;
0684: */
0685: if (flags != 0
0686: && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {
0687: return printGlyphVector(g, x, y);
0688: }
0689:
0690: Font font = g.getFont();
0691: Font2D font2D = FontManager.getFont2D(font);
0692: if (font2D.handle.font2D != font2D) {
0693: /* suspicious, may be a bad font. lets bail */
0694: return false;
0695: }
0696: Hashtable<Font2DHandle, Object> fontMap;
0697: synchronized (PathGraphics.class) {
0698: fontMap = fontMapRef.get();
0699: if (fontMap == null) {
0700: fontMap = new Hashtable<Font2DHandle, Object>();
0701: fontMapRef = new SoftReference<Hashtable<Font2DHandle, Object>>(
0702: fontMap);
0703: }
0704: }
0705:
0706: int numGlyphs = g.getNumGlyphs();
0707: int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);
0708:
0709: char[] glyphToCharMap = null;
0710: char[][] mapArray = null;
0711: CompositeFont cf = null;
0712:
0713: /* Build the needed maps for this font in a synchronized block */
0714: synchronized (fontMap) {
0715: if (font2D instanceof CompositeFont) {
0716: cf = (CompositeFont) font2D;
0717: int numSlots = cf.getNumSlots();
0718: mapArray = (char[][]) fontMap.get(font2D.handle);
0719: if (mapArray == null) {
0720: mapArray = new char[numSlots][];
0721: fontMap.put(font2D.handle, mapArray);
0722: }
0723: for (int i = 0; i < numGlyphs; i++) {
0724: int slot = glyphCodes[i] >>> 24;
0725: if (slot >= numSlots) { /* shouldn't happen */
0726: return false;
0727: }
0728: if (mapArray[slot] == null) {
0729: Font2D slotFont = cf.getSlotFont(slot);
0730: char[] map = (char[]) fontMap
0731: .get(slotFont.handle);
0732: if (map == null) {
0733: map = getGlyphToCharMapForFont(slotFont);
0734: }
0735: mapArray[slot] = map;
0736: }
0737: }
0738: } else {
0739: glyphToCharMap = (char[]) fontMap.get(font2D.handle);
0740: if (glyphToCharMap == null) {
0741: glyphToCharMap = getGlyphToCharMapForFont(font2D);
0742: fontMap.put(font2D.handle, glyphToCharMap);
0743: }
0744: }
0745: }
0746:
0747: char[] chars = new char[numGlyphs];
0748: if (cf != null) {
0749: for (int i = 0; i < numGlyphs; i++) {
0750: int gc = glyphCodes[i];
0751: char[] map = mapArray[gc >>> 24];
0752: gc = gc & 0xffffff;
0753: if (map == null) {
0754: return false;
0755: }
0756: /* X11 symbol & dingbats fonts used only for global metrics,
0757: * so the glyph codes we have really refer to Lucida Sans
0758: * Regular.
0759: * So its possible the glyph code may appear out of range.
0760: * Note that later on we double-check the glyph codes that
0761: * we get from re-creating the GV from the string are the
0762: * same as those we started with.
0763: *
0764: * If the glyphcode is INVISIBLE_GLYPH_ID then this may
0765: * be \t, \n or \r which are mapped to that by layout.
0766: * This is a case we can handle. It doesn't matter what
0767: * character we use (we use \n) so long as layout maps it
0768: * back to this in the verification, since the invisible
0769: * glyph isn't visible :)
0770: */
0771: char ch;
0772: if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
0773: ch = '\n';
0774: } else if (gc < 0 || gc >= map.length) {
0775: return false;
0776: } else {
0777: ch = map[gc];
0778: }
0779: if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
0780: chars[i] = ch;
0781: } else {
0782: return false;
0783: }
0784: }
0785: } else {
0786: for (int i = 0; i < numGlyphs; i++) {
0787: int gc = glyphCodes[i];
0788: char ch;
0789: if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
0790: ch = '\n';
0791: } else if (gc < 0 || gc >= glyphToCharMap.length) {
0792: return false;
0793: } else {
0794: ch = glyphToCharMap[gc];
0795: }
0796: if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
0797: chars[i] = ch;
0798: } else {
0799: return false;
0800: }
0801: }
0802: }
0803:
0804: FontRenderContext gvFrc = g.getFontRenderContext();
0805: GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);
0806: if (gv2.getNumGlyphs() != numGlyphs) {
0807: return printGlyphVector(g, x, y);
0808: }
0809: int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);
0810: /*
0811: * Needed to double-check remapping of X11 symbol & dingbats.
0812: */
0813: for (int i = 0; i < numGlyphs; i++) {
0814: if (glyphCodes[i] != glyphCodes2[i]) {
0815: return printGlyphVector(g, x, y);
0816: }
0817: }
0818:
0819: FontRenderContext g2dFrc = getFontRenderContext();
0820: boolean compatibleFRC = gvFrc.equals(g2dFrc);
0821: /* If differ only in specifying A-A or a translation, these are
0822: * also compatible FRC's, and we can do one drawString call.
0823: */
0824: if (!compatibleFRC
0825: && gvFrc.usesFractionalMetrics() == g2dFrc
0826: .usesFractionalMetrics()) {
0827: AffineTransform gvAT = gvFrc.getTransform();
0828: AffineTransform g2dAT = getTransform();
0829: double[] gvMatrix = new double[4];
0830: double[] g2dMatrix = new double[4];
0831: gvAT.getMatrix(gvMatrix);
0832: g2dAT.getMatrix(g2dMatrix);
0833: compatibleFRC = true;
0834: for (int i = 0; i < 4; i++) {
0835: if (gvMatrix[i] != g2dMatrix[i]) {
0836: compatibleFRC = false;
0837: break;
0838: }
0839: }
0840: }
0841:
0842: String str = new String(chars, 0, numGlyphs);
0843: int numFonts = platformFontCount(font, str);
0844: if (numFonts == 0) {
0845: return false;
0846: }
0847:
0848: float[] positions = g.getGlyphPositions(0, numGlyphs, null);
0849: boolean noPositionAdjustments = ((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0)
0850: || samePositions(gv2, glyphCodes2, glyphCodes,
0851: positions);
0852:
0853: /* We have to consider that the application may be directly
0854: * creating a GlyphVector, rather than one being created by
0855: * TextLayout or indirectly from drawString. In such a case, if the
0856: * font has layout attributes, the text may measure differently
0857: * when we reconstitute it into a String and ask for the length that
0858: * drawString would use. For example, KERNING will be applied in such
0859: * a case but that Font attribute is not applied when the application
0860: * directly created a GlyphVector. So in this case we need to verify
0861: * that the text measures the same in both cases - ie that the
0862: * layout attribute has no effect. If it does we can't always
0863: * use the drawString call unless we can coerce the drawString call
0864: * into measuring and displaying the string to the same length.
0865: * That is the case where there is only one font used and we can
0866: * specify the overall advance of the string. (See below).
0867: */
0868:
0869: Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);
0870: float gvAdvanceX = (float) gvAdvancePt.getX();
0871: boolean layoutAffectsAdvance = false;
0872: if (font.hasLayoutAttributes() && printingGlyphVector
0873: && noPositionAdjustments) {
0874:
0875: /* If TRACKING is in use then the glyph vector will report
0876: * position adjustments, then that ought to be sufficient to
0877: * tell us we can't just ask native to do "drawString". But layout
0878: * always sets the position adjustment flag, so we don't believe
0879: * it and verify the positions are really different than
0880: * createGlyphVector() (with no layout) would create. However
0881: * inconsistently, TRACKING is applied when creating a GlyphVector,
0882: * since it doesn't actually require "layout" (even though its
0883: * considered a layout attribute), it just requires a fractional
0884: * tweak to the[default]advances. So we need to specifically
0885: * check for tracking until such time as as we can trust
0886: * the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.
0887: */
0888: Map<TextAttribute, ?> map = font.getAttributes();
0889: Object o = map.get(TextAttribute.TRACKING);
0890: boolean tracking = o != null && (o instanceof Number)
0891: && (((Number) o).floatValue() != 0f);
0892:
0893: if (tracking) {
0894: noPositionAdjustments = false;
0895: } else {
0896: Rectangle2D bounds = font.getStringBounds(str, gvFrc);
0897: float strAdvanceX = (float) bounds.getWidth();
0898: if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {
0899: layoutAffectsAdvance = true;
0900: }
0901: }
0902: }
0903:
0904: if (compatibleFRC && noPositionAdjustments
0905: && !layoutAffectsAdvance) {
0906: drawString(str, x, y, font, gvFrc, 0f);
0907: return true;
0908: }
0909:
0910: /* If positions have not been explicitly assigned, we can
0911: * ask the string to be drawn adjusted to this width.
0912: * This call is supported only in the PS generator.
0913: * GDI has API to specify the advance for each glyph in a
0914: * string which could be used here too, but that is not yet
0915: * implemented, and we'd need to update the signature of the
0916: * drawString method to take the advances (ie relative positions)
0917: * and use that instead of the width.
0918: */
0919: if (numFonts == 1 && canDrawStringToWidth()
0920: && noPositionAdjustments) {
0921: drawString(str, x, y, font, gvFrc, gvAdvanceX);
0922: return true;
0923: }
0924:
0925: /* In some scripts chars drawn individually do not have the
0926: * same representation (glyphs) as when combined with other chars.
0927: * The logic here is erring on the side of caution, in particular
0928: * in including supplementary characters.
0929: */
0930: if (FontManager.isComplexText(chars, 0, chars.length)) {
0931: return printGlyphVector(g, x, y);
0932: }
0933:
0934: /* If we reach here we have mapped all the glyphs back
0935: * one-to-one to simple unicode chars that we know are in the font.
0936: * We can call "drawChars" on each one of them in turn, setting
0937: * the position based on the glyph positions.
0938: * There's typically overhead in this. If numGlyphs is 'large',
0939: * it may even be better to try printGlyphVector() in this case.
0940: * This may be less recoverable for apps, but sophisticated apps
0941: * should be able to recover the text from simple glyph vectors
0942: * and we can avoid penalising the more common case - although
0943: * this is already a minority case.
0944: */
0945: if (numGlyphs > 10 && printGlyphVector(g, x, y)) {
0946: return true;
0947: }
0948:
0949: for (int i = 0; i < numGlyphs; i++) {
0950: String s = new String(chars, i, 1);
0951: drawString(s, x + positions[i * 2], y
0952: + positions[i * 2 + 1], font, gvFrc, 0f);
0953: }
0954: return true;
0955: }
0956:
0957: /* The same codes must be in the same positions for this to return true.
0958: * This would look cleaner if it took the original GV as a parameter but
0959: * we already have the codes and will need to get the positions array
0960: * too in most cases anyway. So its cheaper to pass them in.
0961: * This call wouldn't be necessary if layout didn't always set the
0962: * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used
0963: * and there was no re-ordering (this should be fixed some day).
0964: */
0965: private boolean samePositions(GlyphVector gv, int[] gvcodes,
0966: int[] origCodes, float[] origPositions) {
0967:
0968: int numGlyphs = gv.getNumGlyphs();
0969: float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null);
0970:
0971: /* this shouldn't happen here, but just in case */
0972: if (numGlyphs != gvcodes.length || /* real paranoia here */
0973: origCodes.length != gvcodes.length
0974: || origPositions.length != gvpos.length) {
0975: return false;
0976: }
0977:
0978: for (int i = 0; i < numGlyphs; i++) {
0979: if (gvcodes[i] != origCodes[i]
0980: || gvpos[i] != origPositions[i]) {
0981: return false;
0982: }
0983: }
0984: return true;
0985: }
0986:
0987: protected boolean canDrawStringToWidth() {
0988: return false;
0989: }
0990:
0991: /* return an array which can map glyphs back to char codes.
0992: * Glyphs which aren't mapped from a simple unicode code point
0993: * will have no mapping in this array, and will be assumed to be
0994: * because of some substitution that we can't handle.
0995: */
0996: private static char[] getGlyphToCharMapForFont(Font2D font2D) {
0997: /* NB Composites report the number of glyphs in slot 0.
0998: * So if a string uses a char from a later slot, or a fallback slot,
0999: * it will not be able to use this faster path.
1000: */
1001: int numGlyphs = font2D.getNumGlyphs();
1002: int missingGlyph = font2D.getMissingGlyphCode();
1003: char[] glyphToCharMap = new char[numGlyphs];
1004: int glyph;
1005:
1006: for (int i = 0; i < numGlyphs; i++) {
1007: glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1008: }
1009:
1010: /* Consider refining the ranges to try to map by asking the font
1011: * what ranges it supports.
1012: * Since a glyph may be mapped by multiple code points, and this
1013: * code can't handle that, we always prefer the earlier code point.
1014: */
1015: for (char c = 0; c < 0xFFFF; c++) {
1016: if (c >= CharToGlyphMapper.HI_SURROGATE_START
1017: && c <= CharToGlyphMapper.LO_SURROGATE_END) {
1018: continue;
1019: }
1020: glyph = font2D.charToGlyph(c);
1021: if (glyph != missingGlyph
1022: && glyph < numGlyphs
1023: && (glyphToCharMap[glyph] == CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {
1024: glyphToCharMap[glyph] = c;
1025: }
1026: }
1027: return glyphToCharMap;
1028: }
1029:
1030: /**
1031: * Strokes the outline of a Shape using the settings of the current
1032: * graphics state. The rendering attributes applied include the
1033: * clip, transform, paint or color, composite and stroke attributes.
1034: * @param s The shape to be drawn.
1035: * @see #setStroke
1036: * @see #setPaint
1037: * @see java.awt.Graphics#setColor
1038: * @see #transform
1039: * @see #setTransform
1040: * @see #clip
1041: * @see #setClip
1042: * @see #setComposite
1043: */
1044: public void draw(Shape s) {
1045:
1046: fill(getStroke().createStrokedShape(s));
1047: }
1048:
1049: /**
1050: * Fills the interior of a Shape using the settings of the current
1051: * graphics state. The rendering attributes applied include the
1052: * clip, transform, paint or color, and composite.
1053: * @see #setPaint
1054: * @see java.awt.Graphics#setColor
1055: * @see #transform
1056: * @see #setTransform
1057: * @see #setComposite
1058: * @see #clip
1059: * @see #setClip
1060: */
1061: public void fill(Shape s) {
1062: Paint paint = getPaint();
1063:
1064: try {
1065: fill(s, (Color) paint);
1066:
1067: /* The PathGraphics class only supports filling with
1068: * solid colors and so we do not expect the cast of Paint
1069: * to Color to fail. If it does fail then something went
1070: * wrong, like the app draw a page with a solid color but
1071: * then redrew it with a Gradient.
1072: */
1073: } catch (ClassCastException e) {
1074: throw new IllegalArgumentException(
1075: "Expected a Color instance");
1076: }
1077: }
1078:
1079: public void fill(Shape s, Color color) {
1080: AffineTransform deviceTransform = getTransform();
1081:
1082: if (getClip() != null) {
1083: deviceClip(getClip().getPathIterator(deviceTransform));
1084: }
1085: deviceFill(s.getPathIterator(deviceTransform), color);
1086: }
1087:
1088: /**
1089: * Fill the path defined by <code>pathIter</code>
1090: * with the specified color.
1091: * The path is provided in device coordinates.
1092: */
1093: protected abstract void deviceFill(PathIterator pathIter,
1094: Color color);
1095:
1096: /*
1097: * Set the clipping path to that defined by
1098: * the passed in <code>PathIterator</code>.
1099: */
1100: protected abstract void deviceClip(PathIterator pathIter);
1101:
1102: /*
1103: * Draw the outline of the rectangle without using path
1104: * if supported by platform.
1105: */
1106: protected abstract void deviceFrameRect(int x, int y, int width,
1107: int height, Color color);
1108:
1109: /*
1110: * Draw a line without using path if supported by platform.
1111: */
1112: protected abstract void deviceDrawLine(int xBegin, int yBegin,
1113: int xEnd, int yEnd, Color color);
1114:
1115: /*
1116: * Fill a rectangle using specified color.
1117: */
1118: protected abstract void deviceFillRect(int x, int y, int width,
1119: int height, Color color);
1120:
1121: /* Obtain a BI from known implementations of java.awt.Image
1122: */
1123: protected BufferedImage getBufferedImage(Image img) {
1124: if (img instanceof BufferedImage) {
1125: // Otherwise we expect a BufferedImage to behave as a standard BI
1126: return (BufferedImage) img;
1127: } else if (img instanceof ToolkitImage) {
1128: // This can be null if the image isn't loaded yet.
1129: // This is fine as in that case our caller will return
1130: // as it will only draw a fully loaded image
1131: return ((ToolkitImage) img).getBufferedImage();
1132: } else if (img instanceof VolatileImage) {
1133: // VI needs to make a new BI: this is unavoidable but
1134: // I don't expect VI's to be "huge" in any case.
1135: return ((VolatileImage) img).getSnapshot();
1136: } else {
1137: // may be null or may be some non-standard Image which
1138: // shouldn't happen as Image is implemented by the platform
1139: // not by applications
1140: // If you add a new Image implementation to the platform you
1141: // will need to support it here similarly to VI.
1142: return null;
1143: }
1144: }
1145:
1146: /**
1147: * Return true if the BufferedImage argument has non-opaque
1148: * bits in it and therefore can not be directly rendered by
1149: * GDI. Return false if the image is opaque. If this function
1150: * can not tell for sure whether the image has transparent
1151: * pixels then it assumes that it does.
1152: */
1153: protected boolean hasTransparentPixels(BufferedImage bufferedImage) {
1154: ColorModel colorModel = bufferedImage.getColorModel();
1155: boolean hasTransparency = colorModel == null ? true
1156: : colorModel.getTransparency() != ColorModel.OPAQUE;
1157:
1158: /*
1159: * For the default INT ARGB check the image to see if any pixels are
1160: * really transparent. If there are no transparent pixels then the
1161: * transparency of the color model can be ignored.
1162: * We assume that IndexColorModel images have already been
1163: * checked for transparency and will be OPAQUE unless they actually
1164: * have transparent pixels present.
1165: */
1166: if (hasTransparency && bufferedImage != null) {
1167: if (bufferedImage.getType() == BufferedImage.TYPE_INT_ARGB
1168: || bufferedImage.getType() == BufferedImage.TYPE_INT_ARGB_PRE) {
1169: DataBuffer db = bufferedImage.getRaster()
1170: .getDataBuffer();
1171: SampleModel sm = bufferedImage.getRaster()
1172: .getSampleModel();
1173: if (db instanceof DataBufferInt
1174: && sm instanceof SinglePixelPackedSampleModel) {
1175: SinglePixelPackedSampleModel psm = (SinglePixelPackedSampleModel) sm;
1176: // Stealing the data array for reading only...
1177: int[] int_data = SunWritableRaster.stealData(
1178: (DataBufferInt) db, 0);
1179: int x = bufferedImage.getMinX();
1180: int y = bufferedImage.getMinY();
1181: int w = bufferedImage.getWidth();
1182: int h = bufferedImage.getHeight();
1183: int stride = psm.getScanlineStride();
1184: boolean hastranspixel = false;
1185: for (int j = y; j < y + h; j++) {
1186: int yoff = j * stride;
1187: for (int i = x; i < x + w; i++) {
1188: if ((int_data[yoff + i] & 0xff000000) != 0xff000000) {
1189: hastranspixel = true;
1190: break;
1191: }
1192: }
1193: if (hastranspixel) {
1194: break;
1195: }
1196: }
1197: if (hastranspixel == false) {
1198: hasTransparency = false;
1199: }
1200: }
1201: }
1202: }
1203:
1204: return hasTransparency;
1205: }
1206:
1207: protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {
1208: ColorModel colorModel = bufferedImage.getColorModel();
1209: return (colorModel != null && colorModel.getTransparency() == ColorModel.BITMASK);
1210: }
1211:
1212: /* An optimisation for the special case of ICM images which have
1213: * bitmask transparency.
1214: */
1215: protected boolean drawBitmaskImage(BufferedImage bufferedImage,
1216: AffineTransform xform, Color bgcolor, int srcX, int srcY,
1217: int srcWidth, int srcHeight) {
1218:
1219: ColorModel colorModel = bufferedImage.getColorModel();
1220: IndexColorModel icm;
1221: int[] pixels;
1222:
1223: if (!(colorModel instanceof IndexColorModel)) {
1224: return false;
1225: } else {
1226: icm = (IndexColorModel) colorModel;
1227: }
1228:
1229: if (colorModel.getTransparency() != ColorModel.BITMASK) {
1230: return false;
1231: }
1232:
1233: // to be compatible with 1.1 printing which treated b/g colors
1234: // with alpha 128 as opaque
1235: if (bgcolor != null && bgcolor.getAlpha() < 128) {
1236: return false;
1237: }
1238:
1239: if ((xform.getType() & ~(AffineTransform.TYPE_UNIFORM_SCALE
1240: | AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_QUADRANT_ROTATION)) != 0) {
1241: return false;
1242: }
1243:
1244: if ((getTransform().getType() & ~(AffineTransform.TYPE_UNIFORM_SCALE
1245: | AffineTransform.TYPE_TRANSLATION | AffineTransform.TYPE_QUADRANT_ROTATION)) != 0) {
1246: return false;
1247: }
1248:
1249: BufferedImage subImage = null;
1250: Raster raster = bufferedImage.getRaster();
1251: int transpixel = icm.getTransparentPixel();
1252: byte[] alphas = new byte[icm.getMapSize()];
1253: icm.getAlphas(alphas);
1254: if (transpixel >= 0) {
1255: alphas[transpixel] = 0;
1256: }
1257:
1258: /* don't just use srcWidth & srcHeight from application - they
1259: * may exceed the extent of the image - may need to clip.
1260: * The image xform will ensure that points are still mapped properly.
1261: */
1262: int rw = raster.getWidth();
1263: int rh = raster.getHeight();
1264: if (srcX > rw || srcY > rh) {
1265: return false;
1266: }
1267: int right, bottom, wid, hgt;
1268: if (srcX + srcWidth > rw) {
1269: right = rw;
1270: wid = right - srcX;
1271: } else {
1272: right = srcX + srcWidth;
1273: wid = srcWidth;
1274: }
1275: if (srcY + srcHeight > rh) {
1276: bottom = rh;
1277: hgt = bottom - srcY;
1278: } else {
1279: bottom = srcY + srcHeight;
1280: hgt = srcHeight;
1281: }
1282: pixels = new int[wid];
1283: for (int j = srcY; j < bottom; j++) {
1284: int startx = -1;
1285: raster.getPixels(srcX, j, wid, 1, pixels);
1286: for (int i = srcX; i < right; i++) {
1287: if (alphas[pixels[i - srcX]] == 0) {
1288: if (startx >= 0) {
1289: subImage = bufferedImage.getSubimage(startx, j,
1290: i - startx, 1);
1291: xform.translate(startx, j);
1292: drawImageToPlatform(subImage, xform, bgcolor,
1293: 0, 0, i - startx, 1, true);
1294: xform.translate(-startx, -j);
1295: startx = -1;
1296: }
1297: } else if (startx < 0) {
1298: startx = i;
1299: }
1300: }
1301: if (startx >= 0) {
1302: subImage = bufferedImage.getSubimage(startx, j, right
1303: - startx, 1);
1304: xform.translate(startx, j);
1305: drawImageToPlatform(subImage, xform, bgcolor, 0, 0,
1306: right - startx, 1, true);
1307: xform.translate(-startx, -j);
1308: }
1309: }
1310: return true;
1311: }
1312:
1313: /**
1314: * The various <code>drawImage()</code> methods for
1315: * <code>PathGraphics</code> are all decomposed
1316: * into an invocation of <code>drawImageToPlatform</code>.
1317: * The portion of the passed in image defined by
1318: * <code>srcX, srcY, srcWidth, and srcHeight</code>
1319: * is transformed by the supplied AffineTransform and
1320: * drawn using PS to the printer context.
1321: *
1322: * @param img The image to be drawn.
1323: * This method does nothing if <code>img</code> is null.
1324: * @param xform Used to tranform the image before drawing.
1325: * This can be null.
1326: * @param bgcolor This color is drawn where the image has transparent
1327: * pixels. If this parameter is null then the
1328: * pixels already in the destination should show
1329: * through.
1330: * @param srcX With srcY this defines the upper-left corner
1331: * of the portion of the image to be drawn.
1332: *
1333: * @param srcY With srcX this defines the upper-left corner
1334: * of the portion of the image to be drawn.
1335: * @param srcWidth The width of the portion of the image to
1336: * be drawn.
1337: * @param srcHeight The height of the portion of the image to
1338: * be drawn.
1339: * @param handlingTransparency if being recursively called to
1340: * print opaque region of transparent image
1341: */
1342: protected abstract boolean drawImageToPlatform(Image img,
1343: AffineTransform xform, Color bgcolor, int srcX, int srcY,
1344: int srcWidth, int srcHeight, boolean handlingTransparency);
1345:
1346: /**
1347: * Draws as much of the specified image as is currently available.
1348: * The image is drawn with its top-left corner at
1349: * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
1350: * space. Transparent pixels in the image do not affect whatever
1351: * pixels are already there.
1352: * <p>
1353: * This method returns immediately in all cases, even if the
1354: * complete image has not yet been loaded, and it has not been dithered
1355: * and converted for the current output device.
1356: * <p>
1357: * If the image has not yet been completely loaded, then
1358: * <code>drawImage</code> returns <code>false</code>. As more of
1359: * the image becomes available, the process that draws the image notifies
1360: * the specified image observer.
1361: * @param img the specified image to be drawn.
1362: * @param x the <i>x</i> coordinate.
1363: * @param y the <i>y</i> coordinate.
1364: * @param observer object to be notified as more of
1365: * the image is converted.
1366: * @see java.awt.Image
1367: * @see java.awt.image.ImageObserver
1368: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1369: * @since JDK1.0
1370: */
1371: public boolean drawImage(Image img, int x, int y,
1372: ImageObserver observer) {
1373:
1374: return drawImage(img, x, y, null, observer);
1375: }
1376:
1377: /**
1378: * Draws as much of the specified image as has already been scaled
1379: * to fit inside the specified rectangle.
1380: * <p>
1381: * The image is drawn inside the specified rectangle of this
1382: * graphics context's coordinate space, and is scaled if
1383: * necessary. Transparent pixels do not affect whatever pixels
1384: * are already there.
1385: * <p>
1386: * This method returns immediately in all cases, even if the
1387: * entire image has not yet been scaled, dithered, and converted
1388: * for the current output device.
1389: * If the current output representation is not yet complete, then
1390: * <code>drawImage</code> returns <code>false</code>. As more of
1391: * the image becomes available, the process that draws the image notifies
1392: * the image observer by calling its <code>imageUpdate</code> method.
1393: * <p>
1394: * A scaled version of an image will not necessarily be
1395: * available immediately just because an unscaled version of the
1396: * image has been constructed for this output device. Each size of
1397: * the image may be cached separately and generated from the original
1398: * data in a separate image production sequence.
1399: * @param img the specified image to be drawn.
1400: * @param x the <i>x</i> coordinate.
1401: * @param y the <i>y</i> coordinate.
1402: * @param width the width of the rectangle.
1403: * @param height the height of the rectangle.
1404: * @param observer object to be notified as more of
1405: * the image is converted.
1406: * @see java.awt.Image
1407: * @see java.awt.image.ImageObserver
1408: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1409: * @since JDK1.0
1410: */
1411: public boolean drawImage(Image img, int x, int y, int width,
1412: int height, ImageObserver observer) {
1413:
1414: return drawImage(img, x, y, width, height, null, observer);
1415:
1416: }
1417:
1418: /*
1419: * Draws as much of the specified image as is currently available.
1420: * The image is drawn with its top-left corner at
1421: * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
1422: * space. Transparent pixels are drawn in the specified
1423: * background color.
1424: * <p>
1425: * This operation is equivalent to filling a rectangle of the
1426: * width and height of the specified image with the given color and then
1427: * drawing the image on top of it, but possibly more efficient.
1428: * <p>
1429: * This method returns immediately in all cases, even if the
1430: * complete image has not yet been loaded, and it has not been dithered
1431: * and converted for the current output device.
1432: * <p>
1433: * If the image has not yet been completely loaded, then
1434: * <code>drawImage</code> returns <code>false</code>. As more of
1435: * the image becomes available, the process that draws the image notifies
1436: * the specified image observer.
1437: * @param img the specified image to be drawn.
1438: * This method does nothing if <code>img</code> is null.
1439: * @param x the <i>x</i> coordinate.
1440: * @param y the <i>y</i> coordinate.
1441: * @param bgcolor the background color to paint under the
1442: * non-opaque portions of the image.
1443: * In this WPathGraphics implementation,
1444: * this parameter can be null in which
1445: * case that background is made a transparent
1446: * white.
1447: * @param observer object to be notified as more of
1448: * the image is converted.
1449: * @see java.awt.Image
1450: * @see java.awt.image.ImageObserver
1451: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1452: * @since JDK1.0
1453: */
1454: public boolean drawImage(Image img, int x, int y, Color bgcolor,
1455: ImageObserver observer) {
1456:
1457: if (img == null) {
1458: return true;
1459: }
1460:
1461: boolean result;
1462: int srcWidth = img.getWidth(null);
1463: int srcHeight = img.getHeight(null);
1464:
1465: if (srcWidth < 0 || srcHeight < 0) {
1466: result = false;
1467: } else {
1468: result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor,
1469: observer);
1470: }
1471:
1472: return result;
1473: }
1474:
1475: /**
1476: * Draws as much of the specified image as has already been scaled
1477: * to fit inside the specified rectangle.
1478: * <p>
1479: * The image is drawn inside the specified rectangle of this
1480: * graphics context's coordinate space, and is scaled if
1481: * necessary. Transparent pixels are drawn in the specified
1482: * background color.
1483: * This operation is equivalent to filling a rectangle of the
1484: * width and height of the specified image with the given color and then
1485: * drawing the image on top of it, but possibly more efficient.
1486: * <p>
1487: * This method returns immediately in all cases, even if the
1488: * entire image has not yet been scaled, dithered, and converted
1489: * for the current output device.
1490: * If the current output representation is not yet complete then
1491: * <code>drawImage</code> returns <code>false</code>. As more of
1492: * the image becomes available, the process that draws the image notifies
1493: * the specified image observer.
1494: * <p>
1495: * A scaled version of an image will not necessarily be
1496: * available immediately just because an unscaled version of the
1497: * image has been constructed for this output device. Each size of
1498: * the image may be cached separately and generated from the original
1499: * data in a separate image production sequence.
1500: * @param img the specified image to be drawn.
1501: * This method does nothing if <code>img</code> is null.
1502: * @param x the <i>x</i> coordinate.
1503: * @param y the <i>y</i> coordinate.
1504: * @param width the width of the rectangle.
1505: * @param height the height of the rectangle.
1506: * @param bgcolor the background color to paint under the
1507: * non-opaque portions of the image.
1508: * @param observer object to be notified as more of
1509: * the image is converted.
1510: * @see java.awt.Image
1511: * @see java.awt.image.ImageObserver
1512: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1513: * @since JDK1.0
1514: */
1515: public boolean drawImage(Image img, int x, int y, int width,
1516: int height, Color bgcolor, ImageObserver observer) {
1517:
1518: if (img == null) {
1519: return true;
1520: }
1521:
1522: boolean result;
1523: int srcWidth = img.getWidth(null);
1524: int srcHeight = img.getHeight(null);
1525:
1526: if (srcWidth < 0 || srcHeight < 0) {
1527: result = false;
1528: } else {
1529: result = drawImage(img, x, y, x + width, y + height, 0, 0,
1530: srcWidth, srcHeight, observer);
1531: }
1532:
1533: return result;
1534: }
1535:
1536: /**
1537: * Draws as much of the specified area of the specified image as is
1538: * currently available, scaling it on the fly to fit inside the
1539: * specified area of the destination drawable surface. Transparent pixels
1540: * do not affect whatever pixels are already there.
1541: * <p>
1542: * This method returns immediately in all cases, even if the
1543: * image area to be drawn has not yet been scaled, dithered, and converted
1544: * for the current output device.
1545: * If the current output representation is not yet complete then
1546: * <code>drawImage</code> returns <code>false</code>. As more of
1547: * the image becomes available, the process that draws the image notifies
1548: * the specified image observer.
1549: * <p>
1550: * This method always uses the unscaled version of the image
1551: * to render the scaled rectangle and performs the required
1552: * scaling on the fly. It does not use a cached, scaled version
1553: * of the image for this operation. Scaling of the image from source
1554: * to destination is performed such that the first coordinate
1555: * of the source rectangle is mapped to the first coordinate of
1556: * the destination rectangle, and the second source coordinate is
1557: * mapped to the second destination coordinate. The subimage is
1558: * scaled and flipped as needed to preserve those mappings.
1559: * @param img the specified image to be drawn
1560: * @param dx1 the <i>x</i> coordinate of the first corner of the
1561: * destination rectangle.
1562: * @param dy1 the <i>y</i> coordinate of the first corner of the
1563: * destination rectangle.
1564: * @param dx2 the <i>x</i> coordinate of the second corner of the
1565: * destination rectangle.
1566: * @param dy2 the <i>y</i> coordinate of the second corner of the
1567: * destination rectangle.
1568: * @param sx1 the <i>x</i> coordinate of the first corner of the
1569: * source rectangle.
1570: * @param sy1 the <i>y</i> coordinate of the first corner of the
1571: * source rectangle.
1572: * @param sx2 the <i>x</i> coordinate of the second corner of the
1573: * source rectangle.
1574: * @param sy2 the <i>y</i> coordinate of the second corner of the
1575: * source rectangle.
1576: * @param observer object to be notified as more of the image is
1577: * scaled and converted.
1578: * @see java.awt.Image
1579: * @see java.awt.image.ImageObserver
1580: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1581: * @since JDK1.1
1582: */
1583: public boolean drawImage(Image img, int dx1, int dy1, int dx2,
1584: int dy2, int sx1, int sy1, int sx2, int sy2,
1585: ImageObserver observer) {
1586:
1587: return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,
1588: null, observer);
1589: }
1590:
1591: /**
1592: * Draws as much of the specified area of the specified image as is
1593: * currently available, scaling it on the fly to fit inside the
1594: * specified area of the destination drawable surface.
1595: * <p>
1596: * Transparent pixels are drawn in the specified background color.
1597: * This operation is equivalent to filling a rectangle of the
1598: * width and height of the specified image with the given color and then
1599: * drawing the image on top of it, but possibly more efficient.
1600: * <p>
1601: * This method returns immediately in all cases, even if the
1602: * image area to be drawn has not yet been scaled, dithered, and converted
1603: * for the current output device.
1604: * If the current output representation is not yet complete then
1605: * <code>drawImage</code> returns <code>false</code>. As more of
1606: * the image becomes available, the process that draws the image notifies
1607: * the specified image observer.
1608: * <p>
1609: * This method always uses the unscaled version of the image
1610: * to render the scaled rectangle and performs the required
1611: * scaling on the fly. It does not use a cached, scaled version
1612: * of the image for this operation. Scaling of the image from source
1613: * to destination is performed such that the first coordinate
1614: * of the source rectangle is mapped to the first coordinate of
1615: * the destination rectangle, and the second source coordinate is
1616: * mapped to the second destination coordinate. The subimage is
1617: * scaled and flipped as needed to preserve those mappings.
1618: * @param img the specified image to be drawn
1619: * This method does nothing if <code>img</code> is null.
1620: * @param dx1 the <i>x</i> coordinate of the first corner of the
1621: * destination rectangle.
1622: * @param dy1 the <i>y</i> coordinate of the first corner of the
1623: * destination rectangle.
1624: * @param dx2 the <i>x</i> coordinate of the second corner of the
1625: * destination rectangle.
1626: * @param dy2 the <i>y</i> coordinate of the second corner of the
1627: * destination rectangle.
1628: * @param sx1 the <i>x</i> coordinate of the first corner of the
1629: * source rectangle.
1630: * @param sy1 the <i>y</i> coordinate of the first corner of the
1631: * source rectangle.
1632: * @param sx2 the <i>x</i> coordinate of the second corner of the
1633: * source rectangle.
1634: * @param sy2 the <i>y</i> coordinate of the second corner of the
1635: * source rectangle.
1636: * @param bgcolor the background color to paint under the
1637: * non-opaque portions of the image.
1638: * @param observer object to be notified as more of the image is
1639: * scaled and converted.
1640: * @see java.awt.Image
1641: * @see java.awt.image.ImageObserver
1642: * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1643: * @since JDK1.1
1644: */
1645: public boolean drawImage(Image img, int dx1, int dy1, int dx2,
1646: int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor,
1647: ImageObserver observer) {
1648:
1649: if (img == null) {
1650: return true;
1651: }
1652: int imgWidth = img.getWidth(null);
1653: int imgHeight = img.getHeight(null);
1654:
1655: if (imgWidth < 0 || imgHeight < 0) {
1656: return true;
1657: }
1658:
1659: int srcWidth = sx2 - sx1;
1660: int srcHeight = sy2 - sy1;
1661:
1662: /* Create a transform which describes the changes
1663: * from the source coordinates to the destination
1664: * coordinates. The scaling is determined by the
1665: * ratio of the two rectangles, while the translation
1666: * comes from the difference of their origins.
1667: */
1668: float scalex = (float) (dx2 - dx1) / srcWidth;
1669: float scaley = (float) (dy2 - dy1) / srcHeight;
1670: AffineTransform xForm = new AffineTransform(scalex, 0, 0,
1671: scaley, dx1 - (sx1 * scalex), dy1 - (sy1 * scaley));
1672:
1673: /* drawImageToPlatform needs the top-left of the source area and
1674: * a positive width and height. The xform describes how to map
1675: * src->dest, so that information is not lost.
1676: */
1677: int tmp = 0;
1678: if (sx2 < sx1) {
1679: tmp = sx1;
1680: sx1 = sx2;
1681: sx2 = tmp;
1682: }
1683: if (sy2 < sy1) {
1684: tmp = sy1;
1685: sy1 = sy2;
1686: sy2 = tmp;
1687: }
1688:
1689: /* if src area is beyond the bounds of the image, we must clip it.
1690: * The transform is based on the specified area, not the clipped one.
1691: */
1692: if (sx1 < 0) {
1693: sx1 = 0;
1694: } else if (sx1 > imgWidth) { // empty srcArea, nothing to draw
1695: sx1 = imgWidth;
1696: }
1697: if (sx2 < 0) { // empty srcArea, nothing to draw
1698: sx2 = 0;
1699: } else if (sx2 > imgWidth) {
1700: sx2 = imgWidth;
1701: }
1702: if (sy1 < 0) {
1703: sy1 = 0;
1704: } else if (sy1 > imgHeight) { // empty srcArea
1705: sy1 = imgHeight;
1706: }
1707: if (sy2 < 0) { // empty srcArea
1708: sy2 = 0;
1709: } else if (sy2 > imgHeight) {
1710: sy2 = imgHeight;
1711: }
1712:
1713: srcWidth = sx2 - sx1;
1714: srcHeight = sy2 - sy1;
1715:
1716: if (srcWidth <= 0 || srcHeight <= 0) {
1717: return true;
1718: }
1719:
1720: return drawImageToPlatform(img, xForm, bgcolor, sx1, sy1,
1721: srcWidth, srcHeight, false);
1722:
1723: }
1724:
1725: /**
1726: * Draws an image, applying a transform from image space into user space
1727: * before drawing.
1728: * The transformation from user space into device space is done with
1729: * the current transform in the Graphics2D.
1730: * The given transformation is applied to the image before the
1731: * transform attribute in the Graphics2D state is applied.
1732: * The rendering attributes applied include the clip, transform,
1733: * and composite attributes. Note that the result is
1734: * undefined, if the given transform is noninvertible.
1735: * @param img The image to be drawn.
1736: * This method does nothing if <code>img</code> is null.
1737: * @param xform The transformation from image space into user space.
1738: * @param obs The image observer to be notified as more of the image
1739: * is converted.
1740: * @see #transform
1741: * @see #setTransform
1742: * @see #setComposite
1743: * @see #clip
1744: * @see #setClip
1745: */
1746: public boolean drawImage(Image img, AffineTransform xform,
1747: ImageObserver obs) {
1748:
1749: if (img == null) {
1750: return true;
1751: }
1752:
1753: boolean result;
1754: int srcWidth = img.getWidth(null);
1755: int srcHeight = img.getHeight(null);
1756:
1757: if (srcWidth < 0 || srcHeight < 0) {
1758: result = false;
1759: } else {
1760: result = drawImageToPlatform(img, xform, null, 0, 0,
1761: srcWidth, srcHeight, false);
1762: }
1763:
1764: return result;
1765: }
1766:
1767: /**
1768: * Draws a BufferedImage that is filtered with a BufferedImageOp.
1769: * The rendering attributes applied include the clip, transform
1770: * and composite attributes. This is equivalent to:
1771: * <pre>
1772: * img1 = op.filter(img, null);
1773: * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
1774: * </pre>
1775: * @param op The filter to be applied to the image before drawing.
1776: * @param img The BufferedImage to be drawn.
1777: * This method does nothing if <code>img</code> is null.
1778: * @param x,y The location in user space where the image should be drawn.
1779: * @see #transform
1780: * @see #setTransform
1781: * @see #setComposite
1782: * @see #clip
1783: * @see #setClip
1784: */
1785: public void drawImage(BufferedImage img, BufferedImageOp op, int x,
1786: int y) {
1787:
1788: if (img == null) {
1789: return;
1790: }
1791:
1792: int srcWidth = img.getWidth(null);
1793: int srcHeight = img.getHeight(null);
1794:
1795: if (op != null) {
1796: img = op.filter(img, null);
1797: }
1798: if (srcWidth <= 0 || srcHeight <= 0) {
1799: return;
1800: } else {
1801: AffineTransform xform = new AffineTransform(1f, 0f, 0f, 1f,
1802: x, y);
1803: drawImageToPlatform(img, xform, null, 0, 0, srcWidth,
1804: srcHeight, false);
1805: }
1806:
1807: }
1808:
1809: /**
1810: * Draws an image, applying a transform from image space into user space
1811: * before drawing.
1812: * The transformation from user space into device space is done with
1813: * the current transform in the Graphics2D.
1814: * The given transformation is applied to the image before the
1815: * transform attribute in the Graphics2D state is applied.
1816: * The rendering attributes applied include the clip, transform,
1817: * and composite attributes. Note that the result is
1818: * undefined, if the given transform is noninvertible.
1819: * @param img The image to be drawn.
1820: * This method does nothing if <code>img</code> is null.
1821: * @param xform The transformation from image space into user space.
1822: * @see #transform
1823: * @see #setTransform
1824: * @see #setComposite
1825: * @see #clip
1826: * @see #setClip
1827: */
1828: public void drawRenderedImage(RenderedImage img,
1829: AffineTransform xform) {
1830:
1831: if (img == null) {
1832: return;
1833: }
1834:
1835: BufferedImage bufferedImage = null;
1836: int srcWidth = img.getWidth();
1837: int srcHeight = img.getHeight();
1838:
1839: if (srcWidth <= 0 || srcHeight <= 0) {
1840: return;
1841: }
1842:
1843: if (img instanceof BufferedImage) {
1844: bufferedImage = (BufferedImage) img;
1845: } else {
1846: bufferedImage = new BufferedImage(srcWidth, srcHeight,
1847: BufferedImage.TYPE_INT_ARGB);
1848: Graphics2D imageGraphics = bufferedImage.createGraphics();
1849: imageGraphics.drawRenderedImage(img, xform);
1850: }
1851:
1852: drawImageToPlatform(bufferedImage, xform, null, 0, 0, srcWidth,
1853: srcHeight, false);
1854:
1855: }
1856:
1857: }
|