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
26package sun.java2d;
27
28import java.awt.Graphics;
29import java.awt.Graphics2D;
30import java.awt.RenderingHints;
31import java.awt.RenderingHints.Key;
32import java.awt.geom.Area;
33import java.awt.geom.AffineTransform;
34import java.awt.geom.NoninvertibleTransformException;
35import java.awt.AlphaComposite;
36import java.awt.BasicStroke;
37import java.awt.image.BufferedImage;
38import java.awt.image.BufferedImageOp;
39import java.awt.image.RenderedImage;
40import java.awt.image.renderable.RenderableImage;
41import java.awt.image.renderable.RenderContext;
42import java.awt.image.AffineTransformOp;
43import java.awt.image.Raster;
44import java.awt.image.WritableRaster;
45import java.awt.Image;
46import java.awt.Composite;
47import java.awt.Color;
48import java.awt.image.ColorModel;
49import java.awt.GraphicsConfiguration;
50import java.awt.Paint;
51import java.awt.GradientPaint;
52import java.awt.LinearGradientPaint;
53import java.awt.RadialGradientPaint;
54import java.awt.TexturePaint;
55import java.awt.geom.Rectangle2D;
56import java.awt.geom.PathIterator;
57import java.awt.geom.GeneralPath;
58import java.awt.Shape;
59import java.awt.Stroke;
60import java.awt.FontMetrics;
61import java.awt.Rectangle;
62import java.text.AttributedCharacterIterator;
63import java.awt.Font;
64import java.awt.image.ImageObserver;
65import java.awt.Transparency;
66import java.awt.font.GlyphVector;
67import java.awt.font.TextLayout;
68import sun.font.FontDesignMetrics;
69import sun.font.FontUtilities;
70import sun.java2d.pipe.PixelDrawPipe;
71import sun.java2d.pipe.PixelFillPipe;
72import sun.java2d.pipe.ShapeDrawPipe;
73import sun.java2d.pipe.ValidatePipe;
74import sun.java2d.pipe.ShapeSpanIterator;
75import sun.java2d.pipe.Region;
76import sun.java2d.pipe.TextPipe;
77import sun.java2d.pipe.DrawImagePipe;
78import sun.java2d.pipe.LoopPipe;
79import sun.java2d.loops.FontInfo;
80import sun.java2d.loops.RenderLoops;
81import sun.java2d.loops.CompositeType;
82import sun.java2d.loops.SurfaceType;
83import sun.java2d.loops.Blit;
84import sun.java2d.loops.MaskFill;
85import sun.font.FontManager;
86import java.awt.font.FontRenderContext;
87import sun.java2d.loops.XORComposite;
88import sun.awt.ConstrainableGraphics;
89import sun.awt.SunHints;
90import java.util.Map;
91import java.util.Iterator;
92import sun.java2d.DestSurfaceProvider;
93import 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 */
104public 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}