0N/A/*
2362N/A * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.java2d.pipe;
0N/A
0N/Aimport java.awt.AlphaComposite;
0N/Aimport java.awt.Color;
0N/Aimport java.awt.Image;
0N/Aimport java.awt.Transparency;
0N/Aimport java.awt.geom.AffineTransform;
0N/Aimport java.awt.geom.NoninvertibleTransformException;
0N/Aimport java.awt.image.AffineTransformOp;
0N/Aimport java.awt.image.BufferedImage;
0N/Aimport java.awt.image.BufferedImageOp;
0N/Aimport java.awt.image.ColorModel;
0N/Aimport java.awt.image.DataBuffer;
0N/Aimport java.awt.image.ImageObserver;
0N/Aimport java.awt.image.IndexColorModel;
0N/Aimport java.awt.image.Raster;
0N/Aimport java.awt.image.VolatileImage;
0N/Aimport sun.awt.SunHints;
0N/Aimport sun.awt.image.ImageRepresentation;
6084N/Aimport sun.awt.image.SurfaceManager;
0N/Aimport sun.awt.image.ToolkitImage;
0N/Aimport sun.java2d.InvalidPipeException;
0N/Aimport sun.java2d.SunGraphics2D;
0N/Aimport sun.java2d.SurfaceData;
0N/Aimport sun.java2d.loops.Blit;
0N/Aimport sun.java2d.loops.BlitBg;
0N/Aimport sun.java2d.loops.TransformHelper;
0N/Aimport sun.java2d.loops.MaskBlit;
0N/Aimport sun.java2d.loops.CompositeType;
0N/Aimport sun.java2d.loops.ScaledBlit;
0N/Aimport sun.java2d.loops.SurfaceType;
0N/A
0N/Apublic class DrawImage implements DrawImagePipe
0N/A{
0N/A public boolean copyImage(SunGraphics2D sg, Image img,
0N/A int x, int y,
0N/A Color bgColor)
0N/A {
0N/A int imgw = img.getWidth(null);
0N/A int imgh = img.getHeight(null);
0N/A if (isSimpleTranslate(sg)) {
0N/A return renderImageCopy(sg, img, bgColor,
0N/A x + sg.transX, y + sg.transY,
0N/A 0, 0, imgw, imgh);
0N/A }
0N/A AffineTransform atfm = sg.transform;
0N/A if ((x | y) != 0) {
0N/A atfm = new AffineTransform(atfm);
0N/A atfm.translate(x, y);
0N/A }
0N/A transformImage(sg, img, atfm, sg.interpolationType,
0N/A 0, 0, imgw, imgh, bgColor);
0N/A return true;
0N/A }
0N/A
0N/A public boolean copyImage(SunGraphics2D sg, Image img,
0N/A int dx, int dy, int sx, int sy, int w, int h,
0N/A Color bgColor)
0N/A {
0N/A if (isSimpleTranslate(sg)) {
0N/A return renderImageCopy(sg, img, bgColor,
0N/A dx + sg.transX, dy + sg.transY,
0N/A sx, sy, w, h);
0N/A }
0N/A scaleImage(sg, img, dx, dy, (dx + w), (dy + h),
0N/A sx, sy, (sx + w), (sy + h), bgColor);
0N/A return true;
0N/A }
0N/A
0N/A public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,
0N/A int width, int height,
0N/A Color bgColor)
0N/A {
0N/A int imgw = img.getWidth(null);
0N/A int imgh = img.getHeight(null);
0N/A // Only accelerate scale if:
0N/A // - w/h positive values
0N/A // - sg transform integer translate/identity only
0N/A // - no bgColor in operation
0N/A if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {
0N/A double dx1 = x + sg.transX;
0N/A double dy1 = y + sg.transY;
0N/A double dx2 = dx1 + width;
0N/A double dy2 = dy1 + height;
0N/A if (renderImageScale(sg, img, bgColor, sg.interpolationType,
0N/A 0, 0, imgw, imgh,
0N/A dx1, dy1, dx2, dy2))
0N/A {
0N/A return true;
0N/A }
0N/A }
0N/A
0N/A AffineTransform atfm = sg.transform;
0N/A if ((x | y) != 0 || width != imgw || height != imgh) {
0N/A atfm = new AffineTransform(atfm);
0N/A atfm.translate(x, y);
0N/A atfm.scale(((double)width)/imgw, ((double)height)/imgh);
0N/A }
0N/A transformImage(sg, img, atfm, sg.interpolationType,
0N/A 0, 0, imgw, imgh, bgColor);
0N/A return true;
0N/A }
0N/A
0N/A /*
0N/A * This method is only called in those circumstances where the
0N/A * operation has a non-null secondary transform specfied. Its
0N/A * role is to check for various optimizations based on the types
0N/A * of both the secondary and SG2D transforms and to do some
0N/A * quick calculations to avoid having to combine the transforms
0N/A * and/or to call a more generalized method.
0N/A */
0N/A protected void transformImage(SunGraphics2D sg, Image img, int x, int y,
0N/A AffineTransform extraAT, int interpType)
0N/A {
0N/A int txtype = extraAT.getType();
0N/A int imgw = img.getWidth(null);
0N/A int imgh = img.getHeight(null);
0N/A boolean checkfinalxform;
0N/A
0N/A if (sg.transformState <= sg.TRANSFORM_ANY_TRANSLATE &&
0N/A (txtype == AffineTransform.TYPE_IDENTITY ||
0N/A txtype == AffineTransform.TYPE_TRANSLATION))
0N/A {
0N/A // First optimization - both are some kind of translate
0N/A
0N/A // Combine the translations and check if interpolation is necessary.
0N/A double tx = extraAT.getTranslateX();
0N/A double ty = extraAT.getTranslateY();
0N/A tx += sg.transform.getTranslateX();
0N/A ty += sg.transform.getTranslateY();
0N/A int itx = (int) Math.floor(tx + 0.5);
0N/A int ity = (int) Math.floor(ty + 0.5);
0N/A if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
0N/A (closeToInteger(itx, tx) && closeToInteger(ity, ty)))
0N/A {
0N/A renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);
0N/A return;
0N/A }
0N/A checkfinalxform = false;
0N/A } else if (sg.transformState <= sg.TRANSFORM_TRANSLATESCALE &&
0N/A ((txtype & (AffineTransform.TYPE_FLIP |
0N/A AffineTransform.TYPE_MASK_ROTATION |
0N/A AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))
0N/A {
0N/A // Second optimization - both are some kind of translate or scale
0N/A
0N/A // Combine the scales and check if interpolation is necessary.
0N/A
0N/A // Transform source bounds by extraAT,
0N/A // then translate the bounds again by x, y
0N/A // then transform the bounds again by sg.transform
0N/A double coords[] = new double[] {
0N/A 0, 0, imgw, imgh,
0N/A };
0N/A extraAT.transform(coords, 0, coords, 0, 2);
0N/A coords[0] += x;
0N/A coords[1] += y;
0N/A coords[2] += x;
0N/A coords[3] += y;
0N/A sg.transform.transform(coords, 0, coords, 0, 2);
0N/A
0N/A if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,
0N/A null, interpType, coords))
0N/A {
0N/A return;
0N/A }
0N/A checkfinalxform = false;
0N/A } else {
0N/A checkfinalxform = true;
0N/A }
0N/A
0N/A // Begin Transform
0N/A AffineTransform tx = new AffineTransform(sg.transform);
0N/A tx.translate(x, y);
0N/A tx.concatenate(extraAT);
0N/A
0N/A // Do not try any more optimizations if either of the cases
0N/A // above was tried as we have already verified that the
0N/A // resulting transform will not simplify.
0N/A if (checkfinalxform) {
0N/A // In this case neither of the above simple transform
0N/A // pairs was found so we will do some final tests on
0N/A // the final rendering transform which may be the
0N/A // simple product of two complex transforms.
0N/A transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
0N/A } else {
0N/A renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * This method is called with a final rendering transform that
0N/A * has combined all of the information about the Graphics2D
0N/A * transform attribute with the transformations specified by
0N/A * the arguments to the drawImage call.
0N/A * Its role is to see if the combined transform ends up being
0N/A * acceleratable by either a renderImageCopy or renderImageScale
0N/A * once all of the math is done.
0N/A *
0N/A * Note: The transform supplied here has an origin that is
0N/A * already adjusted to point to the device location where
0N/A * the (sx1, sy1) location of the source image should be placed.
0N/A */
0N/A protected void transformImage(SunGraphics2D sg, Image img,
0N/A AffineTransform tx, int interpType,
0N/A int sx1, int sy1, int sx2, int sy2,
0N/A Color bgColor)
0N/A {
0N/A // Transform 3 source corners by tx and analyze them
0N/A // for simplified operations (Copy or Scale). Using
0N/A // 3 points lets us analyze any kind of transform,
0N/A // even transforms that involve very tiny amounts of
0N/A // rotation or skew to see if they degenerate to a
0N/A // simple scale or copy operation within the allowable
0N/A // error bounds.
0N/A // Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)
0N/A // because the transform is already translated such that
0N/A // the origin is where sx1, sy1 should go.
0N/A double coords[] = new double[6];
0N/A /* index: 0 1 2 3 4 5 */
0N/A /* coord: (0, 0), (w, h), (0, h) */
0N/A coords[2] = sx2 - sx1;
0N/A coords[3] = coords[5] = sy2 - sy1;
0N/A tx.transform(coords, 0, coords, 0, 3);
0N/A // First test if the X coords of the transformed UL
0N/A // and LL points match and that the Y coords of the
0N/A // transformed LR and LL points also match.
0N/A // If they do then it is a "rectilinear" transform and
0N/A // tryCopyOrScale will make sure it is upright and
0N/A // integer-based.
0N/A if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&
0N/A Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&
0N/A tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,
0N/A bgColor, interpType, coords))
0N/A {
0N/A return;
0N/A }
0N/A
0N/A renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);
0N/A }
0N/A
0N/A /*
0N/A * Check the bounding coordinates of the transformed source
0N/A * image to see if they fall on integer coordinates such
0N/A * that they will cause no interpolation anomalies if we
0N/A * use our simplified Blit or ScaledBlit operations instead
0N/A * of a full transform operation.
0N/A */
0N/A protected boolean tryCopyOrScale(SunGraphics2D sg,
0N/A Image img,
0N/A int sx1, int sy1,
0N/A int sx2, int sy2,
0N/A Color bgColor, int interpType,
0N/A double coords[])
0N/A {
0N/A double dx = coords[0];
0N/A double dy = coords[1];
0N/A double dw = coords[2] - dx;
0N/A double dh = coords[3] - dy;
0N/A // First check if width and height are very close to img w&h.
0N/A if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {
0N/A // Round location to nearest pixel and then test
0N/A // if it will cause interpolation anomalies.
0N/A int idx = (int) Math.floor(dx + 0.5);
0N/A int idy = (int) Math.floor(dy + 0.5);
0N/A if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||
0N/A (closeToInteger(idx, dx) && closeToInteger(idy, dy)))
0N/A {
0N/A renderImageCopy(sg, img, bgColor,
0N/A idx, idy,
0N/A sx1, sy1, sx2-sx1, sy2-sy1);
0N/A return true;
0N/A }
0N/A }
0N/A // (For now) We can only use our ScaledBlits if the image
0N/A // is upright (i.e. dw & dh both > 0)
0N/A if (dw > 0 && dh > 0) {
0N/A if (renderImageScale(sg, img, bgColor, interpType,
0N/A sx1, sy1, sx2, sy2,
0N/A coords[0], coords[1], coords[2], coords[3]))
0N/A {
0N/A return true;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /*
0N/A * Return a BufferedImage of the requested type with the indicated
0N/A * subimage of the original image located at 0,0 in the new image.
0N/A * If a bgColor is supplied, composite the original image over that
0N/A * color with a SrcOver operation, otherwise make a SrcNoEa copy.
0N/A */
0N/A BufferedImage makeBufferedImage(Image img, Color bgColor, int type,
0N/A int sx1, int sy1, int sx2, int sy2)
0N/A {
6084N/A final int width = sx2 - sx1;
6084N/A final int height = sy2 - sy1;
6084N/A final BufferedImage bimg = new BufferedImage(width, height, type);
6084N/A final SunGraphics2D g2d = (SunGraphics2D) bimg.createGraphics();
0N/A g2d.setComposite(AlphaComposite.Src);
0N/A if (bgColor != null) {
0N/A g2d.setColor(bgColor);
6084N/A g2d.fillRect(0, 0, width, height);
0N/A g2d.setComposite(AlphaComposite.SrcOver);
0N/A }
6084N/A g2d.copyImage(img, 0, 0, sx1, sy1, width, height, null, null);
0N/A g2d.dispose();
0N/A return bimg;
0N/A }
0N/A
0N/A protected void renderImageXform(SunGraphics2D sg, Image img,
0N/A AffineTransform tx, int interpType,
0N/A int sx1, int sy1, int sx2, int sy2,
0N/A Color bgColor)
0N/A {
0N/A Region clip = sg.getCompClip();
0N/A SurfaceData dstData = sg.surfaceData;
0N/A SurfaceData srcData = dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_GENERIC,
0N/A sg.imageComp,
0N/A bgColor);
0N/A
0N/A if (srcData == null) {
0N/A img = getBufferedImage(img);
0N/A srcData = dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_GENERIC,
0N/A sg.imageComp,
0N/A bgColor);
0N/A if (srcData == null) {
0N/A // REMIND: Is this correct? Can this happen?
0N/A return;
0N/A }
0N/A }
0N/A
0N/A if (isBgOperation(srcData, bgColor)) {
0N/A // We cannot perform bg operations during transform so make
0N/A // an opaque temp image with the appropriate background
0N/A // and work from there.
0N/A img = makeBufferedImage(img, bgColor, BufferedImage.TYPE_INT_RGB,
0N/A sx1, sy1, sx2, sy2);
0N/A // Temp image has appropriate subimage at 0,0 now.
0N/A sx2 -= sx1;
0N/A sy2 -= sy1;
0N/A sx1 = sy1 = 0;
0N/A
0N/A srcData = dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_GENERIC,
0N/A sg.imageComp,
0N/A bgColor);
0N/A }
0N/A
0N/A SurfaceType srcType = srcData.getSurfaceType();
0N/A TransformHelper helper = TransformHelper.getFromCache(srcType);
0N/A
0N/A if (helper == null) {
0N/A /* We have no helper for this source image type.
0N/A * But we know that we do have helpers for both RGB and ARGB,
0N/A * so convert to one of those types depending on transparency.
0N/A * ARGB_PRE might be a better choice if the source image has
0N/A * alpha, but it may cause some recursion here since we only
0N/A * tend to have converters that convert to ARGB.
0N/A */
0N/A int type = ((srcData.getTransparency() == Transparency.OPAQUE)
0N/A ? BufferedImage.TYPE_INT_RGB
0N/A : BufferedImage.TYPE_INT_ARGB);
0N/A img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);
0N/A // Temp image has appropriate subimage at 0,0 now.
0N/A sx2 -= sx1;
0N/A sy2 -= sy1;
0N/A sx1 = sy1 = 0;
0N/A
0N/A srcData = dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_GENERIC,
0N/A sg.imageComp,
0N/A null);
0N/A srcType = srcData.getSurfaceType();
0N/A helper = TransformHelper.getFromCache(srcType);
0N/A // assert(helper != null);
0N/A }
0N/A
0N/A AffineTransform itx;
0N/A try {
0N/A itx = tx.createInverse();
0N/A } catch (NoninvertibleTransformException e) {
0N/A // Non-invertible transform means no output
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Find the maximum bounds on the destination that will be
0N/A * affected by the transformed source. First, transform all
0N/A * four corners of the source and then min and max the resulting
0N/A * destination coordinates of the transformed corners.
0N/A * Note that tx already has the offset to sx1,sy1 accounted
0N/A * for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the
0N/A * source coordinates.
0N/A */
0N/A double coords[] = new double[8];
0N/A /* corner: UL UR LL LR */
0N/A /* index: 0 1 2 3 4 5 6 7 */
0N/A /* coord: (0, 0), (w, 0), (0, h), (w, h) */
0N/A coords[2] = coords[6] = sx2 - sx1;
0N/A coords[5] = coords[7] = sy2 - sy1;
0N/A tx.transform(coords, 0, coords, 0, 4);
0N/A double ddx1, ddy1, ddx2, ddy2;
0N/A ddx1 = ddx2 = coords[0];
0N/A ddy1 = ddy2 = coords[1];
0N/A for (int i = 2; i < coords.length; i += 2) {
0N/A double d = coords[i];
0N/A if (ddx1 > d) ddx1 = d;
0N/A else if (ddx2 < d) ddx2 = d;
0N/A d = coords[i+1];
0N/A if (ddy1 > d) ddy1 = d;
0N/A else if (ddy2 < d) ddy2 = d;
0N/A }
0N/A int dx1 = (int) Math.floor(ddx1);
0N/A int dy1 = (int) Math.floor(ddy1);
0N/A int dx2 = (int) Math.ceil(ddx2);
0N/A int dy2 = (int) Math.ceil(ddy2);
0N/A
0N/A SurfaceType dstType = dstData.getSurfaceType();
0N/A MaskBlit maskblit;
0N/A Blit blit;
0N/A if (sg.compositeState <= sg.COMP_ALPHA) {
0N/A /* NOTE: We either have, or we can make,
0N/A * a MaskBlit for any alpha composite type
0N/A */
0N/A maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,
0N/A sg.imageComp,
0N/A dstType);
0N/A
0N/A /* NOTE: We can only use the native TransformHelper
0N/A * func to go directly to the dest if both the helper
0N/A * and the MaskBlit are native.
0N/A * All helpers are native at this point, but some MaskBlit
0N/A * objects are implemented in Java, so we need to check.
0N/A */
0N/A if (maskblit.getNativePrim() != 0) {
0N/A // We can render directly.
0N/A helper.Transform(maskblit, srcData, dstData,
0N/A sg.composite, clip,
0N/A itx, interpType,
0N/A sx1, sy1, sx2, sy2,
0N/A dx1, dy1, dx2, dy2,
0N/A null, 0, 0);
0N/A return;
0N/A }
0N/A blit = null;
0N/A } else {
0N/A /* NOTE: We either have, or we can make,
0N/A * a Blit for any composite type, even Custom
0N/A */
0N/A maskblit = null;
0N/A blit = Blit.getFromCache(SurfaceType.IntArgbPre,
0N/A sg.imageComp,
0N/A dstType);
0N/A }
0N/A
0N/A // We need to transform to a temp image and then copy
0N/A // just the pieces that are valid data to the dest.
0N/A BufferedImage tmpimg = new BufferedImage(dx2-dx1, dy2-dy1,
0N/A BufferedImage.TYPE_INT_ARGB);
0N/A SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);
0N/A SurfaceType tmpType = tmpData.getSurfaceType();
0N/A MaskBlit tmpmaskblit =
0N/A MaskBlit.getFromCache(SurfaceType.IntArgbPre,
0N/A CompositeType.SrcNoEa,
0N/A tmpType);
0N/A /*
0N/A * The helper function fills a temporary edges buffer
0N/A * for us with the bounding coordinates of each scanline
0N/A * in the following format:
0N/A *
0N/A * edges[0, 1] = [top y, bottom y)
0N/A * edges[2, 3] = [left x, right x) of top row
0N/A * ...
0N/A * edges[h*2, h*2+1] = [left x, right x) of bottom row
0N/A *
0N/A * all coordinates in the edges array will be relative to dx1, dy1
0N/A *
0N/A * edges thus has to be h*2+2 in length
0N/A */
0N/A int edges[] = new int[(dy2-dy1)*2+2];
4301N/A // It is important that edges[0]=edges[1]=0 when we call
4301N/A // Transform in case it must return early and we would
4301N/A // not want to render anything on an error condition.
0N/A helper.Transform(tmpmaskblit, srcData, tmpData,
0N/A AlphaComposite.Src, null,
0N/A itx, interpType,
0N/A sx1, sy1, sx2, sy2,
0N/A 0, 0, dx2-dx1, dy2-dy1,
0N/A edges, dx1, dy1);
0N/A
0N/A /*
0N/A * Now copy the results, scanline by scanline, into the dest.
0N/A * The edges array helps us minimize the work.
0N/A */
0N/A int index = 2;
0N/A for (int y = edges[0]; y < edges[1]; y++) {
0N/A int relx1 = edges[index++];
0N/A int relx2 = edges[index++];
0N/A if (relx1 >= relx2) {
0N/A continue;
0N/A }
0N/A if (maskblit != null) {
0N/A maskblit.MaskBlit(tmpData, dstData,
0N/A sg.composite, clip,
0N/A relx1, y,
0N/A dx1+relx1, dy1+y,
0N/A relx2 - relx1, 1,
0N/A null, 0, 0);
0N/A } else {
0N/A blit.Blit(tmpData, dstData,
0N/A sg.composite, clip,
0N/A relx1, y,
0N/A dx1+relx1, dy1+y,
0N/A relx2 - relx1, 1);
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Render an image using only integer translation
0N/A // (no scale or transform or sub-pixel interpolated translations).
0N/A protected boolean renderImageCopy(SunGraphics2D sg, Image img,
0N/A Color bgColor,
0N/A int dx, int dy,
0N/A int sx, int sy,
0N/A int w, int h)
0N/A {
0N/A Region clip = sg.getCompClip();
0N/A SurfaceData dstData = sg.surfaceData;
0N/A
0N/A int attempts = 0;
0N/A // Loop up to twice through; this gives us a chance to
0N/A // revalidate the surfaceData objects in case of an exception
0N/A // and try it once more
0N/A while (true) {
0N/A SurfaceData srcData =
0N/A dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_ISIDENT,
0N/A sg.imageComp,
0N/A bgColor);
0N/A if (srcData == null) {
0N/A return false;
0N/A }
0N/A
0N/A try {
0N/A SurfaceType srcType = srcData.getSurfaceType();
0N/A SurfaceType dstType = dstData.getSurfaceType();
0N/A blitSurfaceData(sg, clip,
0N/A srcData, dstData, srcType, dstType,
0N/A sx, sy, dx, dy, w, h, bgColor);
0N/A return true;
0N/A } catch (NullPointerException e) {
0N/A if (!(SurfaceData.isNull(dstData) ||
0N/A SurfaceData.isNull(srcData)))
0N/A {
0N/A // Something else caused the exception, throw it...
0N/A throw e;
0N/A }
0N/A return false;
0N/A // NOP if we have been disposed
0N/A } catch (InvalidPipeException e) {
0N/A // Always catch the exception; try this a couple of times
0N/A // and fail silently if the system is not yet ready to
0N/A // revalidate the source or dest surfaceData objects.
0N/A ++attempts;
0N/A clip = sg.getCompClip(); // ensures sg.surfaceData is valid
0N/A dstData = sg.surfaceData;
0N/A if (SurfaceData.isNull(dstData) ||
0N/A SurfaceData.isNull(srcData) || (attempts > 1))
0N/A {
0N/A return false;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Render an image using only integer scaling (no transform).
0N/A protected boolean renderImageScale(SunGraphics2D sg, Image img,
0N/A Color bgColor, int interpType,
0N/A int sx1, int sy1,
0N/A int sx2, int sy2,
0N/A double dx1, double dy1,
0N/A double dx2, double dy2)
0N/A {
0N/A // Currently only NEAREST_NEIGHBOR interpolation is implemented
0N/A // for ScaledBlit operations.
0N/A if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
0N/A return false;
0N/A }
0N/A
0N/A Region clip = sg.getCompClip();
0N/A SurfaceData dstData = sg.surfaceData;
0N/A
0N/A int attempts = 0;
0N/A // Loop up to twice through; this gives us a chance to
0N/A // revalidate the surfaceData objects in case of an exception
0N/A // and try it once more
0N/A while (true) {
0N/A SurfaceData srcData =
0N/A dstData.getSourceSurfaceData(img,
0N/A sg.TRANSFORM_TRANSLATESCALE,
0N/A sg.imageComp,
0N/A bgColor);
0N/A
0N/A if (srcData == null || isBgOperation(srcData, bgColor)) {
0N/A return false;
0N/A }
0N/A
0N/A try {
0N/A SurfaceType srcType = srcData.getSurfaceType();
0N/A SurfaceType dstType = dstData.getSurfaceType();
0N/A return scaleSurfaceData(sg, clip,
0N/A srcData, dstData, srcType, dstType,
0N/A sx1, sy1, sx2, sy2,
0N/A dx1, dy1, dx2, dy2);
0N/A } catch (NullPointerException e) {
0N/A if (!SurfaceData.isNull(dstData)) {
0N/A // Something else caused the exception, throw it...
0N/A throw e;
0N/A }
0N/A return false;
0N/A // NOP if we have been disposed
0N/A } catch (InvalidPipeException e) {
0N/A // Always catch the exception; try this a couple of times
0N/A // and fail silently if the system is not yet ready to
0N/A // revalidate the source or dest surfaceData objects.
0N/A ++attempts;
0N/A clip = sg.getCompClip(); // ensures sg.surfaceData is valid
0N/A dstData = sg.surfaceData;
0N/A if (SurfaceData.isNull(dstData) ||
0N/A SurfaceData.isNull(srcData) || (attempts > 1))
0N/A {
0N/A return false;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A public boolean scaleImage(SunGraphics2D sg, Image img,
0N/A int dx1, int dy1, int dx2, int dy2,
0N/A int sx1, int sy1, int sx2, int sy2,
0N/A Color bgColor)
0N/A {
0N/A int srcW, srcH, dstW, dstH;
0N/A int srcX, srcY, dstX, dstY;
0N/A boolean srcWidthFlip = false;
0N/A boolean srcHeightFlip = false;
0N/A boolean dstWidthFlip = false;
0N/A boolean dstHeightFlip = false;
0N/A
0N/A if (sx2 > sx1) {
0N/A srcW = sx2 - sx1;
0N/A srcX = sx1;
0N/A } else {
0N/A srcWidthFlip = true;
0N/A srcW = sx1 - sx2;
0N/A srcX = sx2;
0N/A }
0N/A if (sy2 > sy1) {
0N/A srcH = sy2-sy1;
0N/A srcY = sy1;
0N/A } else {
0N/A srcHeightFlip = true;
0N/A srcH = sy1-sy2;
0N/A srcY = sy2;
0N/A }
0N/A if (dx2 > dx1) {
0N/A dstW = dx2 - dx1;
0N/A dstX = dx1;
0N/A } else {
0N/A dstW = dx1 - dx2;
0N/A dstWidthFlip = true;
0N/A dstX = dx2;
0N/A }
0N/A if (dy2 > dy1) {
0N/A dstH = dy2 - dy1;
0N/A dstY = dy1;
0N/A } else {
0N/A dstH = dy1 - dy2;
0N/A dstHeightFlip = true;
0N/A dstY = dy2;
0N/A }
0N/A if (srcW <= 0 || srcH <= 0) {
0N/A return true;
0N/A }
0N/A // Only accelerate scale if it does not involve a flip or transform
0N/A if ((srcWidthFlip == dstWidthFlip) &&
0N/A (srcHeightFlip == dstHeightFlip) &&
0N/A isSimpleTranslate(sg))
0N/A {
0N/A double ddx1 = dstX + sg.transX;
0N/A double ddy1 = dstY + sg.transY;
0N/A double ddx2 = ddx1 + dstW;
0N/A double ddy2 = ddy1 + dstH;
0N/A if (renderImageScale(sg, img, bgColor, sg.interpolationType,
0N/A srcX, srcY, srcX+srcW, srcY+srcH,
0N/A ddx1, ddy1, ddx2, ddy2))
0N/A {
0N/A return true;
0N/A }
0N/A }
0N/A
0N/A AffineTransform atfm = new AffineTransform(sg.transform);
0N/A atfm.translate(dx1, dy1);
0N/A double m00 = (double)(dx2-dx1)/(sx2-sx1);
0N/A double m11 = (double)(dy2-dy1)/(sy2-sy1);
0N/A atfm.scale(m00, m11);
0N/A atfm.translate(srcX-sx1, srcY-sy1);
0N/A
6084N/A final int scale = SurfaceManager.getImageScale(img);
6084N/A final int imgW = img.getWidth(null) * scale;
6084N/A final int imgH = img.getHeight(null) * scale;
0N/A srcW += srcX;
0N/A srcH += srcY;
0N/A // Make sure we are not out of bounds
0N/A if (srcW > imgW) {
0N/A srcW = imgW;
0N/A }
0N/A if (srcH > imgH) {
0N/A srcH = imgH;
0N/A }
0N/A if (srcX < 0) {
0N/A atfm.translate(-srcX, 0);
0N/A srcX = 0;
0N/A }
0N/A if (srcY < 0) {
0N/A atfm.translate(0, -srcY);
0N/A srcY = 0;
0N/A }
0N/A if (srcX >= srcW || srcY >= srcH) {
0N/A return true;
0N/A }
0N/A // Note: src[WH] are currently the right and bottom coordinates.
0N/A // The following two lines would adjust src[WH] back to being
0N/A // dimensions.
0N/A // srcW -= srcX;
0N/A // srcH -= srcY;
0N/A // Since transformImage needs right and bottom coords we will
0N/A // omit this adjustment.
0N/A
0N/A transformImage(sg, img, atfm, sg.interpolationType,
0N/A srcX, srcY, srcW, srcH, bgColor);
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A ** Utilities
0N/A ** The following methods are used by the public methods above
0N/A ** for performing various operations
0N/A **/
0N/A
0N/A /*
0N/A * This constant represents a tradeoff between the
0N/A * need to make sure that image transformations are
0N/A * "very close" to integer device coordinates before
0N/A * we decide to use an integer scale or copy operation
0N/A * as a substitute and the fact that roundoff errors
0N/A * in AffineTransforms are frequently introduced by
0N/A * performing multiple sequential operations on them.
0N/A *
0N/A * The evaluation of bug 4990624 details the potential
0N/A * for this error cutoff to result in display anomalies
0N/A * in different types of image operations and how this
0N/A * value represents a good compromise here.
0N/A */
0N/A private static final double MAX_TX_ERROR = .0001;
0N/A
0N/A public static boolean closeToInteger(int i, double d) {
0N/A return (Math.abs(d-i) < MAX_TX_ERROR);
0N/A }
0N/A
0N/A public static boolean isSimpleTranslate(SunGraphics2D sg) {
0N/A int ts = sg.transformState;
0N/A if (ts <= sg.TRANSFORM_INT_TRANSLATE) {
0N/A // Integer translates are always "simple"
0N/A return true;
0N/A }
0N/A if (ts >= sg.TRANSFORM_TRANSLATESCALE) {
0N/A // Scales and beyond are always "not simple"
0N/A return false;
0N/A }
0N/A // non-integer translates are only simple when not interpolating
0N/A if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
0N/A return true;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {
0N/A // If we cannot get the srcData, then cannot assume anything about
0N/A // the image
0N/A return ((srcData == null) ||
0N/A ((bgColor != null) &&
0N/A (srcData.getTransparency() != Transparency.OPAQUE)));
0N/A }
0N/A
0N/A protected BufferedImage getBufferedImage(Image img) {
0N/A if (img instanceof BufferedImage) {
0N/A return (BufferedImage)img;
0N/A }
0N/A // Must be VolatileImage; get BufferedImage representation
0N/A return ((VolatileImage)img).getSnapshot();
0N/A }
0N/A
0N/A /*
0N/A * Return the color model to be used with this BufferedImage and
0N/A * transform.
0N/A */
0N/A private ColorModel getTransformColorModel(SunGraphics2D sg,
0N/A BufferedImage bImg,
0N/A AffineTransform tx) {
0N/A ColorModel cm = bImg.getColorModel();
0N/A ColorModel dstCM = cm;
0N/A
0N/A if (tx.isIdentity()) {
0N/A return dstCM;
0N/A }
0N/A int type = tx.getType();
0N/A boolean needTrans =
0N/A ((type&(tx.TYPE_MASK_ROTATION|tx.TYPE_GENERAL_TRANSFORM)) != 0);
0N/A if (! needTrans && type != tx.TYPE_TRANSLATION && type != tx.TYPE_IDENTITY)
0N/A {
0N/A double[] mtx = new double[4];
0N/A tx.getMatrix(mtx);
0N/A // Check out the matrix. A non-integral scale will force ARGB
0N/A // since the edge conditions cannot be guaranteed.
0N/A needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);
0N/A }
0N/A
0N/A if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {
0N/A if (cm instanceof IndexColorModel) {
0N/A Raster raster = bImg.getRaster();
0N/A IndexColorModel icm = (IndexColorModel) cm;
0N/A // Just need to make sure that we have a transparent pixel
0N/A if (needTrans && cm.getTransparency() == cm.OPAQUE) {
0N/A // Fix 4221407
0N/A if (raster instanceof sun.awt.image.BytePackedRaster) {
0N/A dstCM = ColorModel.getRGBdefault();
0N/A }
0N/A else {
0N/A double[] matrix = new double[6];
0N/A tx.getMatrix(matrix);
0N/A if (matrix[1] == 0. && matrix[2] ==0.
0N/A && matrix[4] == 0. && matrix[5] == 0.) {
0N/A // Only scaling so do not need to create
0N/A }
0N/A else {
0N/A int mapSize = icm.getMapSize();
0N/A if (mapSize < 256) {
0N/A int[] cmap = new int[mapSize+1];
0N/A icm.getRGBs(cmap);
0N/A cmap[mapSize] = 0x0000;
0N/A dstCM = new
0N/A IndexColorModel(icm.getPixelSize(),
0N/A mapSize+1,
0N/A cmap, 0, true, mapSize,
0N/A DataBuffer.TYPE_BYTE);
0N/A }
0N/A else {
0N/A dstCM = ColorModel.getRGBdefault();
0N/A }
0N/A } /* if (matrix[0] < 1.f ...) */
0N/A } /* raster instanceof sun.awt.image.BytePackedRaster */
0N/A } /* if (cm.getTransparency() == cm.OPAQUE) */
0N/A } /* if (cm instanceof IndexColorModel) */
0N/A else if (needTrans && cm.getTransparency() == cm.OPAQUE) {
0N/A // Need a bitmask transparency
0N/A // REMIND: for now, use full transparency since no loops
0N/A // for bitmask
0N/A dstCM = ColorModel.getRGBdefault();
0N/A }
0N/A } /* if (sg.renderHint == RENDER_QUALITY) */
0N/A else {
0N/A
0N/A if (cm instanceof IndexColorModel ||
0N/A (needTrans && cm.getTransparency() == cm.OPAQUE))
0N/A {
0N/A // Need a bitmask transparency
0N/A // REMIND: for now, use full transparency since no loops
0N/A // for bitmask
0N/A dstCM = ColorModel.getRGBdefault();
0N/A }
0N/A }
0N/A
0N/A return dstCM;
0N/A }
0N/A
0N/A protected void blitSurfaceData(SunGraphics2D sg,
0N/A Region clipRegion,
0N/A SurfaceData srcData,
0N/A SurfaceData dstData,
0N/A SurfaceType srcType,
0N/A SurfaceType dstType,
0N/A int sx, int sy, int dx, int dy,
0N/A int w, int h,
0N/A Color bgColor)
0N/A {
0N/A if (w <= 0 || h <= 0) {
0N/A /*
0N/A * Fix for bugid 4783274 - BlitBg throws an exception for
0N/A * a particular set of anomalous parameters.
0N/A * REMIND: The native loops do proper clipping and would
0N/A * detect this situation themselves, but the Java loops
0N/A * all seem to trust their parameters a little too well
0N/A * to the point where they will try to process a negative
0N/A * area of pixels and throw exceptions. The real fix is
0N/A * to modify the Java loops to do proper clipping so that
0N/A * they can deal with negative dimensions as well as
0N/A * improperly large dimensions, but that fix is too risky
0N/A * to integrate for Mantis at this point. In the meantime
0N/A * eliminating the negative or zero dimensions here is
0N/A * "correct" and saves them from some nasty exceptional
0N/A * conditions, one of which is the test case of 4783274.
0N/A */
0N/A return;
0N/A }
0N/A CompositeType comp = sg.imageComp;
0N/A if (CompositeType.SrcOverNoEa.equals(comp) &&
0N/A (srcData.getTransparency() == Transparency.OPAQUE ||
0N/A (bgColor != null &&
0N/A bgColor.getTransparency() == Transparency.OPAQUE)))
0N/A {
0N/A comp = CompositeType.SrcNoEa;
0N/A }
0N/A if (!isBgOperation(srcData, bgColor)) {
0N/A Blit blit = Blit.getFromCache(srcType, comp, dstType);
0N/A blit.Blit(srcData, dstData, sg.composite, clipRegion,
0N/A sx, sy, dx, dy, w, h);
0N/A } else {
0N/A BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);
0N/A blit.BlitBg(srcData, dstData, sg.composite, clipRegion,
430N/A bgColor.getRGB(), sx, sy, dx, dy, w, h);
0N/A }
0N/A }
0N/A
0N/A protected boolean scaleSurfaceData(SunGraphics2D sg,
0N/A Region clipRegion,
0N/A SurfaceData srcData,
0N/A SurfaceData dstData,
0N/A SurfaceType srcType,
0N/A SurfaceType dstType,
0N/A int sx1, int sy1,
0N/A int sx2, int sy2,
0N/A double dx1, double dy1,
0N/A double dx2, double dy2)
0N/A {
0N/A CompositeType comp = sg.imageComp;
0N/A if (CompositeType.SrcOverNoEa.equals(comp) &&
0N/A (srcData.getTransparency() == Transparency.OPAQUE))
0N/A {
0N/A comp = CompositeType.SrcNoEa;
0N/A }
0N/A
0N/A ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);
0N/A if (blit != null) {
0N/A blit.Scale(srcData, dstData, sg.composite, clipRegion,
0N/A sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
0N/A return true;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A protected static boolean imageReady(ToolkitImage sunimg,
0N/A ImageObserver observer)
0N/A {
0N/A if (sunimg.hasError()) {
0N/A if (observer != null) {
0N/A observer.imageUpdate(sunimg,
0N/A ImageObserver.ERROR|ImageObserver.ABORT,
0N/A -1, -1, -1, -1);
0N/A }
0N/A return false;
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A public boolean copyImage(SunGraphics2D sg, Image img,
0N/A int x, int y,
0N/A Color bgColor,
0N/A ImageObserver observer) {
0N/A if (!(img instanceof ToolkitImage)) {
0N/A return copyImage(sg, img, x, y, bgColor);
0N/A } else {
0N/A ToolkitImage sunimg = (ToolkitImage)img;
0N/A if (!imageReady(sunimg, observer)) {
0N/A return false;
0N/A }
0N/A ImageRepresentation ir = sunimg.getImageRep();
0N/A return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);
0N/A }
0N/A }
0N/A
0N/A public boolean copyImage(SunGraphics2D sg, Image img,
0N/A int dx, int dy, int sx, int sy, int w, int h,
0N/A Color bgColor,
0N/A ImageObserver observer) {
0N/A if (!(img instanceof ToolkitImage)) {
0N/A return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);
0N/A } else {
0N/A ToolkitImage sunimg = (ToolkitImage)img;
0N/A if (!imageReady(sunimg, observer)) {
0N/A return false;
0N/A }
0N/A ImageRepresentation ir = sunimg.getImageRep();
0N/A return ir.drawToBufImage(sg, sunimg,
0N/A dx, dy, (dx + w), (dy + h),
0N/A sx, sy, (sx + w), (sy + h),
0N/A bgColor, observer);
0N/A }
0N/A }
0N/A
0N/A public boolean scaleImage(SunGraphics2D sg, Image img,
0N/A int x, int y,
0N/A int width, int height,
0N/A Color bgColor,
0N/A ImageObserver observer) {
0N/A if (!(img instanceof ToolkitImage)) {
0N/A return scaleImage(sg, img, x, y, width, height, bgColor);
0N/A } else {
0N/A ToolkitImage sunimg = (ToolkitImage)img;
0N/A if (!imageReady(sunimg, observer)) {
0N/A return false;
0N/A }
0N/A ImageRepresentation ir = sunimg.getImageRep();
0N/A return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,
0N/A observer);
0N/A }
0N/A }
0N/A
0N/A public boolean scaleImage(SunGraphics2D sg, Image img,
0N/A int dx1, int dy1, int dx2, int dy2,
0N/A int sx1, int sy1, int sx2, int sy2,
0N/A Color bgColor,
0N/A ImageObserver observer) {
0N/A if (!(img instanceof ToolkitImage)) {
0N/A return scaleImage(sg, img, dx1, dy1, dx2, dy2,
0N/A sx1, sy1, sx2, sy2, bgColor);
0N/A } else {
0N/A ToolkitImage sunimg = (ToolkitImage)img;
0N/A if (!imageReady(sunimg, observer)) {
0N/A return false;
0N/A }
0N/A ImageRepresentation ir = sunimg.getImageRep();
0N/A return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,
0N/A sx1, sy1, sx2, sy2, bgColor, observer);
0N/A }
0N/A }
0N/A
0N/A public boolean transformImage(SunGraphics2D sg, Image img,
0N/A AffineTransform atfm,
0N/A ImageObserver observer) {
0N/A if (!(img instanceof ToolkitImage)) {
0N/A transformImage(sg, img, 0, 0, atfm, sg.interpolationType);
0N/A return true;
0N/A } else {
0N/A ToolkitImage sunimg = (ToolkitImage)img;
0N/A if (!imageReady(sunimg, observer)) {
0N/A return false;
0N/A }
0N/A ImageRepresentation ir = sunimg.getImageRep();
0N/A return ir.drawToBufImage(sg, sunimg, atfm, observer);
0N/A }
0N/A }
0N/A
0N/A public void transformImage(SunGraphics2D sg, BufferedImage img,
0N/A BufferedImageOp op, int x, int y)
0N/A {
0N/A if (op != null) {
0N/A if (op instanceof AffineTransformOp) {
0N/A AffineTransformOp atop = (AffineTransformOp) op;
0N/A transformImage(sg, img, x, y,
0N/A atop.getTransform(),
0N/A atop.getInterpolationType());
0N/A return;
0N/A } else {
0N/A img = op.filter(img, null);
0N/A }
0N/A }
0N/A copyImage(sg, img, x, y, null);
0N/A }
0N/A}