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