1 /*
   2  * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.pipe;
  27 
  28 import java.awt.AlphaComposite;
  29 import java.awt.Color;
  30 import java.awt.Graphics2D;
  31 import java.awt.Image;
  32 import java.awt.Rectangle;
  33 import java.awt.Transparency;
  34 import java.awt.geom.AffineTransform;
  35 import java.awt.geom.NoninvertibleTransformException;
  36 import java.awt.image.AffineTransformOp;
  37 import java.awt.image.BufferedImage;
  38 import java.awt.image.BufferedImageOp;
  39 import java.awt.image.ColorModel;
  40 import java.awt.image.DataBuffer;
  41 import java.awt.image.DirectColorModel;
  42 import java.awt.image.ImageObserver;
  43 import java.awt.image.IndexColorModel;
  44 import java.awt.image.Raster;
  45 import java.awt.image.VolatileImage;
  46 import java.awt.image.WritableRaster;
  47 import java.awt.image.ImagingOpException;
  48 import sun.awt.SunHints;
  49 import sun.awt.image.ImageRepresentation;
  50 import sun.awt.image.ToolkitImage;
  51 import sun.java2d.InvalidPipeException;
  52 import sun.java2d.SunGraphics2D;
  53 import sun.java2d.SurfaceData;
  54 import sun.java2d.loops.Blit;
  55 import sun.java2d.loops.BlitBg;
  56 import sun.java2d.loops.TransformHelper;
  57 import sun.java2d.loops.MaskBlit;
  58 import sun.java2d.loops.CompositeType;
  59 import sun.java2d.loops.ScaledBlit;
  60 import sun.java2d.loops.SurfaceType;
  61 
  62 public class DrawImage implements DrawImagePipe
  63 {
  64     public boolean copyImage(SunGraphics2D sg, Image img,
  65                              int x, int y,
  66                              Color bgColor)
  67     {
  68         int imgw = img.getWidth(null);
  69         int imgh = img.getHeight(null);
  70         if (isSimpleTranslate(sg)) {
  71             return renderImageCopy(sg, img, bgColor,
  72                                    x + sg.transX, y + sg.transY,
  73                                    0, 0, imgw, imgh);
  74         }
  75         AffineTransform atfm = sg.transform;
  76         if ((x | y) != 0) {
  77             atfm = new AffineTransform(atfm);
  78             atfm.translate(x, y);
  79         }
  80         transformImage(sg, img, atfm, sg.interpolationType,
  81                        0, 0, imgw, imgh, bgColor);
  82         return true;
  83     }
  84 
  85     public boolean copyImage(SunGraphics2D sg, Image img,
  86                              int dx, int dy, int sx, int sy, int w, int h,
  87                              Color bgColor)
  88     {
  89         if (isSimpleTranslate(sg)) {
  90             return renderImageCopy(sg, img, bgColor,
  91                                    dx + sg.transX, dy + sg.transY,
  92                                    sx, sy, w, h);
  93         }
  94         scaleImage(sg, img, dx, dy, (dx + w), (dy + h),
  95                    sx, sy, (sx + w), (sy + h), bgColor);
  96         return true;
  97     }
  98 
  99     public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,
 100                               int width, int height,
 101                               Color bgColor)
 102     {
 103         int imgw = img.getWidth(null);
 104         int imgh = img.getHeight(null);
 105         // Only accelerate scale if:
 106         //          - w/h positive values
 107         //          - sg transform integer translate/identity only
 108         //          - no bgColor in operation
 109         if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {
 110             double dx1 = x + sg.transX;
 111             double dy1 = y + sg.transY;
 112             double dx2 = dx1 + width;
 113             double dy2 = dy1 + height;
 114             if (renderImageScale(sg, img, bgColor, sg.interpolationType,
 115                                  0, 0, imgw, imgh,
 116                                  dx1, dy1, dx2, dy2))
 117             {
 118                 return true;
 119             }
 120         }
 121 
 122         AffineTransform atfm = sg.transform;
 123         if ((x | y) != 0 || width != imgw || height != imgh) {
 124             atfm = new AffineTransform(atfm);
 125             atfm.translate(x, y);
 126             atfm.scale(((double)width)/imgw, ((double)height)/imgh);
 127         }
 128         transformImage(sg, img, atfm, sg.interpolationType,
 129                        0, 0, imgw, imgh, bgColor);
 130         return true;
 131     }
 132 
 133     /*
 134      * This method is only called in those circumstances where the
 135      * operation has a non-null secondary transform specfied.  Its
 136      * role is to check for various optimizations based on the types
 137      * of both the secondary and SG2D transforms and to do some
 138      * quick calculations to avoid having to combine the transforms
 139      * and/or to call a more generalized method.
 140      */
 141     protected void transformImage(SunGraphics2D sg, Image img, int x, int y,
 142                                   AffineTransform extraAT, int interpType)
 143     {
 144         int txtype = extraAT.getType();
 145         int imgw = img.getWidth(null);
 146         int imgh = img.getHeight(null);
 147         boolean checkfinalxform;
 148 
 149         if (sg.transformState <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE &&
 150             (txtype == AffineTransform.TYPE_IDENTITY ||
 151              txtype == AffineTransform.TYPE_TRANSLATION))
 152         {
 153             // First optimization - both are some kind of translate
 154 
 155             // Combine the translations and check if interpolation is necessary.
 156             double tx = extraAT.getTranslateX();
 157             double ty = extraAT.getTranslateY();
 158             tx += sg.transform.getTranslateX();
 159             ty += sg.transform.getTranslateY();
 160             int itx = (int) Math.floor(tx + 0.5);
 161             int ity = (int) Math.floor(ty + 0.5);
 162             if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
 163                 (closeToInteger(itx, tx) && closeToInteger(ity, ty)))
 164             {
 165                 renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);
 166                 return;
 167             }
 168             checkfinalxform = false;
 169         } else if (sg.transformState <= SunGraphics2D.TRANSFORM_TRANSLATESCALE &&
 170                    ((txtype & (AffineTransform.TYPE_FLIP |
 171                                AffineTransform.TYPE_MASK_ROTATION |
 172                                AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))
 173         {
 174             // Second optimization - both are some kind of translate or scale
 175 
 176             // Combine the scales and check if interpolation is necessary.
 177 
 178             // Transform source bounds by extraAT,
 179             // then translate the bounds again by x, y
 180             // then transform the bounds again by sg.transform
 181             double coords[] = new double[] {
 182                 0, 0, imgw, imgh,
 183             };
 184             extraAT.transform(coords, 0, coords, 0, 2);
 185             coords[0] += x;
 186             coords[1] += y;
 187             coords[2] += x;
 188             coords[3] += y;
 189             sg.transform.transform(coords, 0, coords, 0, 2);
 190 
 191             if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,
 192                                null, interpType, coords))
 193             {
 194                 return;
 195             }
 196             checkfinalxform = false;
 197         } else {
 198             checkfinalxform = true;
 199         }
 200 
 201         // Begin Transform
 202         AffineTransform tx = new AffineTransform(sg.transform);
 203         tx.translate(x, y);
 204         tx.concatenate(extraAT);
 205 
 206         // Do not try any more optimizations if either of the cases
 207         // above was tried as we have already verified that the
 208         // resulting transform will not simplify.
 209         if (checkfinalxform) {
 210             // In this case neither of the above simple transform
 211             // pairs was found so we will do some final tests on
 212             // the final rendering transform which may be the
 213             // simple product of two complex transforms.
 214             transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
 215         } else {
 216             renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
 217         }
 218     }
 219 
 220     /*
 221      * This method is called with a final rendering transform that
 222      * has combined all of the information about the Graphics2D
 223      * transform attribute with the transformations specified by
 224      * the arguments to the drawImage call.
 225      * Its role is to see if the combined transform ends up being
 226      * acceleratable by either a renderImageCopy or renderImageScale
 227      * once all of the math is done.
 228      *
 229      * Note: The transform supplied here has an origin that is
 230      * already adjusted to point to the device location where
 231      * the (sx1, sy1) location of the source image should be placed.
 232      */
 233     protected void transformImage(SunGraphics2D sg, Image img,
 234                                   AffineTransform tx, int interpType,
 235                                   int sx1, int sy1, int sx2, int sy2,
 236                                   Color bgColor)
 237     {
 238         // Transform 3 source corners by tx and analyze them
 239         // for simplified operations (Copy or Scale).  Using
 240         // 3 points lets us analyze any kind of transform,
 241         // even transforms that involve very tiny amounts of
 242         // rotation or skew to see if they degenerate to a
 243         // simple scale or copy operation within the allowable
 244         // error bounds.
 245         // Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)
 246         // because the transform is already translated such that
 247         // the origin is where sx1, sy1 should go.
 248         double coords[] = new double[6];
 249         /* index:  0  1    2  3    4  5  */
 250         /* coord: (0, 0), (w, h), (0, h) */
 251         coords[2] = sx2 - sx1;
 252         coords[3] = coords[5] = sy2 - sy1;
 253         tx.transform(coords, 0, coords, 0, 3);
 254         // First test if the X coords of the transformed UL
 255         // and LL points match and that the Y coords of the
 256         // transformed LR and LL points also match.
 257         // If they do then it is a "rectilinear" transform and
 258         // tryCopyOrScale will make sure it is upright and
 259         // integer-based.
 260         if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&
 261             Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&
 262             tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,
 263                            bgColor, interpType, coords))
 264         {
 265             return;
 266         }
 267 
 268         renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);
 269     }
 270 
 271     /*
 272      * Check the bounding coordinates of the transformed source
 273      * image to see if they fall on integer coordinates such
 274      * that they will cause no interpolation anomalies if we
 275      * use our simplified Blit or ScaledBlit operations instead
 276      * of a full transform operation.
 277      */
 278     protected boolean tryCopyOrScale(SunGraphics2D sg,
 279                                      Image img,
 280                                      int sx1, int sy1,
 281                                      int sx2, int sy2,
 282                                      Color bgColor, int interpType,
 283                                      double coords[])
 284     {
 285         double dx = coords[0];
 286         double dy = coords[1];
 287         double dw = coords[2] - dx;
 288         double dh = coords[3] - dy;
 289         // First check if width and height are very close to img w&h.
 290         if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {
 291             // Round location to nearest pixel and then test
 292             // if it will cause interpolation anomalies.
 293             int idx = (int) Math.floor(dx + 0.5);
 294             int idy = (int) Math.floor(dy + 0.5);
 295             if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
 296                 (closeToInteger(idx, dx) && closeToInteger(idy, dy)))
 297             {
 298                 renderImageCopy(sg, img, bgColor,
 299                                 idx, idy,
 300                                 sx1, sy1, sx2-sx1, sy2-sy1);
 301                 return true;
 302             }
 303         }
 304         // (For now) We can only use our ScaledBlits if the image
 305         // is upright (i.e. dw & dh both > 0)
 306         if (dw > 0 && dh > 0) {
 307             if (renderImageScale(sg, img, bgColor, interpType,
 308                                  sx1, sy1, sx2, sy2,
 309                                  coords[0], coords[1], coords[2], coords[3]))
 310             {
 311                 return true;
 312             }
 313         }
 314         return false;
 315     }
 316 
 317     /*
 318      * Return a BufferedImage of the requested type with the indicated
 319      * subimage of the original image located at 0,0 in the new image.
 320      * If a bgColor is supplied, composite the original image over that
 321      * color with a SrcOver operation, otherwise make a SrcNoEa copy.
 322      */
 323     BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
 324                                     int sx1, int sy1, int sx2, int sy2)
 325     {
 326         BufferedImage bimg = new BufferedImage(sx2-sx1, sy2-sy1, type);
 327         Graphics2D g2d = bimg.createGraphics();
 328         g2d.setComposite(AlphaComposite.Src);
 329         if (bgColor != null) {
 330             g2d.setColor(bgColor);
 331             g2d.fillRect(0, 0, sx2-sx1, sy2-sy1);
 332             g2d.setComposite(AlphaComposite.SrcOver);
 333         }
 334         g2d.drawImage(img, -sx1, -sy1, null);
 335         g2d.dispose();
 336         return bimg;
 337     }
 338 
 339     protected void renderImageXform(SunGraphics2D sg, Image img,
 340                                     AffineTransform tx, int interpType,
 341                                     int sx1, int sy1, int sx2, int sy2,
 342                                     Color bgColor)
 343     {
 344         Region clip = sg.getCompClip();
 345         SurfaceData dstData = sg.surfaceData;
 346         SurfaceData srcData = dstData.getSourceSurfaceData(img,
 347                                                            SunGraphics2D.TRANSFORM_GENERIC,
 348                                                            sg.imageComp,
 349                                                            bgColor);
 350 
 351         if (srcData == null) {
 352             img = getBufferedImage(img);
 353             srcData = dstData.getSourceSurfaceData(img,
 354                                                    SunGraphics2D.TRANSFORM_GENERIC,
 355                                                    sg.imageComp,
 356                                                    bgColor);
 357             if (srcData == null) {
 358                 // REMIND: Is this correct?  Can this happen?
 359                 return;
 360             }
 361         }
 362 
 363         if (isBgOperation(srcData, bgColor)) {
 364             // We cannot perform bg operations during transform so make
 365             // an opaque temp image with the appropriate background
 366             // and work from there.
 367             img = makeBufferedImage(img, bgColor, BufferedImage.TYPE_INT_RGB,
 368                                     sx1, sy1, sx2, sy2);
 369             // Temp image has appropriate subimage at 0,0 now.
 370             sx2 -= sx1;
 371             sy2 -= sy1;
 372             sx1 = sy1 = 0;
 373 
 374             srcData = dstData.getSourceSurfaceData(img,
 375                                                    SunGraphics2D.TRANSFORM_GENERIC,
 376                                                    sg.imageComp,
 377                                                    bgColor);
 378         }
 379 
 380         SurfaceType srcType = srcData.getSurfaceType();
 381         TransformHelper helper = TransformHelper.getFromCache(srcType);
 382 
 383         if (helper == null) {
 384             /* We have no helper for this source image type.
 385              * But we know that we do have helpers for both RGB and ARGB,
 386              * so convert to one of those types depending on transparency.
 387              * ARGB_PRE might be a better choice if the source image has
 388              * alpha, but it may cause some recursion here since we only
 389              * tend to have converters that convert to ARGB.
 390              */
 391             int type = ((srcData.getTransparency() == Transparency.OPAQUE)
 392                         ? BufferedImage.TYPE_INT_RGB
 393                         : BufferedImage.TYPE_INT_ARGB);
 394             img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);
 395             // Temp image has appropriate subimage at 0,0 now.
 396             sx2 -= sx1;
 397             sy2 -= sy1;
 398             sx1 = sy1 = 0;
 399 
 400             srcData = dstData.getSourceSurfaceData(img,
 401                                                    SunGraphics2D.TRANSFORM_GENERIC,
 402                                                    sg.imageComp,
 403                                                    null);
 404             srcType = srcData.getSurfaceType();
 405             helper = TransformHelper.getFromCache(srcType);
 406             // assert(helper != null);
 407         }
 408 
 409         AffineTransform itx;
 410         try {
 411             itx = tx.createInverse();
 412         } catch (NoninvertibleTransformException e) {
 413             // Non-invertible transform means no output
 414             return;
 415         }
 416 
 417         /*
 418          * Find the maximum bounds on the destination that will be
 419          * affected by the transformed source.  First, transform all
 420          * four corners of the source and then min and max the resulting
 421          * destination coordinates of the transformed corners.
 422          * Note that tx already has the offset to sx1,sy1 accounted
 423          * for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the
 424          * source coordinates.
 425          */
 426         double coords[] = new double[8];
 427         /* corner:  UL      UR      LL      LR   */
 428         /* index:  0  1    2  3    4  5    6  7  */
 429         /* coord: (0, 0), (w, 0), (0, h), (w, h) */
 430         coords[2] = coords[6] = sx2 - sx1;
 431         coords[5] = coords[7] = sy2 - sy1;
 432         tx.transform(coords, 0, coords, 0, 4);
 433         double ddx1, ddy1, ddx2, ddy2;
 434         ddx1 = ddx2 = coords[0];
 435         ddy1 = ddy2 = coords[1];
 436         for (int i = 2; i < coords.length; i += 2) {
 437             double d = coords[i];
 438             if (ddx1 > d) ddx1 = d;
 439             else if (ddx2 < d) ddx2 = d;
 440             d = coords[i+1];
 441             if (ddy1 > d) ddy1 = d;
 442             else if (ddy2 < d) ddy2 = d;
 443         }
 444         int dx1 = (int) Math.floor(ddx1);
 445         int dy1 = (int) Math.floor(ddy1);
 446         int dx2 = (int) Math.ceil(ddx2);
 447         int dy2 = (int) Math.ceil(ddy2);
 448 
 449         SurfaceType dstType = dstData.getSurfaceType();
 450         MaskBlit maskblit;
 451         Blit blit;
 452         if (sg.compositeState <= SunGraphics2D.COMP_ALPHA) {
 453             /* NOTE: We either have, or we can make,
 454              * a MaskBlit for any alpha composite type
 455              */
 456             maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
 457                                              sg.imageComp,
 458                                              dstType);
 459 
 460             /* NOTE: We can only use the native TransformHelper
 461              * func to go directly to the dest if both the helper
 462              * and the MaskBlit are native.
 463              * All helpers are native at this point, but some MaskBlit
 464              * objects are implemented in Java, so we need to check.
 465              */
 466             if (maskblit.getNativePrim() != 0) {
 467                 // We can render directly.
 468                 helper.Transform(maskblit, srcData, dstData,
 469                                  sg.composite, clip,
 470                                  itx, interpType,
 471                                  sx1, sy1, sx2, sy2,
 472                                  dx1, dy1, dx2, dy2,
 473                                  null, 0, 0);
 474                 return;
 475             }
 476             blit = null;
 477         } else {
 478             /* NOTE: We either have, or we can make,
 479              * a Blit for any composite type, even Custom
 480              */
 481             maskblit = null;
 482             blit = Blit.getFromCache(SurfaceType.IntArgbPre,
 483                                      sg.imageComp,
 484                                      dstType);
 485         }
 486 
 487         // We need to transform to a temp image and then copy
 488         // just the pieces that are valid data to the dest.
 489         BufferedImage tmpimg = new BufferedImage(dx2-dx1, dy2-dy1,
 490                                                  BufferedImage.TYPE_INT_ARGB);
 491         SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);
 492         SurfaceType tmpType = tmpData.getSurfaceType();
 493         MaskBlit tmpmaskblit =
 494             MaskBlit.getFromCache(SurfaceType.IntArgbPre,
 495                                   CompositeType.SrcNoEa,
 496                                   tmpType);
 497         /*
 498          * The helper function fills a temporary edges buffer
 499          * for us with the bounding coordinates of each scanline
 500          * in the following format:
 501          *
 502          * edges[0, 1] = [top y, bottom y)
 503          * edges[2, 3] = [left x, right x) of top row
 504          * ...
 505          * edges[h*2, h*2+1] = [left x, right x) of bottom row
 506          *
 507          * all coordinates in the edges array will be relative to dx1, dy1
 508          *
 509          * edges thus has to be h*2+2 in length
 510          */
 511         int edges[] = new int[(dy2-dy1)*2+2];
 512         // It is important that edges[0]=edges[1]=0 when we call
 513         // Transform in case it must return early and we would
 514         // not want to render anything on an error condition.
 515         helper.Transform(tmpmaskblit, srcData, tmpData,
 516                          AlphaComposite.Src, null,
 517                          itx, interpType,
 518                          sx1, sy1, sx2, sy2,
 519                          0, 0, dx2-dx1, dy2-dy1,
 520                          edges, dx1, dy1);
 521 
 522         /*
 523          * Now copy the results, scanline by scanline, into the dest.
 524          * The edges array helps us minimize the work.
 525          */
 526         int index = 2;
 527         for (int y = edges[0]; y < edges[1]; y++) {
 528             int relx1 = edges[index++];
 529             int relx2 = edges[index++];
 530             if (relx1 >= relx2) {
 531                 continue;
 532             }
 533             if (maskblit != null) {
 534                 maskblit.MaskBlit(tmpData, dstData,
 535                                   sg.composite, clip,
 536                                   relx1, y,
 537                                   dx1+relx1, dy1+y,
 538                                   relx2 - relx1, 1,
 539                                   null, 0, 0);
 540             } else {
 541                 blit.Blit(tmpData, dstData,
 542                           sg.composite, clip,
 543                           relx1, y,
 544                           dx1+relx1, dy1+y,
 545                           relx2 - relx1, 1);
 546             }
 547         }
 548     }
 549 
 550     // Render an image using only integer translation
 551     // (no scale or transform or sub-pixel interpolated translations).
 552     protected boolean renderImageCopy(SunGraphics2D sg, Image img,
 553                                       Color bgColor,
 554                                       int dx, int dy,
 555                                       int sx, int sy,
 556                                       int w, int h)
 557     {
 558         Region clip = sg.getCompClip();
 559         SurfaceData dstData = sg.surfaceData;
 560 
 561         int attempts = 0;
 562         // Loop up to twice through; this gives us a chance to
 563         // revalidate the surfaceData objects in case of an exception
 564         // and try it once more
 565         while (true) {
 566             SurfaceData srcData =
 567                 dstData.getSourceSurfaceData(img,
 568                                              SunGraphics2D.TRANSFORM_ISIDENT,
 569                                              sg.imageComp,
 570                                              bgColor);
 571             if (srcData == null) {
 572                 return false;
 573             }
 574 
 575             try {
 576                 SurfaceType srcType = srcData.getSurfaceType();
 577                 SurfaceType dstType = dstData.getSurfaceType();
 578                 blitSurfaceData(sg, clip,
 579                                 srcData, dstData, srcType, dstType,
 580                                 sx, sy, dx, dy, w, h, bgColor);
 581                 return true;
 582             } catch (NullPointerException e) {
 583                 if (!(SurfaceData.isNull(dstData) ||
 584                       SurfaceData.isNull(srcData)))
 585                 {
 586                     // Something else caused the exception, throw it...
 587                     throw e;
 588                 }
 589                 return false;
 590                 // NOP if we have been disposed
 591             } catch (InvalidPipeException e) {
 592                 // Always catch the exception; try this a couple of times
 593                 // and fail silently if the system is not yet ready to
 594                 // revalidate the source or dest surfaceData objects.
 595                 ++attempts;
 596                 clip = sg.getCompClip();   // ensures sg.surfaceData is valid
 597                 dstData = sg.surfaceData;
 598                 if (SurfaceData.isNull(dstData) ||
 599                     SurfaceData.isNull(srcData) || (attempts > 1))
 600                 {
 601                     return false;
 602                 }
 603             }
 604         }
 605     }
 606 
 607     // Render an image using only integer scaling (no transform).
 608     protected boolean renderImageScale(SunGraphics2D sg, Image img,
 609                                        Color bgColor, int interpType,
 610                                        int sx1, int sy1,
 611                                        int sx2, int sy2,
 612                                        double dx1, double dy1,
 613                                        double dx2, double dy2)
 614     {
 615         // Currently only NEAREST_NEIGHBOR interpolation is implemented
 616         // for ScaledBlit operations.
 617         if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
 618             return false;
 619         }
 620 
 621         Region clip = sg.getCompClip();
 622         SurfaceData dstData = sg.surfaceData;
 623 
 624         int attempts = 0;
 625         // Loop up to twice through; this gives us a chance to
 626         // revalidate the surfaceData objects in case of an exception
 627         // and try it once more
 628         while (true) {
 629             SurfaceData srcData =
 630                 dstData.getSourceSurfaceData(img,
 631                                              SunGraphics2D.TRANSFORM_TRANSLATESCALE,
 632                                              sg.imageComp,
 633                                              bgColor);
 634 
 635             if (srcData == null || isBgOperation(srcData, bgColor)) {
 636                 return false;
 637             }
 638 
 639             try {
 640                 SurfaceType srcType = srcData.getSurfaceType();
 641                 SurfaceType dstType = dstData.getSurfaceType();
 642                 return scaleSurfaceData(sg, clip,
 643                                         srcData, dstData, srcType, dstType,
 644                                         sx1, sy1, sx2, sy2,
 645                                         dx1, dy1, dx2, dy2);
 646             } catch (NullPointerException e) {
 647                 if (!SurfaceData.isNull(dstData)) {
 648                     // Something else caused the exception, throw it...
 649                     throw e;
 650                 }
 651                 return false;
 652                 // NOP if we have been disposed
 653             } catch (InvalidPipeException e) {
 654                 // Always catch the exception; try this a couple of times
 655                 // and fail silently if the system is not yet ready to
 656                 // revalidate the source or dest surfaceData objects.
 657                 ++attempts;
 658                 clip = sg.getCompClip();  // ensures sg.surfaceData is valid
 659                 dstData = sg.surfaceData;
 660                 if (SurfaceData.isNull(dstData) ||
 661                     SurfaceData.isNull(srcData) || (attempts > 1))
 662                 {
 663                     return false;
 664                 }
 665             }
 666         }
 667     }
 668 
 669     public boolean scaleImage(SunGraphics2D sg, Image img,
 670                               int dx1, int dy1, int dx2, int dy2,
 671                               int sx1, int sy1, int sx2, int sy2,
 672                               Color bgColor)
 673     {
 674         int srcW, srcH, dstW, dstH;
 675         int srcX, srcY, dstX, dstY;
 676         boolean srcWidthFlip = false;
 677         boolean srcHeightFlip = false;
 678         boolean dstWidthFlip = false;
 679         boolean dstHeightFlip = false;
 680 
 681         if (sx2 > sx1) {
 682             srcW = sx2 - sx1;
 683             srcX = sx1;
 684         } else {
 685             srcWidthFlip = true;
 686             srcW = sx1 - sx2;
 687             srcX = sx2;
 688         }
 689         if (sy2 > sy1) {
 690             srcH = sy2-sy1;
 691             srcY = sy1;
 692         } else {
 693             srcHeightFlip = true;
 694             srcH = sy1-sy2;
 695             srcY = sy2;
 696         }
 697         if (dx2 > dx1) {
 698             dstW = dx2 - dx1;
 699             dstX = dx1;
 700         } else {
 701             dstW = dx1 - dx2;
 702             dstWidthFlip = true;
 703             dstX = dx2;
 704         }
 705         if (dy2 > dy1) {
 706             dstH = dy2 - dy1;
 707             dstY = dy1;
 708         } else {
 709             dstH = dy1 - dy2;
 710             dstHeightFlip = true;
 711             dstY = dy2;
 712         }
 713         if (srcW <= 0 || srcH <= 0) {
 714             return true;
 715         }
 716         // Only accelerate scale if it does not involve a flip or transform
 717         if ((srcWidthFlip == dstWidthFlip) &&
 718             (srcHeightFlip == dstHeightFlip) &&
 719             isSimpleTranslate(sg))
 720         {
 721             double ddx1 = dstX + sg.transX;
 722             double ddy1 = dstY + sg.transY;
 723             double ddx2 = ddx1 + dstW;
 724             double ddy2 = ddy1 + dstH;
 725             if (renderImageScale(sg, img, bgColor, sg.interpolationType,
 726                                  srcX, srcY, srcX+srcW, srcY+srcH,
 727                                  ddx1, ddy1, ddx2, ddy2))
 728             {
 729                 return true;
 730             }
 731         }
 732 
 733         AffineTransform atfm = new AffineTransform(sg.transform);
 734         atfm.translate(dx1, dy1);
 735         double m00 = (double)(dx2-dx1)/(sx2-sx1);
 736         double m11 = (double)(dy2-dy1)/(sy2-sy1);
 737         atfm.scale(m00, m11);
 738         atfm.translate(srcX-sx1, srcY-sy1);
 739 
 740         int imgW = img.getWidth(null);
 741         int imgH = img.getHeight(null);
 742         srcW += srcX;
 743         srcH += srcY;
 744         // Make sure we are not out of bounds
 745         if (srcW > imgW) {
 746             srcW = imgW;
 747         }
 748         if (srcH > imgH) {
 749             srcH = imgH;
 750         }
 751         if (srcX < 0) {
 752             atfm.translate(-srcX, 0);
 753             srcX = 0;
 754         }
 755         if (srcY < 0) {
 756             atfm.translate(0, -srcY);
 757             srcY = 0;
 758         }
 759         if (srcX >= srcW || srcY >= srcH) {
 760             return true;
 761         }
 762         // Note: src[WH] are currently the right and bottom coordinates.
 763         // The following two lines would adjust src[WH] back to being
 764         // dimensions.
 765         //     srcW -= srcX;
 766         //     srcH -= srcY;
 767         // Since transformImage needs right and bottom coords we will
 768         // omit this adjustment.
 769 
 770         transformImage(sg, img, atfm, sg.interpolationType,
 771                        srcX, srcY, srcW, srcH, bgColor);
 772         return true;
 773     }
 774 
 775     /**
 776      ** Utilities
 777      ** The following methods are used by the public methods above
 778      ** for performing various operations
 779      **/
 780 
 781     /*
 782      * This constant represents a tradeoff between the
 783      * need to make sure that image transformations are
 784      * "very close" to integer device coordinates before
 785      * we decide to use an integer scale or copy operation
 786      * as a substitute and the fact that roundoff errors
 787      * in AffineTransforms are frequently introduced by
 788      * performing multiple sequential operations on them.
 789      *
 790      * The evaluation of bug 4990624 details the potential
 791      * for this error cutoff to result in display anomalies
 792      * in different types of image operations and how this
 793      * value represents a good compromise here.
 794      */
 795     private static final double MAX_TX_ERROR = .0001;
 796 
 797     public static boolean closeToInteger(int i, double d) {
 798         return (Math.abs(d-i) < MAX_TX_ERROR);
 799     }
 800 
 801     public static boolean isSimpleTranslate(SunGraphics2D sg) {
 802         int ts = sg.transformState;
 803         if (ts <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
 804             // Integer translates are always "simple"
 805             return true;
 806         }
 807         if (ts >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {
 808             // Scales and beyond are always "not simple"
 809             return false;
 810         }
 811         // non-integer translates are only simple when not interpolating
 812         if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
 813             return true;
 814         }
 815         return false;
 816     }
 817 
 818     protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {
 819         // If we cannot get the srcData, then cannot assume anything about
 820         // the image
 821         return ((srcData == null) ||
 822                 ((bgColor != null) &&
 823                  (srcData.getTransparency() != Transparency.OPAQUE)));
 824     }
 825 
 826     protected BufferedImage getBufferedImage(Image img) {
 827         if (img instanceof BufferedImage) {
 828             return (BufferedImage)img;
 829         }
 830         // Must be VolatileImage; get BufferedImage representation
 831         return ((VolatileImage)img).getSnapshot();
 832     }
 833 
 834     /*
 835      * Return the color model to be used with this BufferedImage and
 836      * transform.
 837      */
 838     private ColorModel getTransformColorModel(SunGraphics2D sg,
 839                                               BufferedImage bImg,
 840                                               AffineTransform tx) {
 841         ColorModel cm = bImg.getColorModel();
 842         ColorModel dstCM = cm;
 843 
 844         if (tx.isIdentity()) {
 845             return dstCM;
 846         }
 847         int type = tx.getType();
 848         boolean needTrans =
 849                 ((type & (AffineTransform.TYPE_MASK_ROTATION |
 850                           AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0);
 851         if (! needTrans &&
 852               type != AffineTransform.TYPE_TRANSLATION &&
 853               type != AffineTransform.TYPE_IDENTITY)
 854         {
 855             double[] mtx = new double[4];
 856             tx.getMatrix(mtx);
 857             // Check out the matrix.  A non-integral scale will force ARGB
 858             // since the edge conditions cannot be guaranteed.
 859             needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
 860         }
 861 
 862         if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
 863             if (cm instanceof IndexColorModel) {
 864                 Raster raster = bImg.getRaster();
 865                 IndexColorModel icm = (IndexColorModel) cm;
 866                 // Just need to make sure that we have a transparent pixel
 867                 if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
 868                     // Fix 4221407
 869                     if (raster instanceof sun.awt.image.BytePackedRaster) {
 870                         dstCM = ColorModel.getRGBdefault();
 871                     }
 872                     else {
 873                         double[] matrix = new double[6];
 874                         tx.getMatrix(matrix);
 875                         if (matrix[1] == 0. && matrix[2] ==0.
 876                             && matrix[4] == 0. && matrix[5] == 0.) {
 877                             // Only scaling so do not need to create
 878                         }
 879                         else {
 880                             int mapSize = icm.getMapSize();
 881                             if (mapSize < 256) {
 882                                 int[] cmap = new int[mapSize+1];
 883                                 icm.getRGBs(cmap);
 884                                 cmap[mapSize] = 0x0000;
 885                                 dstCM = new
 886                                     IndexColorModel(icm.getPixelSize(),
 887                                                     mapSize+1,
 888                                                     cmap, 0, true, mapSize,
 889                                                     DataBuffer.TYPE_BYTE);
 890                             }
 891                             else {
 892                                 dstCM = ColorModel.getRGBdefault();
 893                             }
 894                         }  /* if (matrix[0] < 1.f ...) */
 895                     }   /* raster instanceof sun.awt.image.BytePackedRaster */
 896                 } /* if (cm.getTransparency() == cm.OPAQUE) */
 897             } /* if (cm instanceof IndexColorModel) */
 898             else if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {
 899                 // Need a bitmask transparency
 900                 // REMIND: for now, use full transparency since no loops
 901                 // for bitmask
 902                 dstCM = ColorModel.getRGBdefault();
 903             }
 904         } /* if (sg.renderHint == RENDER_QUALITY) */
 905         else {
 906 
 907             if (cm instanceof IndexColorModel ||
 908                 (needTrans && cm.getTransparency() == Transparency.OPAQUE))
 909             {
 910                 // Need a bitmask transparency
 911                 // REMIND: for now, use full transparency since no loops
 912                 // for bitmask
 913                 dstCM = ColorModel.getRGBdefault();
 914             }
 915         }
 916 
 917         return dstCM;
 918     }
 919 
 920     protected void blitSurfaceData(SunGraphics2D sg,
 921                                    Region clipRegion,
 922                                    SurfaceData srcData,
 923                                    SurfaceData dstData,
 924                                    SurfaceType srcType,
 925                                    SurfaceType dstType,
 926                                    int sx, int sy, int dx, int dy,
 927                                    int w, int h,
 928                                    Color bgColor)
 929     {
 930         if (w <= 0 || h <= 0) {
 931             /*
 932              * Fix for bugid 4783274 - BlitBg throws an exception for
 933              * a particular set of anomalous parameters.
 934              * REMIND: The native loops do proper clipping and would
 935              * detect this situation themselves, but the Java loops
 936              * all seem to trust their parameters a little too well
 937              * to the point where they will try to process a negative
 938              * area of pixels and throw exceptions.  The real fix is
 939              * to modify the Java loops to do proper clipping so that
 940              * they can deal with negative dimensions as well as
 941              * improperly large dimensions, but that fix is too risky
 942              * to integrate for Mantis at this point.  In the meantime
 943              * eliminating the negative or zero dimensions here is
 944              * "correct" and saves them from some nasty exceptional
 945              * conditions, one of which is the test case of 4783274.
 946              */
 947             return;
 948         }
 949         CompositeType comp = sg.imageComp;
 950         if (CompositeType.SrcOverNoEa.equals(comp) &&
 951             (srcData.getTransparency() == Transparency.OPAQUE ||
 952              (bgColor != null &&
 953               bgColor.getTransparency() == Transparency.OPAQUE)))
 954         {
 955             comp = CompositeType.SrcNoEa;
 956         }
 957         if (!isBgOperation(srcData, bgColor)) {
 958             Blit blit = Blit.getFromCache(srcType, comp, dstType);
 959             blit.Blit(srcData, dstData, sg.composite, clipRegion,
 960                       sx, sy, dx, dy, w, h);
 961         } else {
 962             BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
 963             blit.BlitBg(srcData, dstData, sg.composite, clipRegion,
 964                         bgColor.getRGB(), sx, sy, dx, dy, w, h);
 965         }
 966     }
 967 
 968     protected boolean scaleSurfaceData(SunGraphics2D sg,
 969                                        Region clipRegion,
 970                                        SurfaceData srcData,
 971                                        SurfaceData dstData,
 972                                        SurfaceType srcType,
 973                                        SurfaceType dstType,
 974                                        int sx1, int sy1,
 975                                        int sx2, int sy2,
 976                                        double dx1, double dy1,
 977                                        double dx2, double dy2)
 978     {
 979         CompositeType comp = sg.imageComp;
 980         if (CompositeType.SrcOverNoEa.equals(comp) &&
 981             (srcData.getTransparency() == Transparency.OPAQUE))
 982         {
 983             comp = CompositeType.SrcNoEa;
 984         }
 985 
 986         ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);
 987         if (blit != null) {
 988             blit.Scale(srcData, dstData, sg.composite, clipRegion,
 989                        sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
 990             return true;
 991         }
 992         return false;
 993     }
 994 
 995     protected static boolean imageReady(ToolkitImage sunimg,
 996                                         ImageObserver observer)
 997     {
 998         if (sunimg.hasError()) {
 999             if (observer != null) {
1000                 observer.imageUpdate(sunimg,
1001                                      ImageObserver.ERROR|ImageObserver.ABORT,
1002                                      -1, -1, -1, -1);
1003             }
1004             return false;
1005         }
1006         return true;
1007     }
1008 
1009     public boolean copyImage(SunGraphics2D sg, Image img,
1010                              int x, int y,
1011                              Color bgColor,
1012                              ImageObserver observer) {
1013         if (!(img instanceof ToolkitImage)) {
1014             return copyImage(sg, img, x, y, bgColor);
1015         } else {
1016             ToolkitImage sunimg = (ToolkitImage)img;
1017             if (!imageReady(sunimg, observer)) {
1018                 return false;
1019             }
1020             ImageRepresentation ir = sunimg.getImageRep();
1021             return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);
1022         }
1023     }
1024 
1025     public boolean copyImage(SunGraphics2D sg, Image img,
1026                              int dx, int dy, int sx, int sy, int w, int h,
1027                              Color bgColor,
1028                              ImageObserver observer) {
1029         if (!(img instanceof ToolkitImage)) {
1030             return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
1031         } else {
1032             ToolkitImage sunimg = (ToolkitImage)img;
1033             if (!imageReady(sunimg, observer)) {
1034                 return false;
1035             }
1036             ImageRepresentation ir = sunimg.getImageRep();
1037             return ir.drawToBufImage(sg, sunimg,
1038                                      dx, dy, (dx + w), (dy + h),
1039                                      sx, sy, (sx + w), (sy + h),
1040                                      bgColor, observer);
1041         }
1042     }
1043 
1044     public boolean scaleImage(SunGraphics2D sg, Image img,
1045                                 int x, int y,
1046                                 int width, int height,
1047                                 Color bgColor,
1048                                 ImageObserver observer) {
1049         if (!(img instanceof ToolkitImage)) {
1050             return scaleImage(sg, img, x, y, width, height, bgColor);
1051         } else {
1052             ToolkitImage sunimg = (ToolkitImage)img;
1053             if (!imageReady(sunimg, observer)) {
1054                 return false;
1055             }
1056             ImageRepresentation ir = sunimg.getImageRep();
1057             return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,
1058                                      observer);
1059         }
1060     }
1061 
1062     public boolean scaleImage(SunGraphics2D sg, Image img,
1063                               int dx1, int dy1, int dx2, int dy2,
1064                               int sx1, int sy1, int sx2, int sy2,
1065                               Color bgColor,
1066                               ImageObserver observer) {
1067         if (!(img instanceof ToolkitImage)) {
1068             return scaleImage(sg, img, dx1, dy1, dx2, dy2,
1069                               sx1, sy1, sx2, sy2, bgColor);
1070         } else {
1071             ToolkitImage sunimg = (ToolkitImage)img;
1072             if (!imageReady(sunimg, observer)) {
1073                 return false;
1074             }
1075             ImageRepresentation ir = sunimg.getImageRep();
1076             return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
1077                                      sx1, sy1, sx2, sy2, bgColor, observer);
1078         }
1079     }
1080 
1081     public boolean transformImage(SunGraphics2D sg, Image img,
1082                                   AffineTransform atfm,
1083                                   ImageObserver observer) {
1084         if (!(img instanceof ToolkitImage)) {
1085             transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
1086             return true;
1087         } else {
1088             ToolkitImage sunimg = (ToolkitImage)img;
1089             if (!imageReady(sunimg, observer)) {
1090                 return false;
1091             }
1092             ImageRepresentation ir = sunimg.getImageRep();
1093             return ir.drawToBufImage(sg, sunimg, atfm, observer);
1094         }
1095     }
1096 
1097     public void transformImage(SunGraphics2D sg, BufferedImage img,
1098                                BufferedImageOp op, int x, int y)
1099     {
1100         if (op != null) {
1101             if (op instanceof AffineTransformOp) {
1102                 AffineTransformOp atop = (AffineTransformOp) op;
1103                 transformImage(sg, img, x, y,
1104                                atop.getTransform(),
1105                                atop.getInterpolationType());
1106                 return;
1107             } else {
1108                 img = op.filter(img, null);
1109             }
1110         }
1111         copyImage(sg, img, x, y, null);
1112     }
1113 }