src/share/classes/sun/java2d/SunGraphics2D.java

Print this page




  44 import java.awt.image.WritableRaster;
  45 import java.awt.Image;
  46 import java.awt.Composite;
  47 import java.awt.Color;
  48 import java.awt.image.ColorModel;
  49 import java.awt.GraphicsConfiguration;
  50 import java.awt.Paint;
  51 import java.awt.GradientPaint;
  52 import java.awt.LinearGradientPaint;
  53 import java.awt.RadialGradientPaint;
  54 import java.awt.TexturePaint;
  55 import java.awt.geom.Rectangle2D;
  56 import java.awt.geom.PathIterator;
  57 import java.awt.geom.GeneralPath;
  58 import java.awt.Shape;
  59 import java.awt.Stroke;
  60 import java.awt.FontMetrics;
  61 import java.awt.Rectangle;
  62 import java.text.AttributedCharacterIterator;
  63 import java.awt.Font;

  64 import java.awt.image.ImageObserver;
  65 import java.awt.Transparency;
  66 import java.awt.font.GlyphVector;
  67 import java.awt.font.TextLayout;
  68 
  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 java.lang.annotation.Native;







  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 public final class SunGraphics2D
 107     extends Graphics2D
 108     implements ConstrainableGraphics, Cloneable, DestSurfaceProvider
 109 {
 110     /*
 111      * Attribute States
 112      */
 113     /* Paint */
 114     @Native
 115     public static final int PAINT_CUSTOM       = 6; /* Any other Paint object */


 220     public int lcdTextContrast;
 221     private static int lcdTextContrastDefaultValue = 140;
 222 
 223     private int interpolationHint;      // raw value of rendering Hint
 224     public int strokeHint;
 225 
 226     public int interpolationType;       // algorithm choice based on
 227                                         // interpolation and render Hints
 228 
 229     public RenderingHints hints;
 230 
 231     public Region constrainClip;        // lightweight bounds in pixels
 232     public int constrainX;
 233     public int constrainY;
 234 
 235     public Region clipRegion;
 236     public Shape usrClip;
 237     protected Region devClip;           // Actual physical drawable in pixels
 238 
 239     private final int devScale;         // Actual physical scale factor

 240 
 241     // cached state for text rendering
 242     private boolean validFontInfo;
 243     private FontInfo fontInfo;
 244     private FontInfo glyphVectorFontInfo;
 245     private FontRenderContext glyphVectorFRC;
 246 
 247     private final static int slowTextTransformMask =
 248                             AffineTransform.TYPE_GENERAL_TRANSFORM
 249                         |   AffineTransform.TYPE_MASK_ROTATION
 250                         |   AffineTransform.TYPE_FLIP;
 251 
 252     static {
 253         if (PerformanceLogger.loggingEnabled()) {
 254             PerformanceLogger.setTime("SunGraphics2D static initialization");
 255         }
 256     }
 257 
 258     public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {
 259         surfaceData = sd;
 260         foregroundColor = fg;
 261         backgroundColor = bg;
 262 
 263         transform = new AffineTransform();
 264         stroke = defaultStroke;
 265         composite = defaultComposite;
 266         paint = foregroundColor;
 267 
 268         imageComp = CompositeType.SrcOverNoEa;
 269 
 270         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
 271         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
 272         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
 273         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
 274         lcdTextContrast = lcdTextContrastDefaultValue;
 275         interpolationHint = -1;
 276         strokeHint = SunHints.INTVAL_STROKE_DEFAULT;

 277 
 278         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
 279 
 280         validateColor();
 281 
 282         devScale = sd.getDefaultScale();
 283         if (devScale != 1) {
 284             transform.setToScale(devScale, devScale);
 285             invalidateTransform();
 286         }
 287 
 288         font = f;
 289         if (font == null) {
 290             font = defaultFont;
 291         }
 292 
 293         setDevClip(sd.getBounds());
 294         invalidatePipe();
 295     }
 296 


1232                 interpolationHint = newHint;
1233                 switch (newHint) {
1234                 case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1235                     newHint = AffineTransformOp.TYPE_BICUBIC;
1236                     break;
1237                 case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1238                     newHint = AffineTransformOp.TYPE_BILINEAR;
1239                     break;
1240                 default:
1241                 case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1242                     newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1243                     break;
1244                 }
1245                 stateChanged = (interpolationType != newHint);
1246                 interpolationType = newHint;
1247                 break;
1248             case SunHints.INTKEY_STROKE_CONTROL:
1249                 stateChanged = (strokeHint != newHint);
1250                 strokeHint = newHint;
1251                 break;




1252             default:
1253                 recognized = false;
1254                 stateChanged = false;
1255                 break;
1256             }
1257             if (recognized) {
1258                 if (stateChanged) {
1259                     invalidatePipe();
1260                     if (textStateChanged) {
1261                         fontMetrics = null;
1262                         this.cachedFRC = null;
1263                         validFontInfo = false;
1264                         this.glyphVectorFontInfo = null;
1265                     }
1266                 }
1267                 if (hints != null) {
1268                     hints.put(hintKey, hintValue);
1269                 }
1270                 return;
1271             }


1305             return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
1306                                       textAntialiasHint);
1307         case SunHints.INTKEY_FRACTIONALMETRICS:
1308             return SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
1309                                       fractionalMetricsHint);
1310         case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1311             return new Integer(lcdTextContrast);
1312         case SunHints.INTKEY_INTERPOLATION:
1313             switch (interpolationHint) {
1314             case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1315                 return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1316             case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1317                 return SunHints.VALUE_INTERPOLATION_BILINEAR;
1318             case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1319                 return SunHints.VALUE_INTERPOLATION_BICUBIC;
1320             }
1321             return null;
1322         case SunHints.INTKEY_STROKE_CONTROL:
1323             return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
1324                                       strokeHint);



1325         }
1326         return null;
1327     }
1328 
1329     /**
1330      * Sets the preferences for the rendering algorithms.
1331      * Hint categories include controls for rendering quality and
1332      * overall time/quality trade-off in the rendering process.
1333      * @param hints The rendering hints to be set
1334      * @see RenderingHints
1335      */
1336     public void setRenderingHints(Map<?,?> hints) {
1337         this.hints = null;
1338         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
1339         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
1340         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
1341         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
1342         lcdTextContrast = lcdTextContrastDefaultValue;
1343         interpolationHint = -1;
1344         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;


3033             return;
3034         }
3035 
3036         try {
3037             textpipe.drawChars(this, chData, 0, length, x, y);
3038         } catch (InvalidPipeException e) {
3039             try {
3040                 revalidateAll();
3041                 textpipe.drawChars(this, chData, 0, length, x, y);
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             }
3047         } finally {
3048             surfaceData.markDirty();
3049         }
3050     }
3051 // end of text rendering methods
3052 
3053     private static boolean isHiDPIImage(final Image img) {
3054         return SurfaceManager.getImageScale(img) != 1;


3055     }
3056 
3057     private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
3058                                    int dy2, int sx1, int sy1, int sx2, int sy2,
3059                                    Color bgcolor, ImageObserver observer) {


3060         final int scale = SurfaceManager.getImageScale(img);
3061         sx1 = Region.clipScale(sx1, scale);
3062         sx2 = Region.clipScale(sx2, scale);
3063         sy1 = Region.clipScale(sy1, scale);
3064         sy2 = Region.clipScale(sy2, scale);




































3065         try {
3066             return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
3067                                         sx2, sy2, bgcolor, observer);
3068         } catch (InvalidPipeException e) {
3069             try {
3070                 revalidateAll();
3071                 return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1,
3072                                             sy1, sx2, sy2, bgcolor, observer);
3073             } catch (InvalidPipeException e2) {
3074                 // Still catching the exception; we are not yet ready to
3075                 // validate the surfaceData correctly.  Fail for now and
3076                 // try again next time around.
3077                 return false;
3078             }
3079         } finally {
3080             surfaceData.markDirty();
3081         }
3082     }
3083 
















































3084     /**
3085      * Draws an image scaled to x,y,w,h in nonblocking mode with a
3086      * callback object.
3087      */
3088     public boolean drawImage(Image img, int x, int y, int width, int height,
3089                              ImageObserver observer) {
3090         return drawImage(img, x, y, width, height, null, observer);
3091     }
3092 
3093     /**
3094      * Not part of the advertised API but a useful utility method
3095      * to call internally.  This is for the case where we are
3096      * drawing to/from given coordinates using a given width/height,
3097      * but we guarantee that the surfaceData's width/height of the src and dest
3098      * areas are equal (no scale needed). Note that this method intentionally
3099      * ignore scale factor of the source image, and copy it as is.
3100      */
3101     public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
3102                              int width, int height, Color bgcolor,
3103                              ImageObserver observer) {




  44 import java.awt.image.WritableRaster;
  45 import java.awt.Image;
  46 import java.awt.Composite;
  47 import java.awt.Color;
  48 import java.awt.image.ColorModel;
  49 import java.awt.GraphicsConfiguration;
  50 import java.awt.Paint;
  51 import java.awt.GradientPaint;
  52 import java.awt.LinearGradientPaint;
  53 import java.awt.RadialGradientPaint;
  54 import java.awt.TexturePaint;
  55 import java.awt.geom.Rectangle2D;
  56 import java.awt.geom.PathIterator;
  57 import java.awt.geom.GeneralPath;
  58 import java.awt.Shape;
  59 import java.awt.Stroke;
  60 import java.awt.FontMetrics;
  61 import java.awt.Rectangle;
  62 import java.text.AttributedCharacterIterator;
  63 import java.awt.Font;
  64 import java.awt.Point;
  65 import java.awt.image.ImageObserver;
  66 import java.awt.Transparency;
  67 import java.awt.font.GlyphVector;
  68 import java.awt.font.TextLayout;
  69 
  70 import sun.awt.image.SurfaceManager;
  71 import sun.font.FontDesignMetrics;
  72 import sun.font.FontUtilities;
  73 import sun.java2d.pipe.PixelDrawPipe;
  74 import sun.java2d.pipe.PixelFillPipe;
  75 import sun.java2d.pipe.ShapeDrawPipe;
  76 import sun.java2d.pipe.ValidatePipe;
  77 import sun.java2d.pipe.ShapeSpanIterator;
  78 import sun.java2d.pipe.Region;
  79 import sun.java2d.pipe.TextPipe;
  80 import sun.java2d.pipe.DrawImagePipe;
  81 import sun.java2d.pipe.LoopPipe;
  82 import sun.java2d.loops.FontInfo;
  83 import sun.java2d.loops.RenderLoops;
  84 import sun.java2d.loops.CompositeType;
  85 import sun.java2d.loops.SurfaceType;
  86 import sun.java2d.loops.Blit;
  87 import sun.java2d.loops.MaskFill;
  88 import java.awt.font.FontRenderContext;
  89 import sun.java2d.loops.XORComposite;
  90 import sun.awt.ConstrainableGraphics;
  91 import sun.awt.SunHints;
  92 import java.util.Map;
  93 import java.util.Iterator;
  94 import sun.misc.PerformanceLogger;
  95 
  96 import java.lang.annotation.Native;
  97 import sun.awt.image.MultiResolutionImage;
  98 
  99 import static java.awt.geom.AffineTransform.TYPE_FLIP;
 100 import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;
 101 import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
 102 import sun.awt.image.MultiResolutionToolkitImage;
 103 import sun.awt.image.ToolkitImage;
 104 
 105 /**
 106  * This is a the master Graphics2D superclass for all of the Sun
 107  * Graphics implementations.  This class relies on subclasses to
 108  * manage the various device information, but provides an overall
 109  * general framework for performing all of the requests in the
 110  * Graphics and Graphics2D APIs.
 111  *
 112  * @author Jim Graham
 113  */
 114 public final class SunGraphics2D
 115     extends Graphics2D
 116     implements ConstrainableGraphics, Cloneable, DestSurfaceProvider
 117 {
 118     /*
 119      * Attribute States
 120      */
 121     /* Paint */
 122     @Native
 123     public static final int PAINT_CUSTOM       = 6; /* Any other Paint object */


 228     public int lcdTextContrast;
 229     private static int lcdTextContrastDefaultValue = 140;
 230 
 231     private int interpolationHint;      // raw value of rendering Hint
 232     public int strokeHint;
 233 
 234     public int interpolationType;       // algorithm choice based on
 235                                         // interpolation and render Hints
 236 
 237     public RenderingHints hints;
 238 
 239     public Region constrainClip;        // lightweight bounds in pixels
 240     public int constrainX;
 241     public int constrainY;
 242 
 243     public Region clipRegion;
 244     public Shape usrClip;
 245     protected Region devClip;           // Actual physical drawable in pixels
 246 
 247     private final int devScale;         // Actual physical scale factor
 248     private int resolutionVariantHint;
 249 
 250     // cached state for text rendering
 251     private boolean validFontInfo;
 252     private FontInfo fontInfo;
 253     private FontInfo glyphVectorFontInfo;
 254     private FontRenderContext glyphVectorFRC;
 255 
 256     private final static int slowTextTransformMask =
 257                             AffineTransform.TYPE_GENERAL_TRANSFORM
 258                         |   AffineTransform.TYPE_MASK_ROTATION
 259                         |   AffineTransform.TYPE_FLIP;
 260 
 261     static {
 262         if (PerformanceLogger.loggingEnabled()) {
 263             PerformanceLogger.setTime("SunGraphics2D static initialization");
 264         }
 265     }
 266 
 267     public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {
 268         surfaceData = sd;
 269         foregroundColor = fg;
 270         backgroundColor = bg;
 271 
 272         transform = new AffineTransform();
 273         stroke = defaultStroke;
 274         composite = defaultComposite;
 275         paint = foregroundColor;
 276 
 277         imageComp = CompositeType.SrcOverNoEa;
 278 
 279         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
 280         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
 281         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
 282         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
 283         lcdTextContrast = lcdTextContrastDefaultValue;
 284         interpolationHint = -1;
 285         strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
 286         resolutionVariantHint = SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT;
 287 
 288         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
 289 
 290         validateColor();
 291 
 292         devScale = sd.getDefaultScale();
 293         if (devScale != 1) {
 294             transform.setToScale(devScale, devScale);
 295             invalidateTransform();
 296         }
 297 
 298         font = f;
 299         if (font == null) {
 300             font = defaultFont;
 301         }
 302 
 303         setDevClip(sd.getBounds());
 304         invalidatePipe();
 305     }
 306 


1242                 interpolationHint = newHint;
1243                 switch (newHint) {
1244                 case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1245                     newHint = AffineTransformOp.TYPE_BICUBIC;
1246                     break;
1247                 case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1248                     newHint = AffineTransformOp.TYPE_BILINEAR;
1249                     break;
1250                 default:
1251                 case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1252                     newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
1253                     break;
1254                 }
1255                 stateChanged = (interpolationType != newHint);
1256                 interpolationType = newHint;
1257                 break;
1258             case SunHints.INTKEY_STROKE_CONTROL:
1259                 stateChanged = (strokeHint != newHint);
1260                 strokeHint = newHint;
1261                 break;
1262             case SunHints.INTKEY_RESOLUTION_VARIANT:
1263                 stateChanged = (resolutionVariantHint != newHint);
1264                 resolutionVariantHint = newHint;
1265                 break;
1266             default:
1267                 recognized = false;
1268                 stateChanged = false;
1269                 break;
1270             }
1271             if (recognized) {
1272                 if (stateChanged) {
1273                     invalidatePipe();
1274                     if (textStateChanged) {
1275                         fontMetrics = null;
1276                         this.cachedFRC = null;
1277                         validFontInfo = false;
1278                         this.glyphVectorFontInfo = null;
1279                     }
1280                 }
1281                 if (hints != null) {
1282                     hints.put(hintKey, hintValue);
1283                 }
1284                 return;
1285             }


1319             return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,
1320                                       textAntialiasHint);
1321         case SunHints.INTKEY_FRACTIONALMETRICS:
1322             return SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,
1323                                       fractionalMetricsHint);
1324         case SunHints.INTKEY_AATEXT_LCD_CONTRAST:
1325             return new Integer(lcdTextContrast);
1326         case SunHints.INTKEY_INTERPOLATION:
1327             switch (interpolationHint) {
1328             case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:
1329                 return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
1330             case SunHints.INTVAL_INTERPOLATION_BILINEAR:
1331                 return SunHints.VALUE_INTERPOLATION_BILINEAR;
1332             case SunHints.INTVAL_INTERPOLATION_BICUBIC:
1333                 return SunHints.VALUE_INTERPOLATION_BICUBIC;
1334             }
1335             return null;
1336         case SunHints.INTKEY_STROKE_CONTROL:
1337             return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
1338                                       strokeHint);
1339         case SunHints.INTKEY_RESOLUTION_VARIANT:
1340             return SunHints.Value.get(SunHints.INTKEY_RESOLUTION_VARIANT,
1341                                       resolutionVariantHint);
1342         }
1343         return null;
1344     }
1345 
1346     /**
1347      * Sets the preferences for the rendering algorithms.
1348      * Hint categories include controls for rendering quality and
1349      * overall time/quality trade-off in the rendering process.
1350      * @param hints The rendering hints to be set
1351      * @see RenderingHints
1352      */
1353     public void setRenderingHints(Map<?,?> hints) {
1354         this.hints = null;
1355         renderHint = SunHints.INTVAL_RENDER_DEFAULT;
1356         antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;
1357         textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;
1358         fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;
1359         lcdTextContrast = lcdTextContrastDefaultValue;
1360         interpolationHint = -1;
1361         interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;


3050             return;
3051         }
3052 
3053         try {
3054             textpipe.drawChars(this, chData, 0, length, x, y);
3055         } catch (InvalidPipeException e) {
3056             try {
3057                 revalidateAll();
3058                 textpipe.drawChars(this, chData, 0, length, x, y);
3059             } catch (InvalidPipeException e2) {
3060                 // Still catching the exception; we are not yet ready to
3061                 // validate the surfaceData correctly.  Fail for now and
3062                 // try again next time around.
3063             }
3064         } finally {
3065             surfaceData.markDirty();
3066         }
3067     }
3068 // end of text rendering methods
3069 
3070     private boolean isHiDPIImage(final Image img) {
3071         return (SurfaceManager.getImageScale(img) != 1) ||
3072                (resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_OFF
3073                     && img instanceof MultiResolutionImage);
3074     }
3075 
3076     private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
3077                                    int dy2, int sx1, int sy1, int sx2, int sy2,
3078                                    Color bgcolor, ImageObserver observer) {
3079 
3080         if (SurfaceManager.getImageScale(img) != 1) {  // Volatile Image
3081             final int scale = SurfaceManager.getImageScale(img);
3082             sx1 = Region.clipScale(sx1, scale);
3083             sx2 = Region.clipScale(sx2, scale);
3084             sy1 = Region.clipScale(sy1, scale);
3085             sy2 = Region.clipScale(sy2, scale);
3086         } else if (img instanceof MultiResolutionImage) {
3087             // get scaled destination image size
3088 
3089             int width = img.getWidth(observer);
3090             int height = img.getHeight(observer);
3091 
3092             Image resolutionVariant = getResolutionVariant(
3093                     (MultiResolutionImage) img, width, height,
3094                     dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
3095 
3096             if (resolutionVariant != img && resolutionVariant != null) {
3097                 // recalculate source region for the resolution variant
3098                 
3099                 ImageObserver rvObserver = MultiResolutionToolkitImage.
3100                         getResolutionVariantObserver(img, observer,
3101                                 width, height, -1, -1);
3102 
3103                 int rvWidth = resolutionVariant.getWidth(rvObserver);
3104                 int rvHeight = resolutionVariant.getHeight(rvObserver);
3105 
3106                 if (0 < width && 0 < height && 0 < rvWidth && 0 < rvHeight) {
3107 
3108                     float widthScale = ((float) rvWidth) / width;
3109                     float heightScale = ((float) rvHeight) / height;
3110 
3111                     sx1 = Region.clipScale(sx1, widthScale);
3112                     sy1 = Region.clipScale(sy1, heightScale);
3113                     sx2 = Region.clipScale(sx2, widthScale);
3114                     sy2 = Region.clipScale(sy2, heightScale);
3115 
3116                     observer = rvObserver;
3117                     img = resolutionVariant;
3118                 }
3119             }
3120         }
3121 
3122         try {
3123             return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
3124                                         sx2, sy2, bgcolor, observer);
3125         } catch (InvalidPipeException e) {
3126             try {
3127                 revalidateAll();
3128                 return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1,
3129                                             sy1, sx2, sy2, bgcolor, 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     private Image getResolutionVariant(MultiResolutionImage img,
3142             int srcWidth, int srcHeight, int dx1, int dy1, int dx2, int dy2,
3143             int sx1, int sy1, int sx2, int sy2) {
3144 
3145         if (srcWidth <= 0 || srcHeight <= 0) {
3146             return null;
3147         }
3148 
3149         int sw = sx2 - sx1;
3150         int sh = sy2 - sy1;
3151 
3152         if (sw == 0 || sh == 0) {
3153             return null;
3154         }
3155 
3156         int type = transform.getType();
3157         int dw = dx2 - dx1;
3158         int dh = dy2 - dy1;
3159         double destRegionWidth;
3160         double destRegionHeight;
3161 
3162         if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP)) == 0) {
3163             destRegionWidth = dw;
3164             destRegionHeight = dh;
3165         } else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {
3166             destRegionWidth = dw * transform.getScaleX();
3167             destRegionHeight = dh * transform.getScaleY();
3168         } else {
3169             destRegionWidth = dw * Math.hypot(
3170                     transform.getScaleX(), transform.getShearY());
3171             destRegionHeight = dh * Math.hypot(
3172                     transform.getShearX(), transform.getScaleY());
3173         }
3174 
3175         int destImageWidth = (int) Math.abs(srcWidth * destRegionWidth / sw);
3176         int destImageHeight = (int) Math.abs(srcHeight * destRegionHeight / sh);
3177 
3178         Image resolutionVariant
3179                 = img.getResolutionVariant(destImageWidth, destImageHeight);
3180 
3181         if (resolutionVariant instanceof ToolkitImage
3182                 && ((ToolkitImage) resolutionVariant).hasError()) {
3183             return null;
3184         }
3185 
3186         return resolutionVariant;
3187     }
3188 
3189     /**
3190      * Draws an image scaled to x,y,w,h in nonblocking mode with a
3191      * callback object.
3192      */
3193     public boolean drawImage(Image img, int x, int y, int width, int height,
3194                              ImageObserver observer) {
3195         return drawImage(img, x, y, width, height, null, observer);
3196     }
3197 
3198     /**
3199      * Not part of the advertised API but a useful utility method
3200      * to call internally.  This is for the case where we are
3201      * drawing to/from given coordinates using a given width/height,
3202      * but we guarantee that the surfaceData's width/height of the src and dest
3203      * areas are equal (no scale needed). Note that this method intentionally
3204      * ignore scale factor of the source image, and copy it as is.
3205      */
3206     public boolean copyImage(Image img, int dx, int dy, int sx, int sy,
3207                              int width, int height, Color bgcolor,
3208                              ImageObserver observer) {