Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pipe/DrawImage.java
38918 views
/*1* Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.pipe;2627import java.awt.AlphaComposite;28import java.awt.Color;29import java.awt.Image;30import java.awt.Transparency;31import java.awt.geom.AffineTransform;32import java.awt.geom.NoninvertibleTransformException;33import java.awt.image.AffineTransformOp;34import java.awt.image.BufferedImage;35import java.awt.image.BufferedImageOp;36import java.awt.image.ColorModel;37import java.awt.image.DataBuffer;38import java.awt.image.ImageObserver;39import java.awt.image.IndexColorModel;40import java.awt.image.Raster;41import java.awt.image.VolatileImage;42import sun.awt.SunHints;43import sun.awt.image.ImageRepresentation;44import sun.awt.image.SurfaceManager;45import sun.awt.image.ToolkitImage;46import sun.java2d.InvalidPipeException;47import sun.java2d.SunGraphics2D;48import sun.java2d.SurfaceData;49import sun.java2d.loops.Blit;50import sun.java2d.loops.BlitBg;51import sun.java2d.loops.TransformHelper;52import sun.java2d.loops.MaskBlit;53import sun.java2d.loops.CompositeType;54import sun.java2d.loops.ScaledBlit;55import sun.java2d.loops.SurfaceType;5657public class DrawImage implements DrawImagePipe58{59public boolean copyImage(SunGraphics2D sg, Image img,60int x, int y,61Color bgColor)62{63int imgw = img.getWidth(null);64int imgh = img.getHeight(null);65if (isSimpleTranslate(sg)) {66return renderImageCopy(sg, img, bgColor,67x + sg.transX, y + sg.transY,680, 0, imgw, imgh);69}70AffineTransform atfm = sg.transform;71if ((x | y) != 0) {72atfm = new AffineTransform(atfm);73atfm.translate(x, y);74}75transformImage(sg, img, atfm, sg.interpolationType,760, 0, imgw, imgh, bgColor);77return true;78}7980public boolean copyImage(SunGraphics2D sg, Image img,81int dx, int dy, int sx, int sy, int w, int h,82Color bgColor)83{84if (isSimpleTranslate(sg)) {85return renderImageCopy(sg, img, bgColor,86dx + sg.transX, dy + sg.transY,87sx, sy, w, h);88}89scaleImage(sg, img, dx, dy, (dx + w), (dy + h),90sx, sy, (sx + w), (sy + h), bgColor);91return true;92}9394public boolean scaleImage(SunGraphics2D sg, Image img, int x, int y,95int width, int height,96Color bgColor)97{98int imgw = img.getWidth(null);99int imgh = img.getHeight(null);100// Only accelerate scale if:101// - w/h positive values102// - sg transform integer translate/identity only103// - no bgColor in operation104if ((width > 0) && (height > 0) && isSimpleTranslate(sg)) {105double dx1 = x + sg.transX;106double dy1 = y + sg.transY;107double dx2 = dx1 + width;108double dy2 = dy1 + height;109if (renderImageScale(sg, img, bgColor, sg.interpolationType,1100, 0, imgw, imgh,111dx1, dy1, dx2, dy2))112{113return true;114}115}116117AffineTransform atfm = sg.transform;118if ((x | y) != 0 || width != imgw || height != imgh) {119atfm = new AffineTransform(atfm);120atfm.translate(x, y);121atfm.scale(((double)width)/imgw, ((double)height)/imgh);122}123transformImage(sg, img, atfm, sg.interpolationType,1240, 0, imgw, imgh, bgColor);125return true;126}127128/*129* This method is only called in those circumstances where the130* operation has a non-null secondary transform specified. Its131* role is to check for various optimizations based on the types132* of both the secondary and SG2D transforms and to do some133* quick calculations to avoid having to combine the transforms134* and/or to call a more generalized method.135*/136protected void transformImage(SunGraphics2D sg, Image img, int x, int y,137AffineTransform extraAT, int interpType)138{139int txtype = extraAT.getType();140int imgw = img.getWidth(null);141int imgh = img.getHeight(null);142boolean checkfinalxform;143144if (sg.transformState <= SunGraphics2D.TRANSFORM_ANY_TRANSLATE &&145(txtype == AffineTransform.TYPE_IDENTITY ||146txtype == AffineTransform.TYPE_TRANSLATION))147{148// First optimization - both are some kind of translate149150// Combine the translations and check if interpolation is necessary.151double tx = extraAT.getTranslateX();152double ty = extraAT.getTranslateY();153tx += sg.transform.getTranslateX();154ty += sg.transform.getTranslateY();155int itx = (int) Math.floor(tx + 0.5);156int ity = (int) Math.floor(ty + 0.5);157if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||158(closeToInteger(itx, tx) && closeToInteger(ity, ty)))159{160renderImageCopy(sg, img, null, x+itx, y+ity, 0, 0, imgw, imgh);161return;162}163checkfinalxform = false;164} else if (sg.transformState <= SunGraphics2D.TRANSFORM_TRANSLATESCALE &&165((txtype & (AffineTransform.TYPE_FLIP |166AffineTransform.TYPE_MASK_ROTATION |167AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0))168{169// Second optimization - both are some kind of translate or scale170171// Combine the scales and check if interpolation is necessary.172173// Transform source bounds by extraAT,174// then translate the bounds again by x, y175// then transform the bounds again by sg.transform176double coords[] = new double[] {1770, 0, imgw, imgh,178};179extraAT.transform(coords, 0, coords, 0, 2);180coords[0] += x;181coords[1] += y;182coords[2] += x;183coords[3] += y;184sg.transform.transform(coords, 0, coords, 0, 2);185186if (tryCopyOrScale(sg, img, 0, 0, imgw, imgh,187null, interpType, coords))188{189return;190}191checkfinalxform = false;192} else {193checkfinalxform = true;194}195196// Begin Transform197AffineTransform tx = new AffineTransform(sg.transform);198tx.translate(x, y);199tx.concatenate(extraAT);200201// Do not try any more optimizations if either of the cases202// above was tried as we have already verified that the203// resulting transform will not simplify.204if (checkfinalxform) {205// In this case neither of the above simple transform206// pairs was found so we will do some final tests on207// the final rendering transform which may be the208// simple product of two complex transforms.209transformImage(sg, img, tx, interpType, 0, 0, imgw, imgh, null);210} else {211renderImageXform(sg, img, tx, interpType, 0, 0, imgw, imgh, null);212}213}214215/*216* This method is called with a final rendering transform that217* has combined all of the information about the Graphics2D218* transform attribute with the transformations specified by219* the arguments to the drawImage call.220* Its role is to see if the combined transform ends up being221* acceleratable by either a renderImageCopy or renderImageScale222* once all of the math is done.223*224* Note: The transform supplied here has an origin that is225* already adjusted to point to the device location where226* the (sx1, sy1) location of the source image should be placed.227*/228protected void transformImage(SunGraphics2D sg, Image img,229AffineTransform tx, int interpType,230int sx1, int sy1, int sx2, int sy2,231Color bgColor)232{233// Transform 3 source corners by tx and analyze them234// for simplified operations (Copy or Scale). Using235// 3 points lets us analyze any kind of transform,236// even transforms that involve very tiny amounts of237// rotation or skew to see if they degenerate to a238// simple scale or copy operation within the allowable239// error bounds.240// Note that we use (0,0,w,h) instead of (sx1,sy1,sx2,sy2)241// because the transform is already translated such that242// the origin is where sx1, sy1 should go.243double coords[] = new double[6];244/* index: 0 1 2 3 4 5 */245/* coord: (0, 0), (w, h), (0, h) */246coords[2] = sx2 - sx1;247coords[3] = coords[5] = sy2 - sy1;248tx.transform(coords, 0, coords, 0, 3);249// First test if the X coords of the transformed UL250// and LL points match and that the Y coords of the251// transformed LR and LL points also match.252// If they do then it is a "rectilinear" transform and253// tryCopyOrScale will make sure it is upright and254// integer-based.255if (Math.abs(coords[0] - coords[4]) < MAX_TX_ERROR &&256Math.abs(coords[3] - coords[5]) < MAX_TX_ERROR &&257tryCopyOrScale(sg, img, sx1, sy1, sx2, sy2,258bgColor, interpType, coords))259{260return;261}262263renderImageXform(sg, img, tx, interpType, sx1, sy1, sx2, sy2, bgColor);264}265266/*267* Check the bounding coordinates of the transformed source268* image to see if they fall on integer coordinates such269* that they will cause no interpolation anomalies if we270* use our simplified Blit or ScaledBlit operations instead271* of a full transform operation.272*/273protected boolean tryCopyOrScale(SunGraphics2D sg,274Image img,275int sx1, int sy1,276int sx2, int sy2,277Color bgColor, int interpType,278double coords[])279{280double dx1 = coords[0];281double dy1 = coords[1];282double dx2 = coords[2];283double dy2 = coords[3];284double dw = dx2 - dx1;285double dh = dy2 - dy1;286287/* If any of the destination coordinates exceed the integer range,288* then the calculations performed in calls made here cannot be289* guaranteed to be correct, or to converge (terminate).290* So return out of here, deferring to code that can handle this.291*/292if (dx1 < Integer.MIN_VALUE || dx1 > Integer.MAX_VALUE ||293dy1 < Integer.MIN_VALUE || dy1 > Integer.MAX_VALUE ||294dx2 < Integer.MIN_VALUE || dx2 > Integer.MAX_VALUE ||295dy2 < Integer.MIN_VALUE || dy2 > Integer.MAX_VALUE)296{297return false;298}299300// First check if width and height are very close to img w&h.301if (closeToInteger(sx2-sx1, dw) && closeToInteger(sy2-sy1, dh)) {302// Round location to nearest pixel and then test303// if it will cause interpolation anomalies.304int idx = (int) Math.floor(dx1 + 0.5);305int idy = (int) Math.floor(dy1 + 0.5);306if (interpType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR ||307(closeToInteger(idx, dx1) && closeToInteger(idy, dy1)))308{309renderImageCopy(sg, img, bgColor,310idx, idy,311sx1, sy1, sx2-sx1, sy2-sy1);312return true;313}314}315// (For now) We can only use our ScaledBlits if the image316// is upright (i.e. dw & dh both > 0)317if (dw > 0 && dh > 0) {318if (renderImageScale(sg, img, bgColor, interpType,319sx1, sy1, sx2, sy2,320dx1, dy1, dx2, dy2))321{322return true;323}324}325return false;326}327328/**329* Return a non-accelerated BufferedImage of the requested type with the330* indicated subimage of the original image located at 0,0 in the new image.331* If a bgColor is supplied, composite the original image over that color332* with a SrcOver operation, otherwise make a SrcNoEa copy.333* <p>334* Returned BufferedImage is not accelerated for two reasons:335* <ul>336* <li> Types of the image and surface are predefined, because these types337* correspond to the TransformHelpers, which we know we have. And338* acceleration can change the type of the surface339* <li> Image will be used only once and acceleration caching wouldn't help340* </ul>341*/342BufferedImage makeBufferedImage(Image img, Color bgColor, int type,343int sx1, int sy1, int sx2, int sy2)344{345final int width = sx2 - sx1;346final int height = sy2 - sy1;347final BufferedImage bimg = new BufferedImage(width, height, type);348final SunGraphics2D g2d = (SunGraphics2D) bimg.createGraphics();349g2d.setComposite(AlphaComposite.Src);350bimg.setAccelerationPriority(0);351if (bgColor != null) {352g2d.setColor(bgColor);353g2d.fillRect(0, 0, width, height);354g2d.setComposite(AlphaComposite.SrcOver);355}356g2d.copyImage(img, 0, 0, sx1, sy1, width, height, null, null);357g2d.dispose();358return bimg;359}360361protected void renderImageXform(SunGraphics2D sg, Image img,362AffineTransform tx, int interpType,363int sx1, int sy1, int sx2, int sy2,364Color bgColor)365{366final AffineTransform itx;367try {368itx = tx.createInverse();369} catch (final NoninvertibleTransformException ignored) {370// Non-invertible transform means no output371return;372}373374/*375* Find the maximum bounds on the destination that will be376* affected by the transformed source. First, transform all377* four corners of the source and then min and max the resulting378* destination coordinates of the transformed corners.379* Note that tx already has the offset to sx1,sy1 accounted380* for so we use the box (0, 0, sx2-sx1, sy2-sy1) as the381* source coordinates.382*/383final double[] coords = new double[8];384/* corner: UL UR LL LR */385/* index: 0 1 2 3 4 5 6 7 */386/* coord: (0, 0), (w, 0), (0, h), (w, h) */387coords[2] = coords[6] = sx2 - sx1;388coords[5] = coords[7] = sy2 - sy1;389tx.transform(coords, 0, coords, 0, 4);390double ddx1, ddy1, ddx2, ddy2;391ddx1 = ddx2 = coords[0];392ddy1 = ddy2 = coords[1];393for (int i = 2; i < coords.length; i += 2) {394double d = coords[i];395if (ddx1 > d) ddx1 = d;396else if (ddx2 < d) ddx2 = d;397d = coords[i+1];398if (ddy1 > d) ddy1 = d;399else if (ddy2 < d) ddy2 = d;400}401402Region clip = sg.getCompClip();403final int dx1 = Math.max((int) Math.floor(ddx1), clip.lox);404final int dy1 = Math.max((int) Math.floor(ddy1), clip.loy);405final int dx2 = Math.min((int) Math.ceil(ddx2), clip.hix);406final int dy2 = Math.min((int) Math.ceil(ddy2), clip.hiy);407if (dx2 <= dx1 || dy2 <= dy1) {408// empty destination means no output409return;410}411412final SurfaceData dstData = sg.surfaceData;413SurfaceData srcData = dstData.getSourceSurfaceData(img,414SunGraphics2D.TRANSFORM_GENERIC,415sg.imageComp,416bgColor);417418if (srcData == null) {419img = getBufferedImage(img);420srcData = dstData.getSourceSurfaceData(img,421SunGraphics2D.TRANSFORM_GENERIC,422sg.imageComp,423bgColor);424if (srcData == null) {425// REMIND: Is this correct? Can this happen?426return;427}428}429430if (isBgOperation(srcData, bgColor)) {431// We cannot perform bg operations during transform so make432// an opaque temp image with the appropriate background433// and work from there.434img = makeBufferedImage(img, bgColor, BufferedImage.TYPE_INT_RGB,435sx1, sy1, sx2, sy2);436// Temp image has appropriate subimage at 0,0 now.437sx2 -= sx1;438sy2 -= sy1;439sx1 = sy1 = 0;440441srcData = dstData.getSourceSurfaceData(img,442SunGraphics2D.TRANSFORM_GENERIC,443sg.imageComp,444bgColor);445}446447SurfaceType srcType = srcData.getSurfaceType();448TransformHelper helper = TransformHelper.getFromCache(srcType);449450if (helper == null) {451/* We have no helper for this source image type.452* But we know that we do have helpers for both RGB and ARGB,453* so convert to one of those types depending on transparency.454* ARGB_PRE might be a better choice if the source image has455* alpha, but it may cause some recursion here since we only456* tend to have converters that convert to ARGB.457*/458int type = ((srcData.getTransparency() == Transparency.OPAQUE)459? BufferedImage.TYPE_INT_RGB460: BufferedImage.TYPE_INT_ARGB);461img = makeBufferedImage(img, null, type, sx1, sy1, sx2, sy2);462// Temp image has appropriate subimage at 0,0 now.463sx2 -= sx1;464sy2 -= sy1;465sx1 = sy1 = 0;466467srcData = dstData.getSourceSurfaceData(img,468SunGraphics2D.TRANSFORM_GENERIC,469sg.imageComp,470null);471srcType = srcData.getSurfaceType();472helper = TransformHelper.getFromCache(srcType);473// assert(helper != null);474}475476SurfaceType dstType = dstData.getSurfaceType();477if (sg.compositeState <= SunGraphics2D.COMP_ALPHA) {478/* NOTE: We either have, or we can make,479* a MaskBlit for any alpha composite type480*/481MaskBlit maskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,482sg.imageComp, dstType);483484/* NOTE: We can only use the native TransformHelper485* func to go directly to the dest if both the helper486* and the MaskBlit are native.487* All helpers are native at this point, but some MaskBlit488* objects are implemented in Java, so we need to check.489*/490if (maskblit.getNativePrim() != 0) {491// We can render directly.492helper.Transform(maskblit, srcData, dstData,493sg.composite, clip,494itx, interpType,495sx1, sy1, sx2, sy2,496dx1, dy1, dx2, dy2,497null, 0, 0);498return;499}500}501502// We need to transform to a temp image and then copy503// just the pieces that are valid data to the dest.504final int w = dx2 - dx1;505final int h = dy2 - dy1;506BufferedImage tmpimg = new BufferedImage(w, h,507BufferedImage.TYPE_INT_ARGB_PRE);508SurfaceData tmpData = SurfaceData.getPrimarySurfaceData(tmpimg);509SurfaceType tmpType = tmpData.getSurfaceType();510MaskBlit tmpmaskblit = MaskBlit.getFromCache(SurfaceType.IntArgbPre,511CompositeType.SrcNoEa,512tmpType);513/*514* The helper function fills a temporary edges buffer515* for us with the bounding coordinates of each scanline516* in the following format:517*518* edges[0, 1] = [top y, bottom y)519* edges[2, 3] = [left x, right x) of top row520* ...521* edges[h*2, h*2+1] = [left x, right x) of bottom row522*523* all coordinates in the edges array will be relative to dx1, dy1524*525* edges thus has to be h*2+2 in length526*/527final int[] edges = new int[h * 2 + 2];528// It is important that edges[0]=edges[1]=0 when we call529// Transform in case it must return early and we would530// not want to render anything on an error condition.531helper.Transform(tmpmaskblit, srcData, tmpData,532AlphaComposite.Src, null,533itx, interpType,534sx1, sy1, sx2, sy2,5350, 0, w, h,536edges, dx1, dy1);537538final Region region = Region.getInstance(dx1, dy1, dx2, dy2, edges);539clip = clip.getIntersection(region);540541/* NOTE: We either have, or we can make,542* a Blit for any composite type, even Custom543*/544final Blit blit = Blit.getFromCache(tmpType, sg.imageComp, dstType);545blit.Blit(tmpData, dstData, sg.composite, clip, 0, 0, dx1, dy1, w, h);546}547548// Render an image using only integer translation549// (no scale or transform or sub-pixel interpolated translations).550protected boolean renderImageCopy(SunGraphics2D sg, Image img,551Color bgColor,552int dx, int dy,553int sx, int sy,554int w, int h)555{556Region clip = sg.getCompClip();557SurfaceData dstData = sg.surfaceData;558559int attempts = 0;560// Loop up to twice through; this gives us a chance to561// revalidate the surfaceData objects in case of an exception562// and try it once more563while (true) {564SurfaceData srcData =565dstData.getSourceSurfaceData(img,566SunGraphics2D.TRANSFORM_ISIDENT,567sg.imageComp,568bgColor);569if (srcData == null) {570return false;571}572573try {574SurfaceType srcType = srcData.getSurfaceType();575SurfaceType dstType = dstData.getSurfaceType();576blitSurfaceData(sg, clip,577srcData, dstData, srcType, dstType,578sx, sy, dx, dy, w, h, bgColor);579return true;580} catch (NullPointerException e) {581if (!(SurfaceData.isNull(dstData) ||582SurfaceData.isNull(srcData)))583{584// Something else caused the exception, throw it...585throw e;586}587return false;588// NOP if we have been disposed589} catch (InvalidPipeException e) {590// Always catch the exception; try this a couple of times591// and fail silently if the system is not yet ready to592// revalidate the source or dest surfaceData objects.593++attempts;594clip = sg.getCompClip(); // ensures sg.surfaceData is valid595dstData = sg.surfaceData;596if (SurfaceData.isNull(dstData) ||597SurfaceData.isNull(srcData) || (attempts > 1))598{599return false;600}601}602}603}604605// Render an image using only integer scaling (no transform).606protected boolean renderImageScale(SunGraphics2D sg, Image img,607Color bgColor, int interpType,608int sx1, int sy1,609int sx2, int sy2,610double dx1, double dy1,611double dx2, double dy2)612{613// Currently only NEAREST_NEIGHBOR interpolation is implemented614// for ScaledBlit operations.615if (interpType != AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {616return false;617}618619Region clip = sg.getCompClip();620SurfaceData dstData = sg.surfaceData;621622int attempts = 0;623// Loop up to twice through; this gives us a chance to624// revalidate the surfaceData objects in case of an exception625// and try it once more626while (true) {627SurfaceData srcData =628dstData.getSourceSurfaceData(img,629SunGraphics2D.TRANSFORM_TRANSLATESCALE,630sg.imageComp,631bgColor);632633if (srcData == null || isBgOperation(srcData, bgColor)) {634return false;635}636637try {638SurfaceType srcType = srcData.getSurfaceType();639SurfaceType dstType = dstData.getSurfaceType();640return scaleSurfaceData(sg, clip,641srcData, dstData, srcType, dstType,642sx1, sy1, sx2, sy2,643dx1, dy1, dx2, dy2);644} catch (NullPointerException e) {645if (!SurfaceData.isNull(dstData)) {646// Something else caused the exception, throw it...647throw e;648}649return false;650// NOP if we have been disposed651} catch (InvalidPipeException e) {652// Always catch the exception; try this a couple of times653// and fail silently if the system is not yet ready to654// revalidate the source or dest surfaceData objects.655++attempts;656clip = sg.getCompClip(); // ensures sg.surfaceData is valid657dstData = sg.surfaceData;658if (SurfaceData.isNull(dstData) ||659SurfaceData.isNull(srcData) || (attempts > 1))660{661return false;662}663}664}665}666667public boolean scaleImage(SunGraphics2D sg, Image img,668int dx1, int dy1, int dx2, int dy2,669int sx1, int sy1, int sx2, int sy2,670Color bgColor)671{672int srcW, srcH, dstW, dstH;673int srcX, srcY, dstX, dstY;674boolean srcWidthFlip = false;675boolean srcHeightFlip = false;676boolean dstWidthFlip = false;677boolean dstHeightFlip = false;678679if (sx2 > sx1) {680srcW = sx2 - sx1;681srcX = sx1;682} else {683srcWidthFlip = true;684srcW = sx1 - sx2;685srcX = sx2;686}687if (sy2 > sy1) {688srcH = sy2-sy1;689srcY = sy1;690} else {691srcHeightFlip = true;692srcH = sy1-sy2;693srcY = sy2;694}695if (dx2 > dx1) {696dstW = dx2 - dx1;697dstX = dx1;698} else {699dstW = dx1 - dx2;700dstWidthFlip = true;701dstX = dx2;702}703if (dy2 > dy1) {704dstH = dy2 - dy1;705dstY = dy1;706} else {707dstH = dy1 - dy2;708dstHeightFlip = true;709dstY = dy2;710}711if (srcW <= 0 || srcH <= 0) {712return true;713}714// Only accelerate scale if it does not involve a flip or transform715if ((srcWidthFlip == dstWidthFlip) &&716(srcHeightFlip == dstHeightFlip) &&717isSimpleTranslate(sg))718{719double ddx1 = dstX + sg.transX;720double ddy1 = dstY + sg.transY;721double ddx2 = ddx1 + dstW;722double ddy2 = ddy1 + dstH;723if (renderImageScale(sg, img, bgColor, sg.interpolationType,724srcX, srcY, srcX+srcW, srcY+srcH,725ddx1, ddy1, ddx2, ddy2))726{727return true;728}729}730731AffineTransform atfm = new AffineTransform(sg.transform);732atfm.translate(dx1, dy1);733double m00 = (double)(dx2-dx1)/(sx2-sx1);734double m11 = (double)(dy2-dy1)/(sy2-sy1);735atfm.scale(m00, m11);736atfm.translate(srcX-sx1, srcY-sy1);737738final int scale = SurfaceManager.getImageScale(img);739final int imgW = img.getWidth(null) * scale;740final int imgH = img.getHeight(null) * scale;741srcW += srcX;742srcH += srcY;743// Make sure we are not out of bounds744if (srcW > imgW) {745srcW = imgW;746}747if (srcH > imgH) {748srcH = imgH;749}750if (srcX < 0) {751atfm.translate(-srcX, 0);752srcX = 0;753}754if (srcY < 0) {755atfm.translate(0, -srcY);756srcY = 0;757}758if (srcX >= srcW || srcY >= srcH) {759return true;760}761// Note: src[WH] are currently the right and bottom coordinates.762// The following two lines would adjust src[WH] back to being763// dimensions.764// srcW -= srcX;765// srcH -= srcY;766// Since transformImage needs right and bottom coords we will767// omit this adjustment.768769transformImage(sg, img, atfm, sg.interpolationType,770srcX, srcY, srcW, srcH, bgColor);771return true;772}773774/**775** Utilities776** The following methods are used by the public methods above777** for performing various operations778**/779780/*781* This constant represents a tradeoff between the782* need to make sure that image transformations are783* "very close" to integer device coordinates before784* we decide to use an integer scale or copy operation785* as a substitute and the fact that roundoff errors786* in AffineTransforms are frequently introduced by787* performing multiple sequential operations on them.788*789* The evaluation of bug 4990624 details the potential790* for this error cutoff to result in display anomalies791* in different types of image operations and how this792* value represents a good compromise here.793*/794private static final double MAX_TX_ERROR = .0001;795796public static boolean closeToInteger(int i, double d) {797return (Math.abs(d-i) < MAX_TX_ERROR);798}799800public static boolean isSimpleTranslate(SunGraphics2D sg) {801int ts = sg.transformState;802if (ts <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {803// Integer translates are always "simple"804return true;805}806if (ts >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) {807// Scales and beyond are always "not simple"808return false;809}810// non-integer translates are only simple when not interpolating811if (sg.interpolationType == AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {812return true;813}814return false;815}816817protected static boolean isBgOperation(SurfaceData srcData, Color bgColor) {818// If we cannot get the srcData, then cannot assume anything about819// the image820return ((srcData == null) ||821((bgColor != null) &&822(srcData.getTransparency() != Transparency.OPAQUE)));823}824825protected BufferedImage getBufferedImage(Image img) {826if (img instanceof BufferedImage) {827return (BufferedImage)img;828}829// Must be VolatileImage; get BufferedImage representation830return ((VolatileImage)img).getSnapshot();831}832833/*834* Return the color model to be used with this BufferedImage and835* transform.836*/837private ColorModel getTransformColorModel(SunGraphics2D sg,838BufferedImage bImg,839AffineTransform tx) {840ColorModel cm = bImg.getColorModel();841ColorModel dstCM = cm;842843if (tx.isIdentity()) {844return dstCM;845}846int type = tx.getType();847boolean needTrans =848((type & (AffineTransform.TYPE_MASK_ROTATION |849AffineTransform.TYPE_GENERAL_TRANSFORM)) != 0);850if (! needTrans &&851type != AffineTransform.TYPE_TRANSLATION &&852type != AffineTransform.TYPE_IDENTITY)853{854double[] mtx = new double[4];855tx.getMatrix(mtx);856// Check out the matrix. A non-integral scale will force ARGB857// since the edge conditions cannot be guaranteed.858needTrans = (mtx[0] != (int)mtx[0] || mtx[3] != (int)mtx[3]);859}860861if (sg.renderHint != SunHints.INTVAL_RENDER_QUALITY) {862if (cm instanceof IndexColorModel) {863Raster raster = bImg.getRaster();864IndexColorModel icm = (IndexColorModel) cm;865// Just need to make sure that we have a transparent pixel866if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {867// Fix 4221407868if (raster instanceof sun.awt.image.BytePackedRaster) {869dstCM = ColorModel.getRGBdefault();870}871else {872double[] matrix = new double[6];873tx.getMatrix(matrix);874if (matrix[1] == 0. && matrix[2] ==0.875&& matrix[4] == 0. && matrix[5] == 0.) {876// Only scaling so do not need to create877}878else {879int mapSize = icm.getMapSize();880if (mapSize < 256) {881int[] cmap = new int[mapSize+1];882icm.getRGBs(cmap);883cmap[mapSize] = 0x0000;884dstCM = new885IndexColorModel(icm.getPixelSize(),886mapSize+1,887cmap, 0, true, mapSize,888DataBuffer.TYPE_BYTE);889}890else {891dstCM = ColorModel.getRGBdefault();892}893} /* if (matrix[0] < 1.f ...) */894} /* raster instanceof sun.awt.image.BytePackedRaster */895} /* if (cm.getTransparency() == cm.OPAQUE) */896} /* if (cm instanceof IndexColorModel) */897else if (needTrans && cm.getTransparency() == Transparency.OPAQUE) {898// Need a bitmask transparency899// REMIND: for now, use full transparency since no loops900// for bitmask901dstCM = ColorModel.getRGBdefault();902}903} /* if (sg.renderHint == RENDER_QUALITY) */904else {905906if (cm instanceof IndexColorModel ||907(needTrans && cm.getTransparency() == Transparency.OPAQUE))908{909// Need a bitmask transparency910// REMIND: for now, use full transparency since no loops911// for bitmask912dstCM = ColorModel.getRGBdefault();913}914}915916return dstCM;917}918919protected void blitSurfaceData(SunGraphics2D sg,920Region clipRegion,921SurfaceData srcData,922SurfaceData dstData,923SurfaceType srcType,924SurfaceType dstType,925int sx, int sy, int dx, int dy,926int w, int h,927Color bgColor)928{929if (w <= 0 || h <= 0) {930/*931* Fix for bugid 4783274 - BlitBg throws an exception for932* a particular set of anomalous parameters.933* REMIND: The native loops do proper clipping and would934* detect this situation themselves, but the Java loops935* all seem to trust their parameters a little too well936* to the point where they will try to process a negative937* area of pixels and throw exceptions. The real fix is938* to modify the Java loops to do proper clipping so that939* they can deal with negative dimensions as well as940* improperly large dimensions, but that fix is too risky941* to integrate for Mantis at this point. In the meantime942* eliminating the negative or zero dimensions here is943* "correct" and saves them from some nasty exceptional944* conditions, one of which is the test case of 4783274.945*/946return;947}948CompositeType comp = sg.imageComp;949if (CompositeType.SrcOverNoEa.equals(comp) &&950(srcData.getTransparency() == Transparency.OPAQUE ||951(bgColor != null &&952bgColor.getTransparency() == Transparency.OPAQUE)))953{954comp = CompositeType.SrcNoEa;955}956if (!isBgOperation(srcData, bgColor)) {957Blit blit = Blit.getFromCache(srcType, comp, dstType);958blit.Blit(srcData, dstData, sg.composite, clipRegion,959sx, sy, dx, dy, w, h);960} else {961BlitBg blit = BlitBg.getFromCache(srcType, comp, dstType);962blit.BlitBg(srcData, dstData, sg.composite, clipRegion,963bgColor.getRGB(), sx, sy, dx, dy, w, h);964}965}966967protected boolean scaleSurfaceData(SunGraphics2D sg,968Region clipRegion,969SurfaceData srcData,970SurfaceData dstData,971SurfaceType srcType,972SurfaceType dstType,973int sx1, int sy1,974int sx2, int sy2,975double dx1, double dy1,976double dx2, double dy2)977{978CompositeType comp = sg.imageComp;979if (CompositeType.SrcOverNoEa.equals(comp) &&980(srcData.getTransparency() == Transparency.OPAQUE))981{982comp = CompositeType.SrcNoEa;983}984985ScaledBlit blit = ScaledBlit.getFromCache(srcType, comp, dstType);986if (blit != null) {987blit.Scale(srcData, dstData, sg.composite, clipRegion,988sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);989return true;990}991return false;992}993994protected static boolean imageReady(ToolkitImage sunimg,995ImageObserver observer)996{997if (sunimg.hasError()) {998if (observer != null) {999observer.imageUpdate(sunimg,1000ImageObserver.ERROR|ImageObserver.ABORT,1001-1, -1, -1, -1);1002}1003return false;1004}1005return true;1006}10071008public boolean copyImage(SunGraphics2D sg, Image img,1009int x, int y,1010Color bgColor,1011ImageObserver observer) {1012if (!(img instanceof ToolkitImage)) {1013return copyImage(sg, img, x, y, bgColor);1014} else {1015ToolkitImage sunimg = (ToolkitImage)img;1016if (!imageReady(sunimg, observer)) {1017return false;1018}1019ImageRepresentation ir = sunimg.getImageRep();1020return ir.drawToBufImage(sg, sunimg, x, y, bgColor, observer);1021}1022}10231024public boolean copyImage(SunGraphics2D sg, Image img,1025int dx, int dy, int sx, int sy, int w, int h,1026Color bgColor,1027ImageObserver observer) {1028if (!(img instanceof ToolkitImage)) {1029return copyImage(sg, img, dx, dy, sx, sy, w, h, bgColor);1030} else {1031ToolkitImage sunimg = (ToolkitImage)img;1032if (!imageReady(sunimg, observer)) {1033return false;1034}1035ImageRepresentation ir = sunimg.getImageRep();1036return ir.drawToBufImage(sg, sunimg,1037dx, dy, (dx + w), (dy + h),1038sx, sy, (sx + w), (sy + h),1039bgColor, observer);1040}1041}10421043public boolean scaleImage(SunGraphics2D sg, Image img,1044int x, int y,1045int width, int height,1046Color bgColor,1047ImageObserver observer) {1048if (!(img instanceof ToolkitImage)) {1049return scaleImage(sg, img, x, y, width, height, bgColor);1050} else {1051ToolkitImage sunimg = (ToolkitImage)img;1052if (!imageReady(sunimg, observer)) {1053return false;1054}1055ImageRepresentation ir = sunimg.getImageRep();1056return ir.drawToBufImage(sg, sunimg, x, y, width, height, bgColor,1057observer);1058}1059}10601061public boolean scaleImage(SunGraphics2D sg, Image img,1062int dx1, int dy1, int dx2, int dy2,1063int sx1, int sy1, int sx2, int sy2,1064Color bgColor,1065ImageObserver observer) {1066if (!(img instanceof ToolkitImage)) {1067return scaleImage(sg, img, dx1, dy1, dx2, dy2,1068sx1, sy1, sx2, sy2, bgColor);1069} else {1070ToolkitImage sunimg = (ToolkitImage)img;1071if (!imageReady(sunimg, observer)) {1072return false;1073}1074ImageRepresentation ir = sunimg.getImageRep();1075return ir.drawToBufImage(sg, sunimg, dx1, dy1, dx2, dy2,1076sx1, sy1, sx2, sy2, bgColor, observer);1077}1078}10791080public boolean transformImage(SunGraphics2D sg, Image img,1081AffineTransform atfm,1082ImageObserver observer) {1083if (!(img instanceof ToolkitImage)) {1084transformImage(sg, img, 0, 0, atfm, sg.interpolationType);1085return true;1086} else {1087ToolkitImage sunimg = (ToolkitImage)img;1088if (!imageReady(sunimg, observer)) {1089return false;1090}1091ImageRepresentation ir = sunimg.getImageRep();1092return ir.drawToBufImage(sg, sunimg, atfm, observer);1093}1094}10951096public void transformImage(SunGraphics2D sg, BufferedImage img,1097BufferedImageOp op, int x, int y)1098{1099if (op != null) {1100if (op instanceof AffineTransformOp) {1101AffineTransformOp atop = (AffineTransformOp) op;1102transformImage(sg, img, x, y,1103atop.getTransform(),1104atop.getInterpolationType());1105return;1106} else {1107img = op.filter(img, null);1108}1109}1110copyImage(sg, img, x, y, null);1111}1112}111311141115