1110 lines · 44.3 KB · Java
RawDownload
/* * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.java2d.pipe; import java.awt.AlphaComposite;import java.awt.Color;import java.awt.Graphics2D;import java.awt.Image;import java.awt.Rectangle;import java.awt.Transparency;import java.awt.geom.AffineTransform;import java.awt.geom.NoninvertibleTransformException;import java.awt.image.AffineTransformOp;import java.awt.image.BufferedImage;import java.awt.image.BufferedImageOp;import java.awt.image.ColorModel;import java.awt.image.DataBuffer;import java.awt.image.DirectColorModel;import java.awt.image.ImageObserver;import java.awt.image.IndexColorModel;import java.awt.image.Raster;import java.awt.image.VolatileImage;import java.awt.image.WritableRaster;import java.awt.image.ImagingOpException;import sun.awt.SunHints;import sun.awt.image.ImageRepresentation;import sun.awt.image.ToolkitImage;import sun.java2d.InvalidPipeException;import sun.java2d.SunGraphics2D;import sun.java2d.SurfaceData;import sun.java2d.loops.Blit;import sun.java2d.loops.BlitBg;import sun.java2d.loops.TransformHelper;import sun.java2d.loops.MaskBlit;import sun.java2d.loops.CompositeType;import sun.java2d.loops.ScaledBlit;import sun.java2d.loops.SurfaceType; public class DrawImage implements DrawImagePipe{    public boolean copyImage(SunGraphics2D sg, Image img,                             int x, int y,                             Color bgColor)    {        int imgw = img.getWidth(null);        int imgh = img.getHeight(null);        if (isSimpleTranslate(sg)) {            return renderImageCopy(sg, img, bgColor,                                   x + sg.transX, y + sg.transY,                                   0, 0, imgw, imgh);        }        AffineTransform atfm = sg.transform;        if ((x | y) != 0) {            atfm = new AffineTransform(atfm);            atfm.translate(x, y);        }        transformImage(sg, img, atfm, sg.interpolationType,                       0, 0, imgw, imgh, bgColor);        return true;    }     public boolean copyImage(SunGraphics2D sg, Image img,                             int dx, int dy, int sx, int sy, int w, int h,                             Color bgColor)    {        if (isSimpleTranslate(sg)) {            return renderImageCopy(sg, img, bgColor,                                   dx + sg.transX, dy + sg.transY,                                   sx, sy, w, h);        }        scaleImage(sg, img, dx, dy, (dx + w), (dy + h),                   sx, sy, (sx + w), (sy + h), bgColor);        return true;    }     public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,                              int width, int height,                              Color bgColor)    {        int imgw = img.getWidth(null);        int imgh = img.getHeight(null);        // Only accelerate scale if:        //          - w/h positive values        //          - sg transform integer translate/identity only        //          - no bgColor in operation        if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {            double dx1 = x + sg.transX;            double dy1 = y + sg.transY;            double dx2 = dx1 + width;            double dy2 = dy1 + height;            if (renderImageScale(sg, img, bgColor, sg.interpolationType,                                 0, 0, imgw, imgh,                                 dx1, dy1, dx2, dy2))            {                return true;            }        }         AffineTransform atfm = sg.transform;        if ((x | y) != 0 || width != imgw || height != imgh) {            atfm = new AffineTransform(atfm);            atfm.translate(x, y);            atfm.scale(((double)width)/imgw, ((double)height)/imgh);        }        transformImage(sg, img, atfm, sg.interpolationType,                       0, 0, imgw, imgh, bgColor);        return true;    }     /*     * This method is only called in those circumstances where the     * operation has a non-null secondary transform specfied.  Its     * role is to check for various optimizations based on the types     * of both the secondary and SG2D transforms and to do some     * quick calculations to avoid having to combine the transforms     * and/or to call a more generalized method.     */    protected void transformImage(SunGraphics2D sg, Image img, int x, int y,                                  AffineTransform extraAT, int interpType)    {        int txtype = extraAT.getType();        int imgw = img.getWidth(null);        int imgh = img.getHeight(null);        boolean checkfinalxform;         if (sg.transformState <= sg.TRANSFORM_ANY_TRANSLATE &&            (txtype == AffineTransform.TYPE_IDENTITY ||             txtype == AffineTransform.TYPE_TRANSLATION))        {            // First optimization - both are some kind of translate             // Combine the translations and check if interpolation is necessary.            double tx = extraAT.getTranslateX();            double ty = extraAT.getTranslateY();            tx += sg.transform.getTranslateX();            ty += sg.transform.getTranslateY();            int itx = (int) Math.floor(tx + 0.5);            int ity = (int) Math.floor(ty + 0.5);            if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||                (closeToInteger(itx, tx) && closeToInteger(ity, ty)))            {                renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);                return;            }            checkfinalxform = false;        } else if (sg.transformState <= sg.TRANSFORM_TRANSLATESCALE &&                   ((txtype & (AffineTransform.TYPE_FLIP |                               AffineTransform.TYPE_MASK_ROTATION |                               AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))        {            // Second optimization - both are some kind of translate or scale             // Combine the scales and check if interpolation is necessary.             // Transform source bounds by extraAT,            // then translate the bounds again by x, y            // then transform the bounds again by sg.transform            double coords[] = new double[] {                0, 0, imgw, imgh,            };            extraAT.transform(coords, 0, coords, 0, 2);            coords[0] += x;            coords[1] += y;            coords[2] += x;            coords[3] += y;            sg.transform.transform(coords, 0, coords, 0, 2);             if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,                               null, interpType, coords))            {                return;            }            checkfinalxform = false;        } else {            checkfinalxform = true;        }         // Begin Transform        AffineTransform tx = new AffineTransform(sg.transform);        tx.translate(x, y);        tx.concatenate(extraAT);         // Do not try any more optimizations if either of the cases        // above was tried as we have already verified that the        // resulting transform will not simplify.        if (checkfinalxform) {            // In this case neither of the above simple transform            // pairs was found so we will do some final tests on            // the final rendering transform which may be the            // simple product of two complex transforms.            transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);        } else {            renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);        }    }     /*     * This method is called with a final rendering transform that     * has combined all of the information about the Graphics2D     * transform attribute with the transformations specified by     * the arguments to the drawImage call.     * Its role is to see if the combined transform ends up being     * acceleratable by either a renderImageCopy or renderImageScale     * once all of the math is done.     *     * Note: The transform supplied here has an origin that is     * already adjusted to point to the device location where     * the (sx1, sy1) location of the source image should be placed.     */    protected void transformImage(SunGraphics2D sg, Image img,                                  AffineTransform tx, int interpType,                                  int sx1, int sy1, int sx2, int sy2,                                  Color bgColor)    {        // Transform 3 source corners by tx and analyze them        // for simplified operations (Copy or Scale).  Using        // 3 points lets us analyze any kind of transform,        // even transforms that involve very tiny amounts of        // rotation or skew to see if they degenerate to a        // simple scale or copy operation within the allowable        // error bounds.        // Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)        // because the transform is already translated such that        // the origin is where sx1, sy1 should go.        double coords[] = new double[6];        /* index:  0  1    2  3    4  5  */        /* coord: (0, 0), (w, h), (0, h) */        coords[2] = sx2 - sx1;        coords[3] = coords[5] = sy2 - sy1;        tx.transform(coords, 0, coords, 0, 3);        // First test if the X coords of the transformed UL        // and LL points match and that the Y coords of the        // transformed LR and LL points also match.        // If they do then it is a "rectilinear" transform and        // tryCopyOrScale will make sure it is upright and        // integer-based.        if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&            Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&            tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,                           bgColor, interpType, coords))        {            return;        }         renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);    }     /*     * Check the bounding coordinates of the transformed source     * image to see if they fall on integer coordinates such     * that they will cause no interpolation anomalies if we     * use our simplified Blit or ScaledBlit operations instead     * of a full transform operation.     */    protected boolean tryCopyOrScale(SunGraphics2D sg,                                     Image img,                                     int sx1, int sy1,                                     int sx2, int sy2,                                     Color bgColor, int interpType,                                     double coords[])    {        double dx = coords[0];        double dy = coords[1];        double dw = coords[2] - dx;        double dh = coords[3] - dy;        // First check if width and height are very close to img w&h.        if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {            // Round location to nearest pixel and then test            // if it will cause interpolation anomalies.            int idx = (int) Math.floor(dx + 0.5);            int idy = (int) Math.floor(dy + 0.5);            if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||                (closeToInteger(idx, dx) && closeToInteger(idy, dy)))            {                renderImageCopy(sg, img, bgColor,                                idx, idy,                                sx1, sy1, sx2-sx1, sy2-sy1);                return true;            }        }        // (For now) We can only use our ScaledBlits if the image        // is upright (i.e. dw & dh both > 0)        if (dw > 0 && dh > 0) {            if (renderImageScale(sg, img, bgColor, interpType,                                 sx1, sy1, sx2, sy2,                                 coords[0], coords[1], coords[2], coords[3]))            {                return true;            }        }        return false;    }     /*     * Return a BufferedImage of the requested type with the indicated     * subimage of the original image located at 0,0 in the new image.     * If a bgColor is supplied, composite the original image over that     * color with a SrcOver operation, otherwise make a SrcNoEa copy.     */    BufferedImage makeBufferedImage(Image img, Color bgColor, int type,                                    int sx1, int sy1, int sx2, int sy2)    {        BufferedImage bimg = new BufferedImage(sx2-sx1, sy2-sy1, type);        Graphics2D g2d = bimg.createGraphics();        g2d.setComposite(AlphaComposite.Src);        if (bgColor != null) {            g2d.setColor(bgColor);            g2d.fillRect(0, 0, sx2-sx1, sy2-sy1);            g2d.setComposite(AlphaComposite.SrcOver);        }        g2d.drawImage(img, -sx1, -sy1, null);        g2d.dispose();        return bimg;    }     protected void renderImageXform(SunGraphics2D sg, Image img,                                    AffineTransform tx, int interpType,                                    int sx1, int sy1, int sx2, int sy2,                                    Color bgColor)    {        Region clip = sg.getCompClip();        SurfaceData dstData = sg.surfaceData;        SurfaceData srcData = dstData.getSourceSurfaceData(img,                                                           sg.TRANSFORM_GENERIC,                                                           sg.imageComp,                                                           bgColor);         if (srcData == null) {            img = getBufferedImage(img);            srcData = dstData.getSourceSurfaceData(img,                                                   sg.TRANSFORM_GENERIC,                                                   sg.imageComp,                                                   bgColor);            if (srcData == null) {                // REMIND: Is this correct?  Can this happen?                return;            }        }         if (isBgOperation(srcData, bgColor)) {            // We cannot perform bg operations during transform so make            // an opaque temp image with the appropriate background            // and work from there.            img = makeBufferedImage(img, bgColor, BufferedImage.TYPE_INT_RGB,                                    sx1, sy1, sx2, sy2);            // Temp image has appropriate subimage at 0,0 now.            sx2 -= sx1;            sy2 -= sy1;            sx1 = sy1 = 0;             srcData = dstData.getSourceSurfaceData(img,                                                   sg.TRANSFORM_GENERIC,                                                   sg.imageComp,                                                   bgColor);        }         SurfaceType srcType = srcData.getSurfaceType();        TransformHelper helper = TransformHelper.getFromCache(srcType);         if (helper == null) {            /* We have no helper for this source image type.             * But we know that we do have helpers for both RGB and ARGB,             * so convert to one of those types depending on transparency.             * ARGB_PRE might be a better choice if the source image has             * alpha, but it may cause some recursion here since we only             * tend to have converters that convert to ARGB.             */            int type = ((srcData.getTransparency() == Transparency.OPAQUE)                        ? BufferedImage.TYPE_INT_RGB                        : BufferedImage.TYPE_INT_ARGB);            img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);            // Temp image has appropriate subimage at 0,0 now.            sx2 -= sx1;            sy2 -= sy1;            sx1 = sy1 = 0;             srcData = dstData.getSourceSurfaceData(img,                                                   sg.TRANSFORM_GENERIC,                                                   sg.imageComp,                                                   null);            srcType = srcData.getSurfaceType();            helper = TransformHelper.getFromCache(srcType);            // assert(helper != null);        }         AffineTransform itx;        try {            itx = tx.createInverse();        } catch (NoninvertibleTransformException e) {            // Non-invertible transform means no output            return;        }         /*         * Find the maximum bounds on the destination that will be         * affected by the transformed source.  First, transform all         * four corners of the source and then min and max the resulting         * destination coordinates of the transformed corners.         * Note that tx already has the offset to sx1,sy1 accounted         * for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the         * source coordinates.         */        double coords[] = new double[8];        /* corner:  UL      UR      LL      LR   */        /* index:  0  1    2  3    4  5    6  7  */        /* coord: (0, 0), (w, 0), (0, h), (w, h) */        coords[2] = coords[6] = sx2 - sx1;        coords[5] = coords[7] = sy2 - sy1;        tx.transform(coords, 0, coords, 0, 4);        double ddx1, ddy1, ddx2, ddy2;        ddx1 = ddx2 = coords[0];        ddy1 = ddy2 = coords[1];        for (int i = 2; i < coords.length; i += 2) {            double d = coords[i];            if (ddx1 > d) ddx1 = d;            else if (ddx2 < d) ddx2 = d;            d = coords[i+1];            if (ddy1 > d) ddy1 = d;            else if (ddy2 < d) ddy2 = d;        }        int dx1 = (int) Math.floor(ddx1);        int dy1 = (int) Math.floor(ddy1);        int dx2 = (int) Math.ceil(ddx2);        int dy2 = (int) Math.ceil(ddy2);         SurfaceType dstType = dstData.getSurfaceType();        MaskBlit maskblit;        Blit blit;        if (sg.compositeState <= sg.COMP_ALPHA) {            /* NOTE: We either have, or we can make,             * a MaskBlit for any alpha composite type             */            maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,                                             sg.imageComp,                                             dstType);             /* NOTE: We can only use the native TransformHelper             * func to go directly to the dest if both the helper             * and the MaskBlit are native.             * All helpers are native at this point, but some MaskBlit             * objects are implemented in Java, so we need to check.             */            if (maskblit.getNativePrim() != 0) {                // We can render directly.                helper.Transform(maskblit, srcData, dstData,                                 sg.composite, clip,                                 itx, interpType,                                 sx1, sy1, sx2, sy2,                                 dx1, dy1, dx2, dy2,                                 null, 0, 0);                return;            }            blit = null;        } else {            /* NOTE: We either have, or we can make,             * a Blit for any composite type, even Custom             */            maskblit = null;            blit = Blit.getFromCache(SurfaceType.IntArgbPre,                                     sg.imageComp,                                     dstType);        }         // We need to transform to a temp image and then copy        // just the pieces that are valid data to the dest.        BufferedImage tmpimg = new BufferedImage(dx2-dx1, dy2-dy1,                                                 BufferedImage.TYPE_INT_ARGB);        SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);        SurfaceType tmpType = tmpData.getSurfaceType();        MaskBlit tmpmaskblit =            MaskBlit.getFromCache(SurfaceType.IntArgbPre,                                  CompositeType.SrcNoEa,                                  tmpType);        /*         * The helper function fills a temporary edges buffer         * for us with the bounding coordinates of each scanline         * in the following format:

Lines 1–500 of 1110.