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