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/java2d/SunGraphics2D.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/java2d/SunGraphics2D.java+3310−0
@@ -0,0 +1,3310 @@
1+/*
2+ * Copyright (c) 1996, 2008, 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.java2d;
27+
28+import java.awt.Graphics;
29+import java.awt.Graphics2D;
30+import java.awt.RenderingHints;
31+import java.awt.RenderingHints.Key;
32+import java.awt.geom.Area;
33+import java.awt.geom.AffineTransform;
34+import java.awt.geom.NoninvertibleTransformException;
35+import java.awt.AlphaComposite;
36+import java.awt.BasicStroke;
37+import java.awt.image.BufferedImage;
38+import java.awt.image.BufferedImageOp;
39+import java.awt.image.RenderedImage;
40+import java.awt.image.renderable.RenderableImage;
41+import java.awt.image.renderable.RenderContext;
42+import java.awt.image.AffineTransformOp;
43+import java.awt.image.Raster;
44+import java.awt.image.WritableRaster;
45+import java.awt.Image;
46+import java.awt.Composite;
47+import java.awt.Color;
48+import java.awt.image.ColorModel;
49+import java.awt.GraphicsConfiguration;
50+import java.awt.Paint;
51+import java.awt.GradientPaint;
52+import java.awt.LinearGradientPaint;
53+import java.awt.RadialGradientPaint;
54+import java.awt.TexturePaint;
55+import java.awt.geom.Rectangle2D;
56+import java.awt.geom.PathIterator;
57+import java.awt.geom.GeneralPath;
58+import java.awt.Shape;
59+import java.awt.Stroke;
60+import java.awt.FontMetrics;
61+import java.awt.Rectangle;
62+import java.text.AttributedCharacterIterator;
63+import java.awt.Font;
64+import java.awt.image.ImageObserver;
65+import java.awt.Transparency;
66+import java.awt.font.GlyphVector;
67+import java.awt.font.TextLayout;
68+import sun.font.FontDesignMetrics;
69+import sun.font.FontUtilities;
70+import sun.java2d.pipe.PixelDrawPipe;
71+import sun.java2d.pipe.PixelFillPipe;
72+import sun.java2d.pipe.ShapeDrawPipe;
73+import sun.java2d.pipe.ValidatePipe;
74+import sun.java2d.pipe.ShapeSpanIterator;
75+import sun.java2d.pipe.Region;
76+import sun.java2d.pipe.TextPipe;
77+import sun.java2d.pipe.DrawImagePipe;
78+import sun.java2d.pipe.LoopPipe;
79+import sun.java2d.loops.FontInfo;
80+import sun.java2d.loops.RenderLoops;
81+import sun.java2d.loops.CompositeType;
82+import sun.java2d.loops.SurfaceType;
83+import sun.java2d.loops.Blit;
84+import sun.java2d.loops.MaskFill;
85+import sun.font.FontManager;
86+import java.awt.font.FontRenderContext;
87+import sun.java2d.loops.XORComposite;
88+import sun.awt.ConstrainableGraphics;
89+import sun.awt.SunHints;
90+import java.util.Map;
91+import java.util.Iterator;
92+import sun.java2d.DestSurfaceProvider;
93+import sun.misc.PerformanceLogger;
94+
95+/**
96+ * This is a the master Graphics2D superclass for all of the Sun
97+ * Graphics implementations. This class relies on subclasses to
98+ * manage the various device information, but provides an overall
99+ * general framework for performing all of the requests in the
100+ * Graphics and Graphics2D APIs.
101+ *
102+ * @author Jim Graham
103+ */
104+public final class SunGraphics2D
105+ extends Graphics2D
106+ implements ConstrainableGraphics, Cloneable, DestSurfaceProvider
107+{
108+ /*
109+ * Attribute States
110+ */
111+ /* Paint */
112+ public static final int PAINT_CUSTOM = 6; /* Any other Paint object */
113+ public static final int PAINT_TEXTURE = 5; /* Tiled Image */
114+ public static final int PAINT_RAD_GRADIENT = 4; /* Color RadialGradient */
115+ public static final int PAINT_LIN_GRADIENT = 3; /* Color LinearGradient */
116+ public static final int PAINT_GRADIENT = 2; /* Color Gradient */
117+ public static final int PAINT_ALPHACOLOR = 1; /* Non-opaque Color */
118+ public static final int PAINT_OPAQUECOLOR = 0; /* Opaque Color */
119+
120+ /* Composite*/
121+ public static final int COMP_CUSTOM = 3;/* Custom Composite */
122+ public static final int COMP_XOR = 2;/* XOR Mode Composite */
123+ public static final int COMP_ALPHA = 1;/* AlphaComposite */
124+ public static final int COMP_ISCOPY = 0;/* simple stores into destination,
125+ * i.e. Src, SrcOverNoEa, and other
126+ * alpha modes which replace
127+ * the destination.
128+ */
129+
130+ /* Stroke */
131+ public static final int STROKE_CUSTOM = 3; /* custom Stroke */
132+ public static final int STROKE_WIDE = 2; /* BasicStroke */
133+ public static final int STROKE_THINDASHED = 1; /* BasicStroke */
134+ public static final int STROKE_THIN = 0; /* BasicStroke */
135+
136+ /* Transform */
137+ public static final int TRANSFORM_GENERIC = 4; /* any 3x2 */
138+ public static final int TRANSFORM_TRANSLATESCALE = 3; /* scale XY */
139+ public static final int TRANSFORM_ANY_TRANSLATE = 2; /* non-int translate */
140+ public static final int TRANSFORM_INT_TRANSLATE = 1; /* int translate */
141+ public static final int TRANSFORM_ISIDENT = 0; /* Identity */
142+
143+ /* Clipping */
144+ public static final int CLIP_SHAPE = 2; /* arbitrary clip */
145+ public static final int CLIP_RECTANGULAR = 1; /* rectangular clip */
146+ public static final int CLIP_DEVICE = 0; /* no clipping set */
147+
148+ /* The following fields are used when the current Paint is a Color. */
149+ public int eargb; // ARGB value with ExtraAlpha baked in
150+ public int pixel; // pixel value for eargb
151+
152+ public SurfaceData surfaceData;
153+
154+ public PixelDrawPipe drawpipe;
155+ public PixelFillPipe fillpipe;
156+ public DrawImagePipe imagepipe;
157+ public ShapeDrawPipe shapepipe;
158+ public TextPipe textpipe;
159+ public MaskFill alphafill;
160+
161+ public RenderLoops loops;
162+
163+ public CompositeType imageComp; /* Image Transparency checked on fly */
164+
165+ public int paintState;
166+ public int compositeState;
167+ public int strokeState;
168+ public int transformState;
169+ public int clipState;
170+
171+ public Color foregroundColor;
172+ public Color backgroundColor;
173+
174+ public AffineTransform transform;
175+ public int transX;
176+ public int transY;
177+
178+ protected static final Stroke defaultStroke = new BasicStroke();
179+ protected static final Composite defaultComposite = AlphaComposite.SrcOver;
180+ private static final Font defaultFont =
181+ new Font(Font.DIALOG, Font.PLAIN, 12);
182+
183+ public Paint paint;
184+ public Stroke stroke;
185+ public Composite composite;
186+ protected Font font;
187+ protected FontMetrics fontMetrics;
188+
189+ public int renderHint;
190+ public int antialiasHint;
191+ public int textAntialiasHint;
192+ private int fractionalMetricsHint;
193+
194+ /* A gamma adjustment to the colour used in lcd text blitting */
195+ public int lcdTextContrast;
196+ private static int lcdTextContrastDefaultValue = 140;
197+
198+ private int interpolationHint; // raw value of rendering Hint
199+ public int strokeHint;
200+
201+ public int interpolationType; // algorithm choice based on
202+ // interpolation and render Hints
203+
204+ public RenderingHints hints;
205+
206+ public Region constrainClip; // lightweight bounds
207+ public int constrainX;
208+ public int constrainY;
209+
210+ public Region clipRegion;
211+ public Shape usrClip;
212+ protected Region devClip; // Actual physical drawable
213+
214+ // cached state for text rendering
215+ private boolean validFontInfo;
216+ private FontInfo fontInfo;
217+ private FontInfo glyphVectorFontInfo;
218+ private FontRenderContext glyphVectorFRC;
219+
220+ private final static int slowTextTransformMask =
221+ AffineTransform.TYPE_GENERAL_TRANSFORM
222+ | AffineTransform.TYPE_MASK_ROTATION
223+ | AffineTransform.TYPE_FLIP;
224+
225+ static {
226+ if (PerformanceLogger.loggingEnabled()) {
227+ PerformanceLogger.setTime("SunGraphics2D static initialization");
228+ }
229+ }
230+
231+ public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {
232+ surfaceData = sd;
233+ foregroundColor = fg;
234+ backgroundColor = bg;
235+
236+ transform = new AffineTransform();
237+ stroke = defaultStroke;
238+ composite = defaultComposite;
239+ paint = foregroundColor;
240+
241+ imageComp = CompositeType.SrcOverNoEa;
242+
243+ renderHint = SunHints.INTVAL_RENDER_DEFAULT;
244+ antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
245+ textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
246+ fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
247+ lcdTextContrast = lcdTextContrastDefaultValue;
248+ interpolationHint = -1;
249+ strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
250+
251+ interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
252+
253+ validateColor();
254+
255+ font = f;
256+ if (font == null) {
257+ font = defaultFont;
258+ }
259+
260+ setDevClip(sd.getBounds());
261+ invalidatePipe();
262+ }
263+
264+ protected Object clone() {
265+ try {
266+ SunGraphics2D g = (SunGraphics2D) super.clone();
267+ g.transform = new AffineTransform(this.transform);
268+ if (hints != null) {
269+ g.hints = (RenderingHints) this.hints.clone();
270+ }
271+ /* FontInfos are re-used, so must be cloned too, if they
272+ * are valid, and be nulled out if invalid.
273+ * The implied trade-off is that there is more to be gained
274+ * from re-using these objects than is lost by having to
275+ * clone them when the SG2D is cloned.
276+ */
277+ if (this.fontInfo != null) {
278+ if (this.validFontInfo) {
279+ g.fontInfo = (FontInfo)this.fontInfo.clone();
280+ } else {
281+ g.fontInfo = null;
282+ }
283+ }
284+ if (this.glyphVectorFontInfo != null) {
285+ g.glyphVectorFontInfo =
286+ (FontInfo)this.glyphVectorFontInfo.clone();
287+ g.glyphVectorFRC = this.glyphVectorFRC;
288+ }
289+ //g.invalidatePipe();
290+ return g;
291+ } catch (CloneNotSupportedException e) {
292+ }
293+ return null;
294+ }
295+
296+ /**
297+ * Create a new SunGraphics2D based on this one.
298+ */
299+ public Graphics create() {
300+ return (Graphics) clone();
301+ }
302+
303+ public void setDevClip(int x, int y, int w, int h) {
304+ Region c = constrainClip;
305+ if (c == null) {
306+ devClip = Region.getInstanceXYWH(x, y, w, h);
307+ } else {
308+ devClip = c.getIntersectionXYWH(x, y, w, h);
309+ }
310+ validateCompClip();
311+ }
312+
313+ public void setDevClip(Rectangle r) {
314+ setDevClip(r.x, r.y, r.width, r.height);
315+ }
316+
317+ /**
318+ * Constrain rendering for lightweight objects.
319+ *
320+ * REMIND: This method will back off to the "workaround"
321+ * of using translate and clipRect if the Graphics
322+ * to be constrained has a complex transform. The
323+ * drawback of the workaround is that the resulting
324+ * clip and device origin cannot be "enforced".
325+ *
326+ * @exception IllegalStateException If the Graphics
327+ * to be constrained has a complex transform.
328+ */
329+ public void constrain(int x, int y, int w, int h) {
330+ if ((x|y) != 0) {
331+ translate(x, y);
332+ }
333+ if (transformState >= TRANSFORM_TRANSLATESCALE) {
334+ clipRect(0, 0, w, h);
335+ return;
336+ }
337+ x = constrainX = transX;
338+ y = constrainY = transY;
339+ w = Region.dimAdd(x, w);
340+ h = Region.dimAdd(y, h);
341+ Region c = constrainClip;
342+ if (c == null) {
343+ c = Region.getInstanceXYXY(x, y, w, h);
344+ } else {
345+ c = c.getIntersectionXYXY(x, y, w, h);
346+ if (c == constrainClip) {
347+ // Common case to ignore
348+ return;
349+ }
350+ }
351+ constrainClip = c;
352+ if (!devClip.isInsideQuickCheck(c)) {
353+ devClip = devClip.getIntersection(c);
354+ validateCompClip();
355+ }
356+ }
357+
358+ protected static ValidatePipe invalidpipe = new ValidatePipe();
359+
360+ /*
361+ * Invalidate the pipeline
362+ */
363+ protected void invalidatePipe() {
364+ drawpipe = invalidpipe;
365+ fillpipe = invalidpipe;
366+ shapepipe = invalidpipe;
367+ textpipe = invalidpipe;
368+ imagepipe = invalidpipe;
369+ loops = null;
370+ }
371+
372+ public void validatePipe() {
373+ surfaceData.validatePipe(this);
374+ }
375+
376+ /*
377+ * Intersect two Shapes by the simplest method, attempting to produce
378+ * a simplified result.
379+ * The boolean arguments keep1 and keep2 specify whether or not
380+ * the first or second shapes can be modified during the operation
381+ * or whether that shape must be "kept" unmodified.
382+ */
383+ Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) {
384+ if (s1 instanceof Rectangle && s2 instanceof Rectangle) {
385+ return ((Rectangle) s1).intersection((Rectangle) s2);
386+ }
387+ if (s1 instanceof Rectangle2D) {
388+ return intersectRectShape((Rectangle2D) s1, s2, keep1, keep2);
389+ } else if (s2 instanceof Rectangle2D) {
390+ return intersectRectShape((Rectangle2D) s2, s1, keep2, keep1);
391+ }
392+ return intersectByArea(s1, s2, keep1, keep2);
393+ }
394+
395+ /*
396+ * Intersect a Rectangle with a Shape by the simplest method,
397+ * attempting to produce a simplified result.
398+ * The boolean arguments keep1 and keep2 specify whether or not
399+ * the first or second shapes can be modified during the operation
400+ * or whether that shape must be "kept" unmodified.
401+ */
402+ Shape intersectRectShape(Rectangle2D r, Shape s,
403+ boolean keep1, boolean keep2) {
404+ if (s instanceof Rectangle2D) {
405+ Rectangle2D r2 = (Rectangle2D) s;
406+ Rectangle2D outrect;
407+ if (!keep1) {
408+ outrect = r;
409+ } else if (!keep2) {
410+ outrect = r2;
411+ } else {
412+ outrect = new Rectangle2D.Float();
413+ }
414+ double x1 = Math.max(r.getX(), r2.getX());
415+ double x2 = Math.min(r.getX() + r.getWidth(),
416+ r2.getX() + r2.getWidth());
417+ double y1 = Math.max(r.getY(), r2.getY());
418+ double y2 = Math.min(r.getY() + r.getHeight(),
419+ r2.getY() + r2.getHeight());
420+
421+ if (((x2 - x1) < 0) || ((y2 - y1) < 0))
422+ // Width or height is negative. No intersection.
423+ outrect.setFrameFromDiagonal(0, 0, 0, 0);
424+ else
425+ outrect.setFrameFromDiagonal(x1, y1, x2, y2);
426+ return outrect;
427+ }
428+ if (r.contains(s.getBounds2D())) {
429+ if (keep2) {
430+ s = cloneShape(s);
431+ }
432+ return s;
433+ }
434+ return intersectByArea(r, s, keep1, keep2);
435+ }
436+
437+ protected static Shape cloneShape(Shape s) {
438+ return new GeneralPath(s);
439+ }
440+
441+ /*
442+ * Intersect two Shapes using the Area class. Presumably other
443+ * attempts at simpler intersection methods proved fruitless.
444+ * The boolean arguments keep1 and keep2 specify whether or not
445+ * the first or second shapes can be modified during the operation
446+ * or whether that shape must be "kept" unmodified.
447+ * @see #intersectShapes
448+ * @see #intersectRectShape
449+ */
450+ Shape intersectByArea(Shape s1, Shape s2, boolean keep1, boolean keep2) {
451+ Area a1, a2;
452+
453+ // First see if we can find an overwriteable source shape
454+ // to use as our destination area to avoid duplication.
455+ if (!keep1 && (s1 instanceof Area)) {
456+ a1 = (Area) s1;
457+ } else if (!keep2 && (s2 instanceof Area)) {
458+ a1 = (Area) s2;
459+ s2 = s1;
460+ } else {
461+ a1 = new Area(s1);
462+ }
463+
464+ if (s2 instanceof Area) {
465+ a2 = (Area) s2;
466+ } else {
467+ a2 = new Area(s2);
468+ }
469+
470+ a1.intersect(a2);
471+ if (a1.isRectangular()) {
472+ return a1.getBounds();
473+ }
474+
475+ return a1;
476+ }
477+
478+ /*
479+ * Intersect usrClip bounds and device bounds to determine the composite
480+ * rendering boundaries.
481+ */
482+ public Region getCompClip() {
483+ if (!surfaceData.isValid()) {
484+ // revalidateAll() implicitly recalculcates the composite clip
485+ revalidateAll();
486+ }
487+
488+ return clipRegion;
489+ }
490+
491+ public Font getFont() {
492+ if (font == null) {
493+ font = defaultFont;
494+ }
495+ return font;
496+ }
497+
498+ private static final double[] IDENT_MATRIX = {1, 0, 0, 1};
499+ private static final AffineTransform IDENT_ATX =
500+ new AffineTransform();
501+
502+ private static final int MINALLOCATED = 8;
503+ private static final int TEXTARRSIZE = 17;
504+ private static double[][] textTxArr = new double[TEXTARRSIZE][];
505+ private static AffineTransform[] textAtArr =
506+ new AffineTransform[TEXTARRSIZE];
507+
508+ static {
509+ for (int i=MINALLOCATED;i<TEXTARRSIZE;i++) {
510+ textTxArr[i] = new double [] {i, 0, 0, i};
511+ textAtArr[i] = new AffineTransform( textTxArr[i]);
512+ }
513+ }
514+
515+ // cached state for various draw[String,Char,Byte] optimizations
516+ public FontInfo checkFontInfo(FontInfo info, Font font,
517+ FontRenderContext frc) {
518+ /* Do not create a FontInfo object as part of construction of an
519+ * SG2D as its possible it may never be needed - ie if no text
520+ * is drawn using this SG2D.
521+ */
522+ if (info == null) {
523+ info = new FontInfo();
524+ }
525+
526+ float ptSize = font.getSize2D();
527+ int txFontType;
528+ AffineTransform devAt, textAt=null;
529+ if (font.isTransformed()) {
530+ textAt = font.getTransform();
531+ textAt.scale(ptSize, ptSize);
532+ txFontType = textAt.getType();
533+ info.originX = (float)textAt.getTranslateX();
534+ info.originY = (float)textAt.getTranslateY();
535+ textAt.translate(-info.originX, -info.originY);
536+ if (transformState >= TRANSFORM_TRANSLATESCALE) {
537+ transform.getMatrix(info.devTx = new double[4]);
538+ devAt = new AffineTransform(info.devTx);
539+ textAt.preConcatenate(devAt);
540+ } else {
541+ info.devTx = IDENT_MATRIX;
542+ devAt = IDENT_ATX;
543+ }
544+ textAt.getMatrix(info.glyphTx = new double[4]);
545+ double shearx = textAt.getShearX();
546+ double scaley = textAt.getScaleY();
547+ if (shearx != 0) {
548+ scaley = Math.sqrt(shearx * shearx + scaley * scaley);
549+ }
550+ info.pixelHeight = (int)(Math.abs(scaley)+0.5);
551+ } else {
552+ txFontType = AffineTransform.TYPE_IDENTITY;
553+ info.originX = info.originY = 0;
554+ if (transformState >= TRANSFORM_TRANSLATESCALE) {
555+ transform.getMatrix(info.devTx = new double[4]);
556+ devAt = new AffineTransform(info.devTx);
557+ info.glyphTx = new double[4];
558+ for (int i = 0; i < 4; i++) {
559+ info.glyphTx[i] = info.devTx[i] * ptSize;
560+ }
561+ textAt = new AffineTransform(info.glyphTx);
562+ double shearx = transform.getShearX();
563+ double scaley = transform.getScaleY();
564+ if (shearx != 0) {
565+ scaley = Math.sqrt(shearx * shearx + scaley * scaley);
566+ }
567+ info.pixelHeight = (int)(Math.abs(scaley * ptSize)+0.5);
568+ } else {
569+ /* If the double represents a common integral, we
570+ * may have pre-allocated objects.
571+ * A "sparse" array be seems to be as fast as a switch
572+ * even for 3 or 4 pt sizes, and is more flexible.
573+ * This should perform comparably in single-threaded
574+ * rendering to the old code which synchronized on the
575+ * class and scale better on MP systems.
576+ */
577+ int pszInt = (int)ptSize;
578+ if (ptSize == pszInt &&
579+ pszInt >= MINALLOCATED && pszInt < TEXTARRSIZE) {
580+ info.glyphTx = textTxArr[pszInt];
581+ textAt = textAtArr[pszInt];
582+ info.pixelHeight = pszInt;
583+ } else {
584+ info.pixelHeight = (int)(ptSize+0.5);
585+ }
586+ if (textAt == null) {
587+ info.glyphTx = new double[] {ptSize, 0, 0, ptSize};
588+ textAt = new AffineTransform(info.glyphTx);
589+ }
590+
591+ info.devTx = IDENT_MATRIX;
592+ devAt = IDENT_ATX;
593+ }
594+ }
595+
596+ info.font2D = FontUtilities.getFont2D(font);
597+
598+ int fmhint = fractionalMetricsHint;
599+ if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) {
600+ fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
601+ }
602+ info.lcdSubPixPos = false; // conditionally set true in LCD mode.
603+
604+ /* The text anti-aliasing hints that are set by the client need
605+ * to be interpreted for the current state and stored in the
606+ * FontInfo.aahint which is what will actually be used and
607+ * will be one of OFF, ON, LCD_HRGB or LCD_VRGB.
608+ * This is what pipe selection code should typically refer to, not
609+ * textAntialiasHint. This means we are now evaluating the meaning
610+ * of "default" here. Any pipe that really cares about that will
611+ * also need to consult that variable.
612+ * Otherwise these are being used only as args to getStrike,
613+ * and are encapsulated in that object which is part of the
614+ * FontInfo, so we do not need to store them directly as fields
615+ * in the FontInfo object.
616+ * That could change if FontInfo's were more selectively
617+ * revalidated when graphics state changed. Presently this
618+ * method re-evaluates all fields in the fontInfo.
619+ * The strike doesn't need to know the RGB subpixel order. Just
620+ * if its H or V orientation, so if an LCD option is specified we
621+ * always pass in the RGB hint to the strike.
622+ * frc is non-null only if this is a GlyphVector. For reasons
623+ * which are probably a historical mistake the AA hint in a GV
624+ * is honoured when we render, overriding the Graphics setting.
625+ */
626+ int aahint;
627+ if (frc == null) {
628+ aahint = textAntialiasHint;
629+ } else {
630+ aahint = ((SunHints.Value)frc.getAntiAliasingHint()).getIndex();
631+ }
632+ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) {
633+ if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
634+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
635+ } else {
636+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
637+ }
638+ } else {
639+ /* If we are in checkFontInfo because a rendering hint has been
640+ * set then all pipes are revalidated. But we can also
641+ * be here because setFont() has been called when the 'gasp'
642+ * hint is set, as then the font size determines the text pipe.
643+ * See comments in SunGraphics2d.setFont(Font).
644+ */
645+ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) {
646+ if (info.font2D.useAAForPtSize(info.pixelHeight)) {
647+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
648+ } else {
649+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;
650+ }
651+ } else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) {
652+ /* loops for default rendering modes are installed in the SG2D
653+ * constructor. If there are none this will be null.
654+ * Not all compositing modes update the render loops, so
655+ * we also test that this is a mode we know should support
656+ * this. One minor issue is that the loops aren't necessarily
657+ * installed for a new rendering mode until after this
658+ * method is called during pipeline validation. So it is
659+ * theoretically possible that it was set to null for a
660+ * compositing mode, the composite is then set back to Src,
661+ * but the loop is still null when this is called and AA=ON
662+ * is installed instead of an LCD mode.
663+ * However this is done in the right order in SurfaceData.java
664+ * so this is not likely to be a problem - but not
665+ * guaranteed.
666+ */
667+ if (
668+ !surfaceData.canRenderLCDText(this)
669+// loops.drawGlyphListLCDLoop == null ||
670+// compositeState > COMP_ISCOPY ||
671+// paintState > PAINT_ALPHACOLOR
672+ ) {
673+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
674+ } else {
675+ info.lcdRGBOrder = true;
676+ /* Collapse these into just HRGB or VRGB.
677+ * Pipe selection code needs only to test for these two.
678+ * Since these both select the same pipe anyway its
679+ * tempting to collapse into one value. But they are
680+ * different strikes (glyph caches) so the distinction
681+ * needs to be made for that purpose.
682+ */
683+ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) {
684+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
685+ info.lcdRGBOrder = false;
686+ } else if
687+ (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) {
688+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB;
689+ info.lcdRGBOrder = false;
690+ }
691+ /* Support subpixel positioning only for the case in
692+ * which the horizontal resolution is increased
693+ */
694+ info.lcdSubPixPos =
695+ fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON &&
696+ aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;
697+ }
698+ }
699+ }
700+ info.aaHint = aahint;
701+ info.fontStrike = info.font2D.getStrike(font, devAt, textAt,
702+ aahint, fmhint);
703+ return info;
704+ }
705+
706+ public static boolean isRotated(double [] mtx) {
707+ if ((mtx[0] == mtx[3]) &&
708+ (mtx[1] == 0.0) &&
709+ (mtx[2] == 0.0) &&
710+ (mtx[0] > 0.0))
711+ {
712+ return false;
713+ }
714+
715+ return true;
716+ }
717+
718+ public void setFont(Font font) {
719+ /* replacing the reference equality test font != this.font with
720+ * !font.equals(this.font) did not yield any measurable difference
721+ * in testing, but there may be yet to be identified cases where it
722+ * is beneficial.
723+ */
724+ if (font != null && font!=this.font/*!font.equals(this.font)*/) {
725+ /* In the GASP AA case the textpipe depends on the glyph size
726+ * as determined by graphics and font transforms as well as the
727+ * font size, and information in the font. But we may invalidate
728+ * the pipe only to find that it made no difference.
729+ * Deferring pipe invalidation to checkFontInfo won't work because
730+ * when called we may already be rendering to the wrong pipe.
731+ * So, if the font is transformed, or the graphics has more than
732+ * a simple scale, we'll take that as enough of a hint to
733+ * revalidate everything. But if they aren't we will
734+ * use the font's point size to query the gasp table and see if
735+ * what it says matches what's currently being used, in which
736+ * case there's no need to invalidate the textpipe.
737+ * This should be sufficient for all typical uses cases.
738+ */
739+ if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP &&
740+ textpipe != invalidpipe &&
741+ (transformState > TRANSFORM_ANY_TRANSLATE ||
742+ font.isTransformed() ||
743+ fontInfo == null || // Precaution, if true shouldn't get here
744+ (fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) !=
745+ FontUtilities.getFont2D(font).
746+ useAAForPtSize(font.getSize()))) {
747+ textpipe = invalidpipe;
748+ }
749+ this.font = font;
750+ this.fontMetrics = null;
751+ this.validFontInfo = false;
752+ }
753+ }
754+
755+ public FontInfo getFontInfo() {
756+ if (!validFontInfo) {
757+ this.fontInfo = checkFontInfo(this.fontInfo, font, null);
758+ validFontInfo = true;
759+ }
760+ return this.fontInfo;
761+ }
762+
763+ /* Used by drawGlyphVector which specifies its own font. */
764+ public FontInfo getGVFontInfo(Font font, FontRenderContext frc) {
765+ if (glyphVectorFontInfo != null &&
766+ glyphVectorFontInfo.font == font &&
767+ glyphVectorFRC == frc) {
768+ return glyphVectorFontInfo;
769+ } else {
770+ glyphVectorFRC = frc;
771+ return glyphVectorFontInfo =
772+ checkFontInfo(glyphVectorFontInfo, font, frc);
773+ }
774+ }
775+
776+ public FontMetrics getFontMetrics() {
777+ if (this.fontMetrics != null) {
778+ return this.fontMetrics;
779+ }
780+ /* NB the constructor and the setter disallow "font" being null */
781+ return this.fontMetrics =
782+ FontDesignMetrics.getMetrics(font, getFontRenderContext());
783+ }
784+
785+ public FontMetrics getFontMetrics(Font font) {
786+ if ((this.fontMetrics != null) && (font == this.font)) {
787+ return this.fontMetrics;
788+ }
789+ FontMetrics fm =
790+ FontDesignMetrics.getMetrics(font, getFontRenderContext());
791+
792+ if (this.font == font) {
793+ this.fontMetrics = fm;
794+ }
795+ return fm;
796+ }
797+
798+ /**
799+ * Checks to see if a Path intersects the specified Rectangle in device
800+ * space. The rendering attributes taken into account include the
801+ * clip, transform, and stroke attributes.
802+ * @param rect The area in device space to check for a hit.
803+ * @param p The path to check for a hit.
804+ * @param onStroke Flag to choose between testing the stroked or
805+ * the filled path.
806+ * @return True if there is a hit, false otherwise.
807+ * @see #setStroke
808+ * @see #fillPath
809+ * @see #drawPath
810+ * @see #transform
811+ * @see #setTransform
812+ * @see #clip
813+ * @see #setClip
814+ */
815+ public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
816+ if (onStroke) {
817+ s = stroke.createStrokedShape(s);
818+ }
819+
820+ s = transformShape(s);
821+ if ((constrainX|constrainY) != 0) {
822+ rect = new Rectangle(rect);
823+ rect.translate(constrainX, constrainY);
824+ }
825+
826+ return s.intersects(rect);
827+ }
828+
829+ /**
830+ * Return the ColorModel associated with this Graphics2D.
831+ */
832+ public ColorModel getDeviceColorModel() {
833+ return surfaceData.getColorModel();
834+ }
835+
836+ /**
837+ * Return the device configuration associated with this Graphics2D.
838+ */
839+ public GraphicsConfiguration getDeviceConfiguration() {
840+ return surfaceData.getDeviceConfiguration();
841+ }
842+
843+ /**
844+ * Return the SurfaceData object assigned to manage the destination
845+ * drawable surface of this Graphics2D.
846+ */
847+ public final SurfaceData getSurfaceData() {
848+ return surfaceData;
849+ }
850+
851+ /**
852+ * Sets the Composite in the current graphics state. Composite is used
853+ * in all drawing methods such as drawImage, drawString, drawPath,
854+ * and fillPath. It specifies how new pixels are to be combined with
855+ * the existing pixels on the graphics device in the rendering process.
856+ * @param comp The Composite object to be used for drawing.
857+ * @see java.awt.Graphics#setXORMode
858+ * @see java.awt.Graphics#setPaintMode
859+ * @see AlphaComposite
860+ */
861+ public void setComposite(Composite comp) {
862+ if (composite == comp) {
863+ return;
864+ }
865+ int newCompState;
866+ CompositeType newCompType;
867+ if (comp instanceof AlphaComposite) {
868+ AlphaComposite alphacomp = (AlphaComposite) comp;
869+ newCompType = CompositeType.forAlphaComposite(alphacomp);
870+ if (newCompType == CompositeType.SrcOverNoEa) {
871+ if (paintState == PAINT_OPAQUECOLOR ||
872+ (paintState > PAINT_ALPHACOLOR &&
873+ paint.getTransparency() == Transparency.OPAQUE))
874+ {
875+ newCompState = COMP_ISCOPY;
876+ } else {
877+ newCompState = COMP_ALPHA;
878+ }
879+ } else if (newCompType == CompositeType.SrcNoEa ||
880+ newCompType == CompositeType.Src ||
881+ newCompType == CompositeType.Clear)
882+ {
883+ newCompState = COMP_ISCOPY;
884+ } else if (surfaceData.getTransparency() == Transparency.OPAQUE &&
885+ newCompType == CompositeType.SrcIn)
886+ {
887+ newCompState = COMP_ISCOPY;
888+ } else {
889+ newCompState = COMP_ALPHA;
890+ }
891+ } else if (comp instanceof XORComposite) {
892+ newCompState = COMP_XOR;
893+ newCompType = CompositeType.Xor;
894+ } else if (comp == null) {
895+ throw new IllegalArgumentException("null Composite");
896+ } else {
897+ surfaceData.checkCustomComposite();
898+ newCompState = COMP_CUSTOM;
899+ newCompType = CompositeType.General;
900+ }
901+ if (compositeState != newCompState ||
902+ imageComp != newCompType)
903+ {
904+ compositeState = newCompState;
905+ imageComp = newCompType;
906+ invalidatePipe();
907+ validFontInfo = false;
908+ }
909+ composite = comp;
910+ if (paintState <= PAINT_ALPHACOLOR) {
911+ validateColor();
912+ }
913+ }
914+
915+ /**
916+ * Sets the Paint in the current graphics state.
917+ * @param paint The Paint object to be used to generate color in
918+ * the rendering process.
919+ * @see java.awt.Graphics#setColor
920+ * @see GradientPaint
921+ * @see TexturePaint
922+ */
923+ public void setPaint(Paint paint) {
924+ if (paint instanceof Color) {
925+ setColor((Color) paint);
926+ return;
927+ }
928+ if (paint == null || this.paint == paint) {
929+ return;
930+ }
931+ this.paint = paint;
932+ if (imageComp == CompositeType.SrcOverNoEa) {
933+ // special case where compState depends on opacity of paint
934+ if (paint.getTransparency() == Transparency.OPAQUE) {
935+ if (compositeState != COMP_ISCOPY) {
936+ compositeState = COMP_ISCOPY;
937+ }
938+ } else {
939+ if (compositeState == COMP_ISCOPY) {
940+ compositeState = COMP_ALPHA;
941+ }
942+ }
943+ }
944+ Class paintClass = paint.getClass();
945+ if (paintClass == GradientPaint.class) {
946+ paintState = PAINT_GRADIENT;
947+ } else if (paintClass == LinearGradientPaint.class) {
948+ paintState = PAINT_LIN_GRADIENT;
949+ } else if (paintClass == RadialGradientPaint.class) {
950+ paintState = PAINT_RAD_GRADIENT;
951+ } else if (paintClass == TexturePaint.class) {
952+ paintState = PAINT_TEXTURE;
953+ } else {
954+ paintState = PAINT_CUSTOM;
955+ }
956+ validFontInfo = false;
957+ invalidatePipe();
958+ }
959+
960+ static final int NON_UNIFORM_SCALE_MASK =
961+ (AffineTransform.TYPE_GENERAL_TRANSFORM |
962+ AffineTransform.TYPE_GENERAL_SCALE);
963+ public static final double MinPenSizeAA =
964+ sun.java2d.pipe.RenderingEngine.getInstance().getMinimumAAPenSize();
965+ public static final double MinPenSizeAASquared =
966+ (MinPenSizeAA * MinPenSizeAA);
967+ // Since inaccuracies in the trig package can cause us to
968+ // calculated a rotated pen width of just slightly greater
969+ // than 1.0, we add a fudge factor to our comparison value
970+ // here so that we do not misclassify single width lines as
971+ // wide lines under certain rotations.
972+ public static final double MinPenSizeSquared = 1.000000001;
973+
974+ private void validateBasicStroke(BasicStroke bs) {
975+ boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON);
976+ if (transformState < TRANSFORM_TRANSLATESCALE) {
977+ if (aa) {
978+ if (bs.getLineWidth() <= MinPenSizeAA) {
979+ if (bs.getDashArray() == null) {
980+ strokeState = STROKE_THIN;
981+ } else {
982+ strokeState = STROKE_THINDASHED;
983+ }
984+ } else {
985+ strokeState = STROKE_WIDE;
986+ }
987+ } else {
988+ if (bs == defaultStroke) {
989+ strokeState = STROKE_THIN;
990+ } else if (bs.getLineWidth() <= 1.0f) {
991+ if (bs.getDashArray() == null) {
992+ strokeState = STROKE_THIN;
993+ } else {
994+ strokeState = STROKE_THINDASHED;
995+ }
996+ } else {
997+ strokeState = STROKE_WIDE;
998+ }
999+ }
1000+ } else {
1001+ double widthsquared;
1002+ if ((transform.getType() & NON_UNIFORM_SCALE_MASK) == 0) {
1003+ /* sqrt omitted, compare to squared limits below. */
1004+ widthsquared = Math.abs(transform.getDeterminant());
1005+ } else {
1006+ /* First calculate the "maximum scale" of this transform. */
1007+ double A = transform.getScaleX(); // m00
1008+ double C = transform.getShearX(); // m01
1009+ double B = transform.getShearY(); // m10
1010+ double D = transform.getScaleY(); // m11
1011+
1012+ /*
1013+ * Given a 2 x 2 affine matrix [ A B ] such that
1014+ * [ C D ]
1015+ * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to
1016+ * find the maximum magnitude (norm) of the vector v'
1017+ * with the constraint (x^2 + y^2 = 1).
1018+ * The equation to maximize is
1019+ * |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)
1020+ * or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).
1021+ * Since sqrt is monotonic we can maximize |v'|^2
1022+ * instead and plug in the substitution y = sqrt(1 - x^2).
1023+ * Trigonometric equalities can then be used to get
1024+ * rid of most of the sqrt terms.
1025+ */
1026+ double EA = A*A + B*B; // x^2 coefficient
1027+ double EB = 2*(A*C + B*D); // xy coefficient
1028+ double EC = C*C + D*D; // y^2 coefficient
1029+
1030+ /*
1031+ * There is a lot of calculus omitted here.
1032+ *
1033+ * Conceptually, in the interests of understanding the
1034+ * terms that the calculus produced we can consider
1035+ * that EA and EC end up providing the lengths along
1036+ * the major axes and the hypot term ends up being an
1037+ * adjustment for the additional length along the off-axis
1038+ * angle of rotated or sheared ellipses as well as an
1039+ * adjustment for the fact that the equation below
1040+ * averages the two major axis lengths. (Notice that
1041+ * the hypot term contains a part which resolves to the
1042+ * difference of these two axis lengths in the absence
1043+ * of rotation.)
1044+ *
1045+ * In the calculus, the ratio of the EB and (EA-EC) terms
1046+ * ends up being the tangent of 2*theta where theta is
1047+ * the angle that the long axis of the ellipse makes
1048+ * with the horizontal axis. Thus, this equation is
1049+ * calculating the length of the hypotenuse of a triangle
1050+ * along that axis.
1051+ */
1052+ double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
1053+
1054+ /* sqrt omitted, compare to squared limits below. */
1055+ widthsquared = ((EA + EC + hypot)/2.0);
1056+ }
1057+ if (bs != defaultStroke) {
1058+ widthsquared *= bs.getLineWidth() * bs.getLineWidth();
1059+ }
1060+ if (widthsquared <=
1061+ (aa ? MinPenSizeAASquared : MinPenSizeSquared))
1062+ {
1063+ if (bs.getDashArray() == null) {
1064+ strokeState = STROKE_THIN;
1065+ } else {
1066+ strokeState = STROKE_THINDASHED;
1067+ }
1068+ } else {
1069+ strokeState = STROKE_WIDE;
1070+ }
1071+ }
1072+ }
1073+
1074+ /*
1075+ * Sets the Stroke in the current graphics state.
1076+ * @param s The Stroke object to be used to stroke a Path in
1077+ * the rendering process.
1078+ * @see BasicStroke
1079+ */
1080+ public void setStroke(Stroke s) {
1081+ if (s == null) {
1082+ throw new IllegalArgumentException("null Stroke");
1083+ }
1084+ int saveStrokeState = strokeState;
1085+ stroke = s;
1086+ if (s instanceof BasicStroke) {
1087+ validateBasicStroke((BasicStroke) s);
1088+ } else {
1089+ strokeState = STROKE_CUSTOM;
1090+ }
1091+ if (strokeState != saveStrokeState) {
1092+ invalidatePipe();
1093+ }
1094+ }
1095+
1096+ /**
1097+ * Sets the preferences for the rendering algorithms.
1098+ * Hint categories include controls for rendering quality and
1099+ * overall time/quality trade-off in the rendering process.
1100+ * @param hintKey The key of hint to be set. The strings are
1101+ * defined in the RenderingHints class.
1102+ * @param hintValue The value indicating preferences for the specified
1103+ * hint category. These strings are defined in the RenderingHints
1104+ * class.
1105+ * @see RenderingHints
1106+ */
1107+ public void setRenderingHint(Key hintKey, Object hintValue) {
1108+ // If we recognize the key, we must recognize the value
1109+ // otherwise throw an IllegalArgumentException
1110+ // and do not change the Hints object
1111+ // If we do not recognize the key, just pass it through
1112+ // to the Hints object untouched
1113+ if (!hintKey.isCompatibleValue(hintValue)) {
1114+ throw new IllegalArgumentException
1115+ (hintValue+" is not compatible with "+hintKey);
1116+ }
1117+ if (hintKey instanceof SunHints.Key) {
1118+ boolean stateChanged;
1119+ boolean textStateChanged = false;
1120+ boolean recognized = true;
1121+ SunHints.Key sunKey = (SunHints.Key) hintKey;
1122+ int newHint;
1123+ if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) {
1124+ newHint = ((Integer)hintValue).intValue();
1125+ } else {
1126+ newHint = ((SunHints.Value) hintValue).getIndex();
1127+ }
1128+ switch (sunKey.getIndex()) {
1129+ case SunHints.INTKEY_RENDERING:
1130+ stateChanged = (renderHint != newHint);
1131+ if (stateChanged) {
1132+ renderHint = newHint;
1133+ if (interpolationHint == -1) {
1134+ interpolationType =
1135+ (newHint == SunHints.INTVAL_RENDER_QUALITY
1136+ ? AffineTransformOp.TYPE_BILINEAR
1137+ : AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
1138+ }
1139+ }
1140+ break;
1141+ case SunHints.INTKEY_ANTIALIASING:
1142+ stateChanged = (antialiasHint != newHint);
1143+ antialiasHint = newHint;
1144+ if (stateChanged) {
1145+ textStateChanged =
1146+ (textAntialiasHint ==
1147+ SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT);
1148+ if (strokeState != STROKE_CUSTOM) {
1149+ validateBasicStroke((BasicStroke) stroke);
1150+ }
1151+ }
1152+ break;
1153+ case SunHints.INTKEY_TEXT_ANTIALIASING:
1154+ stateChanged = (textAntialiasHint != newHint);
1155+ textStateChanged = stateChanged;
1156+ textAntialiasHint = newHint;
1157+ break;
1158+ case SunHints.INTKEY_FRACTIONALMETRICS:
1159+ stateChanged = (fractionalMetricsHint != newHint);
1160+ textStateChanged = stateChanged;
1161+ fractionalMetricsHint = newHint;
1162+ break;
1163+ case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1164+ stateChanged = false;
1165+ /* Already have validated it is an int 100 <= newHint <= 250 */
1166+ lcdTextContrast = newHint;
1167+ break;
1168+ case SunHints.INTKEY_INTERPOLATION:
1169+ interpolationHint = newHint;
1170+ switch (newHint) {
1171+ case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1172+ newHint = AffineTransformOp.TYPE_BICUBIC;
1173+ break;
1174+ case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1175+ newHint = AffineTransformOp.TYPE_BILINEAR;
1176+ break;
1177+ default:
1178+ case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1179+ newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1180+ break;
1181+ }
1182+ stateChanged = (interpolationType != newHint);
1183+ interpolationType = newHint;
1184+ break;
1185+ case SunHints.INTKEY_STROKE_CONTROL:
1186+ stateChanged = (strokeHint != newHint);
1187+ strokeHint = newHint;
1188+ break;
1189+ default:
1190+ recognized = false;
1191+ stateChanged = false;
1192+ break;
1193+ }
1194+ if (recognized) {
1195+ if (stateChanged) {
1196+ invalidatePipe();
1197+ if (textStateChanged) {
1198+ fontMetrics = null;
1199+ this.cachedFRC = null;
1200+ validFontInfo = false;
1201+ this.glyphVectorFontInfo = null;
1202+ }
1203+ }
1204+ if (hints != null) {
1205+ hints.put(hintKey, hintValue);
1206+ }
1207+ return;
1208+ }
1209+ }
1210+ // Nothing we recognize so none of "our state" has changed
1211+ if (hints == null) {
1212+ hints = makeHints(null);
1213+ }
1214+ hints.put(hintKey, hintValue);
1215+ }
1216+
1217+
1218+ /**
1219+ * Returns the preferences for the rendering algorithms.
1220+ * @param hintCategory The category of hint to be set. The strings
1221+ * are defined in the RenderingHints class.
1222+ * @return The preferences for rendering algorithms. The strings
1223+ * are defined in the RenderingHints class.
1224+ * @see RenderingHints
1225+ */
1226+ public Object getRenderingHint(Key hintKey) {
1227+ if (hints != null) {
1228+ return hints.get(hintKey);
1229+ }
1230+ if (!(hintKey instanceof SunHints.Key)) {
1231+ return null;
1232+ }
1233+ int keyindex = ((SunHints.Key)hintKey).getIndex();
1234+ switch (keyindex) {
1235+ case SunHints.INTKEY_RENDERING:
1236+ return SunHints.Value.get(SunHints.INTKEY_RENDERING,
1237+ renderHint);
1238+ case SunHints.INTKEY_ANTIALIASING:
1239+ return SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,
1240+ antialiasHint);
1241+ case SunHints.INTKEY_TEXT_ANTIALIASING:
1242+ return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
1243+ textAntialiasHint);
1244+ case SunHints.INTKEY_FRACTIONALMETRICS:
1245+ return SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
1246+ fractionalMetricsHint);
1247+ case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1248+ return new Integer(lcdTextContrast);
1249+ case SunHints.INTKEY_INTERPOLATION:
1250+ switch (interpolationHint) {
1251+ case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1252+ return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1253+ case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1254+ return SunHints.VALUE_INTERPOLATION_BILINEAR;
1255+ case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1256+ return SunHints.VALUE_INTERPOLATION_BICUBIC;
1257+ }
1258+ return null;
1259+ case SunHints.INTKEY_STROKE_CONTROL:
1260+ return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
1261+ strokeHint);
1262+ }
1263+ return null;
1264+ }
1265+
1266+ /**
1267+ * Sets the preferences for the rendering algorithms.
1268+ * Hint categories include controls for rendering quality and
1269+ * overall time/quality trade-off in the rendering process.
1270+ * @param hints The rendering hints to be set
1271+ * @see RenderingHints
1272+ */
1273+ public void setRenderingHints(Map<?,?> hints) {
1274+ this.hints = null;
1275+ renderHint = SunHints.INTVAL_RENDER_DEFAULT;
1276+ antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
1277+ textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
1278+ fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
1279+ lcdTextContrast = lcdTextContrastDefaultValue;
1280+ interpolationHint = -1;
1281+ interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1282+ boolean customHintPresent = false;
1283+ Iterator iter = hints.keySet().iterator();
1284+ while (iter.hasNext()) {
1285+ Object key = iter.next();
1286+ if (key == SunHints.KEY_RENDERING ||
1287+ key == SunHints.KEY_ANTIALIASING ||
1288+ key == SunHints.KEY_TEXT_ANTIALIASING ||
1289+ key == SunHints.KEY_FRACTIONALMETRICS ||
1290+ key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||
1291+ key == SunHints.KEY_STROKE_CONTROL ||
1292+ key == SunHints.KEY_INTERPOLATION)
1293+ {
1294+ setRenderingHint((Key) key, hints.get(key));
1295+ } else {
1296+ customHintPresent = true;
1297+ }
1298+ }
1299+ if (customHintPresent) {
1300+ this.hints = makeHints(hints);
1301+ }
1302+ invalidatePipe();
1303+ }
1304+
1305+ /**
1306+ * Adds a number of preferences for the rendering algorithms.
1307+ * Hint categories include controls for rendering quality and
1308+ * overall time/quality trade-off in the rendering process.
1309+ * @param hints The rendering hints to be set
1310+ * @see RenderingHints
1311+ */
1312+ public void addRenderingHints(Map<?,?> hints) {
1313+ boolean customHintPresent = false;
1314+ Iterator iter = hints.keySet().iterator();
1315+ while (iter.hasNext()) {
1316+ Object key = iter.next();
1317+ if (key == SunHints.KEY_RENDERING ||
1318+ key == SunHints.KEY_ANTIALIASING ||
1319+ key == SunHints.KEY_TEXT_ANTIALIASING ||
1320+ key == SunHints.KEY_FRACTIONALMETRICS ||
1321+ key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||
1322+ key == SunHints.KEY_STROKE_CONTROL ||
1323+ key == SunHints.KEY_INTERPOLATION)
1324+ {
1325+ setRenderingHint((Key) key, hints.get(key));
1326+ } else {
1327+ customHintPresent = true;
1328+ }
1329+ }
1330+ if (customHintPresent) {
1331+ if (this.hints == null) {
1332+ this.hints = makeHints(hints);
1333+ } else {
1334+ this.hints.putAll(hints);
1335+ }
1336+ }
1337+ }
1338+
1339+ /**
1340+ * Gets the preferences for the rendering algorithms.
1341+ * Hint categories include controls for rendering quality and
1342+ * overall time/quality trade-off in the rendering process.
1343+ * @see RenderingHints
1344+ */
1345+ public RenderingHints getRenderingHints() {
1346+ if (hints == null) {
1347+ return makeHints(null);
1348+ } else {
1349+ return (RenderingHints) hints.clone();
1350+ }
1351+ }
1352+
1353+ RenderingHints makeHints(Map hints) {
1354+ RenderingHints model = new RenderingHints(hints);
1355+ model.put(SunHints.KEY_RENDERING,
1356+ SunHints.Value.get(SunHints.INTKEY_RENDERING,
1357+ renderHint));
1358+ model.put(SunHints.KEY_ANTIALIASING,
1359+ SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,
1360+ antialiasHint));
1361+ model.put(SunHints.KEY_TEXT_ANTIALIASING,
1362+ SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
1363+ textAntialiasHint));
1364+ model.put(SunHints.KEY_FRACTIONALMETRICS,
1365+ SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
1366+ fractionalMetricsHint));
1367+ model.put(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST,
1368+ Integer.valueOf(lcdTextContrast));
1369+ Object value;
1370+ switch (interpolationHint) {
1371+ case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1372+ value = SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1373+ break;
1374+ case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1375+ value = SunHints.VALUE_INTERPOLATION_BILINEAR;
1376+ break;
1377+ case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1378+ value = SunHints.VALUE_INTERPOLATION_BICUBIC;
1379+ break;
1380+ default:
1381+ value = null;
1382+ break;
1383+ }
1384+ if (value != null) {
1385+ model.put(SunHints.KEY_INTERPOLATION, value);
1386+ }
1387+ model.put(SunHints.KEY_STROKE_CONTROL,
1388+ SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
1389+ strokeHint));
1390+ return model;
1391+ }
1392+
1393+ /**
1394+ * Concatenates the current transform of this Graphics2D with a
1395+ * translation transformation.
1396+ * This is equivalent to calling transform(T), where T is an
1397+ * AffineTransform represented by the following matrix:
1398+ * <pre>
1399+ * [ 1 0 tx ]
1400+ * [ 0 1 ty ]
1401+ * [ 0 0 1 ]
1402+ * </pre>
1403+ */
1404+ public void translate(double tx, double ty) {
1405+ transform.translate(tx, ty);
1406+ invalidateTransform();
1407+ }
1408+
1409+ /**
1410+ * Concatenates the current transform of this Graphics2D with a
1411+ * rotation transformation.
1412+ * This is equivalent to calling transform(R), where R is an
1413+ * AffineTransform represented by the following matrix:
1414+ * <pre>
1415+ * [ cos(theta) -sin(theta) 0 ]
1416+ * [ sin(theta) cos(theta) 0 ]
1417+ * [ 0 0 1 ]
1418+ * </pre>
1419+ * Rotating with a positive angle theta rotates points on the positive
1420+ * x axis toward the positive y axis.
1421+ * @param theta The angle of rotation in radians.
1422+ */
1423+ public void rotate(double theta) {
1424+ transform.rotate(theta);
1425+ invalidateTransform();
1426+ }
1427+
1428+ /**
1429+ * Concatenates the current transform of this Graphics2D with a
1430+ * translated rotation transformation.
1431+ * This is equivalent to the following sequence of calls:
1432+ * <pre>
1433+ * translate(x, y);
1434+ * rotate(theta);
1435+ * translate(-x, -y);
1436+ * </pre>
1437+ * Rotating with a positive angle theta rotates points on the positive
1438+ * x axis toward the positive y axis.
1439+ * @param theta The angle of rotation in radians.
1440+ * @param x The x coordinate of the origin of the rotation
1441+ * @param y The x coordinate of the origin of the rotation
1442+ */
1443+ public void rotate(double theta, double x, double y) {
1444+ transform.rotate(theta, x, y);
1445+ invalidateTransform();
1446+ }
1447+
1448+ /**
1449+ * Concatenates the current transform of this Graphics2D with a
1450+ * scaling transformation.
1451+ * This is equivalent to calling transform(S), where S is an
1452+ * AffineTransform represented by the following matrix:
1453+ * <pre>
1454+ * [ sx 0 0 ]
1455+ * [ 0 sy 0 ]
1456+ * [ 0 0 1 ]
1457+ * </pre>
1458+ */
1459+ public void scale(double sx, double sy) {
1460+ transform.scale(sx, sy);
1461+ invalidateTransform();
1462+ }
1463+
1464+ /**
1465+ * Concatenates the current transform of this Graphics2D with a
1466+ * shearing transformation.
1467+ * This is equivalent to calling transform(SH), where SH is an
1468+ * AffineTransform represented by the following matrix:
1469+ * <pre>
1470+ * [ 1 shx 0 ]
1471+ * [ shy 1 0 ]
1472+ * [ 0 0 1 ]
1473+ * </pre>
1474+ * @param shx The factor by which coordinates are shifted towards the
1475+ * positive X axis direction according to their Y coordinate
1476+ * @param shy The factor by which coordinates are shifted towards the
1477+ * positive Y axis direction according to their X coordinate
1478+ */
1479+ public void shear(double shx, double shy) {
1480+ transform.shear(shx, shy);
1481+ invalidateTransform();
1482+ }
1483+
1484+ /**
1485+ * Composes a Transform object with the transform in this
1486+ * Graphics2D according to the rule last-specified-first-applied.
1487+ * If the currrent transform is Cx, the result of composition
1488+ * with Tx is a new transform Cx'. Cx' becomes the current
1489+ * transform for this Graphics2D.
1490+ * Transforming a point p by the updated transform Cx' is
1491+ * equivalent to first transforming p by Tx and then transforming
1492+ * the result by the original transform Cx. In other words,
1493+ * Cx'(p) = Cx(Tx(p)).
1494+ * A copy of the Tx is made, if necessary, so further
1495+ * modifications to Tx do not affect rendering.
1496+ * @param Tx The Transform object to be composed with the current
1497+ * transform.
1498+ * @see #setTransform
1499+ * @see AffineTransform
1500+ */
1501+ public void transform(AffineTransform xform) {
1502+ this.transform.concatenate(xform);
1503+ invalidateTransform();
1504+ }
1505+
1506+ /**
1507+ * Translate
1508+ */
1509+ public void translate(int x, int y) {
1510+ transform.translate(x, y);
1511+ if (transformState <= TRANSFORM_INT_TRANSLATE) {
1512+ transX += x;
1513+ transY += y;
1514+ transformState = (((transX | transY) == 0) ?
1515+ TRANSFORM_ISIDENT : TRANSFORM_INT_TRANSLATE);
1516+ } else {
1517+ invalidateTransform();
1518+ }
1519+ }
1520+
1521+ /**
1522+ * Sets the Transform in the current graphics state.
1523+ * @param Tx The Transform object to be used in the rendering process.
1524+ * @see #transform
1525+ * @see TransformChain
1526+ * @see AffineTransform
1527+ */
1528+ public void setTransform(AffineTransform Tx) {
1529+ if ((constrainX|constrainY) == 0) {
1530+ transform.setTransform(Tx);
1531+ } else {
1532+ transform.setToTranslation(constrainX, constrainY);
1533+ transform.concatenate(Tx);
1534+ }
1535+ invalidateTransform();
1536+ }
1537+
1538+ protected void invalidateTransform() {
1539+ int type = transform.getType();
1540+ int origTransformState = transformState;
1541+ if (type == AffineTransform.TYPE_IDENTITY) {
1542+ transformState = TRANSFORM_ISIDENT;
1543+ transX = transY = 0;
1544+ } else if (type == AffineTransform.TYPE_TRANSLATION) {
1545+ double dtx = transform.getTranslateX();
1546+ double dty = transform.getTranslateY();
1547+ transX = (int) Math.floor(dtx + 0.5);
1548+ transY = (int) Math.floor(dty + 0.5);
1549+ if (dtx == transX && dty == transY) {
1550+ transformState = TRANSFORM_INT_TRANSLATE;
1551+ } else {
1552+ transformState = TRANSFORM_ANY_TRANSLATE;
1553+ }
1554+ } else if ((type & (AffineTransform.TYPE_FLIP |
1555+ AffineTransform.TYPE_MASK_ROTATION |
1556+ AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0)
1557+ {
1558+ transformState = TRANSFORM_TRANSLATESCALE;
1559+ transX = transY = 0;
1560+ } else {
1561+ transformState = TRANSFORM_GENERIC;
1562+ transX = transY = 0;
1563+ }
1564+
1565+ if (transformState >= TRANSFORM_TRANSLATESCALE ||
1566+ origTransformState >= TRANSFORM_TRANSLATESCALE)
1567+ {
1568+ /* Its only in this case that the previous or current transform
1569+ * was more than a translate that font info is invalidated
1570+ */
1571+ cachedFRC = null;
1572+ this.validFontInfo = false;
1573+ this.fontMetrics = null;
1574+ this.glyphVectorFontInfo = null;
1575+
1576+ if (transformState != origTransformState) {
1577+ invalidatePipe();
1578+ }
1579+ }
1580+ if (strokeState != STROKE_CUSTOM) {
1581+ validateBasicStroke((BasicStroke) stroke);
1582+ }
1583+ }
1584+
1585+ /**
1586+ * Returns the current Transform in the Graphics2D state.
1587+ * @see #transform
1588+ * @see #setTransform
1589+ */
1590+ public AffineTransform getTransform() {
1591+ if ((constrainX|constrainY) == 0) {
1592+ return new AffineTransform(transform);
1593+ }
1594+ AffineTransform tx =
1595+ AffineTransform.getTranslateInstance(-constrainX, -constrainY);
1596+ tx.concatenate(transform);
1597+ return tx;
1598+ }
1599+
1600+ /**
1601+ * Returns the current Transform ignoring the "constrain"
1602+ * rectangle.
1603+ */
1604+ public AffineTransform cloneTransform() {
1605+ return new AffineTransform(transform);
1606+ }
1607+
1608+ /**
1609+ * Returns the current Paint in the Graphics2D state.
1610+ * @see #setPaint
1611+ * @see java.awt.Graphics#setColor
1612+ */
1613+ public Paint getPaint() {
1614+ return paint;
1615+ }
1616+
1617+ /**
1618+ * Returns the current Composite in the Graphics2D state.
1619+ * @see #setComposite
1620+ */
1621+ public Composite getComposite() {
1622+ return composite;
1623+ }
1624+
1625+ public Color getColor() {
1626+ return foregroundColor;
1627+ }
1628+
1629+ /*
1630+ * Validate the eargb and pixel fields against the current color.
1631+ *
1632+ * The eargb field must take into account the extraAlpha
1633+ * value of an AlphaComposite. It may also take into account
1634+ * the Fsrc Porter-Duff blending function if such a function is
1635+ * a constant (see handling of Clear mode below). For instance,
1636+ * by factoring in the (Fsrc == 0) state of the Clear mode we can
1637+ * use a SrcNoEa loop just as easily as a general Alpha loop
1638+ * since the math will be the same in both cases.
1639+ *
1640+ * The pixel field will always be the best pixel data choice for
1641+ * the final result of all calculations applied to the eargb field.
1642+ *
1643+ * Note that this method is only necessary under the following
1644+ * conditions:
1645+ * (paintState <= PAINT_ALPHA_COLOR &&
1646+ * compositeState <= COMP_CUSTOM)
1647+ * though nothing bad will happen if it is run in other states.
1648+ */
1649+ final void validateColor() {
1650+ int eargb;
1651+ if (imageComp == CompositeType.Clear) {
1652+ eargb = 0;
1653+ } else {
1654+ eargb = foregroundColor.getRGB();
1655+ if (compositeState <= COMP_ALPHA &&
1656+ imageComp != CompositeType.SrcNoEa &&
1657+ imageComp != CompositeType.SrcOverNoEa)
1658+ {
1659+ AlphaComposite alphacomp = (AlphaComposite) composite;
1660+ int a = Math.round(alphacomp.getAlpha() * (eargb >>> 24));
1661+ eargb = (eargb & 0x00ffffff) | (a << 24);
1662+ }
1663+ }
1664+ this.eargb = eargb;
1665+ this.pixel = surfaceData.pixelFor(eargb);
1666+ }
1667+
1668+ public void setColor(Color color) {
1669+ if (color == null || color == paint) {
1670+ return;
1671+ }
1672+ this.paint = foregroundColor = color;
1673+ validateColor();
1674+ if ((eargb >> 24) == -1) {
1675+ if (paintState == PAINT_OPAQUECOLOR) {
1676+ return;
1677+ }
1678+ paintState = PAINT_OPAQUECOLOR;
1679+ if (imageComp == CompositeType.SrcOverNoEa) {
1680+ // special case where compState depends on opacity of paint
1681+ compositeState = COMP_ISCOPY;
1682+ }
1683+ } else {
1684+ if (paintState == PAINT_ALPHACOLOR) {
1685+ return;
1686+ }
1687+ paintState = PAINT_ALPHACOLOR;
1688+ if (imageComp == CompositeType.SrcOverNoEa) {
1689+ // special case where compState depends on opacity of paint
1690+ compositeState = COMP_ALPHA;
1691+ }
1692+ }
1693+ validFontInfo = false;
1694+ invalidatePipe();
1695+ }
1696+
1697+ /**
1698+ * Sets the background color in this context used for clearing a region.
1699+ * When Graphics2D is constructed for a component, the backgroung color is
1700+ * inherited from the component. Setting the background color in the
1701+ * Graphics2D context only affects the subsequent clearRect() calls and
1702+ * not the background color of the component. To change the background
1703+ * of the component, use appropriate methods of the component.
1704+ * @param color The background color that should be used in
1705+ * subsequent calls to clearRect().
1706+ * @see getBackground
1707+ * @see Graphics.clearRect()
1708+ */
1709+ public void setBackground(Color color) {
1710+ backgroundColor = color;
1711+ }
1712+
1713+ /**
1714+ * Returns the background color used for clearing a region.
1715+ * @see setBackground
1716+ */
1717+ public Color getBackground() {
1718+ return backgroundColor;
1719+ }
1720+
1721+ /**
1722+ * Returns the current Stroke in the Graphics2D state.
1723+ * @see setStroke
1724+ */
1725+ public Stroke getStroke() {
1726+ return stroke;
1727+ }
1728+
1729+ public Rectangle getClipBounds() {
1730+ Rectangle r;
1731+ if (clipState == CLIP_DEVICE) {
1732+ r = null;
1733+ } else if (transformState <= TRANSFORM_INT_TRANSLATE) {
1734+ if (usrClip instanceof Rectangle) {
1735+ r = new Rectangle((Rectangle) usrClip);
1736+ } else {
1737+ r = usrClip.getBounds();
1738+ }
1739+ r.translate(-transX, -transY);
1740+ } else {
1741+ r = getClip().getBounds();
1742+ }
1743+ return r;
1744+ }
1745+
1746+ public Rectangle getClipBounds(Rectangle r) {
1747+ if (clipState != CLIP_DEVICE) {
1748+ if (transformState <= TRANSFORM_INT_TRANSLATE) {
1749+ if (usrClip instanceof Rectangle) {
1750+ r.setBounds((Rectangle) usrClip);
1751+ } else {
1752+ r.setBounds(usrClip.getBounds());
1753+ }
1754+ r.translate(-transX, -transY);
1755+ } else {
1756+ r.setBounds(getClip().getBounds());
1757+ }
1758+ } else if (r == null) {
1759+ throw new NullPointerException("null rectangle parameter");
1760+ }
1761+ return r;
1762+ }
1763+
1764+ public boolean hitClip(int x, int y, int width, int height) {
1765+ if (width <= 0 || height <= 0) {
1766+ return false;
1767+ }
1768+ if (transformState > TRANSFORM_INT_TRANSLATE) {
1769+ // Note: Technically the most accurate test would be to
1770+ // raster scan the parallelogram of the transformed rectangle
1771+ // and do a span for span hit test against the clip, but for
1772+ // speed we approximate the test with a bounding box of the
1773+ // transformed rectangle. The cost of rasterizing the
1774+ // transformed rectangle is probably high enough that it is
1775+ // not worth doing so to save the caller from having to call
1776+ // a rendering method where we will end up discovering the
1777+ // same answer in about the same amount of time anyway.
1778+ // This logic breaks down if this hit test is being performed
1779+ // on the bounds of a group of shapes in which case it might
1780+ // be beneficial to be a little more accurate to avoid lots
1781+ // of subsequent rendering calls. In either case, this relaxed
1782+ // test should not be significantly less accurate than the
1783+ // optimal test for most transforms and so the conservative
1784+ // answer should not cause too much extra work.
1785+
1786+ double d[] = {
1787+ x, y,
1788+ x+width, y,
1789+ x, y+height,
1790+ x+width, y+height
1791+ };
1792+ transform.transform(d, 0, d, 0, 4);
1793+ x = (int) Math.floor(Math.min(Math.min(d[0], d[2]),
1794+ Math.min(d[4], d[6])));
1795+ y = (int) Math.floor(Math.min(Math.min(d[1], d[3]),
1796+ Math.min(d[5], d[7])));
1797+ width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]),
1798+ Math.max(d[4], d[6])));
1799+ height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),
1800+ Math.max(d[5], d[7])));
1801+ } else {
1802+ x += transX;
1803+ y += transY;
1804+ width += x;
1805+ height += y;
1806+ }
1807+ if (!getCompClip().intersectsQuickCheckXYXY(x, y, width, height)) {
1808+ return false;
1809+ }
1810+ // REMIND: We could go one step further here and examine the
1811+ // non-rectangular clip shape more closely if there is one.
1812+ // Since the clip has already been rasterized, the performance
1813+ // penalty of doing the scan is probably still within the bounds
1814+ // of a good tradeoff between speed and quality of the answer.
1815+ return true;
1816+ }
1817+
1818+ protected void validateCompClip() {
1819+ int origClipState = clipState;
1820+ if (usrClip == null) {
1821+ clipState = CLIP_DEVICE;
1822+ clipRegion = devClip;
1823+ } else if (usrClip instanceof Rectangle2D) {
1824+ clipState = CLIP_RECTANGULAR;
1825+ if (usrClip instanceof Rectangle) {
1826+ clipRegion = devClip.getIntersection((Rectangle)usrClip);
1827+ } else {
1828+ clipRegion = devClip.getIntersection(usrClip.getBounds());
1829+ }
1830+ } else {
1831+ PathIterator cpi = usrClip.getPathIterator(null);
1832+ int box[] = new int[4];
1833+ ShapeSpanIterator sr = LoopPipe.getFillSSI(this);
1834+ try {
1835+ sr.setOutputArea(devClip);
1836+ sr.appendPath(cpi);
1837+ sr.getPathBox(box);
1838+ Region r = Region.getInstance(box);
1839+ r.appendSpans(sr);
1840+ clipRegion = r;
1841+ clipState =
1842+ r.isRectangular() ? CLIP_RECTANGULAR : CLIP_SHAPE;
1843+ } finally {
1844+ sr.dispose();
1845+ }
1846+ }
1847+ if (origClipState != clipState &&
1848+ (clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE))
1849+ {
1850+ validFontInfo = false;
1851+ invalidatePipe();
1852+ }
1853+ }
1854+
1855+ static final int NON_RECTILINEAR_TRANSFORM_MASK =
1856+ (AffineTransform.TYPE_GENERAL_TRANSFORM |
1857+ AffineTransform.TYPE_GENERAL_ROTATION);
1858+
1859+ protected Shape transformShape(Shape s) {
1860+ if (s == null) {
1861+ return null;
1862+ }
1863+ if (transformState > TRANSFORM_INT_TRANSLATE) {
1864+ return transformShape(transform, s);
1865+ } else {
1866+ return transformShape(transX, transY, s);
1867+ }
1868+ }
1869+
1870+ public Shape untransformShape(Shape s) {
1871+ if (s == null) {
1872+ return null;
1873+ }
1874+ if (transformState > TRANSFORM_INT_TRANSLATE) {
1875+ try {
1876+ return transformShape(transform.createInverse(), s);
1877+ } catch (NoninvertibleTransformException e) {
1878+ return null;
1879+ }
1880+ } else {
1881+ return transformShape(-transX, -transY, s);
1882+ }
1883+ }
1884+
1885+ protected static Shape transformShape(int tx, int ty, Shape s) {
1886+ if (s == null) {
1887+ return null;
1888+ }
1889+
1890+ if (s instanceof Rectangle) {
1891+ Rectangle r = s.getBounds();
1892+ r.translate(tx, ty);
1893+ return r;
1894+ }
1895+ if (s instanceof Rectangle2D) {
1896+ Rectangle2D rect = (Rectangle2D) s;
1897+ return new Rectangle2D.Double(rect.getX() + tx,
1898+ rect.getY() + ty,
1899+ rect.getWidth(),
1900+ rect.getHeight());
1901+ }
1902+
1903+ if (tx == 0 && ty == 0) {
1904+ return cloneShape(s);
1905+ }
1906+
1907+ AffineTransform mat = AffineTransform.getTranslateInstance(tx, ty);
1908+ return mat.createTransformedShape(s);
1909+ }
1910+
1911+ protected static Shape transformShape(AffineTransform tx, Shape clip) {
1912+ if (clip == null) {
1913+ return null;
1914+ }
1915+
1916+ if (clip instanceof Rectangle2D &&
1917+ (tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0)
1918+ {
1919+ Rectangle2D rect = (Rectangle2D) clip;
1920+ double matrix[] = new double[4];
1921+ matrix[0] = rect.getX();
1922+ matrix[1] = rect.getY();
1923+ matrix[2] = matrix[0] + rect.getWidth();
1924+ matrix[3] = matrix[1] + rect.getHeight();
1925+ tx.transform(matrix, 0, matrix, 0, 2);
1926+ rect = new Rectangle2D.Float();
1927+ rect.setFrameFromDiagonal(matrix[0], matrix[1],
1928+ matrix[2], matrix[3]);
1929+ return rect;
1930+ }
1931+
1932+ if (tx.isIdentity()) {
1933+ return cloneShape(clip);
1934+ }
1935+
1936+ return tx.createTransformedShape(clip);
1937+ }
1938+
1939+ public void clipRect(int x, int y, int w, int h) {
1940+ clip(new Rectangle(x, y, w, h));
1941+ }
1942+
1943+ public void setClip(int x, int y, int w, int h) {
1944+ setClip(new Rectangle(x, y, w, h));
1945+ }
1946+
1947+ public Shape getClip() {
1948+ return untransformShape(usrClip);
1949+ }
1950+
1951+ public void setClip(Shape sh) {
1952+ usrClip = transformShape(sh);
1953+ validateCompClip();
1954+ }
1955+
1956+ /**
1957+ * Intersects the current clip with the specified Path and sets the
1958+ * current clip to the resulting intersection. The clip is transformed
1959+ * with the current transform in the Graphics2D state before being
1960+ * intersected with the current clip. This method is used to make the
1961+ * current clip smaller. To make the clip larger, use any setClip method.
1962+ * @param p The Path to be intersected with the current clip.
1963+ */
1964+ public void clip(Shape s) {
1965+ s = transformShape(s);
1966+ if (usrClip != null) {
1967+ s = intersectShapes(usrClip, s, true, true);
1968+ }
1969+ usrClip = s;
1970+ validateCompClip();
1971+ }
1972+
1973+ public void setPaintMode() {
1974+ setComposite(AlphaComposite.SrcOver);
1975+ }
1976+
1977+ public void setXORMode(Color c) {
1978+ if (c == null) {
1979+ throw new IllegalArgumentException("null XORColor");
1980+ }
1981+ setComposite(new XORComposite(c, surfaceData));
1982+ }
1983+
1984+ Blit lastCAblit;
1985+ Composite lastCAcomp;
1986+
1987+ public void copyArea(int x, int y, int w, int h, int dx, int dy) {
1988+ try {
1989+ doCopyArea(x, y, w, h, dx, dy);
1990+ } catch (InvalidPipeException e) {
1991+ revalidateAll();
1992+ try {
1993+ doCopyArea(x, y, w, h, dx, dy);
1994+ } catch (InvalidPipeException e2) {
1995+ // Still catching the exception; we are not yet ready to
1996+ // validate the surfaceData correctly. Fail for now and
1997+ // try again next time around.
1998+ }
1999+ } finally {
2000+ surfaceData.markDirty();
2001+ }
2002+ }
2003+
2004+ private void doCopyArea(int x, int y, int w, int h, int dx, int dy) {
2005+ if (w <= 0 || h <= 0) {
2006+ return;
2007+ }
2008+ SurfaceData theData = surfaceData;
2009+ if (theData.copyArea(this, x, y, w, h, dx, dy)) {
2010+ return;
2011+ }
2012+ if (transformState >= TRANSFORM_TRANSLATESCALE) {
2013+ throw new InternalError("transformed copyArea not implemented yet");
2014+ }
2015+ // REMIND: This method does not deal with missing data from the
2016+ // source object (i.e. it does not send exposure events...)
2017+
2018+ Region clip = getCompClip();
2019+
2020+ Composite comp = composite;
2021+ if (lastCAcomp != comp) {
2022+ SurfaceType dsttype = theData.getSurfaceType();
2023+ CompositeType comptype = imageComp;
2024+ if (CompositeType.SrcOverNoEa.equals(comptype) &&
2025+ theData.getTransparency() == Transparency.OPAQUE)
2026+ {
2027+ comptype = CompositeType.SrcNoEa;
2028+ }
2029+ lastCAblit = Blit.locate(dsttype, comptype, dsttype);
2030+ lastCAcomp = comp;
2031+ }
2032+
2033+ x += transX;
2034+ y += transY;
2035+
2036+ Blit ob = lastCAblit;
2037+ if (dy == 0 && dx > 0 && dx < w) {
2038+ while (w > 0) {
2039+ int partW = Math.min(w, dx);
2040+ w -= partW;
2041+ int sx = x + w;
2042+ ob.Blit(theData, theData, comp, clip,
2043+ sx, y, sx+dx, y+dy, partW, h);
2044+ }
2045+ return;
2046+ }
2047+ if (dy > 0 && dy < h && dx > -w && dx < w) {
2048+ while (h > 0) {
2049+ int partH = Math.min(h, dy);
2050+ h -= partH;
2051+ int sy = y + h;
2052+ ob.Blit(theData, theData, comp, clip,
2053+ x, sy, x+dx, sy+dy, w, partH);
2054+ }
2055+ return;
2056+ }
2057+ ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h);
2058+ }
2059+
2060+ /*
2061+ public void XcopyArea(int x, int y, int w, int h, int dx, int dy) {
2062+ Rectangle rect = new Rectangle(x, y, w, h);
2063+ rect = transformBounds(rect, transform);
2064+ Point2D point = new Point2D.Float(dx, dy);
2065+ Point2D root = new Point2D.Float(0, 0);
2066+ point = transform.transform(point, point);
2067+ root = transform.transform(root, root);
2068+ int fdx = (int)(point.getX()-root.getX());
2069+ int fdy = (int)(point.getY()-root.getY());
2070+
2071+ Rectangle r = getCompBounds().intersection(rect.getBounds());
2072+
2073+ if (r.isEmpty()) {
2074+ return;
2075+ }
2076+
2077+ // Begin Rasterizer for Clip Shape
2078+ boolean skipClip = true;
2079+ byte[] clipAlpha = null;
2080+
2081+ if (clipState == CLIP_SHAPE) {
2082+
2083+ int box[] = new int[4];
2084+
2085+ clipRegion.getBounds(box);
2086+ Rectangle devR = new Rectangle(box[0], box[1],
2087+ box[2] - box[0],
2088+ box[3] - box[1]);
2089+ if (!devR.isEmpty()) {
2090+ OutputManager mgr = getOutputManager();
2091+ RegionIterator ri = clipRegion.getIterator();
2092+ while (ri.nextYRange(box)) {
2093+ int spany = box[1];
2094+ int spanh = box[3] - spany;
2095+ while (ri.nextXBand(box)) {
2096+ int spanx = box[0];
2097+ int spanw = box[2] - spanx;
2098+ mgr.copyArea(this, null,
2099+ spanw, 0,
2100+ spanx, spany,
2101+ spanw, spanh,
2102+ fdx, fdy,
2103+ null);
2104+ }
2105+ }
2106+ }
2107+ return;
2108+ }
2109+ // End Rasterizer for Clip Shape
2110+
2111+ getOutputManager().copyArea(this, null,
2112+ r.width, 0,
2113+ r.x, r.y, r.width,
2114+ r.height, fdx, fdy,
2115+ null);
2116+ }
2117+ */
2118+
2119+ public void drawLine(int x1, int y1, int x2, int y2) {
2120+ try {
2121+ drawpipe.drawLine(this, x1, y1, x2, y2);
2122+ } catch (InvalidPipeException e) {
2123+ revalidateAll();
2124+ try {
2125+ drawpipe.drawLine(this, x1, y1, x2, y2);
2126+ } catch (InvalidPipeException e2) {
2127+ // Still catching the exception; we are not yet ready to
2128+ // validate the surfaceData correctly. Fail for now and
2129+ // try again next time around.
2130+ }
2131+ } finally {
2132+ surfaceData.markDirty();
2133+ }
2134+ }
2135+
2136+ public void drawRoundRect(int x, int y, int w, int h, int arcW, int arcH) {
2137+ try {
2138+ drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);
2139+ } catch (InvalidPipeException e) {
2140+ revalidateAll();
2141+ try {
2142+ drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);
2143+ } catch (InvalidPipeException e2) {
2144+ // Still catching the exception; we are not yet ready to
2145+ // validate the surfaceData correctly. Fail for now and
2146+ // try again next time around.
2147+ }
2148+ } finally {
2149+ surfaceData.markDirty();
2150+ }
2151+ }
2152+
2153+ public void fillRoundRect(int x, int y, int w, int h, int arcW, int arcH) {
2154+ try {
2155+ fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);
2156+ } catch (InvalidPipeException e) {
2157+ revalidateAll();
2158+ try {
2159+ fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);
2160+ } catch (InvalidPipeException e2) {
2161+ // Still catching the exception; we are not yet ready to
2162+ // validate the surfaceData correctly. Fail for now and
2163+ // try again next time around.
2164+ }
2165+ } finally {
2166+ surfaceData.markDirty();
2167+ }
2168+ }
2169+
2170+ public void drawOval(int x, int y, int w, int h) {
2171+ try {
2172+ drawpipe.drawOval(this, x, y, w, h);
2173+ } catch (InvalidPipeException e) {
2174+ revalidateAll();
2175+ try {
2176+ drawpipe.drawOval(this, x, y, w, h);
2177+ } catch (InvalidPipeException e2) {
2178+ // Still catching the exception; we are not yet ready to
2179+ // validate the surfaceData correctly. Fail for now and
2180+ // try again next time around.
2181+ }
2182+ } finally {
2183+ surfaceData.markDirty();
2184+ }
2185+ }
2186+
2187+ public void fillOval(int x, int y, int w, int h) {
2188+ try {
2189+ fillpipe.fillOval(this, x, y, w, h);
2190+ } catch (InvalidPipeException e) {
2191+ revalidateAll();
2192+ try {
2193+ fillpipe.fillOval(this, x, y, w, h);
2194+ } catch (InvalidPipeException e2) {
2195+ // Still catching the exception; we are not yet ready to
2196+ // validate the surfaceData correctly. Fail for now and
2197+ // try again next time around.
2198+ }
2199+ } finally {
2200+ surfaceData.markDirty();
2201+ }
2202+ }
2203+
2204+ public void drawArc(int x, int y, int w, int h,
2205+ int startAngl, int arcAngl) {
2206+ try {
2207+ drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);
2208+ } catch (InvalidPipeException e) {
2209+ revalidateAll();
2210+ try {
2211+ drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);
2212+ } catch (InvalidPipeException e2) {
2213+ // Still catching the exception; we are not yet ready to
2214+ // validate the surfaceData correctly. Fail for now and
2215+ // try again next time around.
2216+ }
2217+ } finally {
2218+ surfaceData.markDirty();
2219+ }
2220+ }
2221+
2222+ public void fillArc(int x, int y, int w, int h,
2223+ int startAngl, int arcAngl) {
2224+ try {
2225+ fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2226+ } catch (InvalidPipeException e) {
2227+ revalidateAll();
2228+ try {
2229+ fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);
2230+ } catch (InvalidPipeException e2) {
2231+ // Still catching the exception; we are not yet ready to
2232+ // validate the surfaceData correctly. Fail for now and
2233+ // try again next time around.
2234+ }
2235+ } finally {
2236+ surfaceData.markDirty();
2237+ }
2238+ }
2239+
2240+ public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {
2241+ try {
2242+ drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2243+ } catch (InvalidPipeException e) {
2244+ revalidateAll();
2245+ try {
2246+ drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);
2247+ } catch (InvalidPipeException e2) {
2248+ // Still catching the exception; we are not yet ready to
2249+ // validate the surfaceData correctly. Fail for now and
2250+ // try again next time around.
2251+ }
2252+ } finally {
2253+ surfaceData.markDirty();
2254+ }
2255+ }
2256+
2257+ public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {
2258+ try {
2259+ drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2260+ } catch (InvalidPipeException e) {
2261+ revalidateAll();
2262+ try {
2263+ drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);
2264+ } catch (InvalidPipeException e2) {
2265+ // Still catching the exception; we are not yet ready to
2266+ // validate the surfaceData correctly. Fail for now and
2267+ // try again next time around.
2268+ }
2269+ } finally {
2270+ surfaceData.markDirty();
2271+ }
2272+ }
2273+
2274+ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {
2275+ try {
2276+ fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2277+ } catch (InvalidPipeException e) {
2278+ revalidateAll();
2279+ try {
2280+ fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);
2281+ } catch (InvalidPipeException e2) {
2282+ // Still catching the exception; we are not yet ready to
2283+ // validate the surfaceData correctly. Fail for now and
2284+ // try again next time around.
2285+ }
2286+ } finally {
2287+ surfaceData.markDirty();
2288+ }
2289+ }
2290+
2291+ public void drawRect (int x, int y, int w, int h) {
2292+ try {
2293+ drawpipe.drawRect(this, x, y, w, h);
2294+ } catch (InvalidPipeException e) {
2295+ revalidateAll();
2296+ try {
2297+ drawpipe.drawRect(this, x, y, w, h);
2298+ } catch (InvalidPipeException e2) {
2299+ // Still catching the exception; we are not yet ready to
2300+ // validate the surfaceData correctly. Fail for now and
2301+ // try again next time around.
2302+ }
2303+ } finally {
2304+ surfaceData.markDirty();
2305+ }
2306+ }
2307+
2308+ public void fillRect (int x, int y, int w, int h) {
2309+ try {
2310+ fillpipe.fillRect(this, x, y, w, h);
2311+ } catch (InvalidPipeException e) {
2312+ revalidateAll();
2313+ try {
2314+ fillpipe.fillRect(this, x, y, w, h);
2315+ } catch (InvalidPipeException e2) {
2316+ // Still catching the exception; we are not yet ready to
2317+ // validate the surfaceData correctly. Fail for now and
2318+ // try again next time around.
2319+ }
2320+ } finally {
2321+ surfaceData.markDirty();
2322+ }
2323+ }
2324+
2325+ private void revalidateAll() {
2326+ try {
2327+ // REMIND: This locking needs to be done around the
2328+ // caller of this method so that the pipe stays valid
2329+ // long enough to call the new primitive.
2330+ // REMIND: No locking yet in screen SurfaceData objects!
2331+ // surfaceData.lock();
2332+ surfaceData = surfaceData.getReplacement();
2333+ if (surfaceData == null) {
2334+ surfaceData = NullSurfaceData.theInstance;
2335+ }
2336+
2337+ // this will recalculate the composite clip
2338+ setDevClip(surfaceData.getBounds());
2339+
2340+ if (paintState <= PAINT_ALPHACOLOR) {
2341+ validateColor();
2342+ }
2343+ if (composite instanceof XORComposite) {
2344+ Color c = ((XORComposite) composite).getXorColor();
2345+ setComposite(new XORComposite(c, surfaceData));
2346+ }
2347+ validatePipe();
2348+ } finally {
2349+ // REMIND: No locking yet in screen SurfaceData objects!
2350+ // surfaceData.unlock();
2351+ }
2352+ }
2353+
2354+ public void clearRect(int x, int y, int w, int h) {
2355+ // REMIND: has some "interesting" consequences if threads are
2356+ // not synchronized
2357+ Composite c = composite;
2358+ Paint p = paint;
2359+ setComposite(AlphaComposite.Src);
2360+ setColor(getBackground());
2361+ validatePipe();
2362+ fillRect(x, y, w, h);
2363+ setPaint(p);
2364+ setComposite(c);
2365+ }
2366+
2367+ /**
2368+ * Strokes the outline of a Path using the settings of the current
2369+ * graphics state. The rendering attributes applied include the
2370+ * clip, transform, paint or color, composite and stroke attributes.
2371+ * @param p The path to be drawn.
2372+ * @see #setStroke
2373+ * @see #setPaint
2374+ * @see java.awt.Graphics#setColor
2375+ * @see #transform
2376+ * @see #setTransform
2377+ * @see #clip
2378+ * @see #setClip
2379+ * @see #setComposite
2380+ */
2381+ public void draw(Shape s) {
2382+ try {
2383+ shapepipe.draw(this, s);
2384+ } catch (InvalidPipeException e) {
2385+ revalidateAll();
2386+ try {
2387+ shapepipe.draw(this, s);
2388+ } catch (InvalidPipeException e2) {
2389+ // Still catching the exception; we are not yet ready to
2390+ // validate the surfaceData correctly. Fail for now and
2391+ // try again next time around.
2392+ }
2393+ } finally {
2394+ surfaceData.markDirty();
2395+ }
2396+ }
2397+
2398+
2399+ /**
2400+ * Fills the interior of a Path using the settings of the current
2401+ * graphics state. The rendering attributes applied include the
2402+ * clip, transform, paint or color, and composite.
2403+ * @see #setPaint
2404+ * @see java.awt.Graphics#setColor
2405+ * @see #transform
2406+ * @see #setTransform
2407+ * @see #setComposite
2408+ * @see #clip
2409+ * @see #setClip
2410+ */
2411+ public void fill(Shape s) {
2412+ try {
2413+ shapepipe.fill(this, s);
2414+ } catch (InvalidPipeException e) {
2415+ revalidateAll();
2416+ try {
2417+ shapepipe.fill(this, s);
2418+ } catch (InvalidPipeException e2) {
2419+ // Still catching the exception; we are not yet ready to
2420+ // validate the surfaceData correctly. Fail for now and
2421+ // try again next time around.
2422+ }
2423+ } finally {
2424+ surfaceData.markDirty();
2425+ }
2426+ }
2427+
2428+ /**
2429+ * Returns true if the given AffineTransform is an integer
2430+ * translation.
2431+ */
2432+ private static boolean isIntegerTranslation(AffineTransform xform) {
2433+ if (xform.isIdentity()) {
2434+ return true;
2435+ }
2436+ if (xform.getType() == AffineTransform.TYPE_TRANSLATION) {
2437+ double tx = xform.getTranslateX();
2438+ double ty = xform.getTranslateY();
2439+ return (tx == (int)tx && ty == (int)ty);
2440+ }
2441+ return false;
2442+ }
2443+
2444+ /**
2445+ * Returns the index of the tile corresponding to the supplied position
2446+ * given the tile grid offset and size along the same axis.
2447+ */
2448+ private static int getTileIndex(int p, int tileGridOffset, int tileSize) {
2449+ p -= tileGridOffset;
2450+ if (p < 0) {
2451+ p += 1 - tileSize; // force round to -infinity (ceiling)
2452+ }
2453+ return p/tileSize;
2454+ }
2455+
2456+ /**
2457+ * Returns a rectangle in image coordinates that may be required
2458+ * in order to draw the given image into the given clipping region
2459+ * through a pair of AffineTransforms. In addition, horizontal and
2460+ * vertical padding factors for antialising and interpolation may
2461+ * be used.
2462+ */
2463+ private static Rectangle getImageRegion(RenderedImage img,
2464+ Region compClip,
2465+ AffineTransform transform,
2466+ AffineTransform xform,
2467+ int padX, int padY) {
2468+ Rectangle imageRect =
2469+ new Rectangle(img.getMinX(), img.getMinY(),
2470+ img.getWidth(), img.getHeight());
2471+
2472+ Rectangle result = null;
2473+ try {
2474+ double p[] = new double[8];
2475+ p[0] = p[2] = compClip.getLoX();
2476+ p[4] = p[6] = compClip.getHiX();
2477+ p[1] = p[5] = compClip.getLoY();
2478+ p[3] = p[7] = compClip.getHiY();
2479+
2480+ // Inverse transform the output bounding rect
2481+ transform.inverseTransform(p, 0, p, 0, 4);
2482+ xform.inverseTransform(p, 0, p, 0, 4);
2483+
2484+ // Determine a bounding box for the inverse transformed region
2485+ double x0,x1,y0,y1;
2486+ x0 = x1 = p[0];
2487+ y0 = y1 = p[1];
2488+
2489+ for (int i = 2; i < 8; ) {
2490+ double pt = p[i++];
2491+ if (pt < x0) {
2492+ x0 = pt;
2493+ } else if (pt > x1) {
2494+ x1 = pt;
2495+ }
2496+ pt = p[i++];
2497+ if (pt < y0) {
2498+ y0 = pt;
2499+ } else if (pt > y1) {
2500+ y1 = pt;
2501+ }
2502+ }
2503+
2504+ // This is padding for anti-aliasing and such. It may
2505+ // be more than is needed.
2506+ int x = (int)x0 - padX;
2507+ int w = (int)(x1 - x0 + 2*padX);
2508+ int y = (int)y0 - padY;
2509+ int h = (int)(y1 - y0 + 2*padY);
2510+
2511+ Rectangle clipRect = new Rectangle(x,y,w,h);
2512+ result = clipRect.intersection(imageRect);
2513+ } catch (NoninvertibleTransformException nte) {
2514+ // Worst case bounds are the bounds of the image.
2515+ result = imageRect;
2516+ }
2517+
2518+ return result;
2519+ }
2520+
2521+ /**
2522+ * Draws an image, applying a transform from image space into user space
2523+ * before drawing.
2524+ * The transformation from user space into device space is done with
2525+ * the current transform in the Graphics2D.
2526+ * The given transformation is applied to the image before the
2527+ * transform attribute in the Graphics2D state is applied.
2528+ * The rendering attributes applied include the clip, transform,
2529+ * and composite attributes. Note that the result is
2530+ * undefined, if the given transform is noninvertible.
2531+ * @param img The image to be drawn. Does nothing if img is null.
2532+ * @param xform The transformation from image space into user space.
2533+ * @see #transform
2534+ * @see #setTransform
2535+ * @see #setComposite
2536+ * @see #clip
2537+ * @see #setClip
2538+ */
2539+ public void drawRenderedImage(RenderedImage img,
2540+ AffineTransform xform) {
2541+
2542+ if (img == null) {
2543+ return;
2544+ }
2545+
2546+ // BufferedImage case: use a simple drawImage call
2547+ if (img instanceof BufferedImage) {
2548+ BufferedImage bufImg = (BufferedImage)img;
2549+ drawImage(bufImg,xform,null);
2550+ return;
2551+ }
2552+
2553+ // transformState tracks the state of transform and
2554+ // transX, transY contain the integer casts of the
2555+ // translation factors
2556+ boolean isIntegerTranslate =
2557+ (transformState <= TRANSFORM_INT_TRANSLATE) &&
2558+ isIntegerTranslation(xform);
2559+
2560+ // Include padding for interpolation/antialiasing if necessary
2561+ int pad = isIntegerTranslate ? 0 : 3;
2562+
2563+ // Determine the region of the image that may contribute to
2564+ // the clipped drawing area
2565+ Rectangle region = getImageRegion(img,
2566+ getCompClip(),
2567+ transform,
2568+ xform,
2569+ pad, pad);
2570+ if (region.width <= 0 || region.height <= 0) {
2571+ return;
2572+ }
2573+
2574+ // Attempt to optimize integer translation of tiled images.
2575+ // Although theoretically we are O.K. if the concatenation of
2576+ // the user transform and the device transform is an integer
2577+ // translation, we'll play it safe and only optimize the case
2578+ // where both are integer translations.
2579+ if (isIntegerTranslate) {
2580+ // Use optimized code
2581+ // Note that drawTranslatedRenderedImage calls copyImage
2582+ // which takes the user space to device space transform into
2583+ // account, but we need to provide the image space to user space
2584+ // translations.
2585+
2586+ drawTranslatedRenderedImage(img, region,
2587+ (int) xform.getTranslateX(),
2588+ (int) xform.getTranslateY());
2589+ return;
2590+ }
2591+
2592+ // General case: cobble the necessary region into a single Raster
2593+ Raster raster = img.getData(region);
2594+
2595+ // Make a new Raster with the same contents as raster
2596+ // but starting at (0, 0). This raster is thus in the same
2597+ // coordinate system as the SampleModel of the original raster.
2598+ WritableRaster wRaster =
2599+ Raster.createWritableRaster(raster.getSampleModel(),
2600+ raster.getDataBuffer(),
2601+ null);
2602+
2603+ // If the original raster was in a different coordinate
2604+ // system than its SampleModel, we need to perform an
2605+ // additional translation in order to get the (minX, minY)
2606+ // pixel of raster to be pixel (0, 0) of wRaster. We also
2607+ // have to have the correct width and height.
2608+ int minX = raster.getMinX();
2609+ int minY = raster.getMinY();
2610+ int width = raster.getWidth();
2611+ int height = raster.getHeight();
2612+ int px = minX - raster.getSampleModelTranslateX();
2613+ int py = minY - raster.getSampleModelTranslateY();
2614+ if (px != 0 || py != 0 || width != wRaster.getWidth() ||
2615+ height != wRaster.getHeight()) {
2616+ wRaster =
2617+ wRaster.createWritableChild(px,
2618+ py,
2619+ width,
2620+ height,
2621+ 0, 0,
2622+ null);
2623+ }
2624+
2625+ // Now we have a BufferedImage starting at (0, 0)
2626+ // with the same contents that started at (minX, minY)
2627+ // in raster. So we must draw the BufferedImage with a
2628+ // translation of (minX, minY).
2629+ AffineTransform transXform = (AffineTransform)xform.clone();
2630+ transXform.translate(minX, minY);
2631+
2632+ ColorModel cm = img.getColorModel();
2633+ BufferedImage bufImg = new BufferedImage(cm,
2634+ wRaster,
2635+ cm.isAlphaPremultiplied(),
2636+ null);
2637+ drawImage(bufImg, transXform, null);
2638+ }
2639+
2640+ /**
2641+ * Intersects <code>destRect</code> with <code>clip</code> and
2642+ * overwrites <code>destRect</code> with the result.
2643+ * Returns false if the intersection was empty, true otherwise.
2644+ */
2645+ private boolean clipTo(Rectangle destRect, Rectangle clip) {
2646+ int x1 = Math.max(destRect.x, clip.x);
2647+ int x2 = Math.min(destRect.x + destRect.width, clip.x + clip.width);
2648+ int y1 = Math.max(destRect.y, clip.y);
2649+ int y2 = Math.min(destRect.y + destRect.height, clip.y + clip.height);
2650+ if (((x2 - x1) < 0) || ((y2 - y1) < 0)) {
2651+ destRect.width = -1; // Set both just to be safe
2652+ destRect.height = -1;
2653+ return false;
2654+ } else {
2655+ destRect.x = x1;
2656+ destRect.y = y1;
2657+ destRect.width = x2 - x1;
2658+ destRect.height = y2 - y1;
2659+ return true;
2660+ }
2661+ }
2662+
2663+ /**
2664+ * Draw a portion of a RenderedImage tile-by-tile with a given
2665+ * integer image to user space translation. The user to
2666+ * device transform must also be an integer translation.
2667+ */
2668+ private void drawTranslatedRenderedImage(RenderedImage img,
2669+ Rectangle region,
2670+ int i2uTransX,
2671+ int i2uTransY) {
2672+ // Cache tile grid info
2673+ int tileGridXOffset = img.getTileGridXOffset();
2674+ int tileGridYOffset = img.getTileGridYOffset();
2675+ int tileWidth = img.getTileWidth();
2676+ int tileHeight = img.getTileHeight();
2677+
2678+ // Determine the tile index extrema in each direction
2679+ int minTileX =
2680+ getTileIndex(region.x, tileGridXOffset, tileWidth);
2681+ int minTileY =
2682+ getTileIndex(region.y, tileGridYOffset, tileHeight);
2683+ int maxTileX =
2684+ getTileIndex(region.x + region.width - 1,
2685+ tileGridXOffset, tileWidth);
2686+ int maxTileY =
2687+ getTileIndex(region.y + region.height - 1,
2688+ tileGridYOffset, tileHeight);
2689+
2690+ // Create a single ColorModel to use for all BufferedImages
2691+ ColorModel colorModel = img.getColorModel();
2692+
2693+ // Reuse the same Rectangle for each iteration
2694+ Rectangle tileRect = new Rectangle();
2695+
2696+ for (int ty = minTileY; ty <= maxTileY; ty++) {
2697+ for (int tx = minTileX; tx <= maxTileX; tx++) {
2698+ // Get the current tile.
2699+ Raster raster = img.getTile(tx, ty);
2700+
2701+ // Fill in tileRect with the tile bounds
2702+ tileRect.x = tx*tileWidth + tileGridXOffset;
2703+ tileRect.y = ty*tileHeight + tileGridYOffset;
2704+ tileRect.width = tileWidth;
2705+ tileRect.height = tileHeight;
2706+
2707+ // Clip the tile against the image bounds and
2708+ // backwards mapped clip region
2709+ // The result can't be empty
2710+ clipTo(tileRect, region);
2711+
2712+ // Create a WritableRaster containing the tile
2713+ WritableRaster wRaster = null;
2714+ if (raster instanceof WritableRaster) {
2715+ wRaster = (WritableRaster)raster;
2716+ } else {
2717+ // Create a WritableRaster in the same coordinate system
2718+ // as the original raster.
2719+ wRaster =
2720+ Raster.createWritableRaster(raster.getSampleModel(),
2721+ raster.getDataBuffer(),
2722+ null);
2723+ }
2724+
2725+ // Translate wRaster to start at (0, 0) and to contain
2726+ // only the relevent portion of the tile
2727+ wRaster = wRaster.createWritableChild(tileRect.x, tileRect.y,
2728+ tileRect.width,
2729+ tileRect.height,
2730+ 0, 0,
2731+ null);
2732+
2733+ // Wrap wRaster in a BufferedImage
2734+ BufferedImage bufImg =
2735+ new BufferedImage(colorModel,
2736+ wRaster,
2737+ colorModel.isAlphaPremultiplied(),
2738+ null);
2739+ // Now we have a BufferedImage starting at (0, 0) that
2740+ // represents data from a Raster starting at
2741+ // (tileRect.x, tileRect.y). Additionally, it needs
2742+ // to be translated by (i2uTransX, i2uTransY). We call
2743+ // copyImage to draw just the region of interest
2744+ // without needing to create a child image.
2745+ copyImage(bufImg, tileRect.x + i2uTransX,
2746+ tileRect.y + i2uTransY, 0, 0, tileRect.width,
2747+ tileRect.height, null, null);
2748+ }
2749+ }
2750+ }
2751+
2752+ public void drawRenderableImage(RenderableImage img,
2753+ AffineTransform xform) {
2754+
2755+ if (img == null) {
2756+ return;
2757+ }
2758+
2759+ AffineTransform pipeTransform = transform;
2760+ AffineTransform concatTransform = new AffineTransform(xform);
2761+ concatTransform.concatenate(pipeTransform);
2762+ AffineTransform reverseTransform;
2763+
2764+ RenderContext rc = new RenderContext(concatTransform);
2765+
2766+ try {
2767+ reverseTransform = pipeTransform.createInverse();
2768+ } catch (NoninvertibleTransformException nte) {
2769+ rc = new RenderContext(pipeTransform);
2770+ reverseTransform = new AffineTransform();
2771+ }
2772+
2773+ RenderedImage rendering = img.createRendering(rc);
2774+ drawRenderedImage(rendering,reverseTransform);
2775+ }
2776+
2777+
2778+
2779+ /*
2780+ * Transform the bounding box of the BufferedImage
2781+ */
2782+ protected Rectangle transformBounds(Rectangle rect,
2783+ AffineTransform tx) {
2784+ if (tx.isIdentity()) {
2785+ return rect;
2786+ }
2787+
2788+ Shape s = transformShape(tx, rect);
2789+ return s.getBounds();
2790+ }
2791+
2792+ // text rendering methods
2793+ public void drawString(String str, int x, int y) {
2794+ if (str == null) {
2795+ throw new NullPointerException("String is null");
2796+ }
2797+
2798+ if (font.hasLayoutAttributes()) {
2799+ if (str.length() == 0) {
2800+ return;
2801+ }
2802+ new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);
2803+ return;
2804+ }
2805+
2806+ try {
2807+ textpipe.drawString(this, str, x, y);
2808+ } catch (InvalidPipeException e) {
2809+ revalidateAll();
2810+ try {
2811+ textpipe.drawString(this, str, x, y);
2812+ } catch (InvalidPipeException e2) {
2813+ // Still catching the exception; we are not yet ready to
2814+ // validate the surfaceData correctly. Fail for now and
2815+ // try again next time around.
2816+ }
2817+ } finally {
2818+ surfaceData.markDirty();
2819+ }
2820+ }
2821+
2822+ public void drawString(String str, float x, float y) {
2823+ if (str == null) {
2824+ throw new NullPointerException("String is null");
2825+ }
2826+
2827+ if (font.hasLayoutAttributes()) {
2828+ if (str.length() == 0) {
2829+ return;
2830+ }
2831+ new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);
2832+ return;
2833+ }
2834+
2835+ try {
2836+ textpipe.drawString(this, str, x, y);
2837+ } catch (InvalidPipeException e) {
2838+ revalidateAll();
2839+ try {
2840+ textpipe.drawString(this, str, x, y);
2841+ } catch (InvalidPipeException e2) {
2842+ // Still catching the exception; we are not yet ready to
2843+ // validate the surfaceData correctly. Fail for now and
2844+ // try again next time around.
2845+ }
2846+ } finally {
2847+ surfaceData.markDirty();
2848+ }
2849+ }
2850+
2851+ public void drawString(AttributedCharacterIterator iterator,
2852+ int x, int y) {
2853+ if (iterator == null) {
2854+ throw new NullPointerException("AttributedCharacterIterator is null");
2855+ }
2856+ if (iterator.getBeginIndex() == iterator.getEndIndex()) {
2857+ return; /* nothing to draw */
2858+ }
2859+ TextLayout tl = new TextLayout(iterator, getFontRenderContext());
2860+ tl.draw(this, (float) x, (float) y);
2861+ }
2862+
2863+ public void drawString(AttributedCharacterIterator iterator,
2864+ float x, float y) {
2865+ if (iterator == null) {
2866+ throw new NullPointerException("AttributedCharacterIterator is null");
2867+ }
2868+ if (iterator.getBeginIndex() == iterator.getEndIndex()) {
2869+ return; /* nothing to draw */
2870+ }
2871+ TextLayout tl = new TextLayout(iterator, getFontRenderContext());
2872+ tl.draw(this, x, y);
2873+ }
2874+
2875+ public void drawGlyphVector(GlyphVector gv, float x, float y)
2876+ {
2877+ if (gv == null) {
2878+ throw new NullPointerException("GlyphVector is null");
2879+ }
2880+
2881+ try {
2882+ textpipe.drawGlyphVector(this, gv, x, y);
2883+ } catch (InvalidPipeException e) {
2884+ revalidateAll();
2885+ try {
2886+ textpipe.drawGlyphVector(this, gv, x, y);
2887+ } catch (InvalidPipeException e2) {
2888+ // Still catching the exception; we are not yet ready to
2889+ // validate the surfaceData correctly. Fail for now and
2890+ // try again next time around.
2891+ }
2892+ } finally {
2893+ surfaceData.markDirty();
2894+ }
2895+ }
2896+
2897+ public void drawChars(char data[], int offset, int length, int x, int y) {
2898+
2899+ if (data == null) {
2900+ throw new NullPointerException("char data is null");
2901+ }
2902+ if (offset < 0 || length < 0 || offset + length > data.length) {
2903+ throw new ArrayIndexOutOfBoundsException("bad offset/length");
2904+ }
2905+ if (font.hasLayoutAttributes()) {
2906+ if (data.length == 0) {
2907+ return;
2908+ }
2909+ new TextLayout(new String(data, offset, length),
2910+ font, getFontRenderContext()).draw(this, x, y);
2911+ return;
2912+ }
2913+
2914+ try {
2915+ textpipe.drawChars(this, data, offset, length, x, y);
2916+ } catch (InvalidPipeException e) {
2917+ revalidateAll();
2918+ try {
2919+ textpipe.drawChars(this, data, offset, length, x, y);
2920+ } catch (InvalidPipeException e2) {
2921+ // Still catching the exception; we are not yet ready to
2922+ // validate the surfaceData correctly. Fail for now and
2923+ // try again next time around.
2924+ }
2925+ } finally {
2926+ surfaceData.markDirty();
2927+ }
2928+ }
2929+
2930+ public void drawBytes(byte data[], int offset, int length, int x, int y) {
2931+ if (data == null) {
2932+ throw new NullPointerException("byte data is null");
2933+ }
2934+ if (offset < 0 || length < 0 || offset + length > data.length) {
2935+ throw new ArrayIndexOutOfBoundsException("bad offset/length");
2936+ }
2937+ /* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */
2938+ char chData[] = new char[length];
2939+ for (int i = length; i-- > 0; ) {
2940+ chData[i] = (char)(data[i+offset] & 0xff);
2941+ }
2942+ if (font.hasLayoutAttributes()) {
2943+ if (data.length == 0) {
2944+ return;
2945+ }
2946+ new TextLayout(new String(chData),
2947+ font, getFontRenderContext()).draw(this, x, y);
2948+ return;
2949+ }
2950+
2951+ try {
2952+ textpipe.drawChars(this, chData, 0, length, x, y);
2953+ } catch (InvalidPipeException e) {
2954+ revalidateAll();
2955+ try {
2956+ textpipe.drawChars(this, chData, 0, length, x, y);
2957+ } catch (InvalidPipeException e2) {
2958+ // Still catching the exception; we are not yet ready to
2959+ // validate the surfaceData correctly. Fail for now and
2960+ // try again next time around.
2961+ }
2962+ } finally {
2963+ surfaceData.markDirty();
2964+ }
2965+ }
2966+// end of text rendering methods
2967+
2968+ /**
2969+ * Draws an image scaled to x,y,w,h in nonblocking mode with a
2970+ * callback object.
2971+ */
2972+ public boolean drawImage(Image img, int x, int y, int width, int height,
2973+ ImageObserver observer) {
2974+ return drawImage(img, x, y, width, height, null, observer);
2975+ }
2976+
2977+ /**
2978+ * Not part of the advertised API but a useful utility method
2979+ * to call internally. This is for the case where we are
2980+ * drawing to/from given coordinates using a given width/height,
2981+ * but we guarantee that the weidth/height of the src and dest
2982+ * areas are equal (no scale needed).
2983+ */
2984+ public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
2985+ int width, int height, Color bgcolor,
2986+ ImageObserver observer) {
2987+ try {
2988+ return imagepipe.copyImage(this, img, dx, dy, sx, sy,
2989+ width, height, bgcolor, observer);
2990+ } catch (InvalidPipeException e) {
2991+ revalidateAll();
2992+ try {
2993+ return imagepipe.copyImage(this, img, dx, dy, sx, sy,
2994+ width, height, bgcolor, observer);
2995+ } catch (InvalidPipeException e2) {
2996+ // Still catching the exception; we are not yet ready to
2997+ // validate the surfaceData correctly. Fail for now and
2998+ // try again next time around.
2999+ return false;
3000+ }
3001+ } finally {
3002+ surfaceData.markDirty();
3003+ }
3004+ }
3005+
3006+ /**
3007+ * Draws an image scaled to x,y,w,h in nonblocking mode with a
3008+ * solid background color and a callback object.
3009+ */
3010+ public boolean drawImage(Image img, int x, int y, int width, int height,
3011+ Color bg, ImageObserver observer) {
3012+
3013+ if (img == null) {
3014+ return true;
3015+ }
3016+
3017+ if ((width == 0) || (height == 0)) {
3018+ return true;
3019+ }
3020+ if (width == img.getWidth(null) && height == img.getHeight(null)) {
3021+ return copyImage(img, x, y, 0, 0, width, height, bg, observer);
3022+ }
3023+
3024+ try {
3025+ return imagepipe.scaleImage(this, img, x, y, width, height,
3026+ bg, observer);
3027+ } catch (InvalidPipeException e) {
3028+ revalidateAll();
3029+ try {
3030+ return imagepipe.scaleImage(this, img, x, y, width, height,
3031+ bg, observer);
3032+ } catch (InvalidPipeException e2) {
3033+ // Still catching the exception; we are not yet ready to
3034+ // validate the surfaceData correctly. Fail for now and
3035+ // try again next time around.
3036+ return false;
3037+ }
3038+ } finally {
3039+ surfaceData.markDirty();
3040+ }
3041+ }
3042+
3043+ /**
3044+ * Draws an image at x,y in nonblocking mode.
3045+ */
3046+ public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
3047+ return drawImage(img, x, y, null, observer);
3048+ }
3049+
3050+ /**
3051+ * Draws an image at x,y in nonblocking mode with a solid background
3052+ * color and a callback object.
3053+ */
3054+ public boolean drawImage(Image img, int x, int y, Color bg,
3055+ ImageObserver observer) {
3056+
3057+ if (img == null) {
3058+ return true;
3059+ }
3060+
3061+ try {
3062+ return imagepipe.copyImage(this, img, x, y, bg, observer);
3063+ } catch (InvalidPipeException e) {
3064+ revalidateAll();
3065+ try {
3066+ return imagepipe.copyImage(this, img, x, y, bg, observer);
3067+ } catch (InvalidPipeException e2) {
3068+ // Still catching the exception; we are not yet ready to
3069+ // validate the surfaceData correctly. Fail for now and
3070+ // try again next time around.
3071+ return false;
3072+ }
3073+ } finally {
3074+ surfaceData.markDirty();
3075+ }
3076+ }
3077+
3078+ /**
3079+ * Draws a subrectangle of an image scaled to a destination rectangle
3080+ * in nonblocking mode with a callback object.
3081+ */
3082+ public boolean drawImage(Image img,
3083+ int dx1, int dy1, int dx2, int dy2,
3084+ int sx1, int sy1, int sx2, int sy2,
3085+ ImageObserver observer) {
3086+ return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,
3087+ observer);
3088+ }
3089+
3090+ /**
3091+ * Draws a subrectangle of an image scaled to a destination rectangle in
3092+ * nonblocking mode with a solid background color and a callback object.
3093+ */
3094+ public boolean drawImage(Image img,
3095+ int dx1, int dy1, int dx2, int dy2,
3096+ int sx1, int sy1, int sx2, int sy2,
3097+ Color bgcolor, ImageObserver observer) {
3098+
3099+ if (img == null) {
3100+ return true;
3101+ }
3102+
3103+ if (dx1 == dx2 || dy1 == dy2 ||
3104+ sx1 == sx2 || sy1 == sy2)
3105+ {
3106+ return true;
3107+ }
3108+
3109+ if (((sx2 - sx1) == (dx2 - dx1)) &&
3110+ ((sy2 - sy1) == (dy2 - dy1)))
3111+ {
3112+ // Not a scale - forward it to a copy routine
3113+ int srcX, srcY, dstX, dstY, width, height;
3114+ if (sx2 > sx1) {
3115+ width = sx2 - sx1;
3116+ srcX = sx1;
3117+ dstX = dx1;
3118+ } else {
3119+ width = sx1 - sx2;
3120+ srcX = sx2;
3121+ dstX = dx2;
3122+ }
3123+ if (sy2 > sy1) {
3124+ height = sy2-sy1;
3125+ srcY = sy1;
3126+ dstY = dy1;
3127+ } else {
3128+ height = sy1-sy2;
3129+ srcY = sy2;
3130+ dstY = dy2;
3131+ }
3132+ return copyImage(img, dstX, dstY, srcX, srcY,
3133+ width, height, bgcolor, observer);
3134+ }
3135+
3136+ try {
3137+ return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,
3138+ sx1, sy1, sx2, sy2, bgcolor,
3139+ observer);
3140+ } catch (InvalidPipeException e) {
3141+ revalidateAll();
3142+ try {
3143+ return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,
3144+ sx1, sy1, sx2, sy2, bgcolor,
3145+ observer);
3146+ } catch (InvalidPipeException e2) {
3147+ // Still catching the exception; we are not yet ready to
3148+ // validate the surfaceData correctly. Fail for now and
3149+ // try again next time around.
3150+ return false;
3151+ }
3152+ } finally {
3153+ surfaceData.markDirty();
3154+ }
3155+ }
3156+
3157+ /**
3158+ * Draw an image, applying a transform from image space into user space
3159+ * before drawing.
3160+ * The transformation from user space into device space is done with
3161+ * the current transform in the Graphics2D.
3162+ * The given transformation is applied to the image before the
3163+ * transform attribute in the Graphics2D state is applied.
3164+ * The rendering attributes applied include the clip, transform,
3165+ * paint or color and composite attributes. Note that the result is
3166+ * undefined, if the given transform is non-invertible.
3167+ * @param img The image to be drawn.
3168+ * @param xform The transformation from image space into user space.
3169+ * @param observer The image observer to be notified on the image producing
3170+ * progress.
3171+ * @see #transform
3172+ * @see #setComposite
3173+ * @see #setClip
3174+ */
3175+ public boolean drawImage(Image img,
3176+ AffineTransform xform,
3177+ ImageObserver observer) {
3178+
3179+ if (img == null) {
3180+ return true;
3181+ }
3182+
3183+ if (xform == null || xform.isIdentity()) {
3184+ return drawImage(img, 0, 0, null, observer);
3185+ }
3186+
3187+ try {
3188+ return imagepipe.transformImage(this, img, xform, observer);
3189+ } catch (InvalidPipeException e) {
3190+ revalidateAll();
3191+ try {
3192+ return imagepipe.transformImage(this, img, xform, observer);
3193+ } catch (InvalidPipeException e2) {
3194+ // Still catching the exception; we are not yet ready to
3195+ // validate the surfaceData correctly. Fail for now and
3196+ // try again next time around.
3197+ return false;
3198+ }
3199+ } finally {
3200+ surfaceData.markDirty();
3201+ }
3202+ }
3203+
3204+ public void drawImage(BufferedImage bImg,
3205+ BufferedImageOp op,
3206+ int x,
3207+ int y) {
3208+
3209+ if (bImg == null) {
3210+ return;
3211+ }
3212+
3213+ try {
3214+ imagepipe.transformImage(this, bImg, op, x, y);
3215+ } catch (InvalidPipeException e) {
3216+ revalidateAll();
3217+ try {
3218+ imagepipe.transformImage(this, bImg, op, x, y);
3219+ } catch (InvalidPipeException e2) {
3220+ // Still catching the exception; we are not yet ready to
3221+ // validate the surfaceData correctly. Fail for now and
3222+ // try again next time around.
3223+ }
3224+ } finally {
3225+ surfaceData.markDirty();
3226+ }
3227+ }
3228+
3229+ /**
3230+ * Get the rendering context of the font
3231+ * within this Graphics2D context.
3232+ */
3233+ public FontRenderContext getFontRenderContext() {
3234+ if (cachedFRC == null) {
3235+ int aahint = textAntialiasHint;
3236+ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT &&
3237+ antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {
3238+ aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;
3239+ }
3240+ // Translation components should be excluded from the FRC transform
3241+ AffineTransform tx = null;
3242+ if (transformState >= TRANSFORM_TRANSLATESCALE) {
3243+ if (transform.getTranslateX() == 0 &&
3244+ transform.getTranslateY() == 0) {
3245+ tx = transform;
3246+ } else {
3247+ tx = new AffineTransform(transform.getScaleX(),
3248+ transform.getShearY(),
3249+ transform.getShearX(),
3250+ transform.getScaleY(),
3251+ 0, 0);
3252+ }
3253+ }
3254+ cachedFRC = new FontRenderContext(tx,
3255+ SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING, aahint),
3256+ SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
3257+ fractionalMetricsHint));
3258+ }
3259+ return cachedFRC;
3260+ }
3261+ private FontRenderContext cachedFRC;
3262+
3263+ /**
3264+ * This object has no resources to dispose of per se, but the
3265+ * doc comments for the base method in java.awt.Graphics imply
3266+ * that this object will not be useable after it is disposed.
3267+ * So, we sabotage the object to prevent further use to prevent
3268+ * developers from relying on behavior that may not work on
3269+ * other, less forgiving, VMs that really need to dispose of
3270+ * resources.
3271+ */
3272+ public void dispose() {
3273+ surfaceData = NullSurfaceData.theInstance;
3274+ invalidatePipe();
3275+ }
3276+
3277+ /**
3278+ * Graphics has a finalize method that automatically calls dispose()
3279+ * for subclasses. For SunGraphics2D we do not need to be finalized
3280+ * so that method simply causes us to be enqueued on the Finalizer
3281+ * queues for no good reason. Unfortunately, that method and
3282+ * implementation are now considered part of the public contract
3283+ * of that base class so we can not remove or gut the method.
3284+ * We override it here with an empty method and the VM is smart
3285+ * enough to know that if our override is empty then it should not
3286+ * mark us as finalizeable.
3287+ */
3288+ public void finalize() {
3289+ // DO NOT REMOVE THIS METHOD
3290+ }
3291+
3292+ /**
3293+ * Returns destination that this Graphics renders to. This could be
3294+ * either an Image or a Component; subclasses of SurfaceData are
3295+ * responsible for returning the appropriate object.
3296+ */
3297+ public Object getDestination() {
3298+ return surfaceData.getDestination();
3299+ }
3300+
3301+ /**
3302+ * {@inheritDoc}
3303+ *
3304+ * @see sun.java2d.DestSurfaceProvider#getDestSurface
3305+ */
3306+ @Override
3307+ public Surface getDestSurface() {
3308+ return surfaceData;
3309+ }
3310+}