1/*
2 * Copyright (c) 1998, 2013, 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
26package sun.print;
27
28import java.lang.ref.SoftReference;
29import java.util.Hashtable;
30import sun.font.CharToGlyphMapper;
31import sun.font.CompositeFont;
32import sun.font.Font2D;
33import sun.font.Font2DHandle;
34import sun.font.FontManager;
35import sun.font.FontManagerFactory;
36import sun.font.FontUtilities;
37
38import java.awt.Color;
39import java.awt.Font;
40import java.awt.Graphics2D;
41import java.awt.Image;
42import java.awt.Paint;
43import java.awt.Polygon;
44import java.awt.Shape;
45
46import java.awt.geom.Path2D;
47import java.text.AttributedCharacterIterator;
48
49import java.awt.font.FontRenderContext;
50import java.awt.font.GlyphVector;
51import java.awt.font.TextAttribute;
52import java.awt.font.TextLayout;
53
54import java.awt.geom.AffineTransform;
55import java.awt.geom.Arc2D;
56import java.awt.geom.Ellipse2D;
57import java.awt.geom.Line2D;
58import java.awt.geom.Point2D;
59import java.awt.geom.Rectangle2D;
60import java.awt.geom.RoundRectangle2D;
61import java.awt.geom.PathIterator;
62
63import java.awt.image.BufferedImage;
64import java.awt.image.BufferedImageOp;
65import java.awt.image.ColorModel;
66import java.awt.image.DataBuffer;
67import java.awt.image.DataBufferInt;
68import java.awt.image.ImageObserver;
69import java.awt.image.IndexColorModel;
70import java.awt.image.Raster;
71import java.awt.image.RenderedImage;
72import java.awt.image.SampleModel;
73import java.awt.image.SinglePixelPackedSampleModel;
74import java.awt.image.VolatileImage;
75import sun.awt.image.ByteComponentRaster;
76import sun.awt.image.ToolkitImage;
77import sun.awt.image.SunWritableRaster;
78
79import java.awt.print.PageFormat;
80import java.awt.print.Printable;
81import java.awt.print.PrinterException;
82import java.awt.print.PrinterGraphics;
83import java.awt.print.PrinterJob;
84
85import java.util.Map;
86
87public 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}
1894