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