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