0001: /*
0002: * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.java2d.pipe;
0027:
0028: import java.awt.AlphaComposite;
0029: import java.awt.Color;
0030: import java.awt.Graphics2D;
0031: import java.awt.Image;
0032: import java.awt.Rectangle;
0033: import java.awt.Transparency;
0034: import java.awt.geom.AffineTransform;
0035: import java.awt.geom.NoninvertibleTransformException;
0036: import java.awt.image.AffineTransformOp;
0037: import java.awt.image.BufferedImage;
0038: import java.awt.image.BufferedImageOp;
0039: import java.awt.image.ColorModel;
0040: import java.awt.image.DataBuffer;
0041: import java.awt.image.DirectColorModel;
0042: import java.awt.image.ImageObserver;
0043: import java.awt.image.IndexColorModel;
0044: import java.awt.image.Raster;
0045: import java.awt.image.VolatileImage;
0046: import java.awt.image.WritableRaster;
0047: import java.awt.image.ImagingOpException;
0048: import sun.awt.SunHints;
0049: import sun.awt.image.ImageRepresentation;
0050: import sun.awt.image.ToolkitImage;
0051: import sun.java2d.InvalidPipeException;
0052: import sun.java2d.SunGraphics2D;
0053: import sun.java2d.SurfaceData;
0054: import sun.java2d.loops.Blit;
0055: import sun.java2d.loops.BlitBg;
0056: import sun.java2d.loops.TransformHelper;
0057: import sun.java2d.loops.MaskBlit;
0058: import sun.java2d.loops.CompositeType;
0059: import sun.java2d.loops.ScaledBlit;
0060: import sun.java2d.loops.SurfaceType;
0061:
0062: public class DrawImage implements DrawImagePipe {
0063: public boolean copyImage(SunGraphics2D sg, Image img, int x, int y,
0064: Color bgColor) {
0065: int imgw = img.getWidth(null);
0066: int imgh = img.getHeight(null);
0067: if (isSimpleTranslate(sg)) {
0068: return renderImageCopy(sg, img, bgColor, x + sg.transX, y
0069: + sg.transY, 0, 0, imgw, imgh);
0070: }
0071: AffineTransform atfm = sg.transform;
0072: if ((x | y) != 0) {
0073: atfm = new AffineTransform(atfm);
0074: atfm.translate(x, y);
0075: }
0076: transformImage(sg, img, atfm, sg.interpolationType, 0, 0, imgw,
0077: imgh, bgColor);
0078: return true;
0079: }
0080:
0081: public boolean copyImage(SunGraphics2D sg, Image img, int dx,
0082: int dy, int sx, int sy, int w, int h, Color bgColor) {
0083: if (isSimpleTranslate(sg)) {
0084: return renderImageCopy(sg, img, bgColor, dx + sg.transX, dy
0085: + sg.transY, sx, sy, w, h);
0086: }
0087: scaleImage(sg, img, dx, dy, (dx + w), (dy + h), sx, sy,
0088: (sx + w), (sy + h), bgColor);
0089: return true;
0090: }
0091:
0092: public boolean scaleImage(SunGraphics2D sg, Image img, int x,
0093: int y, int width, int height, Color bgColor) {
0094: int imgw = img.getWidth(null);
0095: int imgh = img.getHeight(null);
0096: // Only accelerate scale if:
0097: // - w/h positive values
0098: // - sg transform integer translate/identity only
0099: // - no bgColor in operation
0100: if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {
0101: double dx1 = x + sg.transX;
0102: double dy1 = y + sg.transY;
0103: double dx2 = dx1 + width;
0104: double dy2 = dy1 + height;
0105: if (renderImageScale(sg, img, bgColor,
0106: sg.interpolationType, 0, 0, imgw, imgh, dx1, dy1,
0107: dx2, dy2)) {
0108: return true;
0109: }
0110: }
0111:
0112: AffineTransform atfm = sg.transform;
0113: if ((x | y) != 0 || width != imgw || height != imgh) {
0114: atfm = new AffineTransform(atfm);
0115: atfm.translate(x, y);
0116: atfm.scale(((double) width) / imgw, ((double) height)
0117: / imgh);
0118: }
0119: transformImage(sg, img, atfm, sg.interpolationType, 0, 0, imgw,
0120: imgh, bgColor);
0121: return true;
0122: }
0123:
0124: /*
0125: * This method is only called in those circumstances where the
0126: * operation has a non-null secondary transform specfied. Its
0127: * role is to check for various optimizations based on the types
0128: * of both the secondary and SG2D transforms and to do some
0129: * quick calculations to avoid having to combine the transforms
0130: * and/or to call a more generalized method.
0131: */
0132: protected void transformImage(SunGraphics2D sg, Image img, int x,
0133: int y, AffineTransform extraAT, int interpType) {
0134: int txtype = extraAT.getType();
0135: int imgw = img.getWidth(null);
0136: int imgh = img.getHeight(null);
0137: boolean checkfinalxform;
0138:
0139: if (sg.transformState <= sg.TRANSFORM_ANY_TRANSLATE
0140: && (txtype == AffineTransform.TYPE_IDENTITY || txtype == AffineTransform.TYPE_TRANSLATION)) {
0141: // First optimization - both are some kind of translate
0142:
0143: // Combine the translations and check if interpolation is necessary.
0144: double tx = extraAT.getTranslateX();
0145: double ty = extraAT.getTranslateY();
0146: tx += sg.transform.getTranslateX();
0147: ty += sg.transform.getTranslateY();
0148: int itx = (int) Math.floor(tx + 0.5);
0149: int ity = (int) Math.floor(ty + 0.5);
0150: if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR
0151: || (closeToInteger(itx, tx) && closeToInteger(ity,
0152: ty))) {
0153: renderImageCopy(sg, img, null, x + itx, y + ity, 0, 0,
0154: imgw, imgh);
0155: return;
0156: }
0157: checkfinalxform = false;
0158: } else if (sg.transformState <= sg.TRANSFORM_TRANSLATESCALE
0159: && ((txtype & (AffineTransform.TYPE_FLIP
0160: | AffineTransform.TYPE_MASK_ROTATION | AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0)) {
0161: // Second optimization - both are some kind of translate or scale
0162:
0163: // Combine the scales and check if interpolation is necessary.
0164:
0165: // Transform source bounds by extraAT,
0166: // then translate the bounds again by x, y
0167: // then transform the bounds again by sg.transform
0168: double coords[] = new double[] { 0, 0, imgw, imgh, };
0169: extraAT.transform(coords, 0, coords, 0, 2);
0170: coords[0] += x;
0171: coords[1] += y;
0172: coords[2] += x;
0173: coords[3] += y;
0174: sg.transform.transform(coords, 0, coords, 0, 2);
0175:
0176: if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh, null,
0177: interpType, coords)) {
0178: return;
0179: }
0180: checkfinalxform = false;
0181: } else {
0182: checkfinalxform = true;
0183: }
0184:
0185: // Begin Transform
0186: AffineTransform tx = new AffineTransform(sg.transform);
0187: tx.translate(x, y);
0188: tx.concatenate(extraAT);
0189:
0190: // Do not try any more optimizations if either of the cases
0191: // above was tried as we have already verified that the
0192: // resulting transform will not simplify.
0193: if (checkfinalxform) {
0194: // In this case neither of the above simple transform
0195: // pairs was found so we will do some final tests on
0196: // the final rendering transform which may be the
0197: // simple product of two complex transforms.
0198: transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh,
0199: null);
0200: } else {
0201: renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh,
0202: null);
0203: }
0204: }
0205:
0206: /*
0207: * This method is called with a final rendering transform that
0208: * has combined all of the information about the Graphics2D
0209: * transform attribute with the transformations specified by
0210: * the arguments to the drawImage call.
0211: * Its role is to see if the combined transform ends up being
0212: * acceleratable by either a renderImageCopy or renderImageScale
0213: * once all of the math is done.
0214: *
0215: * Note: The transform supplied here has an origin that is
0216: * already adjusted to point to the device location where
0217: * the (sx1, sy1) location of the source image should be placed.
0218: */
0219: protected void transformImage(SunGraphics2D sg, Image img,
0220: AffineTransform tx, int interpType, int sx1, int sy1,
0221: int sx2, int sy2, Color bgColor) {
0222: // Transform 3 source corners by tx and analyze them
0223: // for simplified operations (Copy or Scale). Using
0224: // 3 points lets us analyze any kind of transform,
0225: // even transforms that involve very tiny amounts of
0226: // rotation or skew to see if they degenerate to a
0227: // simple scale or copy operation within the allowable
0228: // error bounds.
0229: // Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)
0230: // because the transform is already translated such that
0231: // the origin is where sx1, sy1 should go.
0232: double coords[] = new double[6];
0233: /* index: 0 1 2 3 4 5 */
0234: /* coord: (0, 0), (w, h), (0, h) */
0235: coords[2] = sx2 - sx1;
0236: coords[3] = coords[5] = sy2 - sy1;
0237: tx.transform(coords, 0, coords, 0, 3);
0238: // First test if the X coords of the transformed UL
0239: // and LL points match and that the Y coords of the
0240: // transformed LR and LL points also match.
0241: // If they do then it is a "rectilinear" transform and
0242: // tryCopyOrScale will make sure it is upright and
0243: // integer-based.
0244: if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR
0245: && Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR
0246: && tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2, bgColor,
0247: interpType, coords)) {
0248: return;
0249: }
0250:
0251: renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2,
0252: bgColor);
0253: }
0254:
0255: /*
0256: * Check the bounding coordinates of the transformed source
0257: * image to see if they fall on integer coordinates such
0258: * that they will cause no interpolation anomalies if we
0259: * use our simplified Blit or ScaledBlit operations instead
0260: * of a full transform operation.
0261: */
0262: protected boolean tryCopyOrScale(SunGraphics2D sg, Image img,
0263: int sx1, int sy1, int sx2, int sy2, Color bgColor,
0264: int interpType, double coords[]) {
0265: double dx = coords[0];
0266: double dy = coords[1];
0267: double dw = coords[2] - dx;
0268: double dh = coords[3] - dy;
0269: // First check if width and height are very close to img w&h.
0270: if (closeToInteger(sx2 - sx1, dw)
0271: && closeToInteger(sy2 - sy1, dh)) {
0272: // Round location to nearest pixel and then test
0273: // if it will cause interpolation anomalies.
0274: int idx = (int) Math.floor(dx + 0.5);
0275: int idy = (int) Math.floor(dy + 0.5);
0276: if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR
0277: || (closeToInteger(idx, dx) && closeToInteger(idy,
0278: dy))) {
0279: renderImageCopy(sg, img, bgColor, idx, idy, sx1, sy1,
0280: sx2 - sx1, sy2 - sy1);
0281: return true;
0282: }
0283: }
0284: // (For now) We can only use our ScaledBlits if the image
0285: // is upright (i.e. dw & dh both > 0)
0286: if (dw > 0 && dh > 0) {
0287: if (renderImageScale(sg, img, bgColor, interpType, sx1,
0288: sy1, sx2, sy2, coords[0], coords[1], coords[2],
0289: coords[3])) {
0290: return true;
0291: }
0292: }
0293: return false;
0294: }
0295:
0296: /*
0297: * Return a BufferedImage of the requested type with the indicated
0298: * subimage of the original image located at 0,0 in the new image.
0299: * If a bgColor is supplied, composite the original image over that
0300: * color with a SrcOver operation, otherwise make a SrcNoEa copy.
0301: */
0302: BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
0303: int sx1, int sy1, int sx2, int sy2) {
0304: BufferedImage bimg = new BufferedImage(sx2 - sx1, sy2 - sy1,
0305: type);
0306: Graphics2D g2d = bimg.createGraphics();
0307: g2d.setComposite(AlphaComposite.Src);
0308: if (bgColor != null) {
0309: g2d.setColor(bgColor);
0310: g2d.fillRect(0, 0, sx2 - sx1, sy2 - sy1);
0311: g2d.setComposite(AlphaComposite.SrcOver);
0312: }
0313: g2d.drawImage(img, -sx1, -sy1, null);
0314: g2d.dispose();
0315: return bimg;
0316: }
0317:
0318: protected void renderImageXform(SunGraphics2D sg, Image img,
0319: AffineTransform tx, int interpType, int sx1, int sy1,
0320: int sx2, int sy2, Color bgColor) {
0321: Region clip = sg.getCompClip();
0322: SurfaceData dstData = sg.surfaceData;
0323: SurfaceData srcData = dstData.getSourceSurfaceData(img,
0324: sg.TRANSFORM_GENERIC, sg.imageComp, bgColor);
0325:
0326: if (srcData == null) {
0327: img = getBufferedImage(img);
0328: srcData = dstData.getSourceSurfaceData(img,
0329: sg.TRANSFORM_GENERIC, sg.imageComp, bgColor);
0330: if (srcData == null) {
0331: // REMIND: Is this correct? Can this happen?
0332: return;
0333: }
0334: }
0335:
0336: if (isBgOperation(srcData, bgColor)) {
0337: // We cannot perform bg operations during transform so make
0338: // an opaque temp image with the appropriate background
0339: // and work from there.
0340: img = makeBufferedImage(img, bgColor,
0341: BufferedImage.TYPE_INT_RGB, sx1, sy1, sx2, sy2);
0342: // Temp image has appropriate subimage at 0,0 now.
0343: sx2 -= sx1;
0344: sy2 -= sy1;
0345: sx1 = sy1 = 0;
0346:
0347: srcData = dstData.getSourceSurfaceData(img,
0348: sg.TRANSFORM_GENERIC, sg.imageComp, bgColor);
0349: }
0350:
0351: SurfaceType srcType = srcData.getSurfaceType();
0352: TransformHelper helper = TransformHelper.getFromCache(srcType);
0353:
0354: if (helper == null) {
0355: /* We have no helper for this source image type.
0356: * But we know that we do have helpers for both RGB and ARGB,
0357: * so convert to one of those types depending on transparency.
0358: * ARGB_PRE might be a better choice if the source image has
0359: * alpha, but it may cause some recursion here since we only
0360: * tend to have converters that convert to ARGB.
0361: */
0362: int type = ((srcData.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB
0363: : BufferedImage.TYPE_INT_ARGB);
0364: img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);
0365: // Temp image has appropriate subimage at 0,0 now.
0366: sx2 -= sx1;
0367: sy2 -= sy1;
0368: sx1 = sy1 = 0;
0369:
0370: srcData = dstData.getSourceSurfaceData(img,
0371: sg.TRANSFORM_GENERIC, sg.imageComp, null);
0372: srcType = srcData.getSurfaceType();
0373: helper = TransformHelper.getFromCache(srcType);
0374: // assert(helper != null);
0375: }
0376:
0377: AffineTransform itx;
0378: try {
0379: itx = tx.createInverse();
0380: } catch (NoninvertibleTransformException e) {
0381: // Non-invertible transform means no output
0382: return;
0383: }
0384:
0385: /*
0386: * Find the maximum bounds on the destination that will be
0387: * affected by the transformed source. First, transform all
0388: * four corners of the source and then min and max the resulting
0389: * destination coordinates of the transformed corners.
0390: * Note that tx already has the offset to sx1,sy1 accounted
0391: * for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the
0392: * source coordinates.
0393: */
0394: double coords[] = new double[8];
0395: /* corner: UL UR LL LR */
0396: /* index: 0 1 2 3 4 5 6 7 */
0397: /* coord: (0, 0), (w, 0), (0, h), (w, h) */
0398: coords[2] = coords[6] = sx2 - sx1;
0399: coords[5] = coords[7] = sy2 - sy1;
0400: tx.transform(coords, 0, coords, 0, 4);
0401: double ddx1, ddy1, ddx2, ddy2;
0402: ddx1 = ddx2 = coords[0];
0403: ddy1 = ddy2 = coords[1];
0404: for (int i = 2; i < coords.length; i += 2) {
0405: double d = coords[i];
0406: if (ddx1 > d)
0407: ddx1 = d;
0408: else if (ddx2 < d)
0409: ddx2 = d;
0410: d = coords[i + 1];
0411: if (ddy1 > d)
0412: ddy1 = d;
0413: else if (ddy2 < d)
0414: ddy2 = d;
0415: }
0416: int dx1 = (int) Math.floor(ddx1);
0417: int dy1 = (int) Math.floor(ddy1);
0418: int dx2 = (int) Math.ceil(ddx2);
0419: int dy2 = (int) Math.ceil(ddy2);
0420:
0421: SurfaceType dstType = dstData.getSurfaceType();
0422: MaskBlit maskblit;
0423: Blit blit;
0424: if (sg.compositeState <= sg.COMP_ALPHA) {
0425: /* NOTE: We either have, or we can make,
0426: * a MaskBlit for any alpha composite type
0427: */
0428: maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
0429: sg.imageComp, dstType);
0430:
0431: /* NOTE: We can only use the native TransformHelper
0432: * func to go directly to the dest if both the helper
0433: * and the MaskBlit are native.
0434: * All helpers are native at this point, but some MaskBlit
0435: * objects are implemented in Java, so we need to check.
0436: */
0437: if (maskblit.getNativePrim() != 0) {
0438: // We can render directly.
0439: helper.Transform(maskblit, srcData, dstData,
0440: sg.composite, clip, itx, interpType, sx1, sy1,
0441: sx2, sy2, dx1, dy1, dx2, dy2, null, 0, 0);
0442: return;
0443: }
0444: blit = null;
0445: } else {
0446: /* NOTE: We either have, or we can make,
0447: * a Blit for any composite type, even Custom
0448: */
0449: maskblit = null;
0450: blit = Blit.getFromCache(SurfaceType.IntArgbPre,
0451: sg.imageComp, dstType);
0452: }
0453:
0454: // We need to transform to a temp image and then copy
0455: // just the pieces that are valid data to the dest.
0456: BufferedImage tmpimg = new BufferedImage(dx2 - dx1, dy2 - dy1,
0457: BufferedImage.TYPE_INT_ARGB);
0458: SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);
0459: SurfaceType tmpType = tmpData.getSurfaceType();
0460: MaskBlit tmpmaskblit = MaskBlit.getFromCache(
0461: SurfaceType.IntArgbPre, CompositeType.SrcNoEa, tmpType);
0462: /*
0463: * The helper function fills a temporary edges buffer
0464: * for us with the bounding coordinates of each scanline
0465: * in the following format:
0466: *
0467: * edges[0, 1] = [top y, bottom y)
0468: * edges[2, 3] = [left x, right x) of top row
0469: * ...
0470: * edges[h*2, h*2+1] = [left x, right x) of bottom row
0471: *
0472: * all coordinates in the edges array will be relative to dx1, dy1
0473: *
0474: * edges thus has to be h*2+2 in length
0475: */
0476: int edges[] = new int[(dy2 - dy1) * 2 + 2];
0477: helper.Transform(tmpmaskblit, srcData, tmpData,
0478: AlphaComposite.Src, null, itx, interpType, sx1, sy1,
0479: sx2, sy2, 0, 0, dx2 - dx1, dy2 - dy1, edges, dx1, dy1);
0480:
0481: /*
0482: * Now copy the results, scanline by scanline, into the dest.
0483: * The edges array helps us minimize the work.
0484: */
0485: int index = 2;
0486: for (int y = edges[0]; y < edges[1]; y++) {
0487: int relx1 = edges[index++];
0488: int relx2 = edges[index++];
0489: if (relx1 >= relx2) {
0490: continue;
0491: }
0492: if (maskblit != null) {
0493: maskblit.MaskBlit(tmpData, dstData, sg.composite, clip,
0494: relx1, y, dx1 + relx1, dy1 + y, relx2 - relx1,
0495: 1, null, 0, 0);
0496: } else {
0497: blit.Blit(tmpData, dstData, sg.composite, clip, relx1,
0498: y, dx1 + relx1, dy1 + y, relx2 - relx1, 1);
0499: }
0500: }
0501: }
0502:
0503: // Render an image using only integer translation
0504: // (no scale or transform or sub-pixel interpolated translations).
0505: protected boolean renderImageCopy(SunGraphics2D sg, Image img,
0506: Color bgColor, int dx, int dy, int sx, int sy, int w, int h) {
0507: Region clip = sg.getCompClip();
0508: SurfaceData dstData = sg.surfaceData;
0509:
0510: int attempts = 0;
0511: // Loop up to twice through; this gives us a chance to
0512: // revalidate the surfaceData objects in case of an exception
0513: // and try it once more
0514: while (true) {
0515: SurfaceData srcData = dstData.getSourceSurfaceData(img,
0516: sg.TRANSFORM_ISIDENT, sg.imageComp, bgColor);
0517: if (srcData == null) {
0518: return false;
0519: }
0520:
0521: try {
0522: SurfaceType srcType = srcData.getSurfaceType();
0523: SurfaceType dstType = dstData.getSurfaceType();
0524: blitSurfaceData(sg, clip, srcData, dstData, srcType,
0525: dstType, sx, sy, dx, dy, w, h, bgColor);
0526: return true;
0527: } catch (NullPointerException e) {
0528: if (!(SurfaceData.isNull(dstData) || SurfaceData
0529: .isNull(srcData))) {
0530: // Something else caused the exception, throw it...
0531: throw e;
0532: }
0533: return false;
0534: // NOP if we have been disposed
0535: } catch (InvalidPipeException e) {
0536: // Always catch the exception; try this a couple of times
0537: // and fail silently if the system is not yet ready to
0538: // revalidate the source or dest surfaceData objects.
0539: ++attempts;
0540: clip = sg.getCompClip(); // ensures sg.surfaceData is valid
0541: dstData = sg.surfaceData;
0542: if (SurfaceData.isNull(dstData)
0543: || SurfaceData.isNull(srcData)
0544: || (attempts > 1)) {
0545: return false;
0546: }
0547: }
0548: }
0549: }
0550:
0551: // Render an image using only integer scaling (no transform).
0552: protected boolean renderImageScale(SunGraphics2D sg, Image img,
0553: Color bgColor, int interpType, int sx1, int sy1, int sx2,
0554: int sy2, double dx1, double dy1, double dx2, double dy2) {
0555: // Currently only NEAREST_NEIGHBOR interpolation is implemented
0556: // for ScaledBlit operations.
0557: if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
0558: return false;
0559: }
0560:
0561: Region clip = sg.getCompClip();
0562: SurfaceData dstData = sg.surfaceData;
0563:
0564: int attempts = 0;
0565: // Loop up to twice through; this gives us a chance to
0566: // revalidate the surfaceData objects in case of an exception
0567: // and try it once more
0568: while (true) {
0569: SurfaceData srcData = dstData.getSourceSurfaceData(img,
0570: sg.TRANSFORM_TRANSLATESCALE, sg.imageComp, bgColor);
0571:
0572: if (srcData == null || isBgOperation(srcData, bgColor)) {
0573: return false;
0574: }
0575:
0576: try {
0577: SurfaceType srcType = srcData.getSurfaceType();
0578: SurfaceType dstType = dstData.getSurfaceType();
0579: return scaleSurfaceData(sg, clip, srcData, dstData,
0580: srcType, dstType, sx1, sy1, sx2, sy2, dx1, dy1,
0581: dx2, dy2);
0582: } catch (NullPointerException e) {
0583: if (!SurfaceData.isNull(dstData)) {
0584: // Something else caused the exception, throw it...
0585: throw e;
0586: }
0587: return false;
0588: // NOP if we have been disposed
0589: } catch (InvalidPipeException e) {
0590: // Always catch the exception; try this a couple of times
0591: // and fail silently if the system is not yet ready to
0592: // revalidate the source or dest surfaceData objects.
0593: ++attempts;
0594: clip = sg.getCompClip(); // ensures sg.surfaceData is valid
0595: dstData = sg.surfaceData;
0596: if (SurfaceData.isNull(dstData)
0597: || SurfaceData.isNull(srcData)
0598: || (attempts > 1)) {
0599: return false;
0600: }
0601: }
0602: }
0603: }
0604:
0605: public boolean scaleImage(SunGraphics2D sg, Image img, int dx1,
0606: int dy1, int dx2, int dy2, int sx1, int sy1, int sx2,
0607: int sy2, Color bgColor) {
0608: int srcW, srcH, dstW, dstH;
0609: int srcX, srcY, dstX, dstY;
0610: boolean srcWidthFlip = false;
0611: boolean srcHeightFlip = false;
0612: boolean dstWidthFlip = false;
0613: boolean dstHeightFlip = false;
0614:
0615: if (sx2 > sx1) {
0616: srcW = sx2 - sx1;
0617: srcX = sx1;
0618: } else {
0619: srcWidthFlip = true;
0620: srcW = sx1 - sx2;
0621: srcX = sx2;
0622: }
0623: if (sy2 > sy1) {
0624: srcH = sy2 - sy1;
0625: srcY = sy1;
0626: } else {
0627: srcHeightFlip = true;
0628: srcH = sy1 - sy2;
0629: srcY = sy2;
0630: }
0631: if (dx2 > dx1) {
0632: dstW = dx2 - dx1;
0633: dstX = dx1;
0634: } else {
0635: dstW = dx1 - dx2;
0636: dstWidthFlip = true;
0637: dstX = dx2;
0638: }
0639: if (dy2 > dy1) {
0640: dstH = dy2 - dy1;
0641: dstY = dy1;
0642: } else {
0643: dstH = dy1 - dy2;
0644: dstHeightFlip = true;
0645: dstY = dy2;
0646: }
0647: if (srcW <= 0 || srcH <= 0) {
0648: return true;
0649: }
0650: // Only accelerate scale if it does not involve a flip or transform
0651: if ((srcWidthFlip == dstWidthFlip)
0652: && (srcHeightFlip == dstHeightFlip)
0653: && isSimpleTranslate(sg)) {
0654: double ddx1 = dstX + sg.transX;
0655: double ddy1 = dstY + sg.transY;
0656: double ddx2 = ddx1 + dstW;
0657: double ddy2 = ddy1 + dstH;
0658: if (renderImageScale(sg, img, bgColor,
0659: sg.interpolationType, srcX, srcY, srcX + srcW, srcY
0660: + srcH, ddx1, ddy1, ddx2, ddy2)) {
0661: return true;
0662: }
0663: }
0664:
0665: AffineTransform atfm = new AffineTransform(sg.transform);
0666: atfm.translate(dx1, dy1);
0667: double m00 = (double) (dx2 - dx1) / (sx2 - sx1);
0668: double m11 = (double) (dy2 - dy1) / (sy2 - sy1);
0669: atfm.scale(m00, m11);
0670: atfm.translate(srcX - sx1, srcY - sy1);
0671:
0672: int imgW = img.getWidth(null);
0673: int imgH = img.getHeight(null);
0674: srcW += srcX;
0675: srcH += srcY;
0676: // Make sure we are not out of bounds
0677: if (srcW > imgW) {
0678: srcW = imgW;
0679: }
0680: if (srcH > imgH) {
0681: srcH = imgH;
0682: }
0683: if (srcX < 0) {
0684: atfm.translate(-srcX, 0);
0685: srcX = 0;
0686: }
0687: if (srcY < 0) {
0688: atfm.translate(0, -srcY);
0689: srcY = 0;
0690: }
0691: if (srcX >= srcW || srcY >= srcH) {
0692: return true;
0693: }
0694: // Note: src[WH] are currently the right and bottom coordinates.
0695: // The following two lines would adjust src[WH] back to being
0696: // dimensions.
0697: // srcW -= srcX;
0698: // srcH -= srcY;
0699: // Since transformImage needs right and bottom coords we will
0700: // omit this adjustment.
0701:
0702: transformImage(sg, img, atfm, sg.interpolationType, srcX, srcY,
0703: srcW, srcH, bgColor);
0704: return true;
0705: }
0706:
0707: /**
0708: ** Utilities
0709: ** The following methods are used by the public methods above
0710: ** for performing various operations
0711: **/
0712:
0713: /*
0714: * This constant represents a tradeoff between the
0715: * need to make sure that image transformations are
0716: * "very close" to integer device coordinates before
0717: * we decide to use an integer scale or copy operation
0718: * as a substitute and the fact that roundoff errors
0719: * in AffineTransforms are frequently introduced by
0720: * performing multiple sequential operations on them.
0721: *
0722: * The evaluation of bug 4990624 details the potential
0723: * for this error cutoff to result in display anomalies
0724: * in different types of image operations and how this
0725: * value represents a good compromise here.
0726: */
0727: private static final double MAX_TX_ERROR = .0001;
0728:
0729: public static boolean closeToInteger(int i, double d) {
0730: return (Math.abs(d - i) < MAX_TX_ERROR);
0731: }
0732:
0733: public static boolean isSimpleTranslate(SunGraphics2D sg) {
0734: int ts = sg.transformState;
0735: if (ts <= sg.TRANSFORM_INT_TRANSLATE) {
0736: // Integer translates are always "simple"
0737: return true;
0738: }
0739: if (ts >= sg.TRANSFORM_TRANSLATESCALE) {
0740: // Scales and beyond are always "not simple"
0741: return false;
0742: }
0743: // non-integer translates are only simple when not interpolating
0744: if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
0745: return true;
0746: }
0747: return false;
0748: }
0749:
0750: protected static boolean isBgOperation(SurfaceData srcData,
0751: Color bgColor) {
0752: // If we cannot get the srcData, then cannot assume anything about
0753: // the image
0754: return ((srcData == null) || ((bgColor != null) && (srcData
0755: .getTransparency() != Transparency.OPAQUE)));
0756: }
0757:
0758: protected BufferedImage getBufferedImage(Image img) {
0759: if (img instanceof BufferedImage) {
0760: return (BufferedImage) img;
0761: }
0762: // Must be VolatileImage; get BufferedImage representation
0763: return ((VolatileImage) img).getSnapshot();
0764: }
0765:
0766: /*
0767: * Return the color model to be used with this BufferedImage and
0768: * transform.
0769: */
0770: private ColorModel getTransformColorModel(SunGraphics2D sg,
0771: BufferedImage bImg, AffineTransform tx) {
0772: ColorModel cm = bImg.getColorModel();
0773: ColorModel dstCM = cm;
0774:
0775: if (tx.isIdentity()) {
0776: return dstCM;
0777: }
0778: int type = tx.getType();
0779: boolean needTrans = ((type & (tx.TYPE_MASK_ROTATION | tx.TYPE_GENERAL_TRANSFORM)) != 0);
0780: if (!needTrans && type != tx.TYPE_TRANSLATION
0781: && type != tx.TYPE_IDENTITY) {
0782: double[] mtx = new double[4];
0783: tx.getMatrix(mtx);
0784: // Check out the matrix. A non-integral scale will force ARGB
0785: // since the edge conditions cannot be guaranteed.
0786: needTrans = (mtx[0] != (int) mtx[0] || mtx[3] != (int) mtx[3]);
0787: }
0788:
0789: if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
0790: if (cm instanceof IndexColorModel) {
0791: Raster raster = bImg.getRaster();
0792: IndexColorModel icm = (IndexColorModel) cm;
0793: // Just need to make sure that we have a transparent pixel
0794: if (needTrans && cm.getTransparency() == cm.OPAQUE) {
0795: // Fix 4221407
0796: if (raster instanceof sun.awt.image.BytePackedRaster) {
0797: dstCM = ColorModel.getRGBdefault();
0798: } else {
0799: double[] matrix = new double[6];
0800: tx.getMatrix(matrix);
0801: if (matrix[1] == 0. && matrix[2] == 0.
0802: && matrix[4] == 0. && matrix[5] == 0.) {
0803: // Only scaling so do not need to create
0804: } else {
0805: int mapSize = icm.getMapSize();
0806: if (mapSize < 256) {
0807: int[] cmap = new int[mapSize + 1];
0808: icm.getRGBs(cmap);
0809: cmap[mapSize] = 0x0000;
0810: dstCM = new IndexColorModel(icm
0811: .getPixelSize(), mapSize + 1,
0812: cmap, 0, true, mapSize,
0813: DataBuffer.TYPE_BYTE);
0814: } else {
0815: dstCM = ColorModel.getRGBdefault();
0816: }
0817: } /* if (matrix[0] < 1.f ...) */
0818: } /* raster instanceof sun.awt.image.BytePackedRaster */
0819: } /* if (cm.getTransparency() == cm.OPAQUE) */
0820: } /* if (cm instanceof IndexColorModel) */
0821: else if (needTrans && cm.getTransparency() == cm.OPAQUE) {
0822: // Need a bitmask transparency
0823: // REMIND: for now, use full transparency since no loops
0824: // for bitmask
0825: dstCM = ColorModel.getRGBdefault();
0826: }
0827: } /* if (sg.renderHint == RENDER_QUALITY) */
0828: else {
0829:
0830: if (cm instanceof IndexColorModel
0831: || (needTrans && cm.getTransparency() == cm.OPAQUE)) {
0832: // Need a bitmask transparency
0833: // REMIND: for now, use full transparency since no loops
0834: // for bitmask
0835: dstCM = ColorModel.getRGBdefault();
0836: }
0837: }
0838:
0839: return dstCM;
0840: }
0841:
0842: protected void blitSurfaceData(SunGraphics2D sg, Region clipRegion,
0843: SurfaceData srcData, SurfaceData dstData,
0844: SurfaceType srcType, SurfaceType dstType, int sx, int sy,
0845: int dx, int dy, int w, int h, Color bgColor) {
0846: if (w <= 0 || h <= 0) {
0847: /*
0848: * Fix for bugid 4783274 - BlitBg throws an exception for
0849: * a particular set of anomalous parameters.
0850: * REMIND: The native loops do proper clipping and would
0851: * detect this situation themselves, but the Java loops
0852: * all seem to trust their parameters a little too well
0853: * to the point where they will try to process a negative
0854: * area of pixels and throw exceptions. The real fix is
0855: * to modify the Java loops to do proper clipping so that
0856: * they can deal with negative dimensions as well as
0857: * improperly large dimensions, but that fix is too risky
0858: * to integrate for Mantis at this point. In the meantime
0859: * eliminating the negative or zero dimensions here is
0860: * "correct" and saves them from some nasty exceptional
0861: * conditions, one of which is the test case of 4783274.
0862: */
0863: return;
0864: }
0865: CompositeType comp = sg.imageComp;
0866: if (CompositeType.SrcOverNoEa.equals(comp)
0867: && (srcData.getTransparency() == Transparency.OPAQUE || (bgColor != null && bgColor
0868: .getTransparency() == Transparency.OPAQUE))) {
0869: comp = CompositeType.SrcNoEa;
0870: }
0871: if (!isBgOperation(srcData, bgColor)) {
0872: Blit blit = Blit.getFromCache(srcType, comp, dstType);
0873: blit.Blit(srcData, dstData, sg.composite, clipRegion, sx,
0874: sy, dx, dy, w, h);
0875: } else {
0876: BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
0877: blit.BlitBg(srcData, dstData, sg.composite, clipRegion,
0878: bgColor, sx, sy, dx, dy, w, h);
0879: }
0880: }
0881:
0882: protected boolean scaleSurfaceData(SunGraphics2D sg,
0883: Region clipRegion, SurfaceData srcData,
0884: SurfaceData dstData, SurfaceType srcType,
0885: SurfaceType dstType, int sx1, int sy1, int sx2, int sy2,
0886: double dx1, double dy1, double dx2, double dy2) {
0887: CompositeType comp = sg.imageComp;
0888: if (CompositeType.SrcOverNoEa.equals(comp)
0889: && (srcData.getTransparency() == Transparency.OPAQUE)) {
0890: comp = CompositeType.SrcNoEa;
0891: }
0892:
0893: ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp,
0894: dstType);
0895: if (blit != null) {
0896: blit.Scale(srcData, dstData, sg.composite, clipRegion, sx1,
0897: sy1, sx2, sy2, dx1, dy1, dx2, dy2);
0898: return true;
0899: }
0900: return false;
0901: }
0902:
0903: protected static boolean imageReady(ToolkitImage sunimg,
0904: ImageObserver observer) {
0905: if (sunimg.hasError()) {
0906: if (observer != null) {
0907: observer.imageUpdate(sunimg, ImageObserver.ERROR
0908: | ImageObserver.ABORT, -1, -1, -1, -1);
0909: }
0910: return false;
0911: }
0912: return true;
0913: }
0914:
0915: public boolean copyImage(SunGraphics2D sg, Image img, int x, int y,
0916: Color bgColor, ImageObserver observer) {
0917: if (!(img instanceof ToolkitImage)) {
0918: return copyImage(sg, img, x, y, bgColor);
0919: } else {
0920: ToolkitImage sunimg = (ToolkitImage) img;
0921: if (!imageReady(sunimg, observer)) {
0922: return false;
0923: }
0924: ImageRepresentation ir = sunimg.getImageRep();
0925: return ir.drawToBufImage(sg, sunimg, x, y, bgColor,
0926: observer);
0927: }
0928: }
0929:
0930: public boolean copyImage(SunGraphics2D sg, Image img, int dx,
0931: int dy, int sx, int sy, int w, int h, Color bgColor,
0932: ImageObserver observer) {
0933: if (!(img instanceof ToolkitImage)) {
0934: return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
0935: } else {
0936: ToolkitImage sunimg = (ToolkitImage) img;
0937: if (!imageReady(sunimg, observer)) {
0938: return false;
0939: }
0940: ImageRepresentation ir = sunimg.getImageRep();
0941: return ir.drawToBufImage(sg, sunimg, dx, dy, (dx + w),
0942: (dy + h), sx, sy, (sx + w), (sy + h), bgColor,
0943: observer);
0944: }
0945: }
0946:
0947: public boolean scaleImage(SunGraphics2D sg, Image img, int x,
0948: int y, int width, int height, Color bgColor,
0949: ImageObserver observer) {
0950: if (!(img instanceof ToolkitImage)) {
0951: return scaleImage(sg, img, x, y, width, height, bgColor);
0952: } else {
0953: ToolkitImage sunimg = (ToolkitImage) img;
0954: if (!imageReady(sunimg, observer)) {
0955: return false;
0956: }
0957: ImageRepresentation ir = sunimg.getImageRep();
0958: return ir.drawToBufImage(sg, sunimg, x, y, width, height,
0959: bgColor, observer);
0960: }
0961: }
0962:
0963: public boolean scaleImage(SunGraphics2D sg, Image img, int dx1,
0964: int dy1, int dx2, int dy2, int sx1, int sy1, int sx2,
0965: int sy2, Color bgColor, ImageObserver observer) {
0966: if (!(img instanceof ToolkitImage)) {
0967: return scaleImage(sg, img, dx1, dy1, dx2, dy2, sx1, sy1,
0968: sx2, sy2, bgColor);
0969: } else {
0970: ToolkitImage sunimg = (ToolkitImage) img;
0971: if (!imageReady(sunimg, observer)) {
0972: return false;
0973: }
0974: ImageRepresentation ir = sunimg.getImageRep();
0975: return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
0976: sx1, sy1, sx2, sy2, bgColor, observer);
0977: }
0978: }
0979:
0980: public boolean transformImage(SunGraphics2D sg, Image img,
0981: AffineTransform atfm, ImageObserver observer) {
0982: if (!(img instanceof ToolkitImage)) {
0983: transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
0984: return true;
0985: } else {
0986: ToolkitImage sunimg = (ToolkitImage) img;
0987: if (!imageReady(sunimg, observer)) {
0988: return false;
0989: }
0990: ImageRepresentation ir = sunimg.getImageRep();
0991: return ir.drawToBufImage(sg, sunimg, atfm, observer);
0992: }
0993: }
0994:
0995: public void transformImage(SunGraphics2D sg, BufferedImage img,
0996: BufferedImageOp op, int x, int y) {
0997: if (op != null) {
0998: if (op instanceof AffineTransformOp) {
0999: AffineTransformOp atop = (AffineTransformOp) op;
1000: transformImage(sg, img, x, y, atop.getTransform(), atop
1001: .getInterpolationType());
1002: return;
1003: } else {
1004: img = op.filter(img, null);
1005: }
1006: }
1007: copyImage(sg, img, x, y, null);
1008: }
1009: }
|