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 <= sg.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 <= sg.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);
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 sg.TRANSFORM_GENERIC,
348 sg.imageComp,
349 bgColor);
350
351 if (srcData == null) {
352 img = getBufferedImage(img);
353 srcData = dstData.getSourceSurfaceData(img,
354 sg.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 sg.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 sg.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.
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 <= sg.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,
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 sg.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 }
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 sg.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;
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 <= sg.TRANSFORM_INT_TRANSLATE) {
804 // Integer translates are always "simple"
805 return true;
806 }
807 if (ts >= sg.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) {
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&(tx.TYPE_MASK_ROTATION|tx.TYPE_GENERAL_TRANSFORM)) != 0);
850 if (! needTrans && type != tx.TYPE_TRANSLATION && type != tx.TYPE_IDENTITY)
851 {
852 double[] mtx = new double[4];
853 tx.getMatrix(mtx);
854 // Check out the matrix. A non-integral scale will force ARGB
855 // since the edge conditions cannot be guaranteed.
856 needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
857 }
858
859 if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
860 if (cm instanceof IndexColorModel) {
861 Raster raster = bImg.getRaster();
862 IndexColorModel icm = (IndexColorModel) cm;
863 // Just need to make sure that we have a transparent pixel
864 if (needTrans && cm.getTransparency() == cm.OPAQUE) {
865 // Fix 4221407
866 if (raster instanceof sun.awt.image.BytePackedRaster) {
867 dstCM = ColorModel.getRGBdefault();
868 }
869 else {
870 double[] matrix = new double[6];
871 tx.getMatrix(matrix);
872 if (matrix[1] == 0. && matrix[2] ==0.
873 && matrix[4] == 0. && matrix[5] == 0.) {
874 // Only scaling so do not need to create
875 }
876 else {
877 int mapSize = icm.getMapSize();
878 if (mapSize < 256) {
879 int[] cmap = new int[mapSize+1];
880 icm.getRGBs(cmap);
881 cmap[mapSize] = 0x0000;
882 dstCM = new
883 IndexColorModel(icm.getPixelSize(),
884 mapSize+1,
885 cmap, 0, true, mapSize,
886 DataBuffer.TYPE_BYTE);
887 }
888 else {
889 dstCM = ColorModel.getRGBdefault();
890 }
891 } /* if (matrix[0] < 1.f ...) */
892 } /* raster instanceof sun.awt.image.BytePackedRaster */
893 } /* if (cm.getTransparency() == cm.OPAQUE) */
894 } /* if (cm instanceof IndexColorModel) */
895 else if (needTrans && cm.getTransparency() == cm.OPAQUE) {
896 // Need a bitmask transparency
897 // REMIND: for now, use full transparency since no loops
898 // for bitmask
899 dstCM = ColorModel.getRGBdefault();
900 }
901 } /* if (sg.renderHint == RENDER_QUALITY) */
902 else {
903
904 if (cm instanceof IndexColorModel ||
905 (needTrans && cm.getTransparency() == cm.OPAQUE))
906 {
907 // Need a bitmask transparency
908 // REMIND: for now, use full transparency since no loops
909 // for bitmask
910 dstCM = ColorModel.getRGBdefault();
911 }
912 }
913
914 return dstCM;
915 }
916
917 protected void blitSurfaceData(SunGraphics2D sg,
918 Region clipRegion,
919 SurfaceData srcData,
920 SurfaceData dstData,
921 SurfaceType srcType,
922 SurfaceType dstType,
923 int sx, int sy, int dx, int dy,
924 int w, int h,
925 Color bgColor)
|
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);
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.
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,
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 }
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;
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) {
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)
|