1 /*
   2  * Copyright 1998-2007 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.print;
  27 
  28 import java.lang.ref.SoftReference;
  29 import java.util.Hashtable;
  30 import sun.font.CharToGlyphMapper;
  31 import sun.font.CompositeFont;
  32 import sun.font.Font2D;
  33 import sun.font.Font2DHandle;
  34 import sun.font.FontManager;
  35 import sun.font.FontManagerFactory;
  36 import sun.font.FontUtilities;
  37 
  38 import java.awt.Color;
  39 import java.awt.Font;
  40 import java.awt.Graphics2D;
  41 import java.awt.Image;
  42 import java.awt.Paint;
  43 import java.awt.Polygon;
  44 import java.awt.Shape;
  45 
  46 import java.text.AttributedCharacterIterator;
  47 
  48 import java.awt.font.FontRenderContext;
  49 import java.awt.font.GlyphVector;
  50 import java.awt.font.TextAttribute;
  51 import java.awt.font.TextLayout;
  52 
  53 import java.awt.geom.AffineTransform;
  54 import java.awt.geom.Arc2D;
  55 import java.awt.geom.Ellipse2D;
  56 import java.awt.geom.Line2D;
  57 import java.awt.geom.Point2D;
  58 import java.awt.geom.Rectangle2D;
  59 import java.awt.geom.RoundRectangle2D;
  60 import java.awt.geom.PathIterator;
  61 
  62 import java.awt.image.BufferedImage;
  63 import java.awt.image.BufferedImageOp;
  64 import java.awt.image.ColorModel;
  65 import java.awt.image.DataBuffer;
  66 import java.awt.image.DataBufferInt;
  67 import java.awt.image.ImageObserver;
  68 import java.awt.image.IndexColorModel;
  69 import java.awt.image.Raster;
  70 import java.awt.image.RenderedImage;
  71 import java.awt.image.SampleModel;
  72 import java.awt.image.SinglePixelPackedSampleModel;
  73 import java.awt.image.VolatileImage;
  74 import sun.awt.image.ByteComponentRaster;
  75 import sun.awt.image.ToolkitImage;
  76 import sun.awt.image.SunWritableRaster;
  77 
  78 import java.awt.print.PageFormat;
  79 import java.awt.print.Printable;
  80 import java.awt.print.PrinterException;
  81 import java.awt.print.PrinterGraphics;
  82 import java.awt.print.PrinterJob;
  83 
  84 import java.util.Map;
  85 
  86 public abstract class PathGraphics extends ProxyGraphics2D {
  87 
  88     private Printable mPainter;
  89     private PageFormat mPageFormat;
  90     private int mPageIndex;
  91     private boolean mCanRedraw;
  92     protected boolean printingGlyphVector;
  93 
  94     protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,
  95                            Printable painter, PageFormat pageFormat,
  96                            int pageIndex, boolean canRedraw) {
  97         super(graphics, printerJob);
  98 
  99         mPainter = painter;
 100         mPageFormat = pageFormat;
 101         mPageIndex = pageIndex;
 102         mCanRedraw = canRedraw;
 103     }
 104 
 105     /**
 106      * Return the Printable instance responsible for drawing
 107      * into this Graphics.
 108      */
 109     protected Printable getPrintable() {
 110         return mPainter;
 111     }
 112 
 113     /**
 114      * Return the PageFormat associated with this page of
 115      * Graphics.
 116      */
 117     protected PageFormat getPageFormat() {
 118         return mPageFormat;
 119     }
 120 
 121     /**
 122      * Return the page index associated with this Graphics.
 123      */
 124     protected int getPageIndex() {
 125         return mPageIndex;
 126     }
 127 
 128     /**
 129      * Return true if we are allowed to ask the application
 130      * to redraw portions of the page. In general, with the
 131      * PrinterJob API, the application can be asked to do a
 132      * redraw. When PrinterJob is emulating PrintJob then we
 133      * can not.
 134      */
 135     public boolean canDoRedraws() {
 136         return mCanRedraw;
 137     }
 138 
 139      /**
 140       * Redraw a rectanglular area using a proxy graphics
 141       */
 142     public abstract void redrawRegion(Rectangle2D region,
 143                                       double scaleX, double scaleY,
 144                                       Shape clip,
 145                                       AffineTransform devTransform)
 146 
 147                     throws PrinterException ;
 148 
 149     /**
 150      * Draws a line, using the current color, between the points
 151      * <code>(x1,&nbsp;y1)</code> and <code>(x2,&nbsp;y2)</code>
 152      * in this graphics context's coordinate system.
 153      * @param   x1  the first point's <i>x</i> coordinate.
 154      * @param   y1  the first point's <i>y</i> coordinate.
 155      * @param   x2  the second point's <i>x</i> coordinate.
 156      * @param   y2  the second point's <i>y</i> coordinate.
 157      */
 158     public void drawLine(int x1, int y1, int x2, int y2) {
 159 
 160         Paint paint = getPaint();
 161 
 162         try {
 163             AffineTransform deviceTransform = getTransform();
 164             if (getClip() != null) {
 165                 deviceClip(getClip().getPathIterator(deviceTransform));
 166             }
 167 
 168             deviceDrawLine(x1, y1, x2, y2, (Color) paint);
 169 
 170         } catch (ClassCastException e) {
 171             throw new IllegalArgumentException("Expected a Color instance");
 172         }
 173     }
 174 
 175 
 176     /**
 177      * Draws the outline of the specified rectangle.
 178      * The left and right edges of the rectangle are at
 179      * <code>x</code> and <code>x&nbsp;+&nbsp;width</code>.
 180      * The top and bottom edges are at
 181      * <code>y</code> and <code>y&nbsp;+&nbsp;height</code>.
 182      * The rectangle is drawn using the graphics context's current color.
 183      * @param         x   the <i>x</i> coordinate
 184      *                         of the rectangle to be drawn.
 185      * @param         y   the <i>y</i> coordinate
 186      *                         of the rectangle to be drawn.
 187      * @param         width   the width of the rectangle to be drawn.
 188      * @param         height   the height of the rectangle to be drawn.
 189      * @see          java.awt.Graphics#fillRect
 190      * @see          java.awt.Graphics#clearRect
 191      */
 192     public void drawRect(int x, int y, int width, int height) {
 193 
 194         Paint paint = getPaint();
 195 
 196         try {
 197             AffineTransform deviceTransform = getTransform();
 198             if (getClip() != null) {
 199                 deviceClip(getClip().getPathIterator(deviceTransform));
 200             }
 201 
 202             deviceFrameRect(x, y, width, height, (Color) paint);
 203 
 204         } catch (ClassCastException e) {
 205             throw new IllegalArgumentException("Expected a Color instance");
 206         }
 207 
 208     }
 209 
 210     /**
 211      * Fills the specified rectangle.
 212      * The left and right edges of the rectangle are at
 213      * <code>x</code> and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>.
 214      * The top and bottom edges are at
 215      * <code>y</code> and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>.
 216      * The resulting rectangle covers an area
 217      * <code>width</code> pixels wide by
 218      * <code>height</code> pixels tall.
 219      * The rectangle is filled using the graphics context's current color.
 220      * @param         x   the <i>x</i> coordinate
 221      *                         of the rectangle to be filled.
 222      * @param         y   the <i>y</i> coordinate
 223      *                         of the rectangle to be filled.
 224      * @param         width   the width of the rectangle to be filled.
 225      * @param         height   the height of the rectangle to be filled.
 226      * @see           java.awt.Graphics#clearRect
 227      * @see           java.awt.Graphics#drawRect
 228      */
 229     public void fillRect(int x, int y, int width, int height){
 230 
 231         Paint paint = getPaint();
 232 
 233         try {
 234             AffineTransform deviceTransform = getTransform();
 235             if (getClip() != null) {
 236                 deviceClip(getClip().getPathIterator(deviceTransform));
 237             }
 238 
 239             deviceFillRect(x, y, width, height, (Color) paint);
 240 
 241         } catch (ClassCastException e) {
 242             throw new IllegalArgumentException("Expected a Color instance");
 243         }
 244     }
 245 
 246        /**
 247      * Clears the specified rectangle by filling it with the background
 248      * color of the current drawing surface. This operation does not
 249      * use the current paint mode.
 250      * <p>
 251      * Beginning with Java&nbsp;1.1, the background color
 252      * of offscreen images may be system dependent. Applications should
 253      * use <code>setColor</code> followed by <code>fillRect</code> to
 254      * ensure that an offscreen image is cleared to a specific color.
 255      * @param       x the <i>x</i> coordinate of the rectangle to clear.
 256      * @param       y the <i>y</i> coordinate of the rectangle to clear.
 257      * @param       width the width of the rectangle to clear.
 258      * @param       height the height of the rectangle to clear.
 259      * @see         java.awt.Graphics#fillRect(int, int, int, int)
 260      * @see         java.awt.Graphics#drawRect
 261      * @see         java.awt.Graphics#setColor(java.awt.Color)
 262      * @see         java.awt.Graphics#setPaintMode
 263      * @see         java.awt.Graphics#setXORMode(java.awt.Color)
 264      */
 265     public void clearRect(int x, int y, int width, int height) {
 266 
 267         fill(new Rectangle2D.Float(x, y, width, height), getBackground());
 268     }
 269 
 270         /**
 271      * Draws an outlined round-cornered rectangle using this graphics
 272      * context's current color. The left and right edges of the rectangle
 273      * are at <code>x</code> and <code>x&nbsp;+&nbsp;width</code>,
 274      * respectively. The top and bottom edges of the rectangle are at
 275      * <code>y</code> and <code>y&nbsp;+&nbsp;height</code>.
 276      * @param      x the <i>x</i> coordinate of the rectangle to be drawn.
 277      * @param      y the <i>y</i> coordinate of the rectangle to be drawn.
 278      * @param      width the width of the rectangle to be drawn.
 279      * @param      height the height of the rectangle to be drawn.
 280      * @param      arcWidth the horizontal diameter of the arc
 281      *                    at the four corners.
 282      * @param      arcHeight the vertical diameter of the arc
 283      *                    at the four corners.
 284      * @see        java.awt.Graphics#fillRoundRect
 285      */
 286     public void drawRoundRect(int x, int y, int width, int height,
 287                               int arcWidth, int arcHeight) {
 288 
 289         draw(new RoundRectangle2D.Float(x, y,
 290                                         width, height,
 291                                         arcWidth, arcHeight));
 292     }
 293 
 294 
 295     /**
 296      * Fills the specified rounded corner rectangle with the current color.
 297      * The left and right edges of the rectangle
 298      * are at <code>x</code> and <code>x&nbsp;+&nbsp;width&nbsp;-&nbsp;1</code>,
 299      * respectively. The top and bottom edges of the rectangle are at
 300      * <code>y</code> and <code>y&nbsp;+&nbsp;height&nbsp;-&nbsp;1</code>.
 301      * @param       x the <i>x</i> coordinate of the rectangle to be filled.
 302      * @param       y the <i>y</i> coordinate of the rectangle to be filled.
 303      * @param       width the width of the rectangle to be filled.
 304      * @param       height the height of the rectangle to be filled.
 305      * @param       arcWidth the horizontal diameter
 306      *                     of the arc at the four corners.
 307      * @param       arcHeight the vertical diameter
 308      *                     of the arc at the four corners.
 309      * @see         java.awt.Graphics#drawRoundRect
 310      */
 311     public void fillRoundRect(int x, int y, int width, int height,
 312                               int arcWidth, int arcHeight) {
 313 
 314         fill(new RoundRectangle2D.Float(x, y,
 315                                         width, height,
 316                                         arcWidth, arcHeight));
 317     }
 318 
 319     /**
 320      * Draws the outline of an oval.
 321      * The result is a circle or ellipse that fits within the
 322      * rectangle specified by the <code>x</code>, <code>y</code>,
 323      * <code>width</code>, and <code>height</code> arguments.
 324      * <p>
 325      * The oval covers an area that is
 326      * <code>width&nbsp;+&nbsp;1</code> pixels wide
 327      * and <code>height&nbsp;+&nbsp;1</code> pixels tall.
 328      * @param       x the <i>x</i> coordinate of the upper left
 329      *                     corner of the oval to be drawn.
 330      * @param       y the <i>y</i> coordinate of the upper left
 331      *                     corner of the oval to be drawn.
 332      * @param       width the width of the oval to be drawn.
 333      * @param       height the height of the oval to be drawn.
 334      * @see         java.awt.Graphics#fillOval
 335      * @since       JDK1.0
 336      */
 337     public void drawOval(int x, int y, int width, int height) {
 338         draw(new Ellipse2D.Float(x, y, width, height));
 339     }
 340 
 341         /**
 342      * Fills an oval bounded by the specified rectangle with the
 343      * current color.
 344      * @param       x the <i>x</i> coordinate of the upper left corner
 345      *                     of the oval to be filled.
 346      * @param       y the <i>y</i> coordinate of the upper left corner
 347      *                     of the oval to be filled.
 348      * @param       width the width of the oval to be filled.
 349      * @param       height the height of the oval to be filled.
 350      * @see         java.awt.Graphics#drawOval
 351      */
 352     public void fillOval(int x, int y, int width, int height){
 353 
 354         fill(new Ellipse2D.Float(x, y, width, height));
 355     }
 356 
 357     /**
 358      * Draws the outline of a circular or elliptical arc
 359      * covering the specified rectangle.
 360      * <p>
 361      * The resulting arc begins at <code>startAngle</code> and extends
 362      * for <code>arcAngle</code> degrees, using the current color.
 363      * Angles are interpreted such that 0&nbsp;degrees
 364      * is at the 3&nbsp;o'clock position.
 365      * A positive value indicates a counter-clockwise rotation
 366      * while a negative value indicates a clockwise rotation.
 367      * <p>
 368      * The center of the arc is the center of the rectangle whose origin
 369      * is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the
 370      * <code>width</code> and <code>height</code> arguments.
 371      * <p>
 372      * The resulting arc covers an area
 373      * <code>width&nbsp;+&nbsp;1</code> pixels wide
 374      * by <code>height&nbsp;+&nbsp;1</code> pixels tall.
 375      * <p>
 376      * The angles are specified relative to the non-square extents of
 377      * the bounding rectangle such that 45 degrees always falls on the
 378      * line from the center of the ellipse to the upper right corner of
 379      * the bounding rectangle. As a result, if the bounding rectangle is
 380      * noticeably longer in one axis than the other, the angles to the
 381      * start and end of the arc segment will be skewed farther along the
 382      * longer axis of the bounds.
 383      * @param        x the <i>x</i> coordinate of the
 384      *                    upper-left corner of the arc to be drawn.
 385      * @param        y the <i>y</i>  coordinate of the
 386      *                    upper-left corner of the arc to be drawn.
 387      * @param        width the width of the arc to be drawn.
 388      * @param        height the height of the arc to be drawn.
 389      * @param        startAngle the beginning angle.
 390      * @param        arcAngle the angular extent of the arc,
 391      *                    relative to the start angle.
 392      * @see         java.awt.Graphics#fillArc
 393      */
 394     public void drawArc(int x, int y, int width, int height,
 395                                  int startAngle, int arcAngle) {
 396         draw(new Arc2D.Float(x, y, width, height,
 397                              startAngle, arcAngle,
 398                              Arc2D.OPEN));
 399     }
 400 
 401 
 402     /**
 403      * Fills a circular or elliptical arc covering the specified rectangle.
 404      * <p>
 405      * The resulting arc begins at <code>startAngle</code> and extends
 406      * for <code>arcAngle</code> degrees.
 407      * Angles are interpreted such that 0&nbsp;degrees
 408      * is at the 3&nbsp;o'clock position.
 409      * A positive value indicates a counter-clockwise rotation
 410      * while a negative value indicates a clockwise rotation.
 411      * <p>
 412      * The center of the arc is the center of the rectangle whose origin
 413      * is (<i>x</i>,&nbsp;<i>y</i>) and whose size is specified by the
 414      * <code>width</code> and <code>height</code> arguments.
 415      * <p>
 416      * The resulting arc covers an area
 417      * <code>width&nbsp;+&nbsp;1</code> pixels wide
 418      * by <code>height&nbsp;+&nbsp;1</code> pixels tall.
 419      * <p>
 420      * The angles are specified relative to the non-square extents of
 421      * the bounding rectangle such that 45 degrees always falls on the
 422      * line from the center of the ellipse to the upper right corner of
 423      * the bounding rectangle. As a result, if the bounding rectangle is
 424      * noticeably longer in one axis than the other, the angles to the
 425      * start and end of the arc segment will be skewed farther along the
 426      * longer axis of the bounds.
 427      * @param        x the <i>x</i> coordinate of the
 428      *                    upper-left corner of the arc to be filled.
 429      * @param        y the <i>y</i>  coordinate of the
 430      *                    upper-left corner of the arc to be filled.
 431      * @param        width the width of the arc to be filled.
 432      * @param        height the height of the arc to be filled.
 433      * @param        startAngle the beginning angle.
 434      * @param        arcAngle the angular extent of the arc,
 435      *                    relative to the start angle.
 436      * @see         java.awt.Graphics#drawArc
 437      */
 438     public void fillArc(int x, int y, int width, int height,
 439                                  int startAngle, int arcAngle) {
 440 
 441         fill(new Arc2D.Float(x, y, width, height,
 442                              startAngle, arcAngle,
 443                              Arc2D.PIE));
 444     }
 445 
 446     /**
 447      * Draws a sequence of connected lines defined by
 448      * arrays of <i>x</i> and <i>y</i> coordinates.
 449      * Each pair of (<i>x</i>,&nbsp;<i>y</i>) coordinates defines a point.
 450      * The figure is not closed if the first point
 451      * differs from the last point.
 452      * @param       xPoints an array of <i>x</i> points
 453      * @param       yPoints an array of <i>y</i> points
 454      * @param       nPoints the total number of points
 455      * @see         java.awt.Graphics#drawPolygon(int[], int[], int)
 456      * @since       JDK1.1
 457      */
 458     public void drawPolyline(int xPoints[], int yPoints[],
 459                              int nPoints) {
 460         float fromX;
 461         float fromY;
 462         float toX;
 463         float toY;
 464 
 465         if (nPoints > 0) {
 466             fromX = xPoints[0];
 467             fromY = yPoints[0];
 468             for(int i = 1; i < nPoints; i++) {
 469                 toX = xPoints[i];
 470                 toY = yPoints[i];
 471                 draw(new Line2D.Float(fromX, fromY, toX, toY));
 472                 fromX = toX;
 473                 fromY = toY;
 474             }
 475         }
 476 
 477     }
 478 
 479 
 480     /**
 481      * Draws a closed polygon defined by
 482      * arrays of <i>x</i> and <i>y</i> coordinates.
 483      * Each pair of (<i>x</i>,&nbsp;<i>y</i>) coordinates defines a point.
 484      * <p>
 485      * This method draws the polygon defined by <code>nPoint</code> line
 486      * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code>
 487      * line segments are line segments from
 488      * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code>
 489      * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for
 490      * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.
 491      * The figure is automatically closed by drawing a line connecting
 492      * the final point to the first point, if those points are different.
 493      * @param        xPoints   a an array of <code>x</code> coordinates.
 494      * @param        yPoints   a an array of <code>y</code> coordinates.
 495      * @param        nPoints   a the total number of points.
 496      * @see          java.awt.Graphics#fillPolygon
 497      * @see          java.awt.Graphics#drawPolyline
 498      */
 499     public void drawPolygon(int xPoints[], int yPoints[],
 500                                      int nPoints) {
 501 
 502         draw(new Polygon(xPoints, yPoints, nPoints));
 503     }
 504 
 505     /**
 506      * Draws the outline of a polygon defined by the specified
 507      * <code>Polygon</code> object.
 508      * @param        p the polygon to draw.
 509      * @see          java.awt.Graphics#fillPolygon
 510      * @see          java.awt.Graphics#drawPolyline
 511      */
 512     public void drawPolygon(Polygon p) {
 513         draw(p);
 514     }
 515 
 516      /**
 517      * Fills a closed polygon defined by
 518      * arrays of <i>x</i> and <i>y</i> coordinates.
 519      * <p>
 520      * This method draws the polygon defined by <code>nPoint</code> line
 521      * segments, where the first <code>nPoint&nbsp;-&nbsp;1</code>
 522      * line segments are line segments from
 523      * <code>(xPoints[i&nbsp;-&nbsp;1],&nbsp;yPoints[i&nbsp;-&nbsp;1])</code>
 524      * to <code>(xPoints[i],&nbsp;yPoints[i])</code>, for
 525      * 1&nbsp;&le;&nbsp;<i>i</i>&nbsp;&le;&nbsp;<code>nPoints</code>.
 526      * The figure is automatically closed by drawing a line connecting
 527      * the final point to the first point, if those points are different.
 528      * <p>
 529      * The area inside the polygon is defined using an
 530      * even-odd fill rule, also known as the alternating rule.
 531      * @param        xPoints   a an array of <code>x</code> coordinates.
 532      * @param        yPoints   a an array of <code>y</code> coordinates.
 533      * @param        nPoints   a the total number of points.
 534      * @see          java.awt.Graphics#drawPolygon(int[], int[], int)
 535      */
 536     public void fillPolygon(int xPoints[], int yPoints[],
 537                             int nPoints) {
 538 
 539         fill(new Polygon(xPoints, yPoints, nPoints));
 540     }
 541 
 542 
 543     /**
 544      * Fills the polygon defined by the specified Polygon object with
 545      * the graphics context's current color.
 546      * <p>
 547      * The area inside the polygon is defined using an
 548      * even-odd fill rule, also known as the alternating rule.
 549      * @param        p the polygon to fill.
 550      * @see          java.awt.Graphics#drawPolygon(int[], int[], int)
 551      */
 552     public void fillPolygon(Polygon p) {
 553 
 554         fill(p);
 555     }
 556 
 557     /**
 558      * Draws the text given by the specified string, using this
 559      * graphics context's current font and color. The baseline of the
 560      * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this
 561      * graphics context's coordinate system.
 562      * @param       str      the string to be drawn.
 563      * @param       x        the <i>x</i> coordinate.
 564      * @param       y        the <i>y</i> coordinate.
 565      * @see         java.awt.Graphics#drawBytes
 566      * @see         java.awt.Graphics#drawChars
 567      * @since       JDK1.0
 568      */
 569     public void drawString(String str, int x, int y) {
 570         drawString(str, (float) x, (float) y);
 571     }
 572 
 573     public void drawString(String str, float x, float y) {
 574         if (str.length() == 0) {
 575             return;
 576         }
 577         TextLayout layout =
 578             new TextLayout(str, getFont(), getFontRenderContext());
 579         layout.draw(this, x, y);
 580     }
 581 
 582     protected void drawString(String str, float x, float y,
 583                               Font font, FontRenderContext frc, float w) {
 584         TextLayout layout =
 585             new TextLayout(str, font, frc);
 586         Shape textShape =
 587             layout.getOutline(AffineTransform.getTranslateInstance(x, y));
 588         fill(textShape);
 589     }
 590 
 591     /**
 592      * Draws the text given by the specified iterator, using this
 593      * graphics context's current color. The iterator has to specify a font
 594      * for each character. The baseline of the
 595      * first character is at position (<i>x</i>,&nbsp;<i>y</i>) in this
 596      * graphics context's coordinate system.
 597      * @param       iterator the iterator whose text is to be drawn
 598      * @param       x        the <i>x</i> coordinate.
 599      * @param       y        the <i>y</i> coordinate.
 600      * @see         java.awt.Graphics#drawBytes
 601      * @see         java.awt.Graphics#drawChars
 602      */
 603     public void drawString(AttributedCharacterIterator iterator,
 604                            int x, int y) {
 605         drawString(iterator, (float) x, (float) y);
 606     }
 607     public void drawString(AttributedCharacterIterator iterator,
 608                            float x, float y) {
 609         if (iterator == null) {
 610             throw
 611                 new NullPointerException("attributedcharacteriterator is null");
 612         }
 613         TextLayout layout =
 614             new TextLayout(iterator, getFontRenderContext());
 615         layout.draw(this, x, y);
 616     }
 617 
 618     /**
 619      * Draws a GlyphVector.
 620      * The rendering attributes applied include the clip, transform,
 621      * paint or color, and composite attributes.  The GlyphVector specifies
 622      * individual glyphs from a Font.
 623      * @param g The GlyphVector to be drawn.
 624      * @param x,y The coordinates where the glyphs should be drawn.
 625      * @see #setPaint
 626      * @see java.awt.Graphics#setColor
 627      * @see #transform
 628      * @see #setTransform
 629      * @see #setComposite
 630      * @see #clip
 631      * @see #setClip
 632      */
 633     public void drawGlyphVector(GlyphVector g,
 634                                 float x,
 635                                 float y) {
 636 
 637         /* We should not reach here if printingGlyphVector is already true.
 638          * Add an assert so this can be tested if need be.
 639          * But also ensure that we do at least render properly by filling
 640          * the outline.
 641          */
 642         if (printingGlyphVector) {
 643             assert !printingGlyphVector; // ie false.
 644             fill(g.getOutline(x, y));
 645             return;
 646         }
 647 
 648         try {
 649             printingGlyphVector = true;
 650             if (RasterPrinterJob.shapeTextProp ||
 651                 !printedSimpleGlyphVector(g, x, y)) {
 652                 fill(g.getOutline(x, y));
 653             }
 654         } finally {
 655             printingGlyphVector = false;
 656         }
 657     }
 658 
 659     protected static SoftReference<Hashtable<Font2DHandle,Object>>
 660         fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null);
 661 
 662     protected int platformFontCount(Font font, String str) {
 663         return 0;
 664     }
 665 
 666     /**
 667      * Default implementation returns false.
 668      * Callers of this method must always be prepared for this,
 669      * and delegate to outlines or some other solution.
 670      */
 671     protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
 672         return false;
 673     }
 674 
 675     /* GlyphVectors are usually encountered because TextLayout is in use.
 676      * Some times TextLayout is needed to handle complex text or some
 677      * rendering attributes trigger it.
 678      * We try to print GlyphVectors by reconstituting into a String,
 679      * as that is most recoverable for applications that export to formats
 680      * such as Postscript or PDF. In some cases (eg where its not complex
 681      * text and its just that positions aren't what we'd expect) we print
 682      * one character at a time. positioning individually.
 683      * Failing that, if we can directly send glyph codes to the printer
 684      * then we do that (printGlyphVector).
 685      * As a last resort we return false and let the caller print as filled
 686      * shapes.
 687      */
 688     boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {
 689 
 690         int flags = g.getLayoutFlags();
 691 
 692         /* We can't handle RTL, re-ordering, complex glyphs etc by
 693          * reconstituting glyphs into a String. So if any flags besides
 694          * position adjustments are set, see if we can directly
 695          * print the GlyphVector as glyph codes, using the positions
 696          * layout has assigned. If that fails return false;
 697          */
 698         if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {
 699             return printGlyphVector(g, x, y);
 700         }
 701 
 702         Font font = g.getFont();
 703         Font2D font2D = FontUtilities.getFont2D(font);
 704         if (font2D.handle.font2D != font2D) {
 705             /* suspicious, may be a bad font. lets bail */
 706             return false;
 707         }
 708         Hashtable<Font2DHandle,Object> fontMap;
 709         synchronized (PathGraphics.class) {
 710             fontMap = fontMapRef.get();
 711             if (fontMap == null) {
 712                 fontMap = new Hashtable<Font2DHandle,Object>();
 713                 fontMapRef =
 714                     new SoftReference<Hashtable<Font2DHandle,Object>>(fontMap);
 715             }
 716         }
 717 
 718         int numGlyphs = g.getNumGlyphs();
 719         int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);
 720 
 721         char[] glyphToCharMap = null;
 722         char[][] mapArray = null;
 723         CompositeFont cf = null;
 724 
 725         /* Build the needed maps for this font in a synchronized block */
 726         synchronized (fontMap) {
 727             if (font2D instanceof CompositeFont) {
 728                 cf = (CompositeFont)font2D;
 729                 int numSlots = cf.getNumSlots();
 730                 mapArray = (char[][])fontMap.get(font2D.handle);
 731                 if (mapArray == null) {
 732                     mapArray = new char[numSlots][];
 733                     fontMap.put(font2D.handle, mapArray);
 734                 }
 735                 for (int i=0; i<numGlyphs;i++) {
 736                     int slot = glyphCodes[i] >>> 24;
 737                     if (slot >= numSlots) { /* shouldn't happen */
 738                         return false;
 739                     }
 740                     if (mapArray[slot] == null) {
 741                         Font2D slotFont = cf.getSlotFont(slot);
 742                         char[] map = (char[])fontMap.get(slotFont.handle);
 743                         if (map == null) {
 744                             map = getGlyphToCharMapForFont(slotFont);
 745                         }
 746                         mapArray[slot] = map;
 747                     }
 748                 }
 749             } else {
 750                 glyphToCharMap = (char[])fontMap.get(font2D.handle);
 751                 if (glyphToCharMap == null) {
 752                     glyphToCharMap = getGlyphToCharMapForFont(font2D);
 753                     fontMap.put(font2D.handle, glyphToCharMap);
 754                 }
 755             }
 756         }
 757 
 758         char[] chars = new char[numGlyphs];
 759         if (cf != null) {
 760             for (int i=0; i<numGlyphs; i++) {
 761                 int gc = glyphCodes[i];
 762                 char[] map = mapArray[gc >>> 24];
 763                 gc = gc & 0xffffff;
 764                 if (map == null) {
 765                     return false;
 766                 }
 767                 /* X11 symbol & dingbats fonts used only for global metrics,
 768                  * so the glyph codes we have really refer to Lucida Sans
 769                  * Regular.
 770                  * So its possible the glyph code may appear out of range.
 771                  * Note that later on we double-check the glyph codes that
 772                  * we get from re-creating the GV from the string are the
 773                  * same as those we started with.
 774                  *
 775                  * If the glyphcode is INVISIBLE_GLYPH_ID then this may
 776                  * be \t, \n or \r which are mapped to that by layout.
 777                  * This is a case we can handle. It doesn't matter what
 778                  * character we use (we use \n) so long as layout maps it
 779                  * back to this in the verification, since the invisible
 780                  * glyph isn't visible :)
 781                  */
 782                 char ch;
 783                 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
 784                     ch = '\n';
 785                 } else if (gc < 0 || gc >= map.length) {
 786                     return false;
 787                 } else {
 788                     ch = map[gc];
 789                 }
 790                 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
 791                     chars[i] = ch;
 792                 } else {
 793                     return false;
 794                 }
 795             }
 796         } else {
 797             for (int i=0; i<numGlyphs; i++) {
 798                 int gc = glyphCodes[i];
 799                 char ch;
 800                 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
 801                     ch = '\n';
 802                 } else if (gc < 0 || gc >= glyphToCharMap.length) {
 803                     return false;
 804                 } else {
 805                     ch = glyphToCharMap[gc];
 806                 }
 807                 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
 808                     chars[i] = ch;
 809                 } else {
 810                     return false;
 811                 }
 812             }
 813         }
 814 
 815         FontRenderContext gvFrc = g.getFontRenderContext();
 816         GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);
 817         if (gv2.getNumGlyphs() != numGlyphs) {
 818             return printGlyphVector(g, x, y);
 819         }
 820         int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);
 821         /*
 822          * Needed to double-check remapping of X11 symbol & dingbats.
 823          */
 824         for (int i=0; i<numGlyphs; i++) {
 825             if (glyphCodes[i] != glyphCodes2[i]) {
 826                 return printGlyphVector(g, x, y);
 827             }
 828         }
 829 
 830         FontRenderContext g2dFrc = getFontRenderContext();
 831         boolean compatibleFRC = gvFrc.equals(g2dFrc);
 832         /* If differ only in specifying A-A or a translation, these are
 833          * also compatible FRC's, and we can do one drawString call.
 834          */
 835         if (!compatibleFRC &&
 836             gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) {
 837             AffineTransform gvAT = gvFrc.getTransform();
 838             AffineTransform g2dAT = getTransform();
 839             double[] gvMatrix = new double[4];
 840             double[] g2dMatrix = new double[4];
 841             gvAT.getMatrix(gvMatrix);
 842             g2dAT.getMatrix(g2dMatrix);
 843             compatibleFRC = true;
 844             for (int i=0;i<4;i++) {
 845                 if (gvMatrix[i] != g2dMatrix[i]) {
 846                     compatibleFRC = false;
 847                     break;
 848                 }
 849             }
 850         }
 851 
 852         String str = new String(chars, 0, numGlyphs);
 853         int numFonts = platformFontCount(font, str);
 854         if (numFonts == 0) {
 855             return false;
 856         }
 857 
 858         float[] positions = g.getGlyphPositions(0, numGlyphs, null);
 859         boolean noPositionAdjustments =
 860             ((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) ||
 861             samePositions(gv2, glyphCodes2, glyphCodes, positions);
 862 
 863         /* We have to consider that the application may be directly
 864          * creating a GlyphVector, rather than one being created by
 865          * TextLayout or indirectly from drawString. In such a case, if the
 866          * font has layout attributes, the text may measure differently
 867          * when we reconstitute it into a String and ask for the length that
 868          * drawString would use. For example, KERNING will be applied in such
 869          * a case but that Font attribute is not applied when the application
 870          * directly created a GlyphVector. So in this case we need to verify
 871          * that the text measures the same in both cases - ie that the
 872          * layout attribute has no effect. If it does we can't always
 873          * use the drawString call unless we can coerce the drawString call
 874          * into measuring and displaying the string to the same length.
 875          * That is the case where there is only one font used and we can
 876          * specify the overall advance of the string. (See below).
 877          */
 878 
 879         Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);
 880         float gvAdvanceX = (float)gvAdvancePt.getX();
 881         boolean layoutAffectsAdvance = false;
 882         if (font.hasLayoutAttributes() && printingGlyphVector &&
 883             noPositionAdjustments) {
 884 
 885             /* If TRACKING is in use then the glyph vector will report
 886              * position adjustments, then that ought to be sufficient to
 887              * tell us we can't just ask native to do "drawString". But layout
 888              * always sets the position adjustment flag, so we don't believe
 889              * it and verify the positions are really different than
 890              * createGlyphVector() (with no layout) would create. However
 891              * inconsistently, TRACKING is applied when creating a GlyphVector,
 892              * since it doesn't actually require "layout" (even though its
 893              * considered a layout attribute), it just requires a fractional
 894              * tweak to the[default]advances. So we need to specifically
 895              * check for tracking until such time as as we can trust
 896              * the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.
 897              */
 898             Map<TextAttribute, ?> map = font.getAttributes();
 899             Object o = map.get(TextAttribute.TRACKING);
 900             boolean tracking = o != null && (o instanceof Number) &&
 901                 (((Number)o).floatValue() != 0f);
 902 
 903             if (tracking) {
 904                 noPositionAdjustments = false;
 905             } else {
 906                 Rectangle2D bounds = font.getStringBounds(str, gvFrc);
 907                 float strAdvanceX = (float)bounds.getWidth();
 908                 if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {
 909                     layoutAffectsAdvance = true;
 910                 }
 911             }
 912         }
 913 
 914         if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) {
 915             drawString(str, x, y, font, gvFrc, 0f);
 916             return true;
 917         }
 918 
 919         /* If positions have not been explicitly assigned, we can
 920          * ask the string to be drawn adjusted to this width.
 921          * This call is supported only in the PS generator.
 922          * GDI has API to specify the advance for each glyph in a
 923          * string which could be used here too, but that is not yet
 924          * implemented, and we'd need to update the signature of the
 925          * drawString method to take the advances (ie relative positions)
 926          * and use that instead of the width.
 927          */
 928         if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) {
 929             drawString(str, x, y, font, gvFrc, gvAdvanceX);
 930             return true;
 931         }
 932 
 933         /* In some scripts chars drawn individually do not have the
 934          * same representation (glyphs) as when combined with other chars.
 935          * The logic here is erring on the side of caution, in particular
 936          * in including supplementary characters.
 937          */
 938         if (FontUtilities.isComplexText(chars, 0, chars.length)) {
 939             return printGlyphVector(g, x, y);
 940         }
 941 
 942         /* If we reach here we have mapped all the glyphs back
 943          * one-to-one to simple unicode chars that we know are in the font.
 944          * We can call "drawChars" on each one of them in turn, setting
 945          * the position based on the glyph positions.
 946          * There's typically overhead in this. If numGlyphs is 'large',
 947          * it may even be better to try printGlyphVector() in this case.
 948          * This may be less recoverable for apps, but sophisticated apps
 949          * should be able to recover the text from simple glyph vectors
 950          * and we can avoid penalising the more common case - although
 951          * this is already a minority case.
 952          */
 953         if (numGlyphs > 10 && printGlyphVector(g, x, y)) {
 954             return true;
 955         }
 956 
 957         for (int i=0; i<numGlyphs; i++) {
 958             String s = new String(chars, i, 1);
 959             drawString(s, x+positions[i*2], y+positions[i*2+1],
 960                        font, gvFrc, 0f);
 961         }
 962         return true;
 963     }
 964 
 965     /* The same codes must be in the same positions for this to return true.
 966      * This would look cleaner if it took the original GV as a parameter but
 967      * we already have the codes and will need to get the positions array
 968      * too in most cases anyway. So its cheaper to pass them in.
 969      * This call wouldn't be necessary if layout didn't always set the
 970      * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used
 971      * and there was no re-ordering (this should be fixed some day).
 972      */
 973     private boolean samePositions(GlyphVector gv, int[] gvcodes,
 974                                   int[] origCodes, float[] origPositions) {
 975 
 976         int numGlyphs = gv.getNumGlyphs();
 977         float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null);
 978 
 979         /* this shouldn't happen here, but just in case */
 980         if (numGlyphs != gvcodes.length ||  /* real paranoia here */
 981             origCodes.length != gvcodes.length ||
 982             origPositions.length != gvpos.length) {
 983             return false;
 984         }
 985 
 986         for (int i=0; i<numGlyphs; i++) {
 987             if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) {
 988                 return false;
 989             }
 990         }
 991         return true;
 992     }
 993 
 994     protected boolean canDrawStringToWidth() {
 995         return false;
 996     }
 997 
 998     /* return an array which can map glyphs back to char codes.
 999      * Glyphs which aren't mapped from a simple unicode code point
1000      * will have no mapping in this array, and will be assumed to be
1001      * because of some substitution that we can't handle.
1002      */
1003     private static char[] getGlyphToCharMapForFont(Font2D font2D) {
1004         /* NB Composites report the number of glyphs in slot 0.
1005          * So if a string uses a char from a later slot, or a fallback slot,
1006          * it will not be able to use this faster path.
1007          */
1008         int numGlyphs = font2D.getNumGlyphs();
1009         int missingGlyph = font2D.getMissingGlyphCode();
1010         char[] glyphToCharMap = new char[numGlyphs];
1011         int glyph;
1012 
1013         for (int i=0;i<numGlyphs; i++) {
1014             glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1015         }
1016 
1017         /* Consider refining the ranges to try to map by asking the font
1018          * what ranges it supports.
1019          * Since a glyph may be mapped by multiple code points, and this
1020          * code can't handle that, we always prefer the earlier code point.
1021          */
1022         for (char c=0; c<0xFFFF; c++) {
1023            if (c >= CharToGlyphMapper.HI_SURROGATE_START &&
1024                c <= CharToGlyphMapper.LO_SURROGATE_END) {
1025                 continue;
1026             }
1027             glyph = font2D.charToGlyph(c);
1028             if (glyph != missingGlyph && glyph < numGlyphs &&
1029                 (glyphToCharMap[glyph] ==
1030                  CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {
1031                 glyphToCharMap[glyph] = c;
1032             }
1033         }
1034         return glyphToCharMap;
1035     }
1036 
1037     /**
1038      * Strokes the outline of a Shape using the settings of the current
1039      * graphics state.  The rendering attributes applied include the
1040      * clip, transform, paint or color, composite and stroke attributes.
1041      * @param s The shape to be drawn.
1042      * @see #setStroke
1043      * @see #setPaint
1044      * @see java.awt.Graphics#setColor
1045      * @see #transform
1046      * @see #setTransform
1047      * @see #clip
1048      * @see #setClip
1049      * @see #setComposite
1050      */
1051     public void draw(Shape s) {
1052 
1053         fill(getStroke().createStrokedShape(s));
1054     }
1055 
1056     /**
1057      * Fills the interior of a Shape using the settings of the current
1058      * graphics state. The rendering attributes applied include the
1059      * clip, transform, paint or color, and composite.
1060      * @see #setPaint
1061      * @see java.awt.Graphics#setColor
1062      * @see #transform
1063      * @see #setTransform
1064      * @see #setComposite
1065      * @see #clip
1066      * @see #setClip
1067      */
1068     public void fill(Shape s) {
1069         Paint paint = getPaint();
1070 
1071         try {
1072             fill(s, (Color) paint);
1073 
1074         /* The PathGraphics class only supports filling with
1075          * solid colors and so we do not expect the cast of Paint
1076          * to Color to fail. If it does fail then something went
1077          * wrong, like the app draw a page with a solid color but
1078          * then redrew it with a Gradient.
1079          */
1080         } catch (ClassCastException e) {
1081             throw new IllegalArgumentException("Expected a Color instance");
1082         }
1083     }
1084 
1085     public void fill(Shape s, Color color) {
1086         AffineTransform deviceTransform = getTransform();
1087 
1088         if (getClip() != null) {
1089             deviceClip(getClip().getPathIterator(deviceTransform));
1090         }
1091         deviceFill(s.getPathIterator(deviceTransform), color);
1092     }
1093 
1094     /**
1095      * Fill the path defined by <code>pathIter</code>
1096      * with the specified color.
1097      * The path is provided in device coordinates.
1098      */
1099     protected abstract void deviceFill(PathIterator pathIter, Color color);
1100 
1101     /*
1102      * Set the clipping path to that defined by
1103      * the passed in <code>PathIterator</code>.
1104      */
1105     protected abstract void deviceClip(PathIterator pathIter);
1106 
1107     /*
1108      * Draw the outline of the rectangle without using path
1109      * if supported by platform.
1110      */
1111     protected abstract void deviceFrameRect(int x, int y,
1112                                             int width, int height,
1113                                             Color color);
1114 
1115     /*
1116      * Draw a line without using path if supported by platform.
1117      */
1118     protected abstract void deviceDrawLine(int xBegin, int yBegin,
1119                                            int xEnd, int yEnd, Color color);
1120 
1121     /*
1122      * Fill a rectangle using specified color.
1123      */
1124     protected abstract void deviceFillRect(int x, int y,
1125                                            int width, int height, Color color);
1126 
1127     /* Obtain a BI from known implementations of java.awt.Image
1128      */
1129     protected BufferedImage getBufferedImage(Image img) {
1130         if (img instanceof BufferedImage) {
1131             // Otherwise we expect a BufferedImage to behave as a standard BI
1132             return (BufferedImage)img;
1133         } else if (img instanceof ToolkitImage) {
1134             // This can be null if the image isn't loaded yet.
1135             // This is fine as in that case our caller will return
1136             // as it will only draw a fully loaded image
1137             return ((ToolkitImage)img).getBufferedImage();
1138         } else if (img instanceof VolatileImage) {
1139             // VI needs to make a new BI: this is unavoidable but
1140             // I don't expect VI's to be "huge" in any case.
1141             return ((VolatileImage)img).getSnapshot();
1142         } else {
1143             // may be null or may be some non-standard Image which
1144             // shouldn't happen as Image is implemented by the platform
1145             // not by applications
1146             // If you add a new Image implementation to the platform you
1147             // will need to support it here similarly to VI.
1148             return null;
1149         }
1150     }
1151 
1152     /**
1153      * Return true if the BufferedImage argument has non-opaque
1154      * bits in it and therefore can not be directly rendered by
1155      * GDI. Return false if the image is opaque. If this function
1156      * can not tell for sure whether the image has transparent
1157      * pixels then it assumes that it does.
1158      */
1159     protected boolean hasTransparentPixels(BufferedImage bufferedImage) {
1160         ColorModel colorModel = bufferedImage.getColorModel();
1161         boolean hasTransparency = colorModel == null
1162             ? true
1163             : colorModel.getTransparency() != ColorModel.OPAQUE;
1164 
1165         /*
1166          * For the default INT ARGB check the image to see if any pixels are
1167          * really transparent. If there are no transparent pixels then the
1168          * transparency of the color model can be ignored.
1169          * We assume that IndexColorModel images have already been
1170          * checked for transparency and will be OPAQUE unless they actually
1171          * have transparent pixels present.
1172          */
1173         if (hasTransparency && bufferedImage != null) {
1174             if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB ||
1175                 bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) {
1176                 DataBuffer db =  bufferedImage.getRaster().getDataBuffer();
1177                 SampleModel sm = bufferedImage.getRaster().getSampleModel();
1178                 if (db instanceof DataBufferInt &&
1179                     sm instanceof SinglePixelPackedSampleModel) {
1180                     SinglePixelPackedSampleModel psm =
1181                         (SinglePixelPackedSampleModel)sm;
1182                     // Stealing the data array for reading only...
1183                     int[] int_data =
1184                         SunWritableRaster.stealData((DataBufferInt) db, 0);
1185                     int x = bufferedImage.getMinX();
1186                     int y = bufferedImage.getMinY();
1187                     int w = bufferedImage.getWidth();
1188                     int h = bufferedImage.getHeight();
1189                     int stride = psm.getScanlineStride();
1190                     boolean hastranspixel = false;
1191                     for (int j = y; j < y+h; j++) {
1192                         int yoff = j * stride;
1193                         for (int i = x; i < x+w; i++) {
1194                             if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) {
1195                                 hastranspixel = true;
1196                                 break;
1197                             }
1198                         }
1199                         if (hastranspixel) {
1200                             break;
1201                         }
1202                     }
1203                     if (hastranspixel == false) {
1204                         hasTransparency = false;
1205                     }
1206                 }
1207             }
1208         }
1209 
1210         return hasTransparency;
1211     }
1212 
1213     protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {
1214         ColorModel colorModel = bufferedImage.getColorModel();
1215         return (colorModel != null &&
1216                 colorModel.getTransparency() == ColorModel.BITMASK);
1217     }
1218 
1219 
1220     /* An optimisation for the special case of ICM images which have
1221      * bitmask transparency.
1222      */
1223     protected boolean drawBitmaskImage(BufferedImage bufferedImage,
1224                                        AffineTransform xform,
1225                                        Color bgcolor,
1226                                        int srcX, int srcY,
1227                                        int srcWidth, int srcHeight) {
1228 
1229         ColorModel colorModel = bufferedImage.getColorModel();
1230         IndexColorModel icm;
1231         int [] pixels;
1232 
1233         if (!(colorModel instanceof IndexColorModel)) {
1234             return false;
1235         } else {
1236             icm = (IndexColorModel)colorModel;
1237         }
1238 
1239         if (colorModel.getTransparency() != ColorModel.BITMASK) {
1240             return false;
1241         }
1242 
1243         // to be compatible with 1.1 printing which treated b/g colors
1244         // with alpha 128 as opaque
1245         if (bgcolor != null && bgcolor.getAlpha() < 128) {
1246             return false;
1247         }
1248 
1249         if ((xform.getType()
1250              & ~( AffineTransform.TYPE_UNIFORM_SCALE
1251                   | AffineTransform.TYPE_TRANSLATION
1252                   | AffineTransform.TYPE_QUADRANT_ROTATION
1253                   )) != 0) {
1254             return false;
1255         }
1256 
1257         if ((getTransform().getType()
1258              & ~( AffineTransform.TYPE_UNIFORM_SCALE
1259                   | AffineTransform.TYPE_TRANSLATION
1260                   | AffineTransform.TYPE_QUADRANT_ROTATION
1261                   )) != 0) {
1262             return false;
1263         }
1264 
1265         BufferedImage subImage = null;
1266         Raster raster = bufferedImage.getRaster();
1267         int transpixel = icm.getTransparentPixel();
1268         byte[] alphas = new byte[icm.getMapSize()];
1269         icm.getAlphas(alphas);
1270         if (transpixel >= 0) {
1271             alphas[transpixel] = 0;
1272         }
1273 
1274         /* don't just use srcWidth & srcHeight from application - they
1275          * may exceed the extent of the image - may need to clip.
1276          * The image xform will ensure that points are still mapped properly.
1277          */
1278         int rw = raster.getWidth();
1279         int rh = raster.getHeight();
1280         if (srcX > rw || srcY > rh) {
1281             return false;
1282         }
1283         int right, bottom, wid, hgt;
1284         if (srcX+srcWidth > rw) {
1285             right = rw;
1286             wid = right - srcX;
1287         } else {
1288             right = srcX+srcWidth;
1289             wid = srcWidth;
1290         }
1291         if (srcY+srcHeight > rh) {
1292             bottom = rh;
1293             hgt = bottom - srcY;
1294         } else {
1295             bottom = srcY+srcHeight;
1296             hgt = srcHeight;
1297         }
1298         pixels = new int[wid];
1299         for (int j=srcY; j<bottom; j++) {
1300             int startx = -1;
1301             raster.getPixels(srcX, j, wid, 1, pixels);
1302             for (int i=srcX; i<right; i++) {
1303                 if (alphas[pixels[i-srcX]] == 0) {
1304                     if (startx >=0) {
1305                         subImage = bufferedImage.getSubimage(startx, j,
1306                                                              i-startx, 1);
1307                         xform.translate(startx, j);
1308                         drawImageToPlatform(subImage, xform, bgcolor,
1309                                       0, 0, i-startx, 1, true);
1310                         xform.translate(-startx, -j);
1311                         startx = -1;
1312                     }
1313                 } else if (startx < 0) {
1314                     startx = i;
1315                 }
1316             }
1317             if (startx >= 0) {
1318                 subImage = bufferedImage.getSubimage(startx, j,
1319                                                      right - startx, 1);
1320                 xform.translate(startx, j);
1321                 drawImageToPlatform(subImage, xform, bgcolor,
1322                               0, 0, right - startx, 1, true);
1323                 xform.translate(-startx, -j);
1324             }
1325         }
1326         return true;
1327     }
1328 
1329 
1330 
1331     /**
1332      * The various <code>drawImage()</code> methods for
1333      * <code>PathGraphics</code> are all decomposed
1334      * into an invocation of <code>drawImageToPlatform</code>.
1335      * The portion of the passed in image defined by
1336      * <code>srcX, srcY, srcWidth, and srcHeight</code>
1337      * is transformed by the supplied AffineTransform and
1338      * drawn using PS to the printer context.
1339      *
1340      * @param   img     The image to be drawn.
1341      *                  This method does nothing if <code>img</code> is null.
1342      * @param   xform   Used to tranform the image before drawing.
1343      *                  This can be null.
1344      * @param   bgcolor This color is drawn where the image has transparent
1345      *                  pixels. If this parameter is null then the
1346      *                  pixels already in the destination should show
1347      *                  through.
1348      * @param   srcX    With srcY this defines the upper-left corner
1349      *                  of the portion of the image to be drawn.
1350      *
1351      * @param   srcY    With srcX this defines the upper-left corner
1352      *                  of the portion of the image to be drawn.
1353      * @param   srcWidth    The width of the portion of the image to
1354      *                      be drawn.
1355      * @param   srcHeight   The height of the portion of the image to
1356      *                      be drawn.
1357      * @param   handlingTransparency if being recursively called to
1358      *                    print opaque region of transparent image
1359      */
1360     protected abstract boolean
1361         drawImageToPlatform(Image img, AffineTransform xform,
1362                             Color bgcolor,
1363                             int srcX, int srcY,
1364                             int srcWidth, int srcHeight,
1365                             boolean handlingTransparency);
1366 
1367     /**
1368      * Draws as much of the specified image as is currently available.
1369      * The image is drawn with its top-left corner at
1370      * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
1371      * space. Transparent pixels in the image do not affect whatever
1372      * pixels are already there.
1373      * <p>
1374      * This method returns immediately in all cases, even if the
1375      * complete image has not yet been loaded, and it has not been dithered
1376      * and converted for the current output device.
1377      * <p>
1378      * If the image has not yet been completely loaded, then
1379      * <code>drawImage</code> returns <code>false</code>. As more of
1380      * the image becomes available, the process that draws the image notifies
1381      * the specified image observer.
1382      * @param    img the specified image to be drawn.
1383      * @param    x   the <i>x</i> coordinate.
1384      * @param    y   the <i>y</i> coordinate.
1385      * @param    observer    object to be notified as more of
1386      *                          the image is converted.
1387      * @see      java.awt.Image
1388      * @see      java.awt.image.ImageObserver
1389      * @see      java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1390      * @since    JDK1.0
1391      */
1392     public boolean drawImage(Image img, int x, int y,
1393                              ImageObserver observer) {
1394 
1395         return drawImage(img, x, y, null, observer);
1396     }
1397 
1398     /**
1399      * Draws as much of the specified image as has already been scaled
1400      * to fit inside the specified rectangle.
1401      * <p>
1402      * The image is drawn inside the specified rectangle of this
1403      * graphics context's coordinate space, and is scaled if
1404      * necessary. Transparent pixels do not affect whatever pixels
1405      * are already there.
1406      * <p>
1407      * This method returns immediately in all cases, even if the
1408      * entire image has not yet been scaled, dithered, and converted
1409      * for the current output device.
1410      * If the current output representation is not yet complete, then
1411      * <code>drawImage</code> returns <code>false</code>. As more of
1412      * the image becomes available, the process that draws the image notifies
1413      * the image observer by calling its <code>imageUpdate</code> method.
1414      * <p>
1415      * A scaled version of an image will not necessarily be
1416      * available immediately just because an unscaled version of the
1417      * image has been constructed for this output device.  Each size of
1418      * the image may be cached separately and generated from the original
1419      * data in a separate image production sequence.
1420      * @param    img    the specified image to be drawn.
1421      * @param    x      the <i>x</i> coordinate.
1422      * @param    y      the <i>y</i> coordinate.
1423      * @param    width  the width of the rectangle.
1424      * @param    height the height of the rectangle.
1425      * @param    observer    object to be notified as more of
1426      *                          the image is converted.
1427      * @see      java.awt.Image
1428      * @see      java.awt.image.ImageObserver
1429      * @see      java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1430      * @since    JDK1.0
1431      */
1432     public boolean drawImage(Image img, int x, int y,
1433                              int width, int height,
1434                              ImageObserver observer) {
1435 
1436         return drawImage(img, x, y, width, height, null, observer);
1437 
1438     }
1439 
1440     /*
1441      * Draws as much of the specified image as is currently available.
1442      * The image is drawn with its top-left corner at
1443      * (<i>x</i>,&nbsp;<i>y</i>) in this graphics context's coordinate
1444      * space.  Transparent pixels are drawn in the specified
1445      * background color.
1446      * <p>
1447      * This operation is equivalent to filling a rectangle of the
1448      * width and height of the specified image with the given color and then
1449      * drawing the image on top of it, but possibly more efficient.
1450      * <p>
1451      * This method returns immediately in all cases, even if the
1452      * complete image has not yet been loaded, and it has not been dithered
1453      * and converted for the current output device.
1454      * <p>
1455      * If the image has not yet been completely loaded, then
1456      * <code>drawImage</code> returns <code>false</code>. As more of
1457      * the image becomes available, the process that draws the image notifies
1458      * the specified image observer.
1459      * @param    img    the specified image to be drawn.
1460      *                  This method does nothing if <code>img</code> is null.
1461      * @param    x      the <i>x</i> coordinate.
1462      * @param    y      the <i>y</i> coordinate.
1463      * @param    bgcolor the background color to paint under the
1464      *                   non-opaque portions of the image.
1465      *                   In this WPathGraphics implementation,
1466      *                   this parameter can be null in which
1467      *                   case that background is made a transparent
1468      *                   white.
1469      * @param    observer    object to be notified as more of
1470      *                          the image is converted.
1471      * @see      java.awt.Image
1472      * @see      java.awt.image.ImageObserver
1473      * @see      java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1474      * @since    JDK1.0
1475      */
1476     public boolean drawImage(Image img, int x, int y,
1477                              Color bgcolor,
1478                              ImageObserver observer) {
1479 
1480         if (img == null) {
1481             return true;
1482         }
1483 
1484         boolean result;
1485         int srcWidth = img.getWidth(null);
1486         int srcHeight = img.getHeight(null);
1487 
1488         if (srcWidth < 0 || srcHeight < 0) {
1489             result = false;
1490         } else {
1491             result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer);
1492         }
1493 
1494         return result;
1495     }
1496 
1497     /**
1498      * Draws as much of the specified image as has already been scaled
1499      * to fit inside the specified rectangle.
1500      * <p>
1501      * The image is drawn inside the specified rectangle of this
1502      * graphics context's coordinate space, and is scaled if
1503      * necessary. Transparent pixels are drawn in the specified
1504      * background color.
1505      * This operation is equivalent to filling a rectangle of the
1506      * width and height of the specified image with the given color and then
1507      * drawing the image on top of it, but possibly more efficient.
1508      * <p>
1509      * This method returns immediately in all cases, even if the
1510      * entire image has not yet been scaled, dithered, and converted
1511      * for the current output device.
1512      * If the current output representation is not yet complete then
1513      * <code>drawImage</code> returns <code>false</code>. As more of
1514      * the image becomes available, the process that draws the image notifies
1515      * the specified image observer.
1516      * <p>
1517      * A scaled version of an image will not necessarily be
1518      * available immediately just because an unscaled version of the
1519      * image has been constructed for this output device.  Each size of
1520      * the image may be cached separately and generated from the original
1521      * data in a separate image production sequence.
1522      * @param    img       the specified image to be drawn.
1523      *                     This method does nothing if <code>img</code> is null.
1524      * @param    x         the <i>x</i> coordinate.
1525      * @param    y         the <i>y</i> coordinate.
1526      * @param    width     the width of the rectangle.
1527      * @param    height    the height of the rectangle.
1528      * @param    bgcolor   the background color to paint under the
1529      *                         non-opaque portions of the image.
1530      * @param    observer    object to be notified as more of
1531      *                          the image is converted.
1532      * @see      java.awt.Image
1533      * @see      java.awt.image.ImageObserver
1534      * @see      java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1535      * @since    JDK1.0
1536      */
1537     public boolean drawImage(Image img, int x, int y,
1538                              int width, int height,
1539                              Color bgcolor,
1540                              ImageObserver observer) {
1541 
1542         if (img == null) {
1543             return true;
1544         }
1545 
1546         boolean result;
1547         int srcWidth = img.getWidth(null);
1548         int srcHeight = img.getHeight(null);
1549 
1550         if (srcWidth < 0 || srcHeight < 0) {
1551             result = false;
1552         } else {
1553             result = drawImage(img,
1554                          x, y, x + width, y + height,
1555                          0, 0, srcWidth, srcHeight,
1556                          observer);
1557         }
1558 
1559         return result;
1560     }
1561 
1562     /**
1563      * Draws as much of the specified area of the specified image as is
1564      * currently available, scaling it on the fly to fit inside the
1565      * specified area of the destination drawable surface. Transparent pixels
1566      * do not affect whatever pixels are already there.
1567      * <p>
1568      * This method returns immediately in all cases, even if the
1569      * image area to be drawn has not yet been scaled, dithered, and converted
1570      * for the current output device.
1571      * If the current output representation is not yet complete then
1572      * <code>drawImage</code> returns <code>false</code>. As more of
1573      * the image becomes available, the process that draws the image notifies
1574      * the specified image observer.
1575      * <p>
1576      * This method always uses the unscaled version of the image
1577      * to render the scaled rectangle and performs the required
1578      * scaling on the fly. It does not use a cached, scaled version
1579      * of the image for this operation. Scaling of the image from source
1580      * to destination is performed such that the first coordinate
1581      * of the source rectangle is mapped to the first coordinate of
1582      * the destination rectangle, and the second source coordinate is
1583      * mapped to the second destination coordinate. The subimage is
1584      * scaled and flipped as needed to preserve those mappings.
1585      * @param       img the specified image to be drawn
1586      * @param       dx1 the <i>x</i> coordinate of the first corner of the
1587      *                    destination rectangle.
1588      * @param       dy1 the <i>y</i> coordinate of the first corner of the
1589      *                    destination rectangle.
1590      * @param       dx2 the <i>x</i> coordinate of the second corner of the
1591      *                    destination rectangle.
1592      * @param       dy2 the <i>y</i> coordinate of the second corner of the
1593      *                    destination rectangle.
1594      * @param       sx1 the <i>x</i> coordinate of the first corner of the
1595      *                    source rectangle.
1596      * @param       sy1 the <i>y</i> coordinate of the first corner of the
1597      *                    source rectangle.
1598      * @param       sx2 the <i>x</i> coordinate of the second corner of the
1599      *                    source rectangle.
1600      * @param       sy2 the <i>y</i> coordinate of the second corner of the
1601      *                    source rectangle.
1602      * @param       observer object to be notified as more of the image is
1603      *                    scaled and converted.
1604      * @see         java.awt.Image
1605      * @see         java.awt.image.ImageObserver
1606      * @see         java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1607      * @since       JDK1.1
1608      */
1609     public boolean drawImage(Image img,
1610                              int dx1, int dy1, int dx2, int dy2,
1611                              int sx1, int sy1, int sx2, int sy2,
1612                              ImageObserver observer) {
1613 
1614         return drawImage(img,
1615                          dx1, dy1, dx2, dy2,
1616                          sx1, sy1, sx2, sy2,
1617                          null, observer);
1618     }
1619 
1620     /**
1621      * Draws as much of the specified area of the specified image as is
1622      * currently available, scaling it on the fly to fit inside the
1623      * specified area of the destination drawable surface.
1624      * <p>
1625      * Transparent pixels are drawn in the specified background color.
1626      * This operation is equivalent to filling a rectangle of the
1627      * width and height of the specified image with the given color and then
1628      * drawing the image on top of it, but possibly more efficient.
1629      * <p>
1630      * This method returns immediately in all cases, even if the
1631      * image area to be drawn has not yet been scaled, dithered, and converted
1632      * for the current output device.
1633      * If the current output representation is not yet complete then
1634      * <code>drawImage</code> returns <code>false</code>. As more of
1635      * the image becomes available, the process that draws the image notifies
1636      * the specified image observer.
1637      * <p>
1638      * This method always uses the unscaled version of the image
1639      * to render the scaled rectangle and performs the required
1640      * scaling on the fly. It does not use a cached, scaled version
1641      * of the image for this operation. Scaling of the image from source
1642      * to destination is performed such that the first coordinate
1643      * of the source rectangle is mapped to the first coordinate of
1644      * the destination rectangle, and the second source coordinate is
1645      * mapped to the second destination coordinate. The subimage is
1646      * scaled and flipped as needed to preserve those mappings.
1647      * @param       img the specified image to be drawn
1648      *                  This method does nothing if <code>img</code> is null.
1649      * @param       dx1 the <i>x</i> coordinate of the first corner of the
1650      *                    destination rectangle.
1651      * @param       dy1 the <i>y</i> coordinate of the first corner of the
1652      *                    destination rectangle.
1653      * @param       dx2 the <i>x</i> coordinate of the second corner of the
1654      *                    destination rectangle.
1655      * @param       dy2 the <i>y</i> coordinate of the second corner of the
1656      *                    destination rectangle.
1657      * @param       sx1 the <i>x</i> coordinate of the first corner of the
1658      *                    source rectangle.
1659      * @param       sy1 the <i>y</i> coordinate of the first corner of the
1660      *                    source rectangle.
1661      * @param       sx2 the <i>x</i> coordinate of the second corner of the
1662      *                    source rectangle.
1663      * @param       sy2 the <i>y</i> coordinate of the second corner of the
1664      *                    source rectangle.
1665      * @param       bgcolor the background color to paint under the
1666      *                    non-opaque portions of the image.
1667      * @param       observer object to be notified as more of the image is
1668      *                    scaled and converted.
1669      * @see         java.awt.Image
1670      * @see         java.awt.image.ImageObserver
1671      * @see         java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1672      * @since       JDK1.1
1673      */
1674     public boolean drawImage(Image img,
1675                              int dx1, int dy1, int dx2, int dy2,
1676                              int sx1, int sy1, int sx2, int sy2,
1677                              Color bgcolor,
1678                              ImageObserver observer) {
1679 
1680         if (img == null) {
1681             return true;
1682         }
1683         int imgWidth = img.getWidth(null);
1684         int imgHeight = img.getHeight(null);
1685 
1686         if (imgWidth < 0 || imgHeight < 0) {
1687             return true;
1688         }
1689 
1690         int srcWidth = sx2 - sx1;
1691         int srcHeight = sy2 - sy1;
1692 
1693         /* Create a transform which describes the changes
1694          * from the source coordinates to the destination
1695          * coordinates. The scaling is determined by the
1696          * ratio of the two rectangles, while the translation
1697          * comes from the difference of their origins.
1698          */
1699         float scalex = (float) (dx2 - dx1) / srcWidth;
1700         float scaley = (float) (dy2 - dy1) / srcHeight;
1701         AffineTransform xForm
1702             = new AffineTransform(scalex,
1703                                   0,
1704                                   0,
1705                                   scaley,
1706                                   dx1 - (sx1 * scalex),
1707                                   dy1 - (sy1 * scaley));
1708 
1709         /* drawImageToPlatform needs the top-left of the source area and
1710          * a positive width and height. The xform describes how to map
1711          * src->dest, so that information is not lost.
1712          */
1713         int tmp=0;
1714         if (sx2 < sx1) {
1715             tmp = sx1;
1716             sx1 = sx2;
1717             sx2 = tmp;
1718         }
1719         if (sy2 < sy1) {
1720             tmp = sy1;
1721             sy1 = sy2;
1722             sy2 = tmp;
1723         }
1724 
1725         /* if src area is beyond the bounds of the image, we must clip it.
1726          * The transform is based on the specified area, not the clipped one.
1727          */
1728         if (sx1 < 0) {
1729             sx1 = 0;
1730         } else if (sx1 > imgWidth) { // empty srcArea, nothing to draw
1731             sx1 = imgWidth;
1732         }
1733         if (sx2 < 0) { // empty srcArea, nothing to draw
1734             sx2 = 0;
1735         } else if (sx2 > imgWidth) {
1736             sx2 = imgWidth;
1737         }
1738         if (sy1 < 0) {
1739             sy1 = 0;
1740         } else if (sy1 > imgHeight) { // empty srcArea
1741             sy1 = imgHeight;
1742         }
1743         if (sy2 < 0) {  // empty srcArea
1744             sy2 = 0;
1745         } else if (sy2 > imgHeight) {
1746             sy2 = imgHeight;
1747         }
1748 
1749         srcWidth =  sx2 - sx1;
1750         srcHeight = sy2 - sy1;
1751 
1752         if (srcWidth <= 0 || srcHeight <= 0) {
1753             return true;
1754         }
1755 
1756         return drawImageToPlatform(img, xForm, bgcolor,
1757                                    sx1, sy1, srcWidth, srcHeight, false);
1758 
1759 
1760     }
1761 
1762     /**
1763      * Draws an image, applying a transform from image space into user space
1764      * before drawing.
1765      * The transformation from user space into device space is done with
1766      * the current transform in the Graphics2D.
1767      * The given transformation is applied to the image before the
1768      * transform attribute in the Graphics2D state is applied.
1769      * The rendering attributes applied include the clip, transform,
1770      * and composite attributes. Note that the result is
1771      * undefined, if the given transform is noninvertible.
1772      * @param img The image to be drawn.
1773      *            This method does nothing if <code>img</code> is null.
1774      * @param xform The transformation from image space into user space.
1775      * @param obs The image observer to be notified as more of the image
1776      * is converted.
1777      * @see #transform
1778      * @see #setTransform
1779      * @see #setComposite
1780      * @see #clip
1781      * @see #setClip
1782      */
1783     public boolean drawImage(Image img,
1784                              AffineTransform xform,
1785                              ImageObserver obs) {
1786 
1787         if (img == null) {
1788             return true;
1789         }
1790 
1791         boolean result;
1792         int srcWidth = img.getWidth(null);
1793         int srcHeight = img.getHeight(null);
1794 
1795         if (srcWidth < 0 || srcHeight < 0) {
1796             result = false;
1797         } else {
1798             result = drawImageToPlatform(img, xform, null,
1799                                          0, 0, srcWidth, srcHeight, false);
1800         }
1801 
1802         return result;
1803     }
1804 
1805     /**
1806      * Draws a BufferedImage that is filtered with a BufferedImageOp.
1807      * The rendering attributes applied include the clip, transform
1808      * and composite attributes.  This is equivalent to:
1809      * <pre>
1810      * img1 = op.filter(img, null);
1811      * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
1812      * </pre>
1813      * @param op The filter to be applied to the image before drawing.
1814      * @param img The BufferedImage to be drawn.
1815      *            This method does nothing if <code>img</code> is null.
1816      * @param x,y The location in user space where the image should be drawn.
1817      * @see #transform
1818      * @see #setTransform
1819      * @see #setComposite
1820      * @see #clip
1821      * @see #setClip
1822      */
1823     public void drawImage(BufferedImage img,
1824                           BufferedImageOp op,
1825                           int x,
1826                           int y) {
1827 
1828         if (img == null) {
1829             return;
1830         }
1831 
1832         int srcWidth = img.getWidth(null);
1833         int srcHeight = img.getHeight(null);
1834 
1835         if (op != null) {
1836             img = op.filter(img, null);
1837         }
1838         if (srcWidth <= 0 || srcHeight <= 0) {
1839             return;
1840         } else {
1841             AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y);
1842             drawImageToPlatform(img, xform, null,
1843                                 0, 0, srcWidth, srcHeight, false);
1844         }
1845 
1846     }
1847 
1848     /**
1849      * Draws an image, applying a transform from image space into user space
1850      * before drawing.
1851      * The transformation from user space into device space is done with
1852      * the current transform in the Graphics2D.
1853      * The given transformation is applied to the image before the
1854      * transform attribute in the Graphics2D state is applied.
1855      * The rendering attributes applied include the clip, transform,
1856      * and composite attributes. Note that the result is
1857      * undefined, if the given transform is noninvertible.
1858      * @param img The image to be drawn.
1859      *            This method does nothing if <code>img</code> is null.
1860      * @param xform The transformation from image space into user space.
1861      * @see #transform
1862      * @see #setTransform
1863      * @see #setComposite
1864      * @see #clip
1865      * @see #setClip
1866      */
1867     public void drawRenderedImage(RenderedImage img,
1868                                   AffineTransform xform) {
1869 
1870         if (img == null) {
1871             return;
1872         }
1873 
1874         BufferedImage bufferedImage = null;
1875         int srcWidth = img.getWidth();
1876         int srcHeight = img.getHeight();
1877 
1878         if (srcWidth <= 0 || srcHeight <= 0) {
1879             return;
1880         }
1881 
1882         if (img instanceof BufferedImage) {
1883             bufferedImage = (BufferedImage) img;
1884         } else {
1885             bufferedImage = new BufferedImage(srcWidth, srcHeight,
1886                                               BufferedImage.TYPE_INT_ARGB);
1887             Graphics2D imageGraphics = bufferedImage.createGraphics();
1888             imageGraphics.drawRenderedImage(img, xform);
1889         }
1890 
1891         drawImageToPlatform(bufferedImage, xform, null,
1892                             0, 0, srcWidth, srcHeight, false);
1893 
1894     }
1895 
1896 }