48 import java.awt.image.ColorModel; 49 import java.awt.GraphicsConfiguration; 50 import java.awt.Paint; 51 import java.awt.GradientPaint; 52 import java.awt.LinearGradientPaint; 53 import java.awt.RadialGradientPaint; 54 import java.awt.TexturePaint; 55 import java.awt.geom.Rectangle2D; 56 import java.awt.geom.PathIterator; 57 import java.awt.geom.GeneralPath; 58 import java.awt.Shape; 59 import java.awt.Stroke; 60 import java.awt.FontMetrics; 61 import java.awt.Rectangle; 62 import java.text.AttributedCharacterIterator; 63 import java.awt.Font; 64 import java.awt.image.ImageObserver; 65 import java.awt.Transparency; 66 import java.awt.font.GlyphVector; 67 import java.awt.font.TextLayout; 68 import sun.font.FontDesignMetrics; 69 import sun.font.FontUtilities; 70 import sun.java2d.pipe.PixelDrawPipe; 71 import sun.java2d.pipe.PixelFillPipe; 72 import sun.java2d.pipe.ShapeDrawPipe; 73 import sun.java2d.pipe.ValidatePipe; 74 import sun.java2d.pipe.ShapeSpanIterator; 75 import sun.java2d.pipe.Region; 76 import sun.java2d.pipe.TextPipe; 77 import sun.java2d.pipe.DrawImagePipe; 78 import sun.java2d.pipe.LoopPipe; 79 import sun.java2d.loops.FontInfo; 80 import sun.java2d.loops.RenderLoops; 81 import sun.java2d.loops.CompositeType; 82 import sun.java2d.loops.SurfaceType; 83 import sun.java2d.loops.Blit; 84 import sun.java2d.loops.MaskFill; 85 import sun.font.FontManager; 86 import java.awt.font.FontRenderContext; 87 import sun.java2d.loops.XORComposite; 88 import sun.awt.ConstrainableGraphics; 89 import sun.awt.SunHints; 90 import java.util.Map; 91 import java.util.Iterator; 92 import sun.java2d.DestSurfaceProvider; 93 import sun.misc.PerformanceLogger; 94 95 /** 96 * This is a the master Graphics2D superclass for all of the Sun 97 * Graphics implementations. This class relies on subclasses to 98 * manage the various device information, but provides an overall 99 * general framework for performing all of the requests in the 100 * Graphics and Graphics2D APIs. 101 * 102 * @author Jim Graham 103 */ 104 public final class SunGraphics2D 105 extends Graphics2D 106 implements ConstrainableGraphics, Cloneable, DestSurfaceProvider 107 { 108 /* 109 * Attribute States 110 */ 111 /* Paint */ 112 public static final int PAINT_CUSTOM = 6; /* Any other Paint object */ 186 protected Font font; 187 protected FontMetrics fontMetrics; 188 189 public int renderHint; 190 public int antialiasHint; 191 public int textAntialiasHint; 192 protected 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; 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 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 /* This workaround is for the situation when we update the Pipelines 374 * for invalid SurfaceData and run further code when the current 375 * pipeline doesn't support the type of new SurfaceData created during 376 * the current pipeline's work (in place of the invalid SurfaceData). 377 * Usually SurfaceData and Pipelines are repaired (through revalidateAll) 1519 */ 1520 public void translate(int x, int y) { 1521 transform.translate(x, y); 1522 if (transformState <= TRANSFORM_INT_TRANSLATE) { 1523 transX += x; 1524 transY += y; 1525 transformState = (((transX | transY) == 0) ? 1526 TRANSFORM_ISIDENT : TRANSFORM_INT_TRANSLATE); 1527 } else { 1528 invalidateTransform(); 1529 } 1530 } 1531 1532 /** 1533 * Sets the Transform in the current graphics state. 1534 * @param Tx The Transform object to be used in the rendering process. 1535 * @see #transform 1536 * @see TransformChain 1537 * @see AffineTransform 1538 */ 1539 public void setTransform(AffineTransform Tx) { 1540 if ((constrainX|constrainY) == 0) { 1541 transform.setTransform(Tx); 1542 } else { 1543 transform.setToTranslation(constrainX, constrainY); 1544 transform.concatenate(Tx); 1545 } 1546 invalidateTransform(); 1547 } 1548 1549 protected void invalidateTransform() { 1550 int type = transform.getType(); 1551 int origTransformState = transformState; 1552 if (type == AffineTransform.TYPE_IDENTITY) { 1553 transformState = TRANSFORM_ISIDENT; 1554 transX = transY = 0; 1555 } else if (type == AffineTransform.TYPE_TRANSLATION) { 1556 double dtx = transform.getTranslateX(); 1557 double dty = transform.getTranslateY(); 1558 transX = (int) Math.floor(dtx + 0.5); 1559 transY = (int) Math.floor(dty + 0.5); 1560 if (dtx == transX && dty == transY) { 1561 transformState = TRANSFORM_INT_TRANSLATE; 1562 } else { 1563 transformState = TRANSFORM_ANY_TRANSLATE; 1581 */ 1582 cachedFRC = null; 1583 this.validFontInfo = false; 1584 this.fontMetrics = null; 1585 this.glyphVectorFontInfo = null; 1586 1587 if (transformState != origTransformState) { 1588 invalidatePipe(); 1589 } 1590 } 1591 if (strokeState != STROKE_CUSTOM) { 1592 validateBasicStroke((BasicStroke) stroke); 1593 } 1594 } 1595 1596 /** 1597 * Returns the current Transform in the Graphics2D state. 1598 * @see #transform 1599 * @see #setTransform 1600 */ 1601 public AffineTransform getTransform() { 1602 if ((constrainX|constrainY) == 0) { 1603 return new AffineTransform(transform); 1604 } 1605 AffineTransform tx = 1606 AffineTransform.getTranslateInstance(-constrainX, -constrainY); 1607 tx.concatenate(transform); 1608 return tx; 1609 } 1610 1611 /** 1612 * Returns the current Transform ignoring the "constrain" 1613 * rectangle. 1614 */ 1615 public AffineTransform cloneTransform() { 1616 return new AffineTransform(transform); 1617 } 1618 1619 /** 1620 * Returns the current Paint in the Graphics2D state. 1621 * @see #setPaint 1622 * @see java.awt.Graphics#setColor 1623 */ 1624 public Paint getPaint() { 1625 return paint; 1626 } 2970 return; 2971 } 2972 2973 try { 2974 textpipe.drawChars(this, chData, 0, length, x, y); 2975 } catch (InvalidPipeException e) { 2976 try { 2977 revalidateAll(); 2978 textpipe.drawChars(this, chData, 0, length, x, y); 2979 } catch (InvalidPipeException e2) { 2980 // Still catching the exception; we are not yet ready to 2981 // validate the surfaceData correctly. Fail for now and 2982 // try again next time around. 2983 } 2984 } finally { 2985 surfaceData.markDirty(); 2986 } 2987 } 2988 // end of text rendering methods 2989 2990 /** 2991 * Draws an image scaled to x,y,w,h in nonblocking mode with a 2992 * callback object. 2993 */ 2994 public boolean drawImage(Image img, int x, int y, int width, int height, 2995 ImageObserver observer) { 2996 return drawImage(img, x, y, width, height, null, observer); 2997 } 2998 2999 /** 3000 * Not part of the advertised API but a useful utility method 3001 * to call internally. This is for the case where we are 3002 * drawing to/from given coordinates using a given width/height, 3003 * but we guarantee that the weidth/height of the src and dest 3004 * areas are equal (no scale needed). 3005 */ 3006 public boolean copyImage(Image img, int dx, int dy, int sx, int sy, 3007 int width, int height, Color bgcolor, 3008 ImageObserver observer) { 3009 try { 3010 return imagepipe.copyImage(this, img, dx, dy, sx, sy, 3011 width, height, bgcolor, observer); 3012 } catch (InvalidPipeException e) { 3013 try { 3014 revalidateAll(); 3015 return imagepipe.copyImage(this, img, dx, dy, sx, sy, 3016 width, height, bgcolor, observer); 3017 } catch (InvalidPipeException e2) { 3018 // Still catching the exception; we are not yet ready to 3019 // validate the surfaceData correctly. Fail for now and 3020 // try again next time around. 3021 return false; 3022 } 3023 } finally { 3024 surfaceData.markDirty(); 3025 } 3026 } 3027 3028 /** 3029 * Draws an image scaled to x,y,w,h in nonblocking mode with a 3030 * solid background color and a callback object. 3031 */ 3032 public boolean drawImage(Image img, int x, int y, int width, int height, 3033 Color bg, ImageObserver observer) { 3034 3035 if (img == null) { 3036 return true; 3037 } 3038 3039 if ((width == 0) || (height == 0)) { 3040 return true; 3041 } 3042 if (width == img.getWidth(null) && height == img.getHeight(null)) { 3043 return copyImage(img, x, y, 0, 0, width, height, bg, observer); 3044 } 3045 3046 try { 3047 return imagepipe.scaleImage(this, img, x, y, width, height, 3048 bg, observer); 3049 } catch (InvalidPipeException e) { 3050 try { 3051 revalidateAll(); 3052 return imagepipe.scaleImage(this, img, x, y, width, height, 3053 bg, observer); 3054 } catch (InvalidPipeException e2) { 3055 // Still catching the exception; we are not yet ready to 3056 // validate the surfaceData correctly. Fail for now and 3057 // try again next time around. 3058 return false; 3059 } 3060 } finally { 3061 surfaceData.markDirty(); 3062 } 3063 } 3064 3065 /** 3066 * Draws an image at x,y in nonblocking mode. 3067 */ 3068 public boolean drawImage(Image img, int x, int y, ImageObserver observer) { 3069 return drawImage(img, x, y, null, observer); 3070 } 3071 3072 /** 3073 * Draws an image at x,y in nonblocking mode with a solid background 3074 * color and a callback object. 3075 */ 3076 public boolean drawImage(Image img, int x, int y, Color bg, 3077 ImageObserver observer) { 3078 3079 if (img == null) { 3080 return true; 3081 } 3082 3083 try { 3084 return imagepipe.copyImage(this, img, x, y, bg, observer); 3085 } catch (InvalidPipeException e) { 3086 try { 3087 revalidateAll(); 3088 return imagepipe.copyImage(this, img, x, y, bg, observer); 3089 } catch (InvalidPipeException e2) { 3090 // Still catching the exception; we are not yet ready to 3091 // validate the surfaceData correctly. Fail for now and 3092 // try again next time around. 3093 return false; 3094 } 3095 } finally { 3096 surfaceData.markDirty(); 3097 } 3098 } 3099 3100 /** 3101 * Draws a subrectangle of an image scaled to a destination rectangle 3102 * in nonblocking mode with a callback object. 3111 3112 /** 3113 * Draws a subrectangle of an image scaled to a destination rectangle in 3114 * nonblocking mode with a solid background color and a callback object. 3115 */ 3116 public boolean drawImage(Image img, 3117 int dx1, int dy1, int dx2, int dy2, 3118 int sx1, int sy1, int sx2, int sy2, 3119 Color bgcolor, ImageObserver observer) { 3120 3121 if (img == null) { 3122 return true; 3123 } 3124 3125 if (dx1 == dx2 || dy1 == dy2 || 3126 sx1 == sx2 || sy1 == sy2) 3127 { 3128 return true; 3129 } 3130 3131 if (((sx2 - sx1) == (dx2 - dx1)) && 3132 ((sy2 - sy1) == (dy2 - dy1))) 3133 { 3134 // Not a scale - forward it to a copy routine 3135 int srcX, srcY, dstX, dstY, width, height; 3136 if (sx2 > sx1) { 3137 width = sx2 - sx1; 3138 srcX = sx1; 3139 dstX = dx1; 3140 } else { 3141 width = sx1 - sx2; 3142 srcX = sx2; 3143 dstX = dx2; 3144 } 3145 if (sy2 > sy1) { 3146 height = sy2-sy1; 3147 srcY = sy1; 3148 dstY = dy1; 3149 } else { 3150 height = sy1-sy2; 3187 * paint or color and composite attributes. Note that the result is 3188 * undefined, if the given transform is non-invertible. 3189 * @param img The image to be drawn. 3190 * @param xform The transformation from image space into user space. 3191 * @param observer The image observer to be notified on the image producing 3192 * progress. 3193 * @see #transform 3194 * @see #setComposite 3195 * @see #setClip 3196 */ 3197 public boolean drawImage(Image img, 3198 AffineTransform xform, 3199 ImageObserver observer) { 3200 3201 if (img == null) { 3202 return true; 3203 } 3204 3205 if (xform == null || xform.isIdentity()) { 3206 return drawImage(img, 0, 0, null, observer); 3207 } 3208 3209 try { 3210 return imagepipe.transformImage(this, img, xform, observer); 3211 } catch (InvalidPipeException e) { 3212 try { 3213 revalidateAll(); 3214 return imagepipe.transformImage(this, img, xform, observer); 3215 } catch (InvalidPipeException e2) { 3216 // Still catching the exception; we are not yet ready to 3217 // validate the surfaceData correctly. Fail for now and 3218 // try again next time around. 3219 return false; 3220 } 3221 } finally { 3222 surfaceData.markDirty(); 3223 } 3224 } 3225 3226 public void drawImage(BufferedImage bImg, | 48 import java.awt.image.ColorModel; 49 import java.awt.GraphicsConfiguration; 50 import java.awt.Paint; 51 import java.awt.GradientPaint; 52 import java.awt.LinearGradientPaint; 53 import java.awt.RadialGradientPaint; 54 import java.awt.TexturePaint; 55 import java.awt.geom.Rectangle2D; 56 import java.awt.geom.PathIterator; 57 import java.awt.geom.GeneralPath; 58 import java.awt.Shape; 59 import java.awt.Stroke; 60 import java.awt.FontMetrics; 61 import java.awt.Rectangle; 62 import java.text.AttributedCharacterIterator; 63 import java.awt.Font; 64 import java.awt.image.ImageObserver; 65 import java.awt.Transparency; 66 import java.awt.font.GlyphVector; 67 import java.awt.font.TextLayout; 68 69 import sun.awt.image.SurfaceManager; 70 import sun.font.FontDesignMetrics; 71 import sun.font.FontUtilities; 72 import sun.java2d.pipe.PixelDrawPipe; 73 import sun.java2d.pipe.PixelFillPipe; 74 import sun.java2d.pipe.ShapeDrawPipe; 75 import sun.java2d.pipe.ValidatePipe; 76 import sun.java2d.pipe.ShapeSpanIterator; 77 import sun.java2d.pipe.Region; 78 import sun.java2d.pipe.TextPipe; 79 import sun.java2d.pipe.DrawImagePipe; 80 import sun.java2d.pipe.LoopPipe; 81 import sun.java2d.loops.FontInfo; 82 import sun.java2d.loops.RenderLoops; 83 import sun.java2d.loops.CompositeType; 84 import sun.java2d.loops.SurfaceType; 85 import sun.java2d.loops.Blit; 86 import sun.java2d.loops.MaskFill; 87 import java.awt.font.FontRenderContext; 88 import sun.java2d.loops.XORComposite; 89 import sun.awt.ConstrainableGraphics; 90 import sun.awt.SunHints; 91 import java.util.Map; 92 import java.util.Iterator; 93 import sun.misc.PerformanceLogger; 94 95 /** 96 * This is a the master Graphics2D superclass for all of the Sun 97 * Graphics implementations. This class relies on subclasses to 98 * manage the various device information, but provides an overall 99 * general framework for performing all of the requests in the 100 * Graphics and Graphics2D APIs. 101 * 102 * @author Jim Graham 103 */ 104 public final class SunGraphics2D 105 extends Graphics2D 106 implements ConstrainableGraphics, Cloneable, DestSurfaceProvider 107 { 108 /* 109 * Attribute States 110 */ 111 /* Paint */ 112 public static final int PAINT_CUSTOM = 6; /* Any other Paint object */ 186 protected Font font; 187 protected FontMetrics fontMetrics; 188 189 public int renderHint; 190 public int antialiasHint; 191 public int textAntialiasHint; 192 protected 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 in pixels 207 public int constrainX; 208 public int constrainY; 209 210 public Region clipRegion; 211 public Shape usrClip; 212 protected Region devClip; // Actual physical drawable in pixels 213 214 private final int devScale; // Actual physical scale factor 215 216 // cached state for text rendering 217 private boolean validFontInfo; 218 private FontInfo fontInfo; 219 private FontInfo glyphVectorFontInfo; 220 private FontRenderContext glyphVectorFRC; 221 222 private final static int slowTextTransformMask = 223 AffineTransform.TYPE_GENERAL_TRANSFORM 224 | AffineTransform.TYPE_MASK_ROTATION 225 | AffineTransform.TYPE_FLIP; 226 227 static { 228 if (PerformanceLogger.loggingEnabled()) { 229 PerformanceLogger.setTime("SunGraphics2D static initialization"); 230 } 231 } 232 233 public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) { 234 surfaceData = sd; 237 238 transform = new AffineTransform(); 239 stroke = defaultStroke; 240 composite = defaultComposite; 241 paint = foregroundColor; 242 243 imageComp = CompositeType.SrcOverNoEa; 244 245 renderHint = SunHints.INTVAL_RENDER_DEFAULT; 246 antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF; 247 textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT; 248 fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF; 249 lcdTextContrast = lcdTextContrastDefaultValue; 250 interpolationHint = -1; 251 strokeHint = SunHints.INTVAL_STROKE_DEFAULT; 252 253 interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; 254 255 validateColor(); 256 257 devScale = sd.getDefaultScale(); 258 if (devScale != 1) { 259 transform.setToScale(devScale, devScale); 260 invalidateTransform(); 261 } 262 263 font = f; 264 if (font == null) { 265 font = defaultFont; 266 } 267 268 setDevClip(sd.getBounds()); 269 invalidatePipe(); 270 } 271 272 protected Object clone() { 273 try { 274 SunGraphics2D g = (SunGraphics2D) super.clone(); 275 g.transform = new AffineTransform(this.transform); 276 if (hints != null) { 277 g.hints = (RenderingHints) this.hints.clone(); 278 } 279 /* FontInfos are re-used, so must be cloned too, if they 280 * are valid, and be nulled out if invalid. 281 * The implied trade-off is that there is more to be gained 282 * from re-using these objects than is lost by having to 307 public Graphics create() { 308 return (Graphics) clone(); 309 } 310 311 public void setDevClip(int x, int y, int w, int h) { 312 Region c = constrainClip; 313 if (c == null) { 314 devClip = Region.getInstanceXYWH(x, y, w, h); 315 } else { 316 devClip = c.getIntersectionXYWH(x, y, w, h); 317 } 318 validateCompClip(); 319 } 320 321 public void setDevClip(Rectangle r) { 322 setDevClip(r.x, r.y, r.width, r.height); 323 } 324 325 /** 326 * Constrain rendering for lightweight objects. 327 */ 328 public void constrain(int x, int y, int w, int h, Region region) { 329 if ((x | y) != 0) { 330 translate(x, y); 331 } 332 if (transformState > TRANSFORM_TRANSLATESCALE) { 333 clipRect(0, 0, w, h); 334 return; 335 } 336 // changes parameters according to the current scale and translate. 337 final double scaleX = transform.getScaleX(); 338 final double scaleY = transform.getScaleY(); 339 x = constrainX = (int) transform.getTranslateX(); 340 y = constrainY = (int) transform.getTranslateY(); 341 w = Region.dimAdd(x, Region.clipScale(w, scaleX)); 342 h = Region.dimAdd(y, Region.clipScale(h, scaleY)); 343 344 Region c = constrainClip; 345 if (c == null) { 346 c = Region.getInstanceXYXY(x, y, w, h); 347 } else { 348 c = c.getIntersectionXYXY(x, y, w, h); 349 } 350 if (region != null) { 351 region = region.getScaledRegion(scaleX, scaleY); 352 region = region.getTranslatedRegion(x, y); 353 c = c.getIntersection(region); 354 } 355 356 if (c == constrainClip) { 357 // Common case to ignore 358 return; 359 } 360 361 constrainClip = c; 362 if (!devClip.isInsideQuickCheck(c)) { 363 devClip = devClip.getIntersection(c); 364 validateCompClip(); 365 } 366 } 367 368 /** 369 * Constrain rendering for lightweight objects. 370 * 371 * REMIND: This method will back off to the "workaround" 372 * of using translate and clipRect if the Graphics 373 * to be constrained has a complex transform. The 374 * drawback of the workaround is that the resulting 375 * clip and device origin cannot be "enforced". 376 * 377 * @exception IllegalStateException If the Graphics 378 * to be constrained has a complex transform. 379 */ 380 @Override 381 public void constrain(int x, int y, int w, int h) { 382 constrain(x, y, w, h, null); 383 } 384 385 protected static ValidatePipe invalidpipe = new ValidatePipe(); 386 387 /* 388 * Invalidate the pipeline 389 */ 390 protected void invalidatePipe() { 391 drawpipe = invalidpipe; 392 fillpipe = invalidpipe; 393 shapepipe = invalidpipe; 394 textpipe = invalidpipe; 395 imagepipe = invalidpipe; 396 loops = null; 397 } 398 399 public void validatePipe() { 400 /* This workaround is for the situation when we update the Pipelines 401 * for invalid SurfaceData and run further code when the current 402 * pipeline doesn't support the type of new SurfaceData created during 403 * the current pipeline's work (in place of the invalid SurfaceData). 404 * Usually SurfaceData and Pipelines are repaired (through revalidateAll) 1546 */ 1547 public void translate(int x, int y) { 1548 transform.translate(x, y); 1549 if (transformState <= TRANSFORM_INT_TRANSLATE) { 1550 transX += x; 1551 transY += y; 1552 transformState = (((transX | transY) == 0) ? 1553 TRANSFORM_ISIDENT : TRANSFORM_INT_TRANSLATE); 1554 } else { 1555 invalidateTransform(); 1556 } 1557 } 1558 1559 /** 1560 * Sets the Transform in the current graphics state. 1561 * @param Tx The Transform object to be used in the rendering process. 1562 * @see #transform 1563 * @see TransformChain 1564 * @see AffineTransform 1565 */ 1566 @Override 1567 public void setTransform(AffineTransform Tx) { 1568 if ((constrainX | constrainY) == 0 && devScale == 1) { 1569 transform.setTransform(Tx); 1570 } else { 1571 transform.setTransform(devScale, 0, 0, devScale, constrainX, 1572 constrainY); 1573 transform.concatenate(Tx); 1574 } 1575 invalidateTransform(); 1576 } 1577 1578 protected void invalidateTransform() { 1579 int type = transform.getType(); 1580 int origTransformState = transformState; 1581 if (type == AffineTransform.TYPE_IDENTITY) { 1582 transformState = TRANSFORM_ISIDENT; 1583 transX = transY = 0; 1584 } else if (type == AffineTransform.TYPE_TRANSLATION) { 1585 double dtx = transform.getTranslateX(); 1586 double dty = transform.getTranslateY(); 1587 transX = (int) Math.floor(dtx + 0.5); 1588 transY = (int) Math.floor(dty + 0.5); 1589 if (dtx == transX && dty == transY) { 1590 transformState = TRANSFORM_INT_TRANSLATE; 1591 } else { 1592 transformState = TRANSFORM_ANY_TRANSLATE; 1610 */ 1611 cachedFRC = null; 1612 this.validFontInfo = false; 1613 this.fontMetrics = null; 1614 this.glyphVectorFontInfo = null; 1615 1616 if (transformState != origTransformState) { 1617 invalidatePipe(); 1618 } 1619 } 1620 if (strokeState != STROKE_CUSTOM) { 1621 validateBasicStroke((BasicStroke) stroke); 1622 } 1623 } 1624 1625 /** 1626 * Returns the current Transform in the Graphics2D state. 1627 * @see #transform 1628 * @see #setTransform 1629 */ 1630 @Override 1631 public AffineTransform getTransform() { 1632 if ((constrainX | constrainY) == 0 && devScale == 1) { 1633 return new AffineTransform(transform); 1634 } 1635 final double invScale = 1.0 / devScale; 1636 AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale, 1637 -constrainX * invScale, 1638 -constrainY * invScale); 1639 tx.concatenate(transform); 1640 return tx; 1641 } 1642 1643 /** 1644 * Returns the current Transform ignoring the "constrain" 1645 * rectangle. 1646 */ 1647 public AffineTransform cloneTransform() { 1648 return new AffineTransform(transform); 1649 } 1650 1651 /** 1652 * Returns the current Paint in the Graphics2D state. 1653 * @see #setPaint 1654 * @see java.awt.Graphics#setColor 1655 */ 1656 public Paint getPaint() { 1657 return paint; 1658 } 3002 return; 3003 } 3004 3005 try { 3006 textpipe.drawChars(this, chData, 0, length, x, y); 3007 } catch (InvalidPipeException e) { 3008 try { 3009 revalidateAll(); 3010 textpipe.drawChars(this, chData, 0, length, x, y); 3011 } catch (InvalidPipeException e2) { 3012 // Still catching the exception; we are not yet ready to 3013 // validate the surfaceData correctly. Fail for now and 3014 // try again next time around. 3015 } 3016 } finally { 3017 surfaceData.markDirty(); 3018 } 3019 } 3020 // end of text rendering methods 3021 3022 private static boolean isHiDPIImage(final Image img) { 3023 return SurfaceManager.getImageScale(img) != 1; 3024 } 3025 3026 private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2, 3027 int dy2, int sx1, int sy1, int sx2, int sy2, 3028 Color bgcolor, ImageObserver observer) { 3029 final int scale = SurfaceManager.getImageScale(img); 3030 sx1 = Region.clipScale(sx1, scale); 3031 sx2 = Region.clipScale(sx2, scale); 3032 sy1 = Region.clipScale(sy1, scale); 3033 sy2 = Region.clipScale(sy2, scale); 3034 try { 3035 return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1, 3036 sx2, sy2, bgcolor, observer); 3037 } catch (InvalidPipeException e) { 3038 try { 3039 revalidateAll(); 3040 return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, 3041 sy1, sx2, sy2, bgcolor, observer); 3042 } catch (InvalidPipeException e2) { 3043 // Still catching the exception; we are not yet ready to 3044 // validate the surfaceData correctly. Fail for now and 3045 // try again next time around. 3046 return false; 3047 } 3048 } finally { 3049 surfaceData.markDirty(); 3050 } 3051 } 3052 3053 /** 3054 * Draws an image scaled to x,y,w,h in nonblocking mode with a 3055 * callback object. 3056 */ 3057 public boolean drawImage(Image img, int x, int y, int width, int height, 3058 ImageObserver observer) { 3059 return drawImage(img, x, y, width, height, null, observer); 3060 } 3061 3062 /** 3063 * Not part of the advertised API but a useful utility method 3064 * to call internally. This is for the case where we are 3065 * drawing to/from given coordinates using a given width/height, 3066 * but we guarantee that the surfaceData's width/height of the src and dest 3067 * areas are equal (no scale needed). Note that this method intentionally 3068 * ignore scale factor of the source image, and copy it as is. 3069 */ 3070 public boolean copyImage(Image img, int dx, int dy, int sx, int sy, 3071 int width, int height, Color bgcolor, 3072 ImageObserver observer) { 3073 try { 3074 return imagepipe.copyImage(this, img, dx, dy, sx, sy, 3075 width, height, bgcolor, observer); 3076 } catch (InvalidPipeException e) { 3077 try { 3078 revalidateAll(); 3079 return imagepipe.copyImage(this, img, dx, dy, sx, sy, 3080 width, height, bgcolor, observer); 3081 } catch (InvalidPipeException e2) { 3082 // Still catching the exception; we are not yet ready to 3083 // validate the surfaceData correctly. Fail for now and 3084 // try again next time around. 3085 return false; 3086 } 3087 } finally { 3088 surfaceData.markDirty(); 3089 } 3090 } 3091 3092 /** 3093 * Draws an image scaled to x,y,w,h in nonblocking mode with a 3094 * solid background color and a callback object. 3095 */ 3096 public boolean drawImage(Image img, int x, int y, int width, int height, 3097 Color bg, ImageObserver observer) { 3098 3099 if (img == null) { 3100 return true; 3101 } 3102 3103 if ((width == 0) || (height == 0)) { 3104 return true; 3105 } 3106 3107 final int imgW = img.getWidth(null); 3108 final int imgH = img.getHeight(null); 3109 if (isHiDPIImage(img)) { 3110 return drawHiDPIImage(img, x, y, x + width, y + height, 0, 0, imgW, 3111 imgH, bg, observer); 3112 } 3113 3114 if (width == imgW && height == imgH) { 3115 return copyImage(img, x, y, 0, 0, width, height, bg, observer); 3116 } 3117 3118 try { 3119 return imagepipe.scaleImage(this, img, x, y, width, height, 3120 bg, observer); 3121 } catch (InvalidPipeException e) { 3122 try { 3123 revalidateAll(); 3124 return imagepipe.scaleImage(this, img, x, y, width, height, 3125 bg, observer); 3126 } catch (InvalidPipeException e2) { 3127 // Still catching the exception; we are not yet ready to 3128 // validate the surfaceData correctly. Fail for now and 3129 // try again next time around. 3130 return false; 3131 } 3132 } finally { 3133 surfaceData.markDirty(); 3134 } 3135 } 3136 3137 /** 3138 * Draws an image at x,y in nonblocking mode. 3139 */ 3140 public boolean drawImage(Image img, int x, int y, ImageObserver observer) { 3141 return drawImage(img, x, y, null, observer); 3142 } 3143 3144 /** 3145 * Draws an image at x,y in nonblocking mode with a solid background 3146 * color and a callback object. 3147 */ 3148 public boolean drawImage(Image img, int x, int y, Color bg, 3149 ImageObserver observer) { 3150 3151 if (img == null) { 3152 return true; 3153 } 3154 3155 if (isHiDPIImage(img)) { 3156 final int imgW = img.getWidth(null); 3157 final int imgH = img.getHeight(null); 3158 return drawHiDPIImage(img, x, y, x + imgW, y + imgH, 0, 0, imgW, 3159 imgH, bg, observer); 3160 } 3161 3162 try { 3163 return imagepipe.copyImage(this, img, x, y, bg, observer); 3164 } catch (InvalidPipeException e) { 3165 try { 3166 revalidateAll(); 3167 return imagepipe.copyImage(this, img, x, y, bg, observer); 3168 } catch (InvalidPipeException e2) { 3169 // Still catching the exception; we are not yet ready to 3170 // validate the surfaceData correctly. Fail for now and 3171 // try again next time around. 3172 return false; 3173 } 3174 } finally { 3175 surfaceData.markDirty(); 3176 } 3177 } 3178 3179 /** 3180 * Draws a subrectangle of an image scaled to a destination rectangle 3181 * in nonblocking mode with a callback object. 3190 3191 /** 3192 * Draws a subrectangle of an image scaled to a destination rectangle in 3193 * nonblocking mode with a solid background color and a callback object. 3194 */ 3195 public boolean drawImage(Image img, 3196 int dx1, int dy1, int dx2, int dy2, 3197 int sx1, int sy1, int sx2, int sy2, 3198 Color bgcolor, ImageObserver observer) { 3199 3200 if (img == null) { 3201 return true; 3202 } 3203 3204 if (dx1 == dx2 || dy1 == dy2 || 3205 sx1 == sx2 || sy1 == sy2) 3206 { 3207 return true; 3208 } 3209 3210 if (isHiDPIImage(img)) { 3211 return drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, 3212 bgcolor, observer); 3213 } 3214 3215 if (((sx2 - sx1) == (dx2 - dx1)) && 3216 ((sy2 - sy1) == (dy2 - dy1))) 3217 { 3218 // Not a scale - forward it to a copy routine 3219 int srcX, srcY, dstX, dstY, width, height; 3220 if (sx2 > sx1) { 3221 width = sx2 - sx1; 3222 srcX = sx1; 3223 dstX = dx1; 3224 } else { 3225 width = sx1 - sx2; 3226 srcX = sx2; 3227 dstX = dx2; 3228 } 3229 if (sy2 > sy1) { 3230 height = sy2-sy1; 3231 srcY = sy1; 3232 dstY = dy1; 3233 } else { 3234 height = sy1-sy2; 3271 * paint or color and composite attributes. Note that the result is 3272 * undefined, if the given transform is non-invertible. 3273 * @param img The image to be drawn. 3274 * @param xform The transformation from image space into user space. 3275 * @param observer The image observer to be notified on the image producing 3276 * progress. 3277 * @see #transform 3278 * @see #setComposite 3279 * @see #setClip 3280 */ 3281 public boolean drawImage(Image img, 3282 AffineTransform xform, 3283 ImageObserver observer) { 3284 3285 if (img == null) { 3286 return true; 3287 } 3288 3289 if (xform == null || xform.isIdentity()) { 3290 return drawImage(img, 0, 0, null, observer); 3291 } 3292 3293 if (isHiDPIImage(img)) { 3294 final int w = img.getWidth(null); 3295 final int h = img.getHeight(null); 3296 final AffineTransform tx = new AffineTransform(transform); 3297 transform(xform); 3298 boolean result = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, null, 3299 observer); 3300 transform.setTransform(tx); 3301 invalidateTransform(); 3302 return result; 3303 } 3304 3305 try { 3306 return imagepipe.transformImage(this, img, xform, observer); 3307 } catch (InvalidPipeException e) { 3308 try { 3309 revalidateAll(); 3310 return imagepipe.transformImage(this, img, xform, observer); 3311 } catch (InvalidPipeException e2) { 3312 // Still catching the exception; we are not yet ready to 3313 // validate the surfaceData correctly. Fail for now and 3314 // try again next time around. 3315 return false; 3316 } 3317 } finally { 3318 surfaceData.markDirty(); 3319 } 3320 } 3321 3322 public void drawImage(BufferedImage bImg, |