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