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 }