1 /*
   2  * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.pipe;
  27 
  28 import java.awt.AlphaComposite;
  29 import java.awt.Color;
  30 import java.awt.Image;
  31 import java.awt.Transparency;
  32 import java.awt.geom.AffineTransform;
  33 import java.awt.geom.NoninvertibleTransformException;
  34 import java.awt.image.AffineTransformOp;
  35 import java.awt.image.BufferedImage;
  36 import java.awt.image.BufferedImageOp;
  37 import java.awt.image.ColorModel;
  38 import java.awt.image.DataBuffer;
  39 import java.awt.image.ImageObserver;
  40 import java.awt.image.IndexColorModel;
  41 import java.awt.image.Raster;
  42 import java.awt.image.VolatileImage;
  43 import sun.awt.SunHints;
  44 import sun.awt.image.ImageRepresentation;
  45 import sun.awt.image.SurfaceManager;
  46 import sun.awt.image.ToolkitImage;
  47 import sun.java2d.InvalidPipeException;
  48 import sun.java2d.SunGraphics2D;
  49 import sun.java2d.SurfaceData;
  50 import sun.java2d.loops.Blit;
  51 import sun.java2d.loops.BlitBg;
  52 import sun.java2d.loops.TransformHelper;
  53 import sun.java2d.loops.MaskBlit;
  54 import sun.java2d.loops.CompositeType;
  55 import sun.java2d.loops.ScaledBlit;
  56 import sun.java2d.loops.SurfaceType;
  57 
  58 public class DrawImage implements DrawImagePipe
  59 {
  60     public boolean copyImage(SunGraphics2D sg, Image img,
  61                              int x, int y,
  62                              Color bgColor)
  63     {
  64         int imgw = img.getWidth(null);
  65         int imgh = img.getHeight(null);
  66         if (isSimpleTranslate(sg)) {
  67             return renderImageCopy(sg, img, bgColor,
  68                                    x + sg.transX, y + sg.transY,
  69                                    0, 0, imgw, imgh);
  70         }
  71         AffineTransform atfm = sg.transform;
  72         if ((x | y) != 0) {
  73             atfm = new AffineTransform(atfm);
  74             atfm.translate(x, y);
  75         }
  76         transformImage(sg, img, atfm, sg.interpolationType,
  77                        0, 0, imgw, imgh, bgColor);
  78         return true;
  79     }
  80 
  81     public boolean copyImage(SunGraphics2D sg, Image img,
  82                              int dx, int dy, int sx, int sy, int w, int h,
  83                              Color bgColor)
  84     {
  85         if (isSimpleTranslate(sg)) {
  86             return renderImageCopy(sg, img, bgColor,
  87                                    dx + sg.transX, dy + sg.transY,
  88                                    sx, sy, w, h);
  89         }
  90         scaleImage(sg, img, dx, dy, (dx + w), (dy + h),
  91                    sx, sy, (sx + w), (sy + h), bgColor);
  92         return true;
  93     }
  94 
  95     public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,
  96                               int width, int height,
  97                               Color bgColor)
  98     {
  99         int imgw = img.getWidth(null);
 100         int imgh = img.getHeight(null);
 101         // Only accelerate scale if:
 102         //          - w/h positive values
 103         //          - sg transform integer translate/identity only
 104         //          - no bgColor in operation
 105         if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {
 106             double dx1 = x + sg.transX;
 107             double dy1 = y + sg.transY;
 108             double dx2 = dx1 + width;
 109             double dy2 = dy1 + height;
 110             if (renderImageScale(sg, img, bgColor, sg.interpolationType,
 111                                  0, 0, imgw, imgh,
 112                                  dx1, dy1, dx2, dy2))
 113             {
 114                 return true;
 115             }
 116         }
 117 
 118         AffineTransform atfm = sg.transform;
 119         if ((x | y) != 0 || width != imgw || height != imgh) {
 120             atfm = new AffineTransform(atfm);
 121             atfm.translate(x, y);
 122             atfm.scale(((double)width)/imgw, ((double)height)/imgh);
 123         }
 124         transformImage(sg, img, atfm, sg.interpolationType,
 125                        0, 0, imgw, imgh, bgColor);
 126         return true;
 127     }
 128 
 129     /*
 130      * This method is only called in those circumstances where the
 131      * operation has a non-null secondary transform specified.  Its
 132      * role is to check for various optimizations based on the types
 133      * of both the secondary and SG2D transforms and to do some
 134      * quick calculations to avoid having to combine the transforms
 135      * and/or to call a more generalized method.
 136      */
 137     protected void transformImage(SunGraphics2D sg, Image img, int x, int y,
 138                                   AffineTransform extraAT, int interpType)
 139     {
 140         int txtype = extraAT.getType();
 141         int imgw = img.getWidth(null);
 142         int imgh = img.getHeight(null);
 143         boolean checkfinalxform;
 144 
 145         if (sg.transformState <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE &&
 146             (txtype == AffineTransform.TYPE_IDENTITY ||
 147              txtype == AffineTransform.TYPE_TRANSLATION))
 148         {
 149             // First optimization - both are some kind of translate
 150 
 151             // Combine the translations and check if interpolation is necessary.
 152             double tx = extraAT.getTranslateX();
 153             double ty = extraAT.getTranslateY();
 154             tx += sg.transform.getTranslateX();
 155             ty += sg.transform.getTranslateY();
 156             int itx = (int) Math.floor(tx + 0.5);
 157             int ity = (int) Math.floor(ty + 0.5);
 158             if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
 159                 (closeToInteger(itx, tx) && closeToInteger(ity, ty)))
 160             {
 161                 renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);
 162                 return;
 163             }
 164             checkfinalxform = false;
 165         } else if (sg.transformState <= SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
 166                    ((txtype & (AffineTransform.TYPE_FLIP |
 167                                AffineTransform.TYPE_MASK_ROTATION |
 168                                AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))
 169         {
 170             // Second optimization - both are some kind of translate or scale
 171 
 172             // Combine the scales and check if interpolation is necessary.
 173 
 174             // Transform source bounds by extraAT,
 175             // then translate the bounds again by x, y
 176             // then transform the bounds again by sg.transform
 177             double coords[] = new double[] {
 178                 0, 0, imgw, imgh,
 179             };
 180             extraAT.transform(coords, 0, coords, 0, 2);
 181             coords[0] += x;
 182             coords[1] += y;
 183             coords[2] += x;
 184             coords[3] += y;
 185             sg.transform.transform(coords, 0, coords, 0, 2);
 186 
 187             if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,
 188                                null, interpType, coords))
 189             {
 190                 return;
 191             }
 192             checkfinalxform = false;
 193         } else {
 194             checkfinalxform = true;
 195         }
 196 
 197         // Begin Transform
 198         AffineTransform tx = new AffineTransform(sg.transform);
 199         tx.translate(x, y);
 200         tx.concatenate(extraAT);
 201 
 202         // Do not try any more optimizations if either of the cases
 203         // above was tried as we have already verified that the
 204         // resulting transform will not simplify.
 205         if (checkfinalxform) {
 206             // In this case neither of the above simple transform
 207             // pairs was found so we will do some final tests on
 208             // the final rendering transform which may be the
 209             // simple product of two complex transforms.
 210             transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
 211         } else {
 212             renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
 213         }
 214     }
 215 
 216     /*
 217      * This method is called with a final rendering transform that
 218      * has combined all of the information about the Graphics2D
 219      * transform attribute with the transformations specified by
 220      * the arguments to the drawImage call.
 221      * Its role is to see if the combined transform ends up being
 222      * acceleratable by either a renderImageCopy or renderImageScale
 223      * once all of the math is done.
 224      *
 225      * Note: The transform supplied here has an origin that is
 226      * already adjusted to point to the device location where
 227      * the (sx1, sy1) location of the source image should be placed.
 228      */
 229     protected void transformImage(SunGraphics2D sg, Image img,
 230                                   AffineTransform tx, int interpType,
 231                                   int sx1, int sy1, int sx2, int sy2,
 232                                   Color bgColor)
 233     {
 234         // Transform 3 source corners by tx and analyze them
 235         // for simplified operations (Copy or Scale).  Using
 236         // 3 points lets us analyze any kind of transform,
 237         // even transforms that involve very tiny amounts of
 238         // rotation or skew to see if they degenerate to a
 239         // simple scale or copy operation within the allowable
 240         // error bounds.
 241         // Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)
 242         // because the transform is already translated such that
 243         // the origin is where sx1, sy1 should go.
 244         double coords[] = new double[6];
 245         /* index:  0  1    2  3    4  5  */
 246         /* coord: (0, 0), (w, h), (0, h) */
 247         coords[2] = sx2 - sx1;
 248         coords[3] = coords[5] = sy2 - sy1;
 249         tx.transform(coords, 0, coords, 0, 3);
 250         // First test if the X coords of the transformed UL
 251         // and LL points match and that the Y coords of the
 252         // transformed LR and LL points also match.
 253         // If they do then it is a "rectilinear" transform and
 254         // tryCopyOrScale will make sure it is upright and
 255         // integer-based.
 256         if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&
 257             Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&
 258             tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,
 259                            bgColor, interpType, coords))
 260         {
 261             return;
 262         }
 263 
 264         renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);
 265     }
 266 
 267     /*
 268      * Check the bounding coordinates of the transformed source
 269      * image to see if they fall on integer coordinates such
 270      * that they will cause no interpolation anomalies if we
 271      * use our simplified Blit or ScaledBlit operations instead
 272      * of a full transform operation.
 273      */
 274     protected boolean tryCopyOrScale(SunGraphics2D sg,
 275                                      Image img,
 276                                      int sx1, int sy1,
 277                                      int sx2, int sy2,
 278                                      Color bgColor, int interpType,
 279                                      double coords[])
 280     {
 281         double dx = coords[0];
 282         double dy = coords[1];
 283         double dw = coords[2] - dx;
 284         double dh = coords[3] - dy;
















 285         // First check if width and height are very close to img w&h.
 286         if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {
 287             // Round location to nearest pixel and then test
 288             // if it will cause interpolation anomalies.
 289             int idx = (int) Math.floor(dx + 0.5);
 290             int idy = (int) Math.floor(dy + 0.5);
 291             if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
 292                 (closeToInteger(idx, dx) && closeToInteger(idy, dy)))
 293             {
 294                 renderImageCopy(sg, img, bgColor,
 295                                 idx, idy,
 296                                 sx1, sy1, sx2-sx1, sy2-sy1);
 297                 return true;
 298             }
 299         }
 300         // (For now) We can only use our ScaledBlits if the image
 301         // is upright (i.e. dw & dh both > 0)
 302         if (dw > 0 && dh > 0) {
 303             if (renderImageScale(sg, img, bgColor, interpType,
 304                                  sx1, sy1, sx2, sy2,
 305                                  coords[0], coords[1], coords[2], coords[3]))
 306             {
 307                 return true;
 308             }
 309         }
 310         return false;
 311     }
 312 
 313     /**
 314      * Return a non-accelerated BufferedImage of the requested type with the
 315      * indicated subimage of the original image located at 0,0 in the new image.
 316      * If a bgColor is supplied, composite the original image over that color
 317      * with a SrcOver operation, otherwise make a SrcNoEa copy.
 318      * <p>
 319      * Returned BufferedImage is not accelerated for two reasons:
 320      * <ul>
 321      * <li> Types of the image and surface are predefined, because these types
 322      *      correspond to the TransformHelpers, which we know we have. And
 323      *      acceleration can change the type of the surface
 324      * <li> Image will be used only once and acceleration caching wouldn't help
 325      * </ul>
 326      */
 327     BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
 328                                     int sx1, int sy1, int sx2, int sy2)
 329     {
 330         final int width = sx2 - sx1;
 331         final int height = sy2 - sy1;
 332         final BufferedImage bimg = new BufferedImage(width, height, type);
 333         final SunGraphics2D g2d = (SunGraphics2D) bimg.createGraphics();
 334         g2d.setComposite(AlphaComposite.Src);
 335         bimg.setAccelerationPriority(0);
 336         if (bgColor != null) {
 337             g2d.setColor(bgColor);
 338             g2d.fillRect(0, 0, width, height);
 339             g2d.setComposite(AlphaComposite.SrcOver);
 340         }
 341         g2d.copyImage(img, 0, 0, sx1, sy1, width, height, null, null);
 342         g2d.dispose();
 343         return bimg;
 344     }
 345 
 346     protected void renderImageXform(SunGraphics2D sg, Image img,
 347                                     AffineTransform tx, int interpType,
 348                                     int sx1, int sy1, int sx2, int sy2,
 349                                     Color bgColor)
 350     {
 351         Region clip = sg.getCompClip();
 352         SurfaceData dstData = sg.surfaceData;
 353         SurfaceData srcData = dstData.getSourceSurfaceData(img,
 354                                                            SunGraphics2D.TRANSFORM_GENERIC,
 355                                                            sg.imageComp,
 356                                                            bgColor);
 357 
 358         if (srcData == null) {
 359             img = getBufferedImage(img);
 360             srcData = dstData.getSourceSurfaceData(img,
 361                                                    SunGraphics2D.TRANSFORM_GENERIC,
 362                                                    sg.imageComp,
 363                                                    bgColor);
 364             if (srcData == null) {
 365                 // REMIND: Is this correct?  Can this happen?
 366                 return;
 367             }
 368         }
 369 
 370         if (isBgOperation(srcData, bgColor)) {
 371             // We cannot perform bg operations during transform so make
 372             // an opaque temp image with the appropriate background
 373             // and work from there.
 374             img = makeBufferedImage(img, bgColor, BufferedImage.TYPE_INT_RGB,
 375                                     sx1, sy1, sx2, sy2);
 376             // Temp image has appropriate subimage at 0,0 now.
 377             sx2 -= sx1;
 378             sy2 -= sy1;
 379             sx1 = sy1 = 0;
 380 
 381             srcData = dstData.getSourceSurfaceData(img,
 382                                                    SunGraphics2D.TRANSFORM_GENERIC,
 383                                                    sg.imageComp,
 384                                                    bgColor);
 385         }
 386 
 387         SurfaceType srcType = srcData.getSurfaceType();
 388         TransformHelper helper = TransformHelper.getFromCache(srcType);
 389 
 390         if (helper == null) {
 391             /* We have no helper for this source image type.
 392              * But we know that we do have helpers for both RGB and ARGB,
 393              * so convert to one of those types depending on transparency.
 394              * ARGB_PRE might be a better choice if the source image has
 395              * alpha, but it may cause some recursion here since we only
 396              * tend to have converters that convert to ARGB.
 397              */
 398             int type = ((srcData.getTransparency() == Transparency.OPAQUE)
 399                         ? BufferedImage.TYPE_INT_RGB
 400                         : BufferedImage.TYPE_INT_ARGB);
 401             img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);
 402             // Temp image has appropriate subimage at 0,0 now.
 403             sx2 -= sx1;
 404             sy2 -= sy1;
 405             sx1 = sy1 = 0;
 406 
 407             srcData = dstData.getSourceSurfaceData(img,
 408                                                    SunGraphics2D.TRANSFORM_GENERIC,
 409                                                    sg.imageComp,
 410                                                    null);
 411             srcType = srcData.getSurfaceType();
 412             helper = TransformHelper.getFromCache(srcType);
 413             // assert(helper != null);
 414         }
 415 
 416         AffineTransform itx;
 417         try {
 418             itx = tx.createInverse();
 419         } catch (NoninvertibleTransformException e) {
 420             // Non-invertible transform means no output
 421             return;
 422         }
 423 
 424         /*
 425          * Find the maximum bounds on the destination that will be
 426          * affected by the transformed source.  First, transform all
 427          * four corners of the source and then min and max the resulting
 428          * destination coordinates of the transformed corners.
 429          * Note that tx already has the offset to sx1,sy1 accounted
 430          * for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the
 431          * source coordinates.
 432          */
 433         double coords[] = new double[8];
 434         /* corner:  UL      UR      LL      LR   */
 435         /* index:  0  1    2  3    4  5    6  7  */
 436         /* coord: (0, 0), (w, 0), (0, h), (w, h) */
 437         coords[2] = coords[6] = sx2 - sx1;
 438         coords[5] = coords[7] = sy2 - sy1;
 439         tx.transform(coords, 0, coords, 0, 4);
 440         double ddx1, ddy1, ddx2, ddy2;
 441         ddx1 = ddx2 = coords[0];
 442         ddy1 = ddy2 = coords[1];
 443         for (int i = 2; i < coords.length; i += 2) {
 444             double d = coords[i];
 445             if (ddx1 > d) ddx1 = d;
 446             else if (ddx2 < d) ddx2 = d;
 447             d = coords[i+1];
 448             if (ddy1 > d) ddy1 = d;
 449             else if (ddy2 < d) ddy2 = d;
 450         }
 451         int dx1 = (int) Math.floor(ddx1);
 452         int dy1 = (int) Math.floor(ddy1);
 453         int dx2 = (int) Math.ceil(ddx2);
 454         int dy2 = (int) Math.ceil(ddy2);
 455 
 456         SurfaceType dstType = dstData.getSurfaceType();
 457         MaskBlit maskblit;
 458         Blit blit;
 459         if (sg.compositeState <= SunGraphics2D.COMP_ALPHA) {
 460             /* NOTE: We either have, or we can make,
 461              * a MaskBlit for any alpha composite type
 462              */
 463             maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
 464                                              sg.imageComp,
 465                                              dstType);
 466 
 467             /* NOTE: We can only use the native TransformHelper
 468              * func to go directly to the dest if both the helper
 469              * and the MaskBlit are native.
 470              * All helpers are native at this point, but some MaskBlit
 471              * objects are implemented in Java, so we need to check.
 472              */
 473             if (maskblit.getNativePrim() != 0) {
 474                 // We can render directly.
 475                 helper.Transform(maskblit, srcData, dstData,
 476                                  sg.composite, clip,
 477                                  itx, interpType,
 478                                  sx1, sy1, sx2, sy2,
 479                                  dx1, dy1, dx2, dy2,
 480                                  null, 0, 0);
 481                 return;
 482             }
 483             blit = null;
 484         } else {
 485             /* NOTE: We either have, or we can make,
 486              * a Blit for any composite type, even Custom
 487              */
 488             maskblit = null;
 489             blit = Blit.getFromCache(SurfaceType.IntArgbPre,
 490                                      sg.imageComp,
 491                                      dstType);
 492         }
 493 
 494         // We need to transform to a temp image and then copy
 495         // just the pieces that are valid data to the dest.
 496         BufferedImage tmpimg = new BufferedImage(dx2-dx1, dy2-dy1,
 497                                                  BufferedImage.TYPE_INT_ARGB);
 498         SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);
 499         SurfaceType tmpType = tmpData.getSurfaceType();
 500         MaskBlit tmpmaskblit =
 501             MaskBlit.getFromCache(SurfaceType.IntArgbPre,
 502                                   CompositeType.SrcNoEa,
 503                                   tmpType);
 504         /*
 505          * The helper function fills a temporary edges buffer
 506          * for us with the bounding coordinates of each scanline
 507          * in the following format:
 508          *
 509          * edges[0, 1] = [top y, bottom y)
 510          * edges[2, 3] = [left x, right x) of top row
 511          * ...
 512          * edges[h*2, h*2+1] = [left x, right x) of bottom row
 513          *
 514          * all coordinates in the edges array will be relative to dx1, dy1
 515          *
 516          * edges thus has to be h*2+2 in length
 517          */
 518         int edges[] = new int[(dy2-dy1)*2+2];
 519         // It is important that edges[0]=edges[1]=0 when we call
 520         // Transform in case it must return early and we would
 521         // not want to render anything on an error condition.
 522         helper.Transform(tmpmaskblit, srcData, tmpData,
 523                          AlphaComposite.Src, null,
 524                          itx, interpType,
 525                          sx1, sy1, sx2, sy2,
 526                          0, 0, dx2-dx1, dy2-dy1,
 527                          edges, dx1, dy1);
 528 
 529         /*
 530          * Now copy the results, scanline by scanline, into the dest.
 531          * The edges array helps us minimize the work.
 532          */
 533         int index = 2;
 534         for (int y = edges[0]; y < edges[1]; y++) {
 535             int relx1 = edges[index++];
 536             int relx2 = edges[index++];
 537             if (relx1 >= relx2) {
 538                 continue;
 539             }
 540             if (maskblit != null) {
 541                 maskblit.MaskBlit(tmpData, dstData,
 542                                   sg.composite, clip,
 543                                   relx1, y,
 544                                   dx1+relx1, dy1+y,
 545                                   relx2 - relx1, 1,
 546                                   null, 0, 0);
 547             } else {
 548                 blit.Blit(tmpData, dstData,
 549                           sg.composite, clip,
 550                           relx1, y,
 551                           dx1+relx1, dy1+y,
 552                           relx2 - relx1, 1);
 553             }
 554         }
 555     }
 556 
 557     // Render an image using only integer translation
 558     // (no scale or transform or sub-pixel interpolated translations).
 559     protected boolean renderImageCopy(SunGraphics2D sg, Image img,
 560                                       Color bgColor,
 561                                       int dx, int dy,
 562                                       int sx, int sy,
 563                                       int w, int h)
 564     {
 565         Region clip = sg.getCompClip();
 566         SurfaceData dstData = sg.surfaceData;
 567 
 568         int attempts = 0;
 569         // Loop up to twice through; this gives us a chance to
 570         // revalidate the surfaceData objects in case of an exception
 571         // and try it once more
 572         while (true) {
 573             SurfaceData srcData =
 574                 dstData.getSourceSurfaceData(img,
 575                                              SunGraphics2D.TRANSFORM_ISIDENT,
 576                                              sg.imageComp,
 577                                              bgColor);
 578             if (srcData == null) {
 579                 return false;
 580             }
 581 
 582             try {
 583                 SurfaceType srcType = srcData.getSurfaceType();
 584                 SurfaceType dstType = dstData.getSurfaceType();
 585                 blitSurfaceData(sg, clip,
 586                                 srcData, dstData, srcType, dstType,
 587                                 sx, sy, dx, dy, w, h, bgColor);
 588                 return true;
 589             } catch (NullPointerException e) {
 590                 if (!(SurfaceData.isNull(dstData) ||
 591                       SurfaceData.isNull(srcData)))
 592                 {
 593                     // Something else caused the exception, throw it...
 594                     throw e;
 595                 }
 596                 return false;
 597                 // NOP if we have been disposed
 598             } catch (InvalidPipeException e) {
 599                 // Always catch the exception; try this a couple of times
 600                 // and fail silently if the system is not yet ready to
 601                 // revalidate the source or dest surfaceData objects.
 602                 ++attempts;
 603                 clip = sg.getCompClip();   // ensures sg.surfaceData is valid
 604                 dstData = sg.surfaceData;
 605                 if (SurfaceData.isNull(dstData) ||
 606                     SurfaceData.isNull(srcData) || (attempts > 1))
 607                 {
 608                     return false;
 609                 }
 610             }
 611         }
 612     }
 613 
 614     // Render an image using only integer scaling (no transform).
 615     protected boolean renderImageScale(SunGraphics2D sg, Image img,
 616                                        Color bgColor, int interpType,
 617                                        int sx1, int sy1,
 618                                        int sx2, int sy2,
 619                                        double dx1, double dy1,
 620                                        double dx2, double dy2)
 621     {
 622         // Currently only NEAREST_NEIGHBOR interpolation is implemented
 623         // for ScaledBlit operations.
 624         if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
 625             return false;
 626         }
 627 
 628         Region clip = sg.getCompClip();
 629         SurfaceData dstData = sg.surfaceData;
 630 
 631         int attempts = 0;
 632         // Loop up to twice through; this gives us a chance to
 633         // revalidate the surfaceData objects in case of an exception
 634         // and try it once more
 635         while (true) {
 636             SurfaceData srcData =
 637                 dstData.getSourceSurfaceData(img,
 638                                              SunGraphics2D.TRANSFORM_TRANSLATESCALE,
 639                                              sg.imageComp,
 640                                              bgColor);
 641 
 642             if (srcData == null || isBgOperation(srcData, bgColor)) {
 643                 return false;
 644             }
 645 
 646             try {
 647                 SurfaceType srcType = srcData.getSurfaceType();
 648                 SurfaceType dstType = dstData.getSurfaceType();
 649                 return scaleSurfaceData(sg, clip,
 650                                         srcData, dstData, srcType, dstType,
 651                                         sx1, sy1, sx2, sy2,
 652                                         dx1, dy1, dx2, dy2);
 653             } catch (NullPointerException e) {
 654                 if (!SurfaceData.isNull(dstData)) {
 655                     // Something else caused the exception, throw it...
 656                     throw e;
 657                 }
 658                 return false;
 659                 // NOP if we have been disposed
 660             } catch (InvalidPipeException e) {
 661                 // Always catch the exception; try this a couple of times
 662                 // and fail silently if the system is not yet ready to
 663                 // revalidate the source or dest surfaceData objects.
 664                 ++attempts;
 665                 clip = sg.getCompClip();  // ensures sg.surfaceData is valid
 666                 dstData = sg.surfaceData;
 667                 if (SurfaceData.isNull(dstData) ||
 668                     SurfaceData.isNull(srcData) || (attempts > 1))
 669                 {
 670                     return false;
 671                 }
 672             }
 673         }
 674     }
 675 
 676     public boolean scaleImage(SunGraphics2D sg, Image img,
 677                               int dx1, int dy1, int dx2, int dy2,
 678                               int sx1, int sy1, int sx2, int sy2,
 679                               Color bgColor)
 680     {
 681         int srcW, srcH, dstW, dstH;
 682         int srcX, srcY, dstX, dstY;
 683         boolean srcWidthFlip = false;
 684         boolean srcHeightFlip = false;
 685         boolean dstWidthFlip = false;
 686         boolean dstHeightFlip = false;
 687 
 688         if (sx2 > sx1) {
 689             srcW = sx2 - sx1;
 690             srcX = sx1;
 691         } else {
 692             srcWidthFlip = true;
 693             srcW = sx1 - sx2;
 694             srcX = sx2;
 695         }
 696         if (sy2 > sy1) {
 697             srcH = sy2-sy1;
 698             srcY = sy1;
 699         } else {
 700             srcHeightFlip = true;
 701             srcH = sy1-sy2;
 702             srcY = sy2;
 703         }
 704         if (dx2 > dx1) {
 705             dstW = dx2 - dx1;
 706             dstX = dx1;
 707         } else {
 708             dstW = dx1 - dx2;
 709             dstWidthFlip = true;
 710             dstX = dx2;
 711         }
 712         if (dy2 > dy1) {
 713             dstH = dy2 - dy1;
 714             dstY = dy1;
 715         } else {
 716             dstH = dy1 - dy2;
 717             dstHeightFlip = true;
 718             dstY = dy2;
 719         }
 720         if (srcW <= 0 || srcH <= 0) {
 721             return true;
 722         }
 723         // Only accelerate scale if it does not involve a flip or transform
 724         if ((srcWidthFlip == dstWidthFlip) &&
 725             (srcHeightFlip == dstHeightFlip) &&
 726             isSimpleTranslate(sg))
 727         {
 728             double ddx1 = dstX + sg.transX;
 729             double ddy1 = dstY + sg.transY;
 730             double ddx2 = ddx1 + dstW;
 731             double ddy2 = ddy1 + dstH;
 732             if (renderImageScale(sg, img, bgColor, sg.interpolationType,
 733                                  srcX, srcY, srcX+srcW, srcY+srcH,
 734                                  ddx1, ddy1, ddx2, ddy2))
 735             {
 736                 return true;
 737             }
 738         }
 739 
 740         AffineTransform atfm = new AffineTransform(sg.transform);
 741         atfm.translate(dx1, dy1);
 742         double m00 = (double)(dx2-dx1)/(sx2-sx1);
 743         double m11 = (double)(dy2-dy1)/(sy2-sy1);
 744         atfm.scale(m00, m11);
 745         atfm.translate(srcX-sx1, srcY-sy1);
 746 
 747         final int scale = SurfaceManager.getImageScale(img);
 748         final int imgW = img.getWidth(null) * scale;
 749         final int imgH = img.getHeight(null) * scale;
 750         srcW += srcX;
 751         srcH += srcY;
 752         // Make sure we are not out of bounds
 753         if (srcW > imgW) {
 754             srcW = imgW;
 755         }
 756         if (srcH > imgH) {
 757             srcH = imgH;
 758         }
 759         if (srcX < 0) {
 760             atfm.translate(-srcX, 0);
 761             srcX = 0;
 762         }
 763         if (srcY < 0) {
 764             atfm.translate(0, -srcY);
 765             srcY = 0;
 766         }
 767         if (srcX >= srcW || srcY >= srcH) {
 768             return true;
 769         }
 770         // Note: src[WH] are currently the right and bottom coordinates.
 771         // The following two lines would adjust src[WH] back to being
 772         // dimensions.
 773         //     srcW -= srcX;
 774         //     srcH -= srcY;
 775         // Since transformImage needs right and bottom coords we will
 776         // omit this adjustment.
 777 
 778         transformImage(sg, img, atfm, sg.interpolationType,
 779                        srcX, srcY, srcW, srcH, bgColor);
 780         return true;
 781     }
 782 
 783     /**
 784      ** Utilities
 785      ** The following methods are used by the public methods above
 786      ** for performing various operations
 787      **/
 788 
 789     /*
 790      * This constant represents a tradeoff between the
 791      * need to make sure that image transformations are
 792      * "very close" to integer device coordinates before
 793      * we decide to use an integer scale or copy operation
 794      * as a substitute and the fact that roundoff errors
 795      * in AffineTransforms are frequently introduced by
 796      * performing multiple sequential operations on them.
 797      *
 798      * The evaluation of bug 4990624 details the potential
 799      * for this error cutoff to result in display anomalies
 800      * in different types of image operations and how this
 801      * value represents a good compromise here.
 802      */
 803     private static final double MAX_TX_ERROR = .0001;
 804 
 805     public static boolean closeToInteger(int i, double d) {
 806         return (Math.abs(d-i) < MAX_TX_ERROR);
 807     }
 808 
 809     public static boolean isSimpleTranslate(SunGraphics2D sg) {
 810         int ts = sg.transformState;
 811         if (ts <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
 812             // Integer translates are always "simple"
 813             return true;
 814         }
 815         if (ts >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
 816             // Scales and beyond are always "not simple"
 817             return false;
 818         }
 819         // non-integer translates are only simple when not interpolating
 820         if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
 821             return true;
 822         }
 823         return false;
 824     }
 825 
 826     protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {
 827         // If we cannot get the srcData, then cannot assume anything about
 828         // the image
 829         return ((srcData == null) ||
 830                 ((bgColor != null) &&
 831                  (srcData.getTransparency() != Transparency.OPAQUE)));
 832     }
 833 
 834     protected BufferedImage getBufferedImage(Image img) {
 835         if (img instanceof BufferedImage) {
 836             return (BufferedImage)img;
 837         }
 838         // Must be VolatileImage; get BufferedImage representation
 839         return ((VolatileImage)img).getSnapshot();
 840     }
 841 
 842     /*
 843      * Return the color model to be used with this BufferedImage and
 844      * transform.
 845      */
 846     private ColorModel getTransformColorModel(SunGraphics2D sg,
 847                                               BufferedImage bImg,
 848                                               AffineTransform tx) {
 849         ColorModel cm = bImg.getColorModel();
 850         ColorModel dstCM = cm;
 851 
 852         if (tx.isIdentity()) {
 853             return dstCM;
 854         }
 855         int type = tx.getType();
 856         boolean needTrans =
 857                 ((type & (AffineTransform.TYPE_MASK_ROTATION |
 858                           AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0);
 859         if (! needTrans &&
 860               type != AffineTransform.TYPE_TRANSLATION &&
 861               type != AffineTransform.TYPE_IDENTITY)
 862         {
 863             double[] mtx = new double[4];
 864             tx.getMatrix(mtx);
 865             // Check out the matrix.  A non-integral scale will force ARGB
 866             // since the edge conditions cannot be guaranteed.
 867             needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
 868         }
 869 
 870         if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
 871             if (cm instanceof IndexColorModel) {
 872                 Raster raster = bImg.getRaster();
 873                 IndexColorModel icm = (IndexColorModel) cm;
 874                 // Just need to make sure that we have a transparent pixel
 875                 if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
 876                     // Fix 4221407
 877                     if (raster instanceof sun.awt.image.BytePackedRaster) {
 878                         dstCM = ColorModel.getRGBdefault();
 879                     }
 880                     else {
 881                         double[] matrix = new double[6];
 882                         tx.getMatrix(matrix);
 883                         if (matrix[1] == 0. && matrix[2] ==0.
 884                             && matrix[4] == 0. && matrix[5] == 0.) {
 885                             // Only scaling so do not need to create
 886                         }
 887                         else {
 888                             int mapSize = icm.getMapSize();
 889                             if (mapSize < 256) {
 890                                 int[] cmap = new int[mapSize+1];
 891                                 icm.getRGBs(cmap);
 892                                 cmap[mapSize] = 0x0000;
 893                                 dstCM = new
 894                                     IndexColorModel(icm.getPixelSize(),
 895                                                     mapSize+1,
 896                                                     cmap, 0, true, mapSize,
 897                                                     DataBuffer.TYPE_BYTE);
 898                             }
 899                             else {
 900                                 dstCM = ColorModel.getRGBdefault();
 901                             }
 902                         }  /* if (matrix[0] < 1.f ...) */
 903                     }   /* raster instanceof sun.awt.image.BytePackedRaster */
 904                 } /* if (cm.getTransparency() == cm.OPAQUE) */
 905             } /* if (cm instanceof IndexColorModel) */
 906             else if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
 907                 // Need a bitmask transparency
 908                 // REMIND: for now, use full transparency since no loops
 909                 // for bitmask
 910                 dstCM = ColorModel.getRGBdefault();
 911             }
 912         } /* if (sg.renderHint == RENDER_QUALITY) */
 913         else {
 914 
 915             if (cm instanceof IndexColorModel ||
 916                 (needTrans && cm.getTransparency() == Transparency.OPAQUE))
 917             {
 918                 // Need a bitmask transparency
 919                 // REMIND: for now, use full transparency since no loops
 920                 // for bitmask
 921                 dstCM = ColorModel.getRGBdefault();
 922             }
 923         }
 924 
 925         return dstCM;
 926     }
 927 
 928     protected void blitSurfaceData(SunGraphics2D sg,
 929                                    Region clipRegion,
 930                                    SurfaceData srcData,
 931                                    SurfaceData dstData,
 932                                    SurfaceType srcType,
 933                                    SurfaceType dstType,
 934                                    int sx, int sy, int dx, int dy,
 935                                    int w, int h,
 936                                    Color bgColor)
 937     {
 938         if (w <= 0 || h <= 0) {
 939             /*
 940              * Fix for bugid 4783274 - BlitBg throws an exception for
 941              * a particular set of anomalous parameters.
 942              * REMIND: The native loops do proper clipping and would
 943              * detect this situation themselves, but the Java loops
 944              * all seem to trust their parameters a little too well
 945              * to the point where they will try to process a negative
 946              * area of pixels and throw exceptions.  The real fix is
 947              * to modify the Java loops to do proper clipping so that
 948              * they can deal with negative dimensions as well as
 949              * improperly large dimensions, but that fix is too risky
 950              * to integrate for Mantis at this point.  In the meantime
 951              * eliminating the negative or zero dimensions here is
 952              * "correct" and saves them from some nasty exceptional
 953              * conditions, one of which is the test case of 4783274.
 954              */
 955             return;
 956         }
 957         CompositeType comp = sg.imageComp;
 958         if (CompositeType.SrcOverNoEa.equals(comp) &&
 959             (srcData.getTransparency() == Transparency.OPAQUE ||
 960              (bgColor != null &&
 961               bgColor.getTransparency() == Transparency.OPAQUE)))
 962         {
 963             comp = CompositeType.SrcNoEa;
 964         }
 965         if (!isBgOperation(srcData, bgColor)) {
 966             Blit blit = Blit.getFromCache(srcType, comp, dstType);
 967             blit.Blit(srcData, dstData, sg.composite, clipRegion,
 968                       sx, sy, dx, dy, w, h);
 969         } else {
 970             BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
 971             blit.BlitBg(srcData, dstData, sg.composite, clipRegion,
 972                         bgColor.getRGB(), sx, sy, dx, dy, w, h);
 973         }
 974     }
 975 
 976     protected boolean scaleSurfaceData(SunGraphics2D sg,
 977                                        Region clipRegion,
 978                                        SurfaceData srcData,
 979                                        SurfaceData dstData,
 980                                        SurfaceType srcType,
 981                                        SurfaceType dstType,
 982                                        int sx1, int sy1,
 983                                        int sx2, int sy2,
 984                                        double dx1, double dy1,
 985                                        double dx2, double dy2)
 986     {
 987         CompositeType comp = sg.imageComp;
 988         if (CompositeType.SrcOverNoEa.equals(comp) &&
 989             (srcData.getTransparency() == Transparency.OPAQUE))
 990         {
 991             comp = CompositeType.SrcNoEa;
 992         }
 993 
 994         ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);
 995         if (blit != null) {
 996             blit.Scale(srcData, dstData, sg.composite, clipRegion,
 997                        sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
 998             return true;
 999         }
1000         return false;
1001     }
1002 
1003     protected static boolean imageReady(ToolkitImage sunimg,
1004                                         ImageObserver observer)
1005     {
1006         if (sunimg.hasError()) {
1007             if (observer != null) {
1008                 observer.imageUpdate(sunimg,
1009                                      ImageObserver.ERROR|ImageObserver.ABORT,
1010                                      -1, -1, -1, -1);
1011             }
1012             return false;
1013         }
1014         return true;
1015     }
1016 
1017     public boolean copyImage(SunGraphics2D sg, Image img,
1018                              int x, int y,
1019                              Color bgColor,
1020                              ImageObserver observer) {
1021         if (!(img instanceof ToolkitImage)) {
1022             return copyImage(sg, img, x, y, bgColor);
1023         } else {
1024             ToolkitImage sunimg = (ToolkitImage)img;
1025             if (!imageReady(sunimg, observer)) {
1026                 return false;
1027             }
1028             ImageRepresentation ir = sunimg.getImageRep();
1029             return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);
1030         }
1031     }
1032 
1033     public boolean copyImage(SunGraphics2D sg, Image img,
1034                              int dx, int dy, int sx, int sy, int w, int h,
1035                              Color bgColor,
1036                              ImageObserver observer) {
1037         if (!(img instanceof ToolkitImage)) {
1038             return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
1039         } else {
1040             ToolkitImage sunimg = (ToolkitImage)img;
1041             if (!imageReady(sunimg, observer)) {
1042                 return false;
1043             }
1044             ImageRepresentation ir = sunimg.getImageRep();
1045             return ir.drawToBufImage(sg, sunimg,
1046                                      dx, dy, (dx + w), (dy + h),
1047                                      sx, sy, (sx + w), (sy + h),
1048                                      bgColor, observer);
1049         }
1050     }
1051 
1052     public boolean scaleImage(SunGraphics2D sg, Image img,
1053                                 int x, int y,
1054                                 int width, int height,
1055                                 Color bgColor,
1056                                 ImageObserver observer) {
1057         if (!(img instanceof ToolkitImage)) {
1058             return scaleImage(sg, img, x, y, width, height, bgColor);
1059         } else {
1060             ToolkitImage sunimg = (ToolkitImage)img;
1061             if (!imageReady(sunimg, observer)) {
1062                 return false;
1063             }
1064             ImageRepresentation ir = sunimg.getImageRep();
1065             return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,
1066                                      observer);
1067         }
1068     }
1069 
1070     public boolean scaleImage(SunGraphics2D sg, Image img,
1071                               int dx1, int dy1, int dx2, int dy2,
1072                               int sx1, int sy1, int sx2, int sy2,
1073                               Color bgColor,
1074                               ImageObserver observer) {
1075         if (!(img instanceof ToolkitImage)) {
1076             return scaleImage(sg, img, dx1, dy1, dx2, dy2,
1077                               sx1, sy1, sx2, sy2, bgColor);
1078         } else {
1079             ToolkitImage sunimg = (ToolkitImage)img;
1080             if (!imageReady(sunimg, observer)) {
1081                 return false;
1082             }
1083             ImageRepresentation ir = sunimg.getImageRep();
1084             return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
1085                                      sx1, sy1, sx2, sy2, bgColor, observer);
1086         }
1087     }
1088 
1089     public boolean transformImage(SunGraphics2D sg, Image img,
1090                                   AffineTransform atfm,
1091                                   ImageObserver observer) {
1092         if (!(img instanceof ToolkitImage)) {
1093             transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
1094             return true;
1095         } else {
1096             ToolkitImage sunimg = (ToolkitImage)img;
1097             if (!imageReady(sunimg, observer)) {
1098                 return false;
1099             }
1100             ImageRepresentation ir = sunimg.getImageRep();
1101             return ir.drawToBufImage(sg, sunimg, atfm, observer);
1102         }
1103     }
1104 
1105     public void transformImage(SunGraphics2D sg, BufferedImage img,
1106                                BufferedImageOp op, int x, int y)
1107     {
1108         if (op != null) {
1109             if (op instanceof AffineTransformOp) {
1110                 AffineTransformOp atop = (AffineTransformOp) op;
1111                 transformImage(sg, img, x, y,
1112                                atop.getTransform(),
1113                                atop.getInterpolationType());
1114                 return;
1115             } else {
1116                 img = op.filter(img, null);
1117             }
1118         }
1119         copyImage(sg, img, x, y, null);
1120     }
1121 }
--- EOF ---