History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/print/PathGraphics.java (1 of 18352 files). Show all

Unified · Split

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