Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/SunGraphics2D.java
38829 views
/*1* Copyright (c) 1996, 2013, 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;2627import java.awt.Graphics;28import java.awt.Graphics2D;29import java.awt.RenderingHints;30import java.awt.RenderingHints.Key;31import java.awt.geom.Area;32import java.awt.geom.AffineTransform;33import java.awt.geom.NoninvertibleTransformException;34import java.awt.AlphaComposite;35import java.awt.BasicStroke;36import java.awt.image.BufferedImage;37import java.awt.image.BufferedImageOp;38import java.awt.image.RenderedImage;39import java.awt.image.renderable.RenderableImage;40import java.awt.image.renderable.RenderContext;41import java.awt.image.AffineTransformOp;42import java.awt.image.Raster;43import java.awt.image.WritableRaster;44import java.awt.Image;45import java.awt.Composite;46import java.awt.Color;47import java.awt.image.ColorModel;48import java.awt.GraphicsConfiguration;49import java.awt.Paint;50import java.awt.GradientPaint;51import java.awt.LinearGradientPaint;52import java.awt.RadialGradientPaint;53import java.awt.TexturePaint;54import java.awt.geom.Rectangle2D;55import java.awt.geom.PathIterator;56import java.awt.geom.GeneralPath;57import java.awt.Shape;58import java.awt.Stroke;59import java.awt.FontMetrics;60import java.awt.Rectangle;61import java.text.AttributedCharacterIterator;62import java.awt.Font;63import java.awt.Point;64import java.awt.image.ImageObserver;65import java.awt.Transparency;66import java.awt.font.GlyphVector;67import java.awt.font.TextLayout;6869import sun.awt.image.SurfaceManager;70import sun.font.FontDesignMetrics;71import sun.font.FontUtilities;72import sun.java2d.pipe.PixelDrawPipe;73import sun.java2d.pipe.PixelFillPipe;74import sun.java2d.pipe.ShapeDrawPipe;75import sun.java2d.pipe.ValidatePipe;76import sun.java2d.pipe.ShapeSpanIterator;77import sun.java2d.pipe.Region;78import sun.java2d.pipe.TextPipe;79import sun.java2d.pipe.DrawImagePipe;80import sun.java2d.pipe.LoopPipe;81import sun.java2d.loops.FontInfo;82import sun.java2d.loops.RenderLoops;83import sun.java2d.loops.CompositeType;84import sun.java2d.loops.SurfaceType;85import sun.java2d.loops.Blit;86import sun.java2d.loops.MaskFill;87import java.awt.font.FontRenderContext;88import sun.java2d.loops.XORComposite;89import sun.awt.ConstrainableGraphics;90import sun.awt.SunHints;91import java.util.Map;92import java.util.Iterator;93import sun.misc.PerformanceLogger;9495import java.lang.annotation.Native;96import sun.awt.image.MultiResolutionImage;9798import static java.awt.geom.AffineTransform.TYPE_FLIP;99import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;100import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;101import sun.awt.image.MultiResolutionToolkitImage;102import sun.awt.image.ToolkitImage;103104/**105* This is a the master Graphics2D superclass for all of the Sun106* Graphics implementations. This class relies on subclasses to107* manage the various device information, but provides an overall108* general framework for performing all of the requests in the109* Graphics and Graphics2D APIs.110*111* @author Jim Graham112*/113public final class SunGraphics2D114extends Graphics2D115implements ConstrainableGraphics, Cloneable, DestSurfaceProvider116{117/*118* Attribute States119*/120/* Paint */121@Native122public static final int PAINT_CUSTOM = 6; /* Any other Paint object */123@Native124public static final int PAINT_TEXTURE = 5; /* Tiled Image */125@Native126public static final int PAINT_RAD_GRADIENT = 4; /* Color RadialGradient */127@Native128public static final int PAINT_LIN_GRADIENT = 3; /* Color LinearGradient */129@Native130public static final int PAINT_GRADIENT = 2; /* Color Gradient */131@Native132public static final int PAINT_ALPHACOLOR = 1; /* Non-opaque Color */133@Native134public static final int PAINT_OPAQUECOLOR = 0; /* Opaque Color */135136/* Composite*/137@Native138public static final int COMP_CUSTOM = 3;/* Custom Composite */139@Native140public static final int COMP_XOR = 2;/* XOR Mode Composite */141@Native142public static final int COMP_ALPHA = 1;/* AlphaComposite */143@Native144public static final int COMP_ISCOPY = 0;/* simple stores into destination,145* i.e. Src, SrcOverNoEa, and other146* alpha modes which replace147* the destination.148*/149150/* Stroke */151@Native152public static final int STROKE_CUSTOM = 3; /* custom Stroke */153@Native154public static final int STROKE_WIDE = 2; /* BasicStroke */155@Native156public static final int STROKE_THINDASHED = 1; /* BasicStroke */157@Native158public static final int STROKE_THIN = 0; /* BasicStroke */159160/* Transform */161@Native162public static final int TRANSFORM_GENERIC = 4; /* any 3x2 */163@Native164public static final int TRANSFORM_TRANSLATESCALE = 3; /* scale XY */165@Native166public static final int TRANSFORM_ANY_TRANSLATE = 2; /* non-int translate */167@Native168public static final int TRANSFORM_INT_TRANSLATE = 1; /* int translate */169@Native170public static final int TRANSFORM_ISIDENT = 0; /* Identity */171172/* Clipping */173@Native174public static final int CLIP_SHAPE = 2; /* arbitrary clip */175@Native176public static final int CLIP_RECTANGULAR = 1; /* rectangular clip */177@Native178public static final int CLIP_DEVICE = 0; /* no clipping set */179180/* The following fields are used when the current Paint is a Color. */181public int eargb; // ARGB value with ExtraAlpha baked in182public int pixel; // pixel value for eargb183184public SurfaceData surfaceData;185186public PixelDrawPipe drawpipe;187public PixelFillPipe fillpipe;188public DrawImagePipe imagepipe;189public ShapeDrawPipe shapepipe;190public TextPipe textpipe;191public MaskFill alphafill;192193public RenderLoops loops;194195public CompositeType imageComp; /* Image Transparency checked on fly */196197public int paintState;198public int compositeState;199public int strokeState;200public int transformState;201public int clipState;202203public Color foregroundColor;204public Color backgroundColor;205206public AffineTransform transform;207public int transX;208public int transY;209210protected static final Stroke defaultStroke = new BasicStroke();211protected static final Composite defaultComposite = AlphaComposite.SrcOver;212private static final Font defaultFont =213new Font(Font.DIALOG, Font.PLAIN, 12);214215public Paint paint;216public Stroke stroke;217public Composite composite;218protected Font font;219protected FontMetrics fontMetrics;220221public int renderHint;222public int antialiasHint;223public int textAntialiasHint;224protected int fractionalMetricsHint;225226/* A gamma adjustment to the colour used in lcd text blitting */227public int lcdTextContrast;228private static int lcdTextContrastDefaultValue = 140;229230private int interpolationHint; // raw value of rendering Hint231public int strokeHint;232233public int interpolationType; // algorithm choice based on234// interpolation and render Hints235236public RenderingHints hints;237238public Region constrainClip; // lightweight bounds in pixels239public int constrainX;240public int constrainY;241242public Region clipRegion;243public Shape usrClip;244protected Region devClip; // Actual physical drawable in pixels245246private final int devScale; // Actual physical scale factor247private int resolutionVariantHint;248249// cached state for text rendering250private boolean validFontInfo;251private FontInfo fontInfo;252private FontInfo glyphVectorFontInfo;253private FontRenderContext glyphVectorFRC;254255private final static int slowTextTransformMask =256AffineTransform.TYPE_GENERAL_TRANSFORM257| AffineTransform.TYPE_MASK_ROTATION258| AffineTransform.TYPE_FLIP;259260static {261if (PerformanceLogger.loggingEnabled()) {262PerformanceLogger.setTime("SunGraphics2D static initialization");263}264}265266public SunGraphics2D(SurfaceData sd, Color fg, Color bg, Font f) {267surfaceData = sd;268foregroundColor = fg;269backgroundColor = bg;270271transform = new AffineTransform();272stroke = defaultStroke;273composite = defaultComposite;274paint = foregroundColor;275276imageComp = CompositeType.SrcOverNoEa;277278renderHint = SunHints.INTVAL_RENDER_DEFAULT;279antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;280textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;281fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;282lcdTextContrast = lcdTextContrastDefaultValue;283interpolationHint = -1;284strokeHint = SunHints.INTVAL_STROKE_DEFAULT;285resolutionVariantHint = SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT;286287interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;288289validateColor();290291devScale = sd.getDefaultScale();292if (devScale != 1) {293transform.setToScale(devScale, devScale);294invalidateTransform();295}296297font = f;298if (font == null) {299font = defaultFont;300}301302setDevClip(sd.getBounds());303invalidatePipe();304}305306protected Object clone() {307try {308SunGraphics2D g = (SunGraphics2D) super.clone();309g.transform = new AffineTransform(this.transform);310if (hints != null) {311g.hints = (RenderingHints) this.hints.clone();312}313/* FontInfos are re-used, so must be cloned too, if they314* are valid, and be nulled out if invalid.315* The implied trade-off is that there is more to be gained316* from re-using these objects than is lost by having to317* clone them when the SG2D is cloned.318*/319if (this.fontInfo != null) {320if (this.validFontInfo) {321g.fontInfo = (FontInfo)this.fontInfo.clone();322} else {323g.fontInfo = null;324}325}326if (this.glyphVectorFontInfo != null) {327g.glyphVectorFontInfo =328(FontInfo)this.glyphVectorFontInfo.clone();329g.glyphVectorFRC = this.glyphVectorFRC;330}331//g.invalidatePipe();332return g;333} catch (CloneNotSupportedException e) {334}335return null;336}337338/**339* Create a new SunGraphics2D based on this one.340*/341public Graphics create() {342return (Graphics) clone();343}344345public void setDevClip(int x, int y, int w, int h) {346Region c = constrainClip;347if (c == null) {348devClip = Region.getInstanceXYWH(x, y, w, h);349} else {350devClip = c.getIntersectionXYWH(x, y, w, h);351}352validateCompClip();353}354355public void setDevClip(Rectangle r) {356setDevClip(r.x, r.y, r.width, r.height);357}358359/**360* Constrain rendering for lightweight objects.361*/362public void constrain(int x, int y, int w, int h, Region region) {363if ((x | y) != 0) {364translate(x, y);365}366if (transformState > TRANSFORM_TRANSLATESCALE) {367clipRect(0, 0, w, h);368return;369}370// changes parameters according to the current scale and translate.371final double scaleX = transform.getScaleX();372final double scaleY = transform.getScaleY();373x = constrainX = (int) transform.getTranslateX();374y = constrainY = (int) transform.getTranslateY();375w = Region.dimAdd(x, Region.clipScale(w, scaleX));376h = Region.dimAdd(y, Region.clipScale(h, scaleY));377378Region c = constrainClip;379if (c == null) {380c = Region.getInstanceXYXY(x, y, w, h);381} else {382c = c.getIntersectionXYXY(x, y, w, h);383}384if (region != null) {385region = region.getScaledRegion(scaleX, scaleY);386region = region.getTranslatedRegion(x, y);387c = c.getIntersection(region);388}389390if (c == constrainClip) {391// Common case to ignore392return;393}394395constrainClip = c;396if (!devClip.isInsideQuickCheck(c)) {397devClip = devClip.getIntersection(c);398validateCompClip();399}400}401402/**403* Constrain rendering for lightweight objects.404*405* REMIND: This method will back off to the "workaround"406* of using translate and clipRect if the Graphics407* to be constrained has a complex transform. The408* drawback of the workaround is that the resulting409* clip and device origin cannot be "enforced".410*411* @exception IllegalStateException If the Graphics412* to be constrained has a complex transform.413*/414@Override415public void constrain(int x, int y, int w, int h) {416constrain(x, y, w, h, null);417}418419protected static ValidatePipe invalidpipe = new ValidatePipe();420421/*422* Invalidate the pipeline423*/424protected void invalidatePipe() {425drawpipe = invalidpipe;426fillpipe = invalidpipe;427shapepipe = invalidpipe;428textpipe = invalidpipe;429imagepipe = invalidpipe;430loops = null;431}432433public void validatePipe() {434/* This workaround is for the situation when we update the Pipelines435* for invalid SurfaceData and run further code when the current436* pipeline doesn't support the type of new SurfaceData created during437* the current pipeline's work (in place of the invalid SurfaceData).438* Usually SurfaceData and Pipelines are repaired (through revalidateAll)439* and called again in the exception handlers */440441if (!surfaceData.isValid()) {442throw new InvalidPipeException("attempt to validate Pipe with invalid SurfaceData");443}444445surfaceData.validatePipe(this);446}447448/*449* Intersect two Shapes by the simplest method, attempting to produce450* a simplified result.451* The boolean arguments keep1 and keep2 specify whether or not452* the first or second shapes can be modified during the operation453* or whether that shape must be "kept" unmodified.454*/455Shape intersectShapes(Shape s1, Shape s2, boolean keep1, boolean keep2) {456if (s1 instanceof Rectangle && s2 instanceof Rectangle) {457return ((Rectangle) s1).intersection((Rectangle) s2);458}459if (s1 instanceof Rectangle2D) {460return intersectRectShape((Rectangle2D) s1, s2, keep1, keep2);461} else if (s2 instanceof Rectangle2D) {462return intersectRectShape((Rectangle2D) s2, s1, keep2, keep1);463}464return intersectByArea(s1, s2, keep1, keep2);465}466467/*468* Intersect a Rectangle with a Shape by the simplest method,469* attempting to produce a simplified result.470* The boolean arguments keep1 and keep2 specify whether or not471* the first or second shapes can be modified during the operation472* or whether that shape must be "kept" unmodified.473*/474Shape intersectRectShape(Rectangle2D r, Shape s,475boolean keep1, boolean keep2) {476if (s instanceof Rectangle2D) {477Rectangle2D r2 = (Rectangle2D) s;478Rectangle2D outrect;479if (!keep1) {480outrect = r;481} else if (!keep2) {482outrect = r2;483} else {484outrect = new Rectangle2D.Float();485}486double x1 = Math.max(r.getX(), r2.getX());487double x2 = Math.min(r.getX() + r.getWidth(),488r2.getX() + r2.getWidth());489double y1 = Math.max(r.getY(), r2.getY());490double y2 = Math.min(r.getY() + r.getHeight(),491r2.getY() + r2.getHeight());492493if (((x2 - x1) < 0) || ((y2 - y1) < 0))494// Width or height is negative. No intersection.495outrect.setFrameFromDiagonal(0, 0, 0, 0);496else497outrect.setFrameFromDiagonal(x1, y1, x2, y2);498return outrect;499}500if (r.contains(s.getBounds2D())) {501if (keep2) {502s = cloneShape(s);503}504return s;505}506return intersectByArea(r, s, keep1, keep2);507}508509protected static Shape cloneShape(Shape s) {510return new GeneralPath(s);511}512513/*514* Intersect two Shapes using the Area class. Presumably other515* attempts at simpler intersection methods proved fruitless.516* The boolean arguments keep1 and keep2 specify whether or not517* the first or second shapes can be modified during the operation518* or whether that shape must be "kept" unmodified.519* @see #intersectShapes520* @see #intersectRectShape521*/522Shape intersectByArea(Shape s1, Shape s2, boolean keep1, boolean keep2) {523Area a1, a2;524525// First see if we can find an overwriteable source shape526// to use as our destination area to avoid duplication.527if (!keep1 && (s1 instanceof Area)) {528a1 = (Area) s1;529} else if (!keep2 && (s2 instanceof Area)) {530a1 = (Area) s2;531s2 = s1;532} else {533a1 = new Area(s1);534}535536if (s2 instanceof Area) {537a2 = (Area) s2;538} else {539a2 = new Area(s2);540}541542a1.intersect(a2);543if (a1.isRectangular()) {544return a1.getBounds();545}546547return a1;548}549550/*551* Intersect usrClip bounds and device bounds to determine the composite552* rendering boundaries.553*/554public Region getCompClip() {555if (!surfaceData.isValid()) {556// revalidateAll() implicitly recalculcates the composite clip557revalidateAll();558}559560return clipRegion;561}562563public Font getFont() {564if (font == null) {565font = defaultFont;566}567return font;568}569570private static final double[] IDENT_MATRIX = {1, 0, 0, 1};571private static final AffineTransform IDENT_ATX =572new AffineTransform();573574private static final int MINALLOCATED = 8;575private static final int TEXTARRSIZE = 17;576private static double[][] textTxArr = new double[TEXTARRSIZE][];577private static AffineTransform[] textAtArr =578new AffineTransform[TEXTARRSIZE];579580static {581for (int i=MINALLOCATED;i<TEXTARRSIZE;i++) {582textTxArr[i] = new double [] {i, 0, 0, i};583textAtArr[i] = new AffineTransform( textTxArr[i]);584}585}586587// cached state for various draw[String,Char,Byte] optimizations588public FontInfo checkFontInfo(FontInfo info, Font font,589FontRenderContext frc) {590/* Do not create a FontInfo object as part of construction of an591* SG2D as its possible it may never be needed - ie if no text592* is drawn using this SG2D.593*/594if (info == null) {595info = new FontInfo();596}597598float ptSize = font.getSize2D();599int txFontType;600AffineTransform devAt, textAt=null;601if (font.isTransformed()) {602textAt = font.getTransform();603textAt.scale(ptSize, ptSize);604txFontType = textAt.getType();605info.originX = (float)textAt.getTranslateX();606info.originY = (float)textAt.getTranslateY();607textAt.translate(-info.originX, -info.originY);608if (transformState >= TRANSFORM_TRANSLATESCALE) {609transform.getMatrix(info.devTx = new double[4]);610devAt = new AffineTransform(info.devTx);611textAt.preConcatenate(devAt);612} else {613info.devTx = IDENT_MATRIX;614devAt = IDENT_ATX;615}616textAt.getMatrix(info.glyphTx = new double[4]);617double shearx = textAt.getShearX();618double scaley = textAt.getScaleY();619if (shearx != 0) {620scaley = Math.sqrt(shearx * shearx + scaley * scaley);621}622info.pixelHeight = (int)(Math.abs(scaley)+0.5);623} else {624txFontType = AffineTransform.TYPE_IDENTITY;625info.originX = info.originY = 0;626if (transformState >= TRANSFORM_TRANSLATESCALE) {627transform.getMatrix(info.devTx = new double[4]);628devAt = new AffineTransform(info.devTx);629info.glyphTx = new double[4];630for (int i = 0; i < 4; i++) {631info.glyphTx[i] = info.devTx[i] * ptSize;632}633textAt = new AffineTransform(info.glyphTx);634double shearx = transform.getShearX();635double scaley = transform.getScaleY();636if (shearx != 0) {637scaley = Math.sqrt(shearx * shearx + scaley * scaley);638}639info.pixelHeight = (int)(Math.abs(scaley * ptSize)+0.5);640} else {641/* If the double represents a common integral, we642* may have pre-allocated objects.643* A "sparse" array be seems to be as fast as a switch644* even for 3 or 4 pt sizes, and is more flexible.645* This should perform comparably in single-threaded646* rendering to the old code which synchronized on the647* class and scale better on MP systems.648*/649int pszInt = (int)ptSize;650if (ptSize == pszInt &&651pszInt >= MINALLOCATED && pszInt < TEXTARRSIZE) {652info.glyphTx = textTxArr[pszInt];653textAt = textAtArr[pszInt];654info.pixelHeight = pszInt;655} else {656info.pixelHeight = (int)(ptSize+0.5);657}658if (textAt == null) {659info.glyphTx = new double[] {ptSize, 0, 0, ptSize};660textAt = new AffineTransform(info.glyphTx);661}662663info.devTx = IDENT_MATRIX;664devAt = IDENT_ATX;665}666}667668info.font2D = FontUtilities.getFont2D(font);669670int fmhint = fractionalMetricsHint;671if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) {672fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;673}674info.lcdSubPixPos = false; // conditionally set true in LCD mode.675676/* The text anti-aliasing hints that are set by the client need677* to be interpreted for the current state and stored in the678* FontInfo.aahint which is what will actually be used and679* will be one of OFF, ON, LCD_HRGB or LCD_VRGB.680* This is what pipe selection code should typically refer to, not681* textAntialiasHint. This means we are now evaluating the meaning682* of "default" here. Any pipe that really cares about that will683* also need to consult that variable.684* Otherwise these are being used only as args to getStrike,685* and are encapsulated in that object which is part of the686* FontInfo, so we do not need to store them directly as fields687* in the FontInfo object.688* That could change if FontInfo's were more selectively689* revalidated when graphics state changed. Presently this690* method re-evaluates all fields in the fontInfo.691* The strike doesn't need to know the RGB subpixel order. Just692* if its H or V orientation, so if an LCD option is specified we693* always pass in the RGB hint to the strike.694* frc is non-null only if this is a GlyphVector. For reasons695* which are probably a historical mistake the AA hint in a GV696* is honoured when we render, overriding the Graphics setting.697*/698int aahint;699if (frc == null) {700aahint = textAntialiasHint;701} else {702aahint = ((SunHints.Value)frc.getAntiAliasingHint()).getIndex();703}704if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) {705if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {706aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;707} else {708aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;709}710} else {711/* If we are in checkFontInfo because a rendering hint has been712* set then all pipes are revalidated. But we can also713* be here because setFont() has been called when the 'gasp'714* hint is set, as then the font size determines the text pipe.715* See comments in SunGraphics2d.setFont(Font).716*/717if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) {718if (info.font2D.useAAForPtSize(info.pixelHeight)) {719aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;720} else {721aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF;722}723} else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) {724/* loops for default rendering modes are installed in the SG2D725* constructor. If there are none this will be null.726* Not all compositing modes update the render loops, so727* we also test that this is a mode we know should support728* this. One minor issue is that the loops aren't necessarily729* installed for a new rendering mode until after this730* method is called during pipeline validation. So it is731* theoretically possible that it was set to null for a732* compositing mode, the composite is then set back to Src,733* but the loop is still null when this is called and AA=ON734* is installed instead of an LCD mode.735* However this is done in the right order in SurfaceData.java736* so this is not likely to be a problem - but not737* guaranteed.738*/739if (740!surfaceData.canRenderLCDText(this)741// loops.drawGlyphListLCDLoop == null ||742// compositeState > COMP_ISCOPY ||743// paintState > PAINT_ALPHACOLOR744) {745aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;746} else {747info.lcdRGBOrder = true;748/* Collapse these into just HRGB or VRGB.749* Pipe selection code needs only to test for these two.750* Since these both select the same pipe anyway its751* tempting to collapse into one value. But they are752* different strikes (glyph caches) so the distinction753* needs to be made for that purpose.754*/755if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) {756aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;757info.lcdRGBOrder = false;758} else if759(aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) {760aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB;761info.lcdRGBOrder = false;762}763/* Support subpixel positioning only for the case in764* which the horizontal resolution is increased765*/766info.lcdSubPixPos =767fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON &&768aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB;769}770}771}772info.aaHint = aahint;773info.fontStrike = info.font2D.getStrike(font, devAt, textAt,774aahint, fmhint);775return info;776}777778public static boolean isRotated(double [] mtx) {779if ((mtx[0] == mtx[3]) &&780(mtx[1] == 0.0) &&781(mtx[2] == 0.0) &&782(mtx[0] > 0.0))783{784return false;785}786787return true;788}789790public void setFont(Font font) {791/* replacing the reference equality test font != this.font with792* !font.equals(this.font) did not yield any measurable difference793* in testing, but there may be yet to be identified cases where it794* is beneficial.795*/796if (font != null && font!=this.font/*!font.equals(this.font)*/) {797/* In the GASP AA case the textpipe depends on the glyph size798* as determined by graphics and font transforms as well as the799* font size, and information in the font. But we may invalidate800* the pipe only to find that it made no difference.801* Deferring pipe invalidation to checkFontInfo won't work because802* when called we may already be rendering to the wrong pipe.803* So, if the font is transformed, or the graphics has more than804* a simple scale, we'll take that as enough of a hint to805* revalidate everything. But if they aren't we will806* use the font's point size to query the gasp table and see if807* what it says matches what's currently being used, in which808* case there's no need to invalidate the textpipe.809* This should be sufficient for all typical uses cases.810*/811if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP &&812textpipe != invalidpipe &&813(transformState > TRANSFORM_ANY_TRANSLATE ||814font.isTransformed() ||815fontInfo == null || // Precaution, if true shouldn't get here816(fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) !=817FontUtilities.getFont2D(font).818useAAForPtSize(font.getSize()))) {819textpipe = invalidpipe;820}821this.font = font;822this.fontMetrics = null;823this.validFontInfo = false;824}825}826827public FontInfo getFontInfo() {828if (!validFontInfo) {829this.fontInfo = checkFontInfo(this.fontInfo, font, null);830validFontInfo = true;831}832return this.fontInfo;833}834835/* Used by drawGlyphVector which specifies its own font. */836public FontInfo getGVFontInfo(Font font, FontRenderContext frc) {837if (glyphVectorFontInfo != null &&838glyphVectorFontInfo.font == font &&839glyphVectorFRC == frc) {840return glyphVectorFontInfo;841} else {842glyphVectorFRC = frc;843return glyphVectorFontInfo =844checkFontInfo(glyphVectorFontInfo, font, frc);845}846}847848public FontMetrics getFontMetrics() {849if (this.fontMetrics != null) {850return this.fontMetrics;851}852/* NB the constructor and the setter disallow "font" being null */853return this.fontMetrics =854FontDesignMetrics.getMetrics(font, getFontRenderContext());855}856857public FontMetrics getFontMetrics(Font font) {858if ((this.fontMetrics != null) && (font == this.font)) {859return this.fontMetrics;860}861FontMetrics fm =862FontDesignMetrics.getMetrics(font, getFontRenderContext());863864if (this.font == font) {865this.fontMetrics = fm;866}867return fm;868}869870/**871* Checks to see if a Path intersects the specified Rectangle in device872* space. The rendering attributes taken into account include the873* clip, transform, and stroke attributes.874* @param rect The area in device space to check for a hit.875* @param p The path to check for a hit.876* @param onStroke Flag to choose between testing the stroked or877* the filled path.878* @return True if there is a hit, false otherwise.879* @see #setStroke880* @see #fillPath881* @see #drawPath882* @see #transform883* @see #setTransform884* @see #clip885* @see #setClip886*/887public boolean hit(Rectangle rect, Shape s, boolean onStroke) {888if (onStroke) {889s = stroke.createStrokedShape(s);890}891892s = transformShape(s);893if ((constrainX|constrainY) != 0) {894rect = new Rectangle(rect);895rect.translate(constrainX, constrainY);896}897898return s.intersects(rect);899}900901/**902* Return the ColorModel associated with this Graphics2D.903*/904public ColorModel getDeviceColorModel() {905return surfaceData.getColorModel();906}907908/**909* Return the device configuration associated with this Graphics2D.910*/911public GraphicsConfiguration getDeviceConfiguration() {912return surfaceData.getDeviceConfiguration();913}914915/**916* Return the SurfaceData object assigned to manage the destination917* drawable surface of this Graphics2D.918*/919public final SurfaceData getSurfaceData() {920return surfaceData;921}922923/**924* Sets the Composite in the current graphics state. Composite is used925* in all drawing methods such as drawImage, drawString, drawPath,926* and fillPath. It specifies how new pixels are to be combined with927* the existing pixels on the graphics device in the rendering process.928* @param comp The Composite object to be used for drawing.929* @see java.awt.Graphics#setXORMode930* @see java.awt.Graphics#setPaintMode931* @see AlphaComposite932*/933public void setComposite(Composite comp) {934if (composite == comp) {935return;936}937int newCompState;938CompositeType newCompType;939if (comp instanceof AlphaComposite) {940AlphaComposite alphacomp = (AlphaComposite) comp;941newCompType = CompositeType.forAlphaComposite(alphacomp);942if (newCompType == CompositeType.SrcOverNoEa) {943if (paintState == PAINT_OPAQUECOLOR ||944(paintState > PAINT_ALPHACOLOR &&945paint.getTransparency() == Transparency.OPAQUE))946{947newCompState = COMP_ISCOPY;948} else {949newCompState = COMP_ALPHA;950}951} else if (newCompType == CompositeType.SrcNoEa ||952newCompType == CompositeType.Src ||953newCompType == CompositeType.Clear)954{955newCompState = COMP_ISCOPY;956} else if (surfaceData.getTransparency() == Transparency.OPAQUE &&957newCompType == CompositeType.SrcIn)958{959newCompState = COMP_ISCOPY;960} else {961newCompState = COMP_ALPHA;962}963} else if (comp instanceof XORComposite) {964newCompState = COMP_XOR;965newCompType = CompositeType.Xor;966} else if (comp == null) {967throw new IllegalArgumentException("null Composite");968} else {969surfaceData.checkCustomComposite();970newCompState = COMP_CUSTOM;971newCompType = CompositeType.General;972}973if (compositeState != newCompState ||974imageComp != newCompType)975{976compositeState = newCompState;977imageComp = newCompType;978invalidatePipe();979validFontInfo = false;980}981composite = comp;982if (paintState <= PAINT_ALPHACOLOR) {983validateColor();984}985}986987/**988* Sets the Paint in the current graphics state.989* @param paint The Paint object to be used to generate color in990* the rendering process.991* @see java.awt.Graphics#setColor992* @see GradientPaint993* @see TexturePaint994*/995public void setPaint(Paint paint) {996if (paint instanceof Color) {997setColor((Color) paint);998return;999}1000if (paint == null || this.paint == paint) {1001return;1002}1003this.paint = paint;1004if (imageComp == CompositeType.SrcOverNoEa) {1005// special case where compState depends on opacity of paint1006if (paint.getTransparency() == Transparency.OPAQUE) {1007if (compositeState != COMP_ISCOPY) {1008compositeState = COMP_ISCOPY;1009}1010} else {1011if (compositeState == COMP_ISCOPY) {1012compositeState = COMP_ALPHA;1013}1014}1015}1016Class<? extends Paint> paintClass = paint.getClass();1017if (paintClass == GradientPaint.class) {1018paintState = PAINT_GRADIENT;1019} else if (paintClass == LinearGradientPaint.class) {1020paintState = PAINT_LIN_GRADIENT;1021} else if (paintClass == RadialGradientPaint.class) {1022paintState = PAINT_RAD_GRADIENT;1023} else if (paintClass == TexturePaint.class) {1024paintState = PAINT_TEXTURE;1025} else {1026paintState = PAINT_CUSTOM;1027}1028validFontInfo = false;1029invalidatePipe();1030}10311032static final int NON_UNIFORM_SCALE_MASK =1033(AffineTransform.TYPE_GENERAL_TRANSFORM |1034AffineTransform.TYPE_GENERAL_SCALE);1035public static final double MinPenSizeAA =1036sun.java2d.pipe.RenderingEngine.getInstance().getMinimumAAPenSize();1037public static final double MinPenSizeAASquared =1038(MinPenSizeAA * MinPenSizeAA);1039// Since inaccuracies in the trig package can cause us to1040// calculated a rotated pen width of just slightly greater1041// than 1.0, we add a fudge factor to our comparison value1042// here so that we do not misclassify single width lines as1043// wide lines under certain rotations.1044public static final double MinPenSizeSquared = 1.000000001;10451046private void validateBasicStroke(BasicStroke bs) {1047boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON);1048if (transformState < TRANSFORM_TRANSLATESCALE) {1049if (aa) {1050if (bs.getLineWidth() <= MinPenSizeAA) {1051if (bs.getDashArray() == null) {1052strokeState = STROKE_THIN;1053} else {1054strokeState = STROKE_THINDASHED;1055}1056} else {1057strokeState = STROKE_WIDE;1058}1059} else {1060if (bs == defaultStroke) {1061strokeState = STROKE_THIN;1062} else if (bs.getLineWidth() <= 1.0f) {1063if (bs.getDashArray() == null) {1064strokeState = STROKE_THIN;1065} else {1066strokeState = STROKE_THINDASHED;1067}1068} else {1069strokeState = STROKE_WIDE;1070}1071}1072} else {1073double widthsquared;1074if ((transform.getType() & NON_UNIFORM_SCALE_MASK) == 0) {1075/* sqrt omitted, compare to squared limits below. */1076widthsquared = Math.abs(transform.getDeterminant());1077} else {1078/* First calculate the "maximum scale" of this transform. */1079double A = transform.getScaleX(); // m001080double C = transform.getShearX(); // m011081double B = transform.getShearY(); // m101082double D = transform.getScaleY(); // m1110831084/*1085* Given a 2 x 2 affine matrix [ A B ] such that1086* [ C D ]1087* v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to1088* find the maximum magnitude (norm) of the vector v'1089* with the constraint (x^2 + y^2 = 1).1090* The equation to maximize is1091* |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2)1092* or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2).1093* Since sqrt is monotonic we can maximize |v'|^21094* instead and plug in the substitution y = sqrt(1 - x^2).1095* Trigonometric equalities can then be used to get1096* rid of most of the sqrt terms.1097*/1098double EA = A*A + B*B; // x^2 coefficient1099double EB = 2*(A*C + B*D); // xy coefficient1100double EC = C*C + D*D; // y^2 coefficient11011102/*1103* There is a lot of calculus omitted here.1104*1105* Conceptually, in the interests of understanding the1106* terms that the calculus produced we can consider1107* that EA and EC end up providing the lengths along1108* the major axes and the hypot term ends up being an1109* adjustment for the additional length along the off-axis1110* angle of rotated or sheared ellipses as well as an1111* adjustment for the fact that the equation below1112* averages the two major axis lengths. (Notice that1113* the hypot term contains a part which resolves to the1114* difference of these two axis lengths in the absence1115* of rotation.)1116*1117* In the calculus, the ratio of the EB and (EA-EC) terms1118* ends up being the tangent of 2*theta where theta is1119* the angle that the long axis of the ellipse makes1120* with the horizontal axis. Thus, this equation is1121* calculating the length of the hypotenuse of a triangle1122* along that axis.1123*/1124double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));11251126/* sqrt omitted, compare to squared limits below. */1127widthsquared = ((EA + EC + hypot)/2.0);1128}1129if (bs != defaultStroke) {1130widthsquared *= bs.getLineWidth() * bs.getLineWidth();1131}1132if (widthsquared <=1133(aa ? MinPenSizeAASquared : MinPenSizeSquared))1134{1135if (bs.getDashArray() == null) {1136strokeState = STROKE_THIN;1137} else {1138strokeState = STROKE_THINDASHED;1139}1140} else {1141strokeState = STROKE_WIDE;1142}1143}1144}11451146/*1147* Sets the Stroke in the current graphics state.1148* @param s The Stroke object to be used to stroke a Path in1149* the rendering process.1150* @see BasicStroke1151*/1152public void setStroke(Stroke s) {1153if (s == null) {1154throw new IllegalArgumentException("null Stroke");1155}1156int saveStrokeState = strokeState;1157stroke = s;1158if (s instanceof BasicStroke) {1159validateBasicStroke((BasicStroke) s);1160} else {1161strokeState = STROKE_CUSTOM;1162}1163if (strokeState != saveStrokeState) {1164invalidatePipe();1165}1166}11671168/**1169* Sets the preferences for the rendering algorithms.1170* Hint categories include controls for rendering quality and1171* overall time/quality trade-off in the rendering process.1172* @param hintKey The key of hint to be set. The strings are1173* defined in the RenderingHints class.1174* @param hintValue The value indicating preferences for the specified1175* hint category. These strings are defined in the RenderingHints1176* class.1177* @see RenderingHints1178*/1179public void setRenderingHint(Key hintKey, Object hintValue) {1180// If we recognize the key, we must recognize the value1181// otherwise throw an IllegalArgumentException1182// and do not change the Hints object1183// If we do not recognize the key, just pass it through1184// to the Hints object untouched1185if (!hintKey.isCompatibleValue(hintValue)) {1186throw new IllegalArgumentException1187(hintValue+" is not compatible with "+hintKey);1188}1189if (hintKey instanceof SunHints.Key) {1190boolean stateChanged;1191boolean textStateChanged = false;1192boolean recognized = true;1193SunHints.Key sunKey = (SunHints.Key) hintKey;1194int newHint;1195if (sunKey == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST) {1196newHint = ((Integer)hintValue).intValue();1197} else {1198newHint = ((SunHints.Value) hintValue).getIndex();1199}1200switch (sunKey.getIndex()) {1201case SunHints.INTKEY_RENDERING:1202stateChanged = (renderHint != newHint);1203if (stateChanged) {1204renderHint = newHint;1205if (interpolationHint == -1) {1206interpolationType =1207(newHint == SunHints.INTVAL_RENDER_QUALITY1208? AffineTransformOp.TYPE_BILINEAR1209: AffineTransformOp.TYPE_NEAREST_NEIGHBOR);1210}1211}1212break;1213case SunHints.INTKEY_ANTIALIASING:1214stateChanged = (antialiasHint != newHint);1215antialiasHint = newHint;1216if (stateChanged) {1217textStateChanged =1218(textAntialiasHint ==1219SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT);1220if (strokeState != STROKE_CUSTOM) {1221validateBasicStroke((BasicStroke) stroke);1222}1223}1224break;1225case SunHints.INTKEY_TEXT_ANTIALIASING:1226stateChanged = (textAntialiasHint != newHint);1227textStateChanged = stateChanged;1228textAntialiasHint = newHint;1229break;1230case SunHints.INTKEY_FRACTIONALMETRICS:1231stateChanged = (fractionalMetricsHint != newHint);1232textStateChanged = stateChanged;1233fractionalMetricsHint = newHint;1234break;1235case SunHints.INTKEY_AATEXT_LCD_CONTRAST:1236stateChanged = false;1237/* Already have validated it is an int 100 <= newHint <= 250 */1238lcdTextContrast = newHint;1239break;1240case SunHints.INTKEY_INTERPOLATION:1241interpolationHint = newHint;1242switch (newHint) {1243case SunHints.INTVAL_INTERPOLATION_BICUBIC:1244newHint = AffineTransformOp.TYPE_BICUBIC;1245break;1246case SunHints.INTVAL_INTERPOLATION_BILINEAR:1247newHint = AffineTransformOp.TYPE_BILINEAR;1248break;1249default:1250case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:1251newHint = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;1252break;1253}1254stateChanged = (interpolationType != newHint);1255interpolationType = newHint;1256break;1257case SunHints.INTKEY_STROKE_CONTROL:1258stateChanged = (strokeHint != newHint);1259strokeHint = newHint;1260break;1261case SunHints.INTKEY_RESOLUTION_VARIANT:1262stateChanged = (resolutionVariantHint != newHint);1263resolutionVariantHint = newHint;1264break;1265default:1266recognized = false;1267stateChanged = false;1268break;1269}1270if (recognized) {1271if (stateChanged) {1272invalidatePipe();1273if (textStateChanged) {1274fontMetrics = null;1275this.cachedFRC = null;1276validFontInfo = false;1277this.glyphVectorFontInfo = null;1278}1279}1280if (hints != null) {1281hints.put(hintKey, hintValue);1282}1283return;1284}1285}1286// Nothing we recognize so none of "our state" has changed1287if (hints == null) {1288hints = makeHints(null);1289}1290hints.put(hintKey, hintValue);1291}129212931294/**1295* Returns the preferences for the rendering algorithms.1296* @param hintCategory The category of hint to be set. The strings1297* are defined in the RenderingHints class.1298* @return The preferences for rendering algorithms. The strings1299* are defined in the RenderingHints class.1300* @see RenderingHints1301*/1302public Object getRenderingHint(Key hintKey) {1303if (hints != null) {1304return hints.get(hintKey);1305}1306if (!(hintKey instanceof SunHints.Key)) {1307return null;1308}1309int keyindex = ((SunHints.Key)hintKey).getIndex();1310switch (keyindex) {1311case SunHints.INTKEY_RENDERING:1312return SunHints.Value.get(SunHints.INTKEY_RENDERING,1313renderHint);1314case SunHints.INTKEY_ANTIALIASING:1315return SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,1316antialiasHint);1317case SunHints.INTKEY_TEXT_ANTIALIASING:1318return SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,1319textAntialiasHint);1320case SunHints.INTKEY_FRACTIONALMETRICS:1321return SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,1322fractionalMetricsHint);1323case SunHints.INTKEY_AATEXT_LCD_CONTRAST:1324return new Integer(lcdTextContrast);1325case SunHints.INTKEY_INTERPOLATION:1326switch (interpolationHint) {1327case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:1328return SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;1329case SunHints.INTVAL_INTERPOLATION_BILINEAR:1330return SunHints.VALUE_INTERPOLATION_BILINEAR;1331case SunHints.INTVAL_INTERPOLATION_BICUBIC:1332return SunHints.VALUE_INTERPOLATION_BICUBIC;1333}1334return null;1335case SunHints.INTKEY_STROKE_CONTROL:1336return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,1337strokeHint);1338case SunHints.INTKEY_RESOLUTION_VARIANT:1339return SunHints.Value.get(SunHints.INTKEY_RESOLUTION_VARIANT,1340resolutionVariantHint);1341}1342return null;1343}13441345/**1346* Sets the preferences for the rendering algorithms.1347* Hint categories include controls for rendering quality and1348* overall time/quality trade-off in the rendering process.1349* @param hints The rendering hints to be set1350* @see RenderingHints1351*/1352public void setRenderingHints(Map<?,?> hints) {1353this.hints = null;1354renderHint = SunHints.INTVAL_RENDER_DEFAULT;1355antialiasHint = SunHints.INTVAL_ANTIALIAS_OFF;1356textAntialiasHint = SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT;1357fractionalMetricsHint = SunHints.INTVAL_FRACTIONALMETRICS_OFF;1358lcdTextContrast = lcdTextContrastDefaultValue;1359interpolationHint = -1;1360interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;1361boolean customHintPresent = false;1362Iterator<?> iter = hints.keySet().iterator();1363while (iter.hasNext()) {1364Object key = iter.next();1365if (key == SunHints.KEY_RENDERING ||1366key == SunHints.KEY_ANTIALIASING ||1367key == SunHints.KEY_TEXT_ANTIALIASING ||1368key == SunHints.KEY_FRACTIONALMETRICS ||1369key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||1370key == SunHints.KEY_STROKE_CONTROL ||1371key == SunHints.KEY_INTERPOLATION)1372{1373setRenderingHint((Key) key, hints.get(key));1374} else {1375customHintPresent = true;1376}1377}1378if (customHintPresent) {1379this.hints = makeHints(hints);1380}1381invalidatePipe();1382}13831384/**1385* Adds a number of preferences for the rendering algorithms.1386* Hint categories include controls for rendering quality and1387* overall time/quality trade-off in the rendering process.1388* @param hints The rendering hints to be set1389* @see RenderingHints1390*/1391public void addRenderingHints(Map<?,?> hints) {1392boolean customHintPresent = false;1393Iterator<?> iter = hints.keySet().iterator();1394while (iter.hasNext()) {1395Object key = iter.next();1396if (key == SunHints.KEY_RENDERING ||1397key == SunHints.KEY_ANTIALIASING ||1398key == SunHints.KEY_TEXT_ANTIALIASING ||1399key == SunHints.KEY_FRACTIONALMETRICS ||1400key == SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST ||1401key == SunHints.KEY_STROKE_CONTROL ||1402key == SunHints.KEY_INTERPOLATION)1403{1404setRenderingHint((Key) key, hints.get(key));1405} else {1406customHintPresent = true;1407}1408}1409if (customHintPresent) {1410if (this.hints == null) {1411this.hints = makeHints(hints);1412} else {1413this.hints.putAll(hints);1414}1415}1416}14171418/**1419* Gets the preferences for the rendering algorithms.1420* Hint categories include controls for rendering quality and1421* overall time/quality trade-off in the rendering process.1422* @see RenderingHints1423*/1424public RenderingHints getRenderingHints() {1425if (hints == null) {1426return makeHints(null);1427} else {1428return (RenderingHints) hints.clone();1429}1430}14311432RenderingHints makeHints(Map hints) {1433RenderingHints model = new RenderingHints(hints);1434model.put(SunHints.KEY_RENDERING,1435SunHints.Value.get(SunHints.INTKEY_RENDERING,1436renderHint));1437model.put(SunHints.KEY_ANTIALIASING,1438SunHints.Value.get(SunHints.INTKEY_ANTIALIASING,1439antialiasHint));1440model.put(SunHints.KEY_TEXT_ANTIALIASING,1441SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING,1442textAntialiasHint));1443model.put(SunHints.KEY_FRACTIONALMETRICS,1444SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,1445fractionalMetricsHint));1446model.put(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST,1447Integer.valueOf(lcdTextContrast));1448Object value;1449switch (interpolationHint) {1450case SunHints.INTVAL_INTERPOLATION_NEAREST_NEIGHBOR:1451value = SunHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;1452break;1453case SunHints.INTVAL_INTERPOLATION_BILINEAR:1454value = SunHints.VALUE_INTERPOLATION_BILINEAR;1455break;1456case SunHints.INTVAL_INTERPOLATION_BICUBIC:1457value = SunHints.VALUE_INTERPOLATION_BICUBIC;1458break;1459default:1460value = null;1461break;1462}1463if (value != null) {1464model.put(SunHints.KEY_INTERPOLATION, value);1465}1466model.put(SunHints.KEY_STROKE_CONTROL,1467SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,1468strokeHint));1469return model;1470}14711472/**1473* Concatenates the current transform of this Graphics2D with a1474* translation transformation.1475* This is equivalent to calling transform(T), where T is an1476* AffineTransform represented by the following matrix:1477* <pre>1478* [ 1 0 tx ]1479* [ 0 1 ty ]1480* [ 0 0 1 ]1481* </pre>1482*/1483public void translate(double tx, double ty) {1484transform.translate(tx, ty);1485invalidateTransform();1486}14871488/**1489* Concatenates the current transform of this Graphics2D with a1490* rotation transformation.1491* This is equivalent to calling transform(R), where R is an1492* AffineTransform represented by the following matrix:1493* <pre>1494* [ cos(theta) -sin(theta) 0 ]1495* [ sin(theta) cos(theta) 0 ]1496* [ 0 0 1 ]1497* </pre>1498* Rotating with a positive angle theta rotates points on the positive1499* x axis toward the positive y axis.1500* @param theta The angle of rotation in radians.1501*/1502public void rotate(double theta) {1503transform.rotate(theta);1504invalidateTransform();1505}15061507/**1508* Concatenates the current transform of this Graphics2D with a1509* translated rotation transformation.1510* This is equivalent to the following sequence of calls:1511* <pre>1512* translate(x, y);1513* rotate(theta);1514* translate(-x, -y);1515* </pre>1516* Rotating with a positive angle theta rotates points on the positive1517* x axis toward the positive y axis.1518* @param theta The angle of rotation in radians.1519* @param x The x coordinate of the origin of the rotation1520* @param y The x coordinate of the origin of the rotation1521*/1522public void rotate(double theta, double x, double y) {1523transform.rotate(theta, x, y);1524invalidateTransform();1525}15261527/**1528* Concatenates the current transform of this Graphics2D with a1529* scaling transformation.1530* This is equivalent to calling transform(S), where S is an1531* AffineTransform represented by the following matrix:1532* <pre>1533* [ sx 0 0 ]1534* [ 0 sy 0 ]1535* [ 0 0 1 ]1536* </pre>1537*/1538public void scale(double sx, double sy) {1539transform.scale(sx, sy);1540invalidateTransform();1541}15421543/**1544* Concatenates the current transform of this Graphics2D with a1545* shearing transformation.1546* This is equivalent to calling transform(SH), where SH is an1547* AffineTransform represented by the following matrix:1548* <pre>1549* [ 1 shx 0 ]1550* [ shy 1 0 ]1551* [ 0 0 1 ]1552* </pre>1553* @param shx The factor by which coordinates are shifted towards the1554* positive X axis direction according to their Y coordinate1555* @param shy The factor by which coordinates are shifted towards the1556* positive Y axis direction according to their X coordinate1557*/1558public void shear(double shx, double shy) {1559transform.shear(shx, shy);1560invalidateTransform();1561}15621563/**1564* Composes a Transform object with the transform in this1565* Graphics2D according to the rule last-specified-first-applied.1566* If the currrent transform is Cx, the result of composition1567* with Tx is a new transform Cx'. Cx' becomes the current1568* transform for this Graphics2D.1569* Transforming a point p by the updated transform Cx' is1570* equivalent to first transforming p by Tx and then transforming1571* the result by the original transform Cx. In other words,1572* Cx'(p) = Cx(Tx(p)).1573* A copy of the Tx is made, if necessary, so further1574* modifications to Tx do not affect rendering.1575* @param Tx The Transform object to be composed with the current1576* transform.1577* @see #setTransform1578* @see AffineTransform1579*/1580public void transform(AffineTransform xform) {1581this.transform.concatenate(xform);1582invalidateTransform();1583}15841585/**1586* Translate1587*/1588public void translate(int x, int y) {1589transform.translate(x, y);1590if (transformState <= TRANSFORM_INT_TRANSLATE) {1591transX += x;1592transY += y;1593transformState = (((transX | transY) == 0) ?1594TRANSFORM_ISIDENT : TRANSFORM_INT_TRANSLATE);1595} else {1596invalidateTransform();1597}1598}15991600/**1601* Sets the Transform in the current graphics state.1602* @param Tx The Transform object to be used in the rendering process.1603* @see #transform1604* @see TransformChain1605* @see AffineTransform1606*/1607@Override1608public void setTransform(AffineTransform Tx) {1609if ((constrainX | constrainY) == 0 && devScale == 1) {1610transform.setTransform(Tx);1611} else {1612transform.setTransform(devScale, 0, 0, devScale, constrainX,1613constrainY);1614transform.concatenate(Tx);1615}1616invalidateTransform();1617}16181619protected void invalidateTransform() {1620int type = transform.getType();1621int origTransformState = transformState;1622if (type == AffineTransform.TYPE_IDENTITY) {1623transformState = TRANSFORM_ISIDENT;1624transX = transY = 0;1625} else if (type == AffineTransform.TYPE_TRANSLATION) {1626double dtx = transform.getTranslateX();1627double dty = transform.getTranslateY();1628transX = (int) Math.floor(dtx + 0.5);1629transY = (int) Math.floor(dty + 0.5);1630if (dtx == transX && dty == transY) {1631transformState = TRANSFORM_INT_TRANSLATE;1632} else {1633transformState = TRANSFORM_ANY_TRANSLATE;1634}1635} else if ((type & (AffineTransform.TYPE_FLIP |1636AffineTransform.TYPE_MASK_ROTATION |1637AffineTransform.TYPE_GENERAL_TRANSFORM)) == 0)1638{1639transformState = TRANSFORM_TRANSLATESCALE;1640transX = transY = 0;1641} else {1642transformState = TRANSFORM_GENERIC;1643transX = transY = 0;1644}16451646if (transformState >= TRANSFORM_TRANSLATESCALE ||1647origTransformState >= TRANSFORM_TRANSLATESCALE)1648{1649/* Its only in this case that the previous or current transform1650* was more than a translate that font info is invalidated1651*/1652cachedFRC = null;1653this.validFontInfo = false;1654this.fontMetrics = null;1655this.glyphVectorFontInfo = null;16561657if (transformState != origTransformState) {1658invalidatePipe();1659}1660}1661if (strokeState != STROKE_CUSTOM) {1662validateBasicStroke((BasicStroke) stroke);1663}1664}16651666/**1667* Returns the current Transform in the Graphics2D state.1668* @see #transform1669* @see #setTransform1670*/1671@Override1672public AffineTransform getTransform() {1673if ((constrainX | constrainY) == 0 && devScale == 1) {1674return new AffineTransform(transform);1675}1676final double invScale = 1.0 / devScale;1677AffineTransform tx = new AffineTransform(invScale, 0, 0, invScale,1678-constrainX * invScale,1679-constrainY * invScale);1680tx.concatenate(transform);1681return tx;1682}16831684/**1685* Returns the current Transform ignoring the "constrain"1686* rectangle.1687*/1688public AffineTransform cloneTransform() {1689return new AffineTransform(transform);1690}16911692/**1693* Returns the current Paint in the Graphics2D state.1694* @see #setPaint1695* @see java.awt.Graphics#setColor1696*/1697public Paint getPaint() {1698return paint;1699}17001701/**1702* Returns the current Composite in the Graphics2D state.1703* @see #setComposite1704*/1705public Composite getComposite() {1706return composite;1707}17081709public Color getColor() {1710return foregroundColor;1711}17121713/*1714* Validate the eargb and pixel fields against the current color.1715*1716* The eargb field must take into account the extraAlpha1717* value of an AlphaComposite. It may also take into account1718* the Fsrc Porter-Duff blending function if such a function is1719* a constant (see handling of Clear mode below). For instance,1720* by factoring in the (Fsrc == 0) state of the Clear mode we can1721* use a SrcNoEa loop just as easily as a general Alpha loop1722* since the math will be the same in both cases.1723*1724* The pixel field will always be the best pixel data choice for1725* the final result of all calculations applied to the eargb field.1726*1727* Note that this method is only necessary under the following1728* conditions:1729* (paintState <= PAINT_ALPHA_COLOR &&1730* compositeState <= COMP_CUSTOM)1731* though nothing bad will happen if it is run in other states.1732*/1733final void validateColor() {1734int eargb;1735if (imageComp == CompositeType.Clear) {1736eargb = 0;1737} else {1738eargb = foregroundColor.getRGB();1739if (compositeState <= COMP_ALPHA &&1740imageComp != CompositeType.SrcNoEa &&1741imageComp != CompositeType.SrcOverNoEa)1742{1743AlphaComposite alphacomp = (AlphaComposite) composite;1744int a = Math.round(alphacomp.getAlpha() * (eargb >>> 24));1745eargb = (eargb & 0x00ffffff) | (a << 24);1746}1747}1748this.eargb = eargb;1749this.pixel = surfaceData.pixelFor(eargb);1750}17511752public void setColor(Color color) {1753if (color == null || color == paint) {1754return;1755}1756this.paint = foregroundColor = color;1757validateColor();1758if ((eargb >> 24) == -1) {1759if (paintState == PAINT_OPAQUECOLOR) {1760return;1761}1762paintState = PAINT_OPAQUECOLOR;1763if (imageComp == CompositeType.SrcOverNoEa) {1764// special case where compState depends on opacity of paint1765compositeState = COMP_ISCOPY;1766}1767} else {1768if (paintState == PAINT_ALPHACOLOR) {1769return;1770}1771paintState = PAINT_ALPHACOLOR;1772if (imageComp == CompositeType.SrcOverNoEa) {1773// special case where compState depends on opacity of paint1774compositeState = COMP_ALPHA;1775}1776}1777validFontInfo = false;1778invalidatePipe();1779}17801781/**1782* Sets the background color in this context used for clearing a region.1783* When Graphics2D is constructed for a component, the backgroung color is1784* inherited from the component. Setting the background color in the1785* Graphics2D context only affects the subsequent clearRect() calls and1786* not the background color of the component. To change the background1787* of the component, use appropriate methods of the component.1788* @param color The background color that should be used in1789* subsequent calls to clearRect().1790* @see getBackground1791* @see Graphics.clearRect()1792*/1793public void setBackground(Color color) {1794backgroundColor = color;1795}17961797/**1798* Returns the background color used for clearing a region.1799* @see setBackground1800*/1801public Color getBackground() {1802return backgroundColor;1803}18041805/**1806* Returns the current Stroke in the Graphics2D state.1807* @see setStroke1808*/1809public Stroke getStroke() {1810return stroke;1811}18121813public Rectangle getClipBounds() {1814if (clipState == CLIP_DEVICE) {1815return null;1816}1817return getClipBounds(new Rectangle());1818}18191820public Rectangle getClipBounds(Rectangle r) {1821if (clipState != CLIP_DEVICE) {1822if (transformState <= TRANSFORM_INT_TRANSLATE) {1823if (usrClip instanceof Rectangle) {1824r.setBounds((Rectangle) usrClip);1825} else {1826r.setFrame(usrClip.getBounds2D());1827}1828r.translate(-transX, -transY);1829} else {1830r.setFrame(getClip().getBounds2D());1831}1832} else if (r == null) {1833throw new NullPointerException("null rectangle parameter");1834}1835return r;1836}18371838public boolean hitClip(int x, int y, int width, int height) {1839if (width <= 0 || height <= 0) {1840return false;1841}1842if (transformState > TRANSFORM_INT_TRANSLATE) {1843// Note: Technically the most accurate test would be to1844// raster scan the parallelogram of the transformed rectangle1845// and do a span for span hit test against the clip, but for1846// speed we approximate the test with a bounding box of the1847// transformed rectangle. The cost of rasterizing the1848// transformed rectangle is probably high enough that it is1849// not worth doing so to save the caller from having to call1850// a rendering method where we will end up discovering the1851// same answer in about the same amount of time anyway.1852// This logic breaks down if this hit test is being performed1853// on the bounds of a group of shapes in which case it might1854// be beneficial to be a little more accurate to avoid lots1855// of subsequent rendering calls. In either case, this relaxed1856// test should not be significantly less accurate than the1857// optimal test for most transforms and so the conservative1858// answer should not cause too much extra work.18591860double d[] = {1861x, y,1862x+width, y,1863x, y+height,1864x+width, y+height1865};1866transform.transform(d, 0, d, 0, 4);1867x = (int) Math.floor(Math.min(Math.min(d[0], d[2]),1868Math.min(d[4], d[6])));1869y = (int) Math.floor(Math.min(Math.min(d[1], d[3]),1870Math.min(d[5], d[7])));1871width = (int) Math.ceil(Math.max(Math.max(d[0], d[2]),1872Math.max(d[4], d[6])));1873height = (int) Math.ceil(Math.max(Math.max(d[1], d[3]),1874Math.max(d[5], d[7])));1875} else {1876x += transX;1877y += transY;1878width += x;1879height += y;1880}18811882try {1883if (!getCompClip().intersectsQuickCheckXYXY(x, y, width, height)) {1884return false;1885}1886} catch (InvalidPipeException e) {1887return false;1888}1889// REMIND: We could go one step further here and examine the1890// non-rectangular clip shape more closely if there is one.1891// Since the clip has already been rasterized, the performance1892// penalty of doing the scan is probably still within the bounds1893// of a good tradeoff between speed and quality of the answer.1894return true;1895}18961897protected void validateCompClip() {1898int origClipState = clipState;1899if (usrClip == null) {1900clipState = CLIP_DEVICE;1901clipRegion = devClip;1902} else if (usrClip instanceof Rectangle2D) {1903clipState = CLIP_RECTANGULAR;1904if (usrClip instanceof Rectangle) {1905clipRegion = devClip.getIntersection((Rectangle)usrClip);1906} else {1907clipRegion = devClip.getIntersection(usrClip.getBounds());1908}1909} else {1910PathIterator cpi = usrClip.getPathIterator(null);1911int box[] = new int[4];1912ShapeSpanIterator sr = LoopPipe.getFillSSI(this);1913try {1914sr.setOutputArea(devClip);1915sr.appendPath(cpi);1916sr.getPathBox(box);1917Region r = Region.getInstance(box);1918r.appendSpans(sr);1919clipRegion = r;1920clipState =1921r.isRectangular() ? CLIP_RECTANGULAR : CLIP_SHAPE;1922} finally {1923sr.dispose();1924}1925}1926if (origClipState != clipState &&1927(clipState == CLIP_SHAPE || origClipState == CLIP_SHAPE))1928{1929validFontInfo = false;1930invalidatePipe();1931}1932}19331934static final int NON_RECTILINEAR_TRANSFORM_MASK =1935(AffineTransform.TYPE_GENERAL_TRANSFORM |1936AffineTransform.TYPE_GENERAL_ROTATION);19371938protected Shape transformShape(Shape s) {1939if (s == null) {1940return null;1941}1942if (transformState > TRANSFORM_INT_TRANSLATE) {1943return transformShape(transform, s);1944} else {1945return transformShape(transX, transY, s);1946}1947}19481949public Shape untransformShape(Shape s) {1950if (s == null) {1951return null;1952}1953if (transformState > TRANSFORM_INT_TRANSLATE) {1954try {1955return transformShape(transform.createInverse(), s);1956} catch (NoninvertibleTransformException e) {1957return null;1958}1959} else {1960return transformShape(-transX, -transY, s);1961}1962}19631964protected static Shape transformShape(int tx, int ty, Shape s) {1965if (s == null) {1966return null;1967}19681969if (s instanceof Rectangle) {1970Rectangle r = s.getBounds();1971r.translate(tx, ty);1972return r;1973}1974if (s instanceof Rectangle2D) {1975Rectangle2D rect = (Rectangle2D) s;1976return new Rectangle2D.Double(rect.getX() + tx,1977rect.getY() + ty,1978rect.getWidth(),1979rect.getHeight());1980}19811982if (tx == 0 && ty == 0) {1983return cloneShape(s);1984}19851986AffineTransform mat = AffineTransform.getTranslateInstance(tx, ty);1987return mat.createTransformedShape(s);1988}19891990protected static Shape transformShape(AffineTransform tx, Shape clip) {1991if (clip == null) {1992return null;1993}19941995if (clip instanceof Rectangle2D &&1996(tx.getType() & NON_RECTILINEAR_TRANSFORM_MASK) == 0)1997{1998Rectangle2D rect = (Rectangle2D) clip;1999double matrix[] = new double[4];2000matrix[0] = rect.getX();2001matrix[1] = rect.getY();2002matrix[2] = matrix[0] + rect.getWidth();2003matrix[3] = matrix[1] + rect.getHeight();2004tx.transform(matrix, 0, matrix, 0, 2);2005fixRectangleOrientation(matrix, rect);2006return new Rectangle2D.Double(matrix[0], matrix[1],2007matrix[2] - matrix[0],2008matrix[3] - matrix[1]);2009}20102011if (tx.isIdentity()) {2012return cloneShape(clip);2013}20142015return tx.createTransformedShape(clip);2016}20172018/**2019* Sets orientation of the rectangle according to the clip.2020*/2021private static void fixRectangleOrientation(double[] m, Rectangle2D clip) {2022if (clip.getWidth() > 0 != (m[2] - m[0] > 0)) {2023double t = m[0];2024m[0] = m[2];2025m[2] = t;2026}2027if (clip.getHeight() > 0 != (m[3] - m[1] > 0)) {2028double t = m[1];2029m[1] = m[3];2030m[3] = t;2031}2032}20332034public void clipRect(int x, int y, int w, int h) {2035clip(new Rectangle(x, y, w, h));2036}20372038public void setClip(int x, int y, int w, int h) {2039setClip(new Rectangle(x, y, w, h));2040}20412042public Shape getClip() {2043return untransformShape(usrClip);2044}20452046public void setClip(Shape sh) {2047usrClip = transformShape(sh);2048validateCompClip();2049}20502051/**2052* Intersects the current clip with the specified Path and sets the2053* current clip to the resulting intersection. The clip is transformed2054* with the current transform in the Graphics2D state before being2055* intersected with the current clip. This method is used to make the2056* current clip smaller. To make the clip larger, use any setClip method.2057* @param p The Path to be intersected with the current clip.2058*/2059public void clip(Shape s) {2060s = transformShape(s);2061if (usrClip != null) {2062s = intersectShapes(usrClip, s, true, true);2063}2064usrClip = s;2065validateCompClip();2066}20672068public void setPaintMode() {2069setComposite(AlphaComposite.SrcOver);2070}20712072public void setXORMode(Color c) {2073if (c == null) {2074throw new IllegalArgumentException("null XORColor");2075}2076setComposite(new XORComposite(c, surfaceData));2077}20782079Blit lastCAblit;2080Composite lastCAcomp;20812082public void copyArea(int x, int y, int w, int h, int dx, int dy) {2083try {2084doCopyArea(x, y, w, h, dx, dy);2085} catch (InvalidPipeException e) {2086try {2087revalidateAll();2088doCopyArea(x, y, w, h, dx, dy);2089} catch (InvalidPipeException e2) {2090// Still catching the exception; we are not yet ready to2091// validate the surfaceData correctly. Fail for now and2092// try again next time around.2093}2094} finally {2095surfaceData.markDirty();2096}2097}20982099private void doCopyArea(int x, int y, int w, int h, int dx, int dy) {2100if (w <= 0 || h <= 0) {2101return;2102}2103SurfaceData theData = surfaceData;2104if (theData.copyArea(this, x, y, w, h, dx, dy)) {2105return;2106}2107if (transformState > TRANSFORM_TRANSLATESCALE) {2108throw new InternalError("transformed copyArea not implemented yet");2109}2110// REMIND: This method does not deal with missing data from the2111// source object (i.e. it does not send exposure events...)21122113Region clip = getCompClip();21142115Composite comp = composite;2116if (lastCAcomp != comp) {2117SurfaceType dsttype = theData.getSurfaceType();2118CompositeType comptype = imageComp;2119if (CompositeType.SrcOverNoEa.equals(comptype) &&2120theData.getTransparency() == Transparency.OPAQUE)2121{2122comptype = CompositeType.SrcNoEa;2123}2124lastCAblit = Blit.locate(dsttype, comptype, dsttype);2125lastCAcomp = comp;2126}21272128double[] coords = {x, y, x + w, y + h, x + dx, y + dy};2129transform.transform(coords, 0, coords, 0, 3);21302131x = (int)Math.ceil(coords[0] - 0.5);2132y = (int)Math.ceil(coords[1] - 0.5);2133w = ((int)Math.ceil(coords[2] - 0.5)) - x;2134h = ((int)Math.ceil(coords[3] - 0.5)) - y;2135dx = ((int)Math.ceil(coords[4] - 0.5)) - x;2136dy = ((int)Math.ceil(coords[5] - 0.5)) - y;21372138// In case of negative scale transform, reflect the rect coords.2139if (w < 0) {2140w *= -1;2141x -= w;2142}2143if (h < 0) {2144h *= -1;2145y -= h;2146}21472148Blit ob = lastCAblit;2149if (dy == 0 && dx > 0 && dx < w) {2150while (w > 0) {2151int partW = Math.min(w, dx);2152w -= partW;2153int sx = x + w;2154ob.Blit(theData, theData, comp, clip,2155sx, y, sx+dx, y+dy, partW, h);2156}2157return;2158}2159if (dy > 0 && dy < h && dx > -w && dx < w) {2160while (h > 0) {2161int partH = Math.min(h, dy);2162h -= partH;2163int sy = y + h;2164ob.Blit(theData, theData, comp, clip,2165x, sy, x+dx, sy+dy, w, partH);2166}2167return;2168}2169ob.Blit(theData, theData, comp, clip, x, y, x+dx, y+dy, w, h);2170}21712172/*2173public void XcopyArea(int x, int y, int w, int h, int dx, int dy) {2174Rectangle rect = new Rectangle(x, y, w, h);2175rect = transformBounds(rect, transform);2176Point2D point = new Point2D.Float(dx, dy);2177Point2D root = new Point2D.Float(0, 0);2178point = transform.transform(point, point);2179root = transform.transform(root, root);2180int fdx = (int)(point.getX()-root.getX());2181int fdy = (int)(point.getY()-root.getY());21822183Rectangle r = getCompBounds().intersection(rect.getBounds());21842185if (r.isEmpty()) {2186return;2187}21882189// Begin Rasterizer for Clip Shape2190boolean skipClip = true;2191byte[] clipAlpha = null;21922193if (clipState == CLIP_SHAPE) {21942195int box[] = new int[4];21962197clipRegion.getBounds(box);2198Rectangle devR = new Rectangle(box[0], box[1],2199box[2] - box[0],2200box[3] - box[1]);2201if (!devR.isEmpty()) {2202OutputManager mgr = getOutputManager();2203RegionIterator ri = clipRegion.getIterator();2204while (ri.nextYRange(box)) {2205int spany = box[1];2206int spanh = box[3] - spany;2207while (ri.nextXBand(box)) {2208int spanx = box[0];2209int spanw = box[2] - spanx;2210mgr.copyArea(this, null,2211spanw, 0,2212spanx, spany,2213spanw, spanh,2214fdx, fdy,2215null);2216}2217}2218}2219return;2220}2221// End Rasterizer for Clip Shape22222223getOutputManager().copyArea(this, null,2224r.width, 0,2225r.x, r.y, r.width,2226r.height, fdx, fdy,2227null);2228}2229*/22302231public void drawLine(int x1, int y1, int x2, int y2) {2232try {2233drawpipe.drawLine(this, x1, y1, x2, y2);2234} catch (InvalidPipeException e) {2235try {2236revalidateAll();2237drawpipe.drawLine(this, x1, y1, x2, y2);2238} catch (InvalidPipeException e2) {2239// Still catching the exception; we are not yet ready to2240// validate the surfaceData correctly. Fail for now and2241// try again next time around.2242}2243} finally {2244surfaceData.markDirty();2245}2246}22472248public void drawRoundRect(int x, int y, int w, int h, int arcW, int arcH) {2249try {2250drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);2251} catch (InvalidPipeException e) {2252try {2253revalidateAll();2254drawpipe.drawRoundRect(this, x, y, w, h, arcW, arcH);2255} catch (InvalidPipeException e2) {2256// Still catching the exception; we are not yet ready to2257// validate the surfaceData correctly. Fail for now and2258// try again next time around.2259}2260} finally {2261surfaceData.markDirty();2262}2263}22642265public void fillRoundRect(int x, int y, int w, int h, int arcW, int arcH) {2266try {2267fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);2268} catch (InvalidPipeException e) {2269try {2270revalidateAll();2271fillpipe.fillRoundRect(this, x, y, w, h, arcW, arcH);2272} catch (InvalidPipeException e2) {2273// Still catching the exception; we are not yet ready to2274// validate the surfaceData correctly. Fail for now and2275// try again next time around.2276}2277} finally {2278surfaceData.markDirty();2279}2280}22812282public void drawOval(int x, int y, int w, int h) {2283try {2284drawpipe.drawOval(this, x, y, w, h);2285} catch (InvalidPipeException e) {2286try {2287revalidateAll();2288drawpipe.drawOval(this, x, y, w, h);2289} catch (InvalidPipeException e2) {2290// Still catching the exception; we are not yet ready to2291// validate the surfaceData correctly. Fail for now and2292// try again next time around.2293}2294} finally {2295surfaceData.markDirty();2296}2297}22982299public void fillOval(int x, int y, int w, int h) {2300try {2301fillpipe.fillOval(this, x, y, w, h);2302} catch (InvalidPipeException e) {2303try {2304revalidateAll();2305fillpipe.fillOval(this, x, y, w, h);2306} catch (InvalidPipeException e2) {2307// Still catching the exception; we are not yet ready to2308// validate the surfaceData correctly. Fail for now and2309// try again next time around.2310}2311} finally {2312surfaceData.markDirty();2313}2314}23152316public void drawArc(int x, int y, int w, int h,2317int startAngl, int arcAngl) {2318try {2319drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);2320} catch (InvalidPipeException e) {2321try {2322revalidateAll();2323drawpipe.drawArc(this, x, y, w, h, startAngl, arcAngl);2324} catch (InvalidPipeException e2) {2325// Still catching the exception; we are not yet ready to2326// validate the surfaceData correctly. Fail for now and2327// try again next time around.2328}2329} finally {2330surfaceData.markDirty();2331}2332}23332334public void fillArc(int x, int y, int w, int h,2335int startAngl, int arcAngl) {2336try {2337fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);2338} catch (InvalidPipeException e) {2339try {2340revalidateAll();2341fillpipe.fillArc(this, x, y, w, h, startAngl, arcAngl);2342} catch (InvalidPipeException e2) {2343// Still catching the exception; we are not yet ready to2344// validate the surfaceData correctly. Fail for now and2345// try again next time around.2346}2347} finally {2348surfaceData.markDirty();2349}2350}23512352public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {2353try {2354drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);2355} catch (InvalidPipeException e) {2356try {2357revalidateAll();2358drawpipe.drawPolyline(this, xPoints, yPoints, nPoints);2359} catch (InvalidPipeException e2) {2360// Still catching the exception; we are not yet ready to2361// validate the surfaceData correctly. Fail for now and2362// try again next time around.2363}2364} finally {2365surfaceData.markDirty();2366}2367}23682369public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {2370try {2371drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);2372} catch (InvalidPipeException e) {2373try {2374revalidateAll();2375drawpipe.drawPolygon(this, xPoints, yPoints, nPoints);2376} catch (InvalidPipeException e2) {2377// Still catching the exception; we are not yet ready to2378// validate the surfaceData correctly. Fail for now and2379// try again next time around.2380}2381} finally {2382surfaceData.markDirty();2383}2384}23852386public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {2387try {2388fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);2389} catch (InvalidPipeException e) {2390try {2391revalidateAll();2392fillpipe.fillPolygon(this, xPoints, yPoints, nPoints);2393} catch (InvalidPipeException e2) {2394// Still catching the exception; we are not yet ready to2395// validate the surfaceData correctly. Fail for now and2396// try again next time around.2397}2398} finally {2399surfaceData.markDirty();2400}2401}24022403public void drawRect (int x, int y, int w, int h) {2404try {2405drawpipe.drawRect(this, x, y, w, h);2406} catch (InvalidPipeException e) {2407try {2408revalidateAll();2409drawpipe.drawRect(this, x, y, w, h);2410} catch (InvalidPipeException e2) {2411// Still catching the exception; we are not yet ready to2412// validate the surfaceData correctly. Fail for now and2413// try again next time around.2414}2415} finally {2416surfaceData.markDirty();2417}2418}24192420public void fillRect (int x, int y, int w, int h) {2421try {2422fillpipe.fillRect(this, x, y, w, h);2423} catch (InvalidPipeException e) {2424try {2425revalidateAll();2426fillpipe.fillRect(this, x, y, w, h);2427} catch (InvalidPipeException e2) {2428// Still catching the exception; we are not yet ready to2429// validate the surfaceData correctly. Fail for now and2430// try again next time around.2431}2432} finally {2433surfaceData.markDirty();2434}2435}24362437private void revalidateAll() {2438try {2439// REMIND: This locking needs to be done around the2440// caller of this method so that the pipe stays valid2441// long enough to call the new primitive.2442// REMIND: No locking yet in screen SurfaceData objects!2443// surfaceData.lock();2444surfaceData = surfaceData.getReplacement();2445if (surfaceData == null) {2446surfaceData = NullSurfaceData.theInstance;2447}24482449invalidatePipe();24502451// this will recalculate the composite clip2452setDevClip(surfaceData.getBounds());24532454if (paintState <= PAINT_ALPHACOLOR) {2455validateColor();2456}2457if (composite instanceof XORComposite) {2458Color c = ((XORComposite) composite).getXorColor();2459setComposite(new XORComposite(c, surfaceData));2460}2461validatePipe();2462} finally {2463// REMIND: No locking yet in screen SurfaceData objects!2464// surfaceData.unlock();2465}2466}24672468public void clearRect(int x, int y, int w, int h) {2469// REMIND: has some "interesting" consequences if threads are2470// not synchronized2471Composite c = composite;2472Paint p = paint;2473setComposite(AlphaComposite.Src);2474setColor(getBackground());2475fillRect(x, y, w, h);2476setPaint(p);2477setComposite(c);2478}24792480/**2481* Strokes the outline of a Path using the settings of the current2482* graphics state. The rendering attributes applied include the2483* clip, transform, paint or color, composite and stroke attributes.2484* @param p The path to be drawn.2485* @see #setStroke2486* @see #setPaint2487* @see java.awt.Graphics#setColor2488* @see #transform2489* @see #setTransform2490* @see #clip2491* @see #setClip2492* @see #setComposite2493*/2494public void draw(Shape s) {2495try {2496shapepipe.draw(this, s);2497} catch (InvalidPipeException e) {2498try {2499revalidateAll();2500shapepipe.draw(this, s);2501} catch (InvalidPipeException e2) {2502// Still catching the exception; we are not yet ready to2503// validate the surfaceData correctly. Fail for now and2504// try again next time around.2505}2506} finally {2507surfaceData.markDirty();2508}2509}251025112512/**2513* Fills the interior of a Path using the settings of the current2514* graphics state. The rendering attributes applied include the2515* clip, transform, paint or color, and composite.2516* @see #setPaint2517* @see java.awt.Graphics#setColor2518* @see #transform2519* @see #setTransform2520* @see #setComposite2521* @see #clip2522* @see #setClip2523*/2524public void fill(Shape s) {2525try {2526shapepipe.fill(this, s);2527} catch (InvalidPipeException e) {2528try {2529revalidateAll();2530shapepipe.fill(this, s);2531} catch (InvalidPipeException e2) {2532// Still catching the exception; we are not yet ready to2533// validate the surfaceData correctly. Fail for now and2534// try again next time around.2535}2536} finally {2537surfaceData.markDirty();2538}2539}25402541/**2542* Returns true if the given AffineTransform is an integer2543* translation.2544*/2545private static boolean isIntegerTranslation(AffineTransform xform) {2546if (xform.isIdentity()) {2547return true;2548}2549if (xform.getType() == AffineTransform.TYPE_TRANSLATION) {2550double tx = xform.getTranslateX();2551double ty = xform.getTranslateY();2552return (tx == (int)tx && ty == (int)ty);2553}2554return false;2555}25562557/**2558* Returns the index of the tile corresponding to the supplied position2559* given the tile grid offset and size along the same axis.2560*/2561private static int getTileIndex(int p, int tileGridOffset, int tileSize) {2562p -= tileGridOffset;2563if (p < 0) {2564p += 1 - tileSize; // force round to -infinity (ceiling)2565}2566return p/tileSize;2567}25682569/**2570* Returns a rectangle in image coordinates that may be required2571* in order to draw the given image into the given clipping region2572* through a pair of AffineTransforms. In addition, horizontal and2573* vertical padding factors for antialising and interpolation may2574* be used.2575*/2576private static Rectangle getImageRegion(RenderedImage img,2577Region compClip,2578AffineTransform transform,2579AffineTransform xform,2580int padX, int padY) {2581Rectangle imageRect =2582new Rectangle(img.getMinX(), img.getMinY(),2583img.getWidth(), img.getHeight());25842585Rectangle result = null;2586try {2587double p[] = new double[8];2588p[0] = p[2] = compClip.getLoX();2589p[4] = p[6] = compClip.getHiX();2590p[1] = p[5] = compClip.getLoY();2591p[3] = p[7] = compClip.getHiY();25922593// Inverse transform the output bounding rect2594transform.inverseTransform(p, 0, p, 0, 4);2595xform.inverseTransform(p, 0, p, 0, 4);25962597// Determine a bounding box for the inverse transformed region2598double x0,x1,y0,y1;2599x0 = x1 = p[0];2600y0 = y1 = p[1];26012602for (int i = 2; i < 8; ) {2603double pt = p[i++];2604if (pt < x0) {2605x0 = pt;2606} else if (pt > x1) {2607x1 = pt;2608}2609pt = p[i++];2610if (pt < y0) {2611y0 = pt;2612} else if (pt > y1) {2613y1 = pt;2614}2615}26162617// This is padding for anti-aliasing and such. It may2618// be more than is needed.2619int x = (int)x0 - padX;2620int w = (int)(x1 - x0 + 2*padX);2621int y = (int)y0 - padY;2622int h = (int)(y1 - y0 + 2*padY);26232624Rectangle clipRect = new Rectangle(x,y,w,h);2625result = clipRect.intersection(imageRect);2626} catch (NoninvertibleTransformException nte) {2627// Worst case bounds are the bounds of the image.2628result = imageRect;2629}26302631return result;2632}26332634/**2635* Draws an image, applying a transform from image space into user space2636* before drawing.2637* The transformation from user space into device space is done with2638* the current transform in the Graphics2D.2639* The given transformation is applied to the image before the2640* transform attribute in the Graphics2D state is applied.2641* The rendering attributes applied include the clip, transform,2642* and composite attributes. Note that the result is2643* undefined, if the given transform is noninvertible.2644* @param img The image to be drawn. Does nothing if img is null.2645* @param xform The transformation from image space into user space.2646* @see #transform2647* @see #setTransform2648* @see #setComposite2649* @see #clip2650* @see #setClip2651*/2652public void drawRenderedImage(RenderedImage img,2653AffineTransform xform) {26542655if (img == null) {2656return;2657}26582659// BufferedImage case: use a simple drawImage call2660if (img instanceof BufferedImage) {2661BufferedImage bufImg = (BufferedImage)img;2662drawImage(bufImg,xform,null);2663return;2664}26652666// transformState tracks the state of transform and2667// transX, transY contain the integer casts of the2668// translation factors2669boolean isIntegerTranslate =2670(transformState <= TRANSFORM_INT_TRANSLATE) &&2671isIntegerTranslation(xform);26722673// Include padding for interpolation/antialiasing if necessary2674int pad = isIntegerTranslate ? 0 : 3;26752676Region clip;2677try {2678clip = getCompClip();2679} catch (InvalidPipeException e) {2680return;2681}26822683// Determine the region of the image that may contribute to2684// the clipped drawing area2685Rectangle region = getImageRegion(img,2686clip,2687transform,2688xform,2689pad, pad);2690if (region.width <= 0 || region.height <= 0) {2691return;2692}26932694// Attempt to optimize integer translation of tiled images.2695// Although theoretically we are O.K. if the concatenation of2696// the user transform and the device transform is an integer2697// translation, we'll play it safe and only optimize the case2698// where both are integer translations.2699if (isIntegerTranslate) {2700// Use optimized code2701// Note that drawTranslatedRenderedImage calls copyImage2702// which takes the user space to device space transform into2703// account, but we need to provide the image space to user space2704// translations.27052706drawTranslatedRenderedImage(img, region,2707(int) xform.getTranslateX(),2708(int) xform.getTranslateY());2709return;2710}27112712// General case: cobble the necessary region into a single Raster2713Raster raster = img.getData(region);27142715// Make a new Raster with the same contents as raster2716// but starting at (0, 0). This raster is thus in the same2717// coordinate system as the SampleModel of the original raster.2718WritableRaster wRaster =2719Raster.createWritableRaster(raster.getSampleModel(),2720raster.getDataBuffer(),2721null);27222723// If the original raster was in a different coordinate2724// system than its SampleModel, we need to perform an2725// additional translation in order to get the (minX, minY)2726// pixel of raster to be pixel (0, 0) of wRaster. We also2727// have to have the correct width and height.2728int minX = raster.getMinX();2729int minY = raster.getMinY();2730int width = raster.getWidth();2731int height = raster.getHeight();2732int px = minX - raster.getSampleModelTranslateX();2733int py = minY - raster.getSampleModelTranslateY();2734if (px != 0 || py != 0 || width != wRaster.getWidth() ||2735height != wRaster.getHeight()) {2736wRaster =2737wRaster.createWritableChild(px,2738py,2739width,2740height,27410, 0,2742null);2743}27442745// Now we have a BufferedImage starting at (0, 0)2746// with the same contents that started at (minX, minY)2747// in raster. So we must draw the BufferedImage with a2748// translation of (minX, minY).2749AffineTransform transXform = (AffineTransform)xform.clone();2750transXform.translate(minX, minY);27512752ColorModel cm = img.getColorModel();2753BufferedImage bufImg = new BufferedImage(cm,2754wRaster,2755cm.isAlphaPremultiplied(),2756null);2757drawImage(bufImg, transXform, null);2758}27592760/**2761* Intersects <code>destRect</code> with <code>clip</code> and2762* overwrites <code>destRect</code> with the result.2763* Returns false if the intersection was empty, true otherwise.2764*/2765private boolean clipTo(Rectangle destRect, Rectangle clip) {2766int x1 = Math.max(destRect.x, clip.x);2767int x2 = Math.min(destRect.x + destRect.width, clip.x + clip.width);2768int y1 = Math.max(destRect.y, clip.y);2769int y2 = Math.min(destRect.y + destRect.height, clip.y + clip.height);2770if (((x2 - x1) < 0) || ((y2 - y1) < 0)) {2771destRect.width = -1; // Set both just to be safe2772destRect.height = -1;2773return false;2774} else {2775destRect.x = x1;2776destRect.y = y1;2777destRect.width = x2 - x1;2778destRect.height = y2 - y1;2779return true;2780}2781}27822783/**2784* Draw a portion of a RenderedImage tile-by-tile with a given2785* integer image to user space translation. The user to2786* device transform must also be an integer translation.2787*/2788private void drawTranslatedRenderedImage(RenderedImage img,2789Rectangle region,2790int i2uTransX,2791int i2uTransY) {2792// Cache tile grid info2793int tileGridXOffset = img.getTileGridXOffset();2794int tileGridYOffset = img.getTileGridYOffset();2795int tileWidth = img.getTileWidth();2796int tileHeight = img.getTileHeight();27972798// Determine the tile index extrema in each direction2799int minTileX =2800getTileIndex(region.x, tileGridXOffset, tileWidth);2801int minTileY =2802getTileIndex(region.y, tileGridYOffset, tileHeight);2803int maxTileX =2804getTileIndex(region.x + region.width - 1,2805tileGridXOffset, tileWidth);2806int maxTileY =2807getTileIndex(region.y + region.height - 1,2808tileGridYOffset, tileHeight);28092810// Create a single ColorModel to use for all BufferedImages2811ColorModel colorModel = img.getColorModel();28122813// Reuse the same Rectangle for each iteration2814Rectangle tileRect = new Rectangle();28152816for (int ty = minTileY; ty <= maxTileY; ty++) {2817for (int tx = minTileX; tx <= maxTileX; tx++) {2818// Get the current tile.2819Raster raster = img.getTile(tx, ty);28202821// Fill in tileRect with the tile bounds2822tileRect.x = tx*tileWidth + tileGridXOffset;2823tileRect.y = ty*tileHeight + tileGridYOffset;2824tileRect.width = tileWidth;2825tileRect.height = tileHeight;28262827// Clip the tile against the image bounds and2828// backwards mapped clip region2829// The result can't be empty2830clipTo(tileRect, region);28312832// Create a WritableRaster containing the tile2833WritableRaster wRaster = null;2834if (raster instanceof WritableRaster) {2835wRaster = (WritableRaster)raster;2836} else {2837// Create a WritableRaster in the same coordinate system2838// as the original raster.2839wRaster =2840Raster.createWritableRaster(raster.getSampleModel(),2841raster.getDataBuffer(),2842null);2843}28442845// Translate wRaster to start at (0, 0) and to contain2846// only the relevent portion of the tile2847wRaster = wRaster.createWritableChild(tileRect.x, tileRect.y,2848tileRect.width,2849tileRect.height,28500, 0,2851null);28522853// Wrap wRaster in a BufferedImage2854BufferedImage bufImg =2855new BufferedImage(colorModel,2856wRaster,2857colorModel.isAlphaPremultiplied(),2858null);2859// Now we have a BufferedImage starting at (0, 0) that2860// represents data from a Raster starting at2861// (tileRect.x, tileRect.y). Additionally, it needs2862// to be translated by (i2uTransX, i2uTransY). We call2863// copyImage to draw just the region of interest2864// without needing to create a child image.2865copyImage(bufImg, tileRect.x + i2uTransX,2866tileRect.y + i2uTransY, 0, 0, tileRect.width,2867tileRect.height, null, null);2868}2869}2870}28712872public void drawRenderableImage(RenderableImage img,2873AffineTransform xform) {28742875if (img == null) {2876return;2877}28782879AffineTransform pipeTransform = transform;2880AffineTransform concatTransform = new AffineTransform(xform);2881concatTransform.concatenate(pipeTransform);2882AffineTransform reverseTransform;28832884RenderContext rc = new RenderContext(concatTransform);28852886try {2887reverseTransform = pipeTransform.createInverse();2888} catch (NoninvertibleTransformException nte) {2889rc = new RenderContext(pipeTransform);2890reverseTransform = new AffineTransform();2891}28922893RenderedImage rendering = img.createRendering(rc);2894drawRenderedImage(rendering,reverseTransform);2895}2896289728982899/*2900* Transform the bounding box of the BufferedImage2901*/2902protected Rectangle transformBounds(Rectangle rect,2903AffineTransform tx) {2904if (tx.isIdentity()) {2905return rect;2906}29072908Shape s = transformShape(tx, rect);2909return s.getBounds();2910}29112912// text rendering methods2913public void drawString(String str, int x, int y) {2914if (str == null) {2915throw new NullPointerException("String is null");2916}29172918if (font.hasLayoutAttributes()) {2919if (str.length() == 0) {2920return;2921}2922new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);2923return;2924}29252926try {2927textpipe.drawString(this, str, x, y);2928} catch (InvalidPipeException e) {2929try {2930revalidateAll();2931textpipe.drawString(this, str, x, y);2932} catch (InvalidPipeException e2) {2933// Still catching the exception; we are not yet ready to2934// validate the surfaceData correctly. Fail for now and2935// try again next time around.2936}2937} finally {2938surfaceData.markDirty();2939}2940}29412942public void drawString(String str, float x, float y) {2943if (str == null) {2944throw new NullPointerException("String is null");2945}29462947if (font.hasLayoutAttributes()) {2948if (str.length() == 0) {2949return;2950}2951new TextLayout(str, font, getFontRenderContext()).draw(this, x, y);2952return;2953}29542955try {2956textpipe.drawString(this, str, x, y);2957} catch (InvalidPipeException e) {2958try {2959revalidateAll();2960textpipe.drawString(this, str, x, y);2961} catch (InvalidPipeException e2) {2962// Still catching the exception; we are not yet ready to2963// validate the surfaceData correctly. Fail for now and2964// try again next time around.2965}2966} finally {2967surfaceData.markDirty();2968}2969}29702971public void drawString(AttributedCharacterIterator iterator,2972int x, int y) {2973if (iterator == null) {2974throw new NullPointerException("AttributedCharacterIterator is null");2975}2976if (iterator.getBeginIndex() == iterator.getEndIndex()) {2977return; /* nothing to draw */2978}2979TextLayout tl = new TextLayout(iterator, getFontRenderContext());2980tl.draw(this, (float) x, (float) y);2981}29822983public void drawString(AttributedCharacterIterator iterator,2984float x, float y) {2985if (iterator == null) {2986throw new NullPointerException("AttributedCharacterIterator is null");2987}2988if (iterator.getBeginIndex() == iterator.getEndIndex()) {2989return; /* nothing to draw */2990}2991TextLayout tl = new TextLayout(iterator, getFontRenderContext());2992tl.draw(this, x, y);2993}29942995public void drawGlyphVector(GlyphVector gv, float x, float y)2996{2997if (gv == null) {2998throw new NullPointerException("GlyphVector is null");2999}30003001try {3002textpipe.drawGlyphVector(this, gv, x, y);3003} catch (InvalidPipeException e) {3004try {3005revalidateAll();3006textpipe.drawGlyphVector(this, gv, x, y);3007} catch (InvalidPipeException e2) {3008// Still catching the exception; we are not yet ready to3009// validate the surfaceData correctly. Fail for now and3010// try again next time around.3011}3012} finally {3013surfaceData.markDirty();3014}3015}30163017public void drawChars(char data[], int offset, int length, int x, int y) {30183019if (data == null) {3020throw new NullPointerException("char data is null");3021}3022if (offset < 0 || length < 0 || offset + length < length ||3023offset + length > data.length) {3024throw new ArrayIndexOutOfBoundsException("bad offset/length");3025}3026if (font.hasLayoutAttributes()) {3027if (data.length == 0) {3028return;3029}3030new TextLayout(new String(data, offset, length),3031font, getFontRenderContext()).draw(this, x, y);3032return;3033}30343035try {3036textpipe.drawChars(this, data, offset, length, x, y);3037} catch (InvalidPipeException e) {3038try {3039revalidateAll();3040textpipe.drawChars(this, data, offset, length, x, y);3041} catch (InvalidPipeException e2) {3042// Still catching the exception; we are not yet ready to3043// validate the surfaceData correctly. Fail for now and3044// try again next time around.3045}3046} finally {3047surfaceData.markDirty();3048}3049}30503051public void drawBytes(byte data[], int offset, int length, int x, int y) {3052if (data == null) {3053throw new NullPointerException("byte data is null");3054}3055if (offset < 0 || length < 0 || offset + length < length ||3056offset + length > data.length) {3057throw new ArrayIndexOutOfBoundsException("bad offset/length");3058}3059/* Byte data is interpreted as 8-bit ASCII. Re-use drawChars loops */3060char chData[] = new char[length];3061for (int i = length; i-- > 0; ) {3062chData[i] = (char)(data[i+offset] & 0xff);3063}3064if (font.hasLayoutAttributes()) {3065if (data.length == 0) {3066return;3067}3068new TextLayout(new String(chData),3069font, getFontRenderContext()).draw(this, x, y);3070return;3071}30723073try {3074textpipe.drawChars(this, chData, 0, length, x, y);3075} catch (InvalidPipeException e) {3076try {3077revalidateAll();3078textpipe.drawChars(this, chData, 0, length, x, y);3079} catch (InvalidPipeException e2) {3080// Still catching the exception; we are not yet ready to3081// validate the surfaceData correctly. Fail for now and3082// try again next time around.3083}3084} finally {3085surfaceData.markDirty();3086}3087}3088// end of text rendering methods30893090private boolean isHiDPIImage(final Image img) {3091return (SurfaceManager.getImageScale(img) != 1) ||3092(resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_OFF3093&& img instanceof MultiResolutionImage);3094}30953096private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,3097int dy2, int sx1, int sy1, int sx2, int sy2,3098Color bgcolor, ImageObserver observer) {30993100if (SurfaceManager.getImageScale(img) != 1) { // Volatile Image3101final int scale = SurfaceManager.getImageScale(img);3102sx1 = Region.clipScale(sx1, scale);3103sx2 = Region.clipScale(sx2, scale);3104sy1 = Region.clipScale(sy1, scale);3105sy2 = Region.clipScale(sy2, scale);3106} else if (img instanceof MultiResolutionImage) {3107// get scaled destination image size31083109int width = img.getWidth(observer);3110int height = img.getHeight(observer);31113112Image resolutionVariant = getResolutionVariant(3113(MultiResolutionImage) img, width, height,3114dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);31153116if (resolutionVariant != img && resolutionVariant != null) {3117// recalculate source region for the resolution variant31183119ImageObserver rvObserver = MultiResolutionToolkitImage.3120getResolutionVariantObserver(img, observer,3121width, height, -1, -1);31223123int rvWidth = resolutionVariant.getWidth(rvObserver);3124int rvHeight = resolutionVariant.getHeight(rvObserver);31253126if (0 < width && 0 < height && 0 < rvWidth && 0 < rvHeight) {31273128float widthScale = ((float) rvWidth) / width;3129float heightScale = ((float) rvHeight) / height;31303131sx1 = Region.clipScale(sx1, widthScale);3132sy1 = Region.clipScale(sy1, heightScale);3133sx2 = Region.clipScale(sx2, widthScale);3134sy2 = Region.clipScale(sy2, heightScale);31353136observer = rvObserver;3137img = resolutionVariant;3138}3139}3140}31413142try {3143return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,3144sx2, sy2, bgcolor, observer);3145} catch (InvalidPipeException e) {3146try {3147revalidateAll();3148return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1,3149sy1, sx2, sy2, bgcolor, observer);3150} catch (InvalidPipeException e2) {3151// Still catching the exception; we are not yet ready to3152// validate the surfaceData correctly. Fail for now and3153// try again next time around.3154return false;3155}3156} finally {3157surfaceData.markDirty();3158}3159}31603161private Image getResolutionVariant(MultiResolutionImage img,3162int srcWidth, int srcHeight, int dx1, int dy1, int dx2, int dy2,3163int sx1, int sy1, int sx2, int sy2) {31643165if (srcWidth <= 0 || srcHeight <= 0) {3166return null;3167}31683169int sw = sx2 - sx1;3170int sh = sy2 - sy1;31713172if (sw == 0 || sh == 0) {3173return null;3174}31753176int type = transform.getType();3177int dw = dx2 - dx1;3178int dh = dy2 - dy1;3179double destRegionWidth;3180double destRegionHeight;31813182if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP)) == 0) {3183destRegionWidth = dw;3184destRegionHeight = dh;3185} else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {3186destRegionWidth = dw * transform.getScaleX();3187destRegionHeight = dh * transform.getScaleY();3188} else {3189destRegionWidth = dw * Math.hypot(3190transform.getScaleX(), transform.getShearY());3191destRegionHeight = dh * Math.hypot(3192transform.getShearX(), transform.getScaleY());3193}31943195int destImageWidth = (int) Math.abs(srcWidth * destRegionWidth / sw);3196int destImageHeight = (int) Math.abs(srcHeight * destRegionHeight / sh);31973198Image resolutionVariant3199= img.getResolutionVariant(destImageWidth, destImageHeight);32003201if (resolutionVariant instanceof ToolkitImage3202&& ((ToolkitImage) resolutionVariant).hasError()) {3203return null;3204}32053206return resolutionVariant;3207}32083209/**3210* Draws an image scaled to x,y,w,h in nonblocking mode with a3211* callback object.3212*/3213public boolean drawImage(Image img, int x, int y, int width, int height,3214ImageObserver observer) {3215return drawImage(img, x, y, width, height, null, observer);3216}32173218/**3219* Not part of the advertised API but a useful utility method3220* to call internally. This is for the case where we are3221* drawing to/from given coordinates using a given width/height,3222* but we guarantee that the surfaceData's width/height of the src and dest3223* areas are equal (no scale needed). Note that this method intentionally3224* ignore scale factor of the source image, and copy it as is.3225*/3226public boolean copyImage(Image img, int dx, int dy, int sx, int sy,3227int width, int height, Color bgcolor,3228ImageObserver observer) {3229try {3230return imagepipe.copyImage(this, img, dx, dy, sx, sy,3231width, height, bgcolor, observer);3232} catch (InvalidPipeException e) {3233try {3234revalidateAll();3235return imagepipe.copyImage(this, img, dx, dy, sx, sy,3236width, height, bgcolor, observer);3237} catch (InvalidPipeException e2) {3238// Still catching the exception; we are not yet ready to3239// validate the surfaceData correctly. Fail for now and3240// try again next time around.3241return false;3242}3243} finally {3244surfaceData.markDirty();3245}3246}32473248/**3249* Draws an image scaled to x,y,w,h in nonblocking mode with a3250* solid background color and a callback object.3251*/3252public boolean drawImage(Image img, int x, int y, int width, int height,3253Color bg, ImageObserver observer) {32543255if (img == null) {3256return true;3257}32583259if ((width == 0) || (height == 0)) {3260return true;3261}32623263final int imgW = img.getWidth(null);3264final int imgH = img.getHeight(null);3265if (isHiDPIImage(img)) {3266return drawHiDPIImage(img, x, y, x + width, y + height, 0, 0, imgW,3267imgH, bg, observer);3268}32693270if (width == imgW && height == imgH) {3271return copyImage(img, x, y, 0, 0, width, height, bg, observer);3272}32733274try {3275return imagepipe.scaleImage(this, img, x, y, width, height,3276bg, observer);3277} catch (InvalidPipeException e) {3278try {3279revalidateAll();3280return imagepipe.scaleImage(this, img, x, y, width, height,3281bg, observer);3282} catch (InvalidPipeException e2) {3283// Still catching the exception; we are not yet ready to3284// validate the surfaceData correctly. Fail for now and3285// try again next time around.3286return false;3287}3288} finally {3289surfaceData.markDirty();3290}3291}32923293/**3294* Draws an image at x,y in nonblocking mode.3295*/3296public boolean drawImage(Image img, int x, int y, ImageObserver observer) {3297return drawImage(img, x, y, null, observer);3298}32993300/**3301* Draws an image at x,y in nonblocking mode with a solid background3302* color and a callback object.3303*/3304public boolean drawImage(Image img, int x, int y, Color bg,3305ImageObserver observer) {33063307if (img == null) {3308return true;3309}33103311if (isHiDPIImage(img)) {3312final int imgW = img.getWidth(null);3313final int imgH = img.getHeight(null);3314return drawHiDPIImage(img, x, y, x + imgW, y + imgH, 0, 0, imgW,3315imgH, bg, observer);3316}33173318try {3319return imagepipe.copyImage(this, img, x, y, bg, observer);3320} catch (InvalidPipeException e) {3321try {3322revalidateAll();3323return imagepipe.copyImage(this, img, x, y, bg, observer);3324} catch (InvalidPipeException e2) {3325// Still catching the exception; we are not yet ready to3326// validate the surfaceData correctly. Fail for now and3327// try again next time around.3328return false;3329}3330} finally {3331surfaceData.markDirty();3332}3333}33343335/**3336* Draws a subrectangle of an image scaled to a destination rectangle3337* in nonblocking mode with a callback object.3338*/3339public boolean drawImage(Image img,3340int dx1, int dy1, int dx2, int dy2,3341int sx1, int sy1, int sx2, int sy2,3342ImageObserver observer) {3343return drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null,3344observer);3345}33463347/**3348* Draws a subrectangle of an image scaled to a destination rectangle in3349* nonblocking mode with a solid background color and a callback object.3350*/3351public boolean drawImage(Image img,3352int dx1, int dy1, int dx2, int dy2,3353int sx1, int sy1, int sx2, int sy2,3354Color bgcolor, ImageObserver observer) {33553356if (img == null) {3357return true;3358}33593360if (dx1 == dx2 || dy1 == dy2 ||3361sx1 == sx2 || sy1 == sy2)3362{3363return true;3364}33653366if (isHiDPIImage(img)) {3367return drawHiDPIImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2,3368bgcolor, observer);3369}33703371if (((sx2 - sx1) == (dx2 - dx1)) &&3372((sy2 - sy1) == (dy2 - dy1)))3373{3374// Not a scale - forward it to a copy routine3375int srcX, srcY, dstX, dstY, width, height;3376if (sx2 > sx1) {3377width = sx2 - sx1;3378srcX = sx1;3379dstX = dx1;3380} else {3381width = sx1 - sx2;3382srcX = sx2;3383dstX = dx2;3384}3385if (sy2 > sy1) {3386height = sy2-sy1;3387srcY = sy1;3388dstY = dy1;3389} else {3390height = sy1-sy2;3391srcY = sy2;3392dstY = dy2;3393}3394return copyImage(img, dstX, dstY, srcX, srcY,3395width, height, bgcolor, observer);3396}33973398try {3399return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,3400sx1, sy1, sx2, sy2, bgcolor,3401observer);3402} catch (InvalidPipeException e) {3403try {3404revalidateAll();3405return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2,3406sx1, sy1, sx2, sy2, bgcolor,3407observer);3408} catch (InvalidPipeException e2) {3409// Still catching the exception; we are not yet ready to3410// validate the surfaceData correctly. Fail for now and3411// try again next time around.3412return false;3413}3414} finally {3415surfaceData.markDirty();3416}3417}34183419/**3420* Draw an image, applying a transform from image space into user space3421* before drawing.3422* The transformation from user space into device space is done with3423* the current transform in the Graphics2D.3424* The given transformation is applied to the image before the3425* transform attribute in the Graphics2D state is applied.3426* The rendering attributes applied include the clip, transform,3427* paint or color and composite attributes. Note that the result is3428* undefined, if the given transform is non-invertible.3429* @param img The image to be drawn.3430* @param xform The transformation from image space into user space.3431* @param observer The image observer to be notified on the image producing3432* progress.3433* @see #transform3434* @see #setComposite3435* @see #setClip3436*/3437public boolean drawImage(Image img,3438AffineTransform xform,3439ImageObserver observer) {34403441if (img == null) {3442return true;3443}34443445if (xform == null || xform.isIdentity()) {3446return drawImage(img, 0, 0, null, observer);3447}34483449if (isHiDPIImage(img)) {3450final int w = img.getWidth(null);3451final int h = img.getHeight(null);3452final AffineTransform tx = new AffineTransform(transform);3453transform(xform);3454boolean result = drawHiDPIImage(img, 0, 0, w, h, 0, 0, w, h, null,3455observer);3456transform.setTransform(tx);3457invalidateTransform();3458return result;3459}34603461try {3462return imagepipe.transformImage(this, img, xform, observer);3463} catch (InvalidPipeException e) {3464try {3465revalidateAll();3466return imagepipe.transformImage(this, img, xform, observer);3467} catch (InvalidPipeException e2) {3468// Still catching the exception; we are not yet ready to3469// validate the surfaceData correctly. Fail for now and3470// try again next time around.3471return false;3472}3473} finally {3474surfaceData.markDirty();3475}3476}34773478public void drawImage(BufferedImage bImg,3479BufferedImageOp op,3480int x,3481int y) {34823483if (bImg == null) {3484return;3485}34863487try {3488imagepipe.transformImage(this, bImg, op, x, y);3489} catch (InvalidPipeException e) {3490try {3491revalidateAll();3492imagepipe.transformImage(this, bImg, op, x, y);3493} catch (InvalidPipeException e2) {3494// Still catching the exception; we are not yet ready to3495// validate the surfaceData correctly. Fail for now and3496// try again next time around.3497}3498} finally {3499surfaceData.markDirty();3500}3501}35023503/**3504* Get the rendering context of the font3505* within this Graphics2D context.3506*/3507public FontRenderContext getFontRenderContext() {3508if (cachedFRC == null) {3509int aahint = textAntialiasHint;3510if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT &&3511antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) {3512aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON;3513}3514// Translation components should be excluded from the FRC transform3515AffineTransform tx = null;3516if (transformState >= TRANSFORM_TRANSLATESCALE) {3517if (transform.getTranslateX() == 0 &&3518transform.getTranslateY() == 0) {3519tx = transform;3520} else {3521tx = new AffineTransform(transform.getScaleX(),3522transform.getShearY(),3523transform.getShearX(),3524transform.getScaleY(),35250, 0);3526}3527}3528cachedFRC = new FontRenderContext(tx,3529SunHints.Value.get(SunHints.INTKEY_TEXT_ANTIALIASING, aahint),3530SunHints.Value.get(SunHints.INTKEY_FRACTIONALMETRICS,3531fractionalMetricsHint));3532}3533return cachedFRC;3534}3535private FontRenderContext cachedFRC;35363537/**3538* This object has no resources to dispose of per se, but the3539* doc comments for the base method in java.awt.Graphics imply3540* that this object will not be useable after it is disposed.3541* So, we sabotage the object to prevent further use to prevent3542* developers from relying on behavior that may not work on3543* other, less forgiving, VMs that really need to dispose of3544* resources.3545*/3546public void dispose() {3547surfaceData = NullSurfaceData.theInstance;3548invalidatePipe();3549}35503551/**3552* Graphics has a finalize method that automatically calls dispose()3553* for subclasses. For SunGraphics2D we do not need to be finalized3554* so that method simply causes us to be enqueued on the Finalizer3555* queues for no good reason. Unfortunately, that method and3556* implementation are now considered part of the public contract3557* of that base class so we can not remove or gut the method.3558* We override it here with an empty method and the VM is smart3559* enough to know that if our override is empty then it should not3560* mark us as finalizeable.3561*/3562public void finalize() {3563// DO NOT REMOVE THIS METHOD3564}35653566/**3567* Returns destination that this Graphics renders to. This could be3568* either an Image or a Component; subclasses of SurfaceData are3569* responsible for returning the appropriate object.3570*/3571public Object getDestination() {3572return surfaceData.getDestination();3573}35743575/**3576* {@inheritDoc}3577*3578* @see sun.java2d.DestSurfaceProvider#getDestSurface3579*/3580@Override3581public Surface getDestSurface() {3582return surfaceData;3583}3584}358535863587