private static final int MINALLOCATED = 8; private static final int TEXTARRSIZE = 17; private static double[][] textTxArr = new double[TEXTARRSIZE][]; private static AffineTransform[] textAtArr = new AffineTransform[TEXTARRSIZE]; static { for (int i=MINALLOCATED;i<TEXTARRSIZE;i++) { textTxArr[i] = new double [] {i, 0, 0, i}; textAtArr[i] = new AffineTransform( textTxArr[i]); } } // cached state for various draw[String,Char,Byte] optimizations public FontInfo checkFontInfo(FontInfo info, Font font, FontRenderContext frc) { /* Do not create a FontInfo object as part of construction of an * SG2D as its possible it may never be needed - ie if no text * is drawn using this SG2D. */ if (info == null) { info = new FontInfo(); } float ptSize = font.getSize2D(); int txFontType; AffineTransform devAt, textAt=null; if (font.isTransformed()) { textAt = font.getTransform(); textAt.scale(ptSize, ptSize); txFontType = textAt.getType(); info.originX = (float)textAt.getTranslateX(); info.originY = (float)textAt.getTranslateY(); textAt.translate(-info.originX, -info.originY); if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); devAt = new AffineTransform(info.devTx); textAt.preConcatenate(devAt); } else { info.devTx = IDENT_MATRIX; devAt = IDENT_ATX; } textAt.getMatrix(info.glyphTx = new double[4]); double shearx = textAt.getShearX(); double scaley = textAt.getScaleY(); if (shearx != 0) { scaley = Math.sqrt(shearx * shearx + scaley * scaley); } info.pixelHeight = (int)(Math.abs(scaley)+0.5); } else { txFontType = AffineTransform.TYPE_IDENTITY; info.originX = info.originY = 0; if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); devAt = new AffineTransform(info.devTx); info.glyphTx = new double[4]; for (int i = 0; i < 4; i++) { info.glyphTx[i] = info.devTx[i] * ptSize; } textAt = new AffineTransform(info.glyphTx); double shearx = transform.getShearX(); double scaley = transform.getScaleY(); if (shearx != 0) { scaley = Math.sqrt(shearx * shearx + scaley * scaley); } info.pixelHeight = (int)(Math.abs(scaley * ptSize)+0.5); } else { /* If the double represents a common integral, we * may have pre-allocated objects. * A "sparse" array be seems to be as fast as a switch * even for 3 or 4 pt sizes, and is more flexible. * This should perform comparably in single-threaded * rendering to the old code which synchronized on the * class and scale better on MP systems. */ int pszInt = (int)ptSize; if (ptSize == pszInt && pszInt >= MINALLOCATED && pszInt < TEXTARRSIZE) { info.glyphTx = textTxArr[pszInt]; textAt = textAtArr[pszInt]; info.pixelHeight = pszInt; } else { info.pixelHeight = (int)(ptSize+0.5); } if (textAt == null) { info.glyphTx = new double[] {ptSize, 0, 0, ptSize}; textAt = new AffineTransform(info.glyphTx); } info.devTx = IDENT_MATRIX; devAt = IDENT_ATX; } } info.font2D = FontUtilities.getFont2D(font); int fmhint = fractionalMetricsHint; if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) { fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF; } info.lcdSubPixPos = false; // conditionally set true in LCD mode. /* The text anti-aliasing hints that are set by the client need * to be interpreted for the current state and stored in the * FontInfo.aahint which is what will actually be used and * will be one of OFF, ON, LCD_HRGB or LCD_VRGB. * This is what pipe selection code should typically refer to, not * textAntialiasHint. This means we are now evaluating the meaning * of "default" here. Any pipe that really cares about that will * also need to consult that variable. * Otherwise these are being used only as args to getStrike, * and are encapsulated in that object which is part of the * FontInfo, so we do not need to store them directly as fields * in the FontInfo object. * That could change if FontInfo's were more selectively * revalidated when graphics state changed. Presently this * method re-evaluates all fields in the fontInfo. * The strike doesn't need to know the RGB subpixel order. Just * if its H or V orientation, so if an LCD option is specified we * always pass in the RGB hint to the strike. * frc is non-null only if this is a GlyphVector. For reasons * which are probably a historical mistake the AA hint in a GV * is honoured when we render, overriding the Graphics setting. */ int aahint; if (frc == null) { aahint = textAntialiasHint; } else { aahint = ((SunHints.Value)frc.getAntiAliasingHint()).getIndex(); } if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) { if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF; } } else { /* If we are in checkFontInfo because a rendering hint has been * set then all pipes are revalidated. But we can also * be here because setFont() has been called when the 'gasp' * hint is set, as then the font size determines the text pipe. * See comments in SunGraphics2d.setFont(Font). */ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) { if (info.font2D.useAAForPtSize(info.pixelHeight)) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF; } } else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) { /* loops for default rendering modes are installed in the SG2D * constructor. If there are none this will be null. * Not all compositing modes update the render loops, so * we also test that this is a mode we know should support * this. One minor issue is that the loops aren't necessarily * installed for a new rendering mode until after this * method is called during pipeline validation. So it is * theoretically possible that it was set to null for a * compositing mode, the composite is then set back to Src, * but the loop is still null when this is called and AA=ON * is installed instead of an LCD mode. * However this is done in the right order in SurfaceData.java * so this is not likely to be a problem - but not * guaranteed. */ if ( !surfaceData.canRenderLCDText(this)// loops.drawGlyphListLCDLoop == null ||// compositeState > COMP_ISCOPY ||// paintState > PAINT_ALPHACOLOR ) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { info.lcdRGBOrder = true; /* Collapse these into just HRGB or VRGB. * Pipe selection code needs only to test for these two. * Since these both select the same pipe anyway its * tempting to collapse into one value. But they are * different strikes (glyph caches) so the distinction * needs to be made for that purpose. */ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB; info.lcdRGBOrder = false; } else if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB; info.lcdRGBOrder = false; } /* Support subpixel positioning only for the case in * which the horizontal resolution is increased */ info.lcdSubPixPos = fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON && aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB; } } } info.aaHint = aahint; info.fontStrike = info.font2D.getStrike(font, devAt, textAt, aahint, fmhint); return info; } public static boolean isRotated(double [] mtx) { if ((mtx[0] == mtx[3]) && (mtx[1] == 0.0) && (mtx[2] == 0.0) && (mtx[0] > 0.0)) { return false; } return true; } public void setFont(Font font) { /* replacing the reference equality test font != this.font with * !font.equals(this.font) did not yield any measurable difference * in testing, but there may be yet to be identified cases where it * is beneficial. */ if (font != null && font!=this.font/*!font.equals(this.font)*/) { /* In the GASP AA case the textpipe depends on the glyph size * as determined by graphics and font transforms as well as the * font size, and information in the font. But we may invalidate * the pipe only to find that it made no difference. * Deferring pipe invalidation to checkFontInfo won't work because * when called we may already be rendering to the wrong pipe. * So, if the font is transformed, or the graphics has more than * a simple scale, we'll take that as enough of a hint to * revalidate everything. But if they aren't we will * use the font's point size to query the gasp table and see if * what it says matches what's currently being used, in which * case there's no need to invalidate the textpipe. * This should be sufficient for all typical uses cases. */ if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP && textpipe != invalidpipe && (transformState > TRANSFORM_ANY_TRANSLATE || font.isTransformed() || fontInfo == null || // Precaution, if true shouldn't get here (fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) != FontUtilities.getFont2D(font). useAAForPtSize(font.getSize()))) { textpipe = invalidpipe; } this.font = font; this.fontMetrics = null; this.validFontInfo = false; } } public FontInfo getFontInfo() { if (!validFontInfo) { this.fontInfo = checkFontInfo(this.fontInfo, font, null); validFontInfo = true; } return this.fontInfo; } /* Used by drawGlyphVector which specifies its own font. */ public FontInfo getGVFontInfo(Font font, FontRenderContext frc) { if (glyphVectorFontInfo != null && glyphVectorFontInfo.font == font && glyphVectorFRC == frc) { return glyphVectorFontInfo; } else { glyphVectorFRC = frc; return glyphVectorFontInfo = checkFontInfo(glyphVectorFontInfo, font, frc); } } public FontMetrics getFontMetrics() { if (this.fontMetrics != null) { return this.fontMetrics; } /* NB the constructor and the setter disallow "font" being null */ return this.fontMetrics = FontDesignMetrics.getMetrics(font, getFontRenderContext()); } public FontMetrics getFontMetrics(Font font) { if ((this.fontMetrics != null) && (font == this.font)) { return this.fontMetrics; } FontMetrics fm = FontDesignMetrics.getMetrics(font, getFontRenderContext()); if (this.font == font) { this.fontMetrics = fm; } return fm; } /** * Checks to see if a Path intersects the specified Rectangle in device * space. The rendering attributes taken into account include the * clip, transform, and stroke attributes. * @param rect The area in device space to check for a hit. * @param p The path to check for a hit. * @param onStroke Flag to choose between testing the stroked or * the filled path. * @return True if there is a hit, false otherwise. * @see #setStroke * @see #fillPath * @see #drawPath * @see #transform * @see #setTransform * @see #clip * @see #setClip */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = stroke.createStrokedShape(s); } s = transformShape(s); if ((constrainX|constrainY) != 0) { rect = new Rectangle(rect); rect.translate(constrainX, constrainY); } return s.intersects(rect); } /** * Return the ColorModel associated with this Graphics2D. */ public ColorModel getDeviceColorModel() { return surfaceData.getColorModel(); } /** * Return the device configuration associated with this Graphics2D. */ public GraphicsConfiguration getDeviceConfiguration() { return surfaceData.getDeviceConfiguration(); } /** * Return the SurfaceData object assigned to manage the destination * drawable surface of this Graphics2D. */ public final SurfaceData getSurfaceData() { return surfaceData; } /** * Sets the Composite in the current graphics state. Composite is used * in all drawing methods such as drawImage, drawString, drawPath, * and fillPath. It specifies how new pixels are to be combined with * the existing pixels on the graphics device in the rendering process. * @param comp The Composite object to be used for drawing. * @see java.awt.Graphics#setXORMode * @see java.awt.Graphics#setPaintMode * @see AlphaComposite */ public void setComposite(Composite comp) { if (composite == comp) { return; } int newCompState; CompositeType newCompType; if (comp instanceof AlphaComposite) { AlphaComposite alphacomp = (AlphaComposite) comp; newCompType = CompositeType.forAlphaComposite(alphacomp); if (newCompType == CompositeType.SrcOverNoEa) { if (paintState == PAINT_OPAQUECOLOR || (paintState > PAINT_ALPHACOLOR && paint.getTransparency() == Transparency.OPAQUE)) { newCompState = COMP_ISCOPY; } else { newCompState = COMP_ALPHA; } } else if (newCompType == CompositeType.SrcNoEa || newCompType == CompositeType.Src || newCompType == CompositeType.Clear) { newCompState = COMP_ISCOPY; } else if (surfaceData.getTransparency() == Transparency.OPAQUE && newCompType == CompositeType.SrcIn) { newCompState = COMP_ISCOPY; } else { newCompState = COMP_ALPHA; } } else if (comp instanceof XORComposite) { newCompState = COMP_XOR; newCompType = CompositeType.Xor; } else if (comp == null) { throw new IllegalArgumentException("null Composite"); } else { surfaceData.checkCustomComposite(); newCompState = COMP_CUSTOM; newCompType = CompositeType.General; } if (compositeState != newCompState || imageComp != newCompType) { compositeState = newCompState; imageComp = newCompType; invalidatePipe(); validFontInfo = false; } composite = comp; if (paintState <= PAINT_ALPHACOLOR) { validateColor(); } } /** * Sets the Paint in the current graphics state. * @param paint The Paint object to be used to generate color in * the rendering process. * @see java.awt.Graphics#setColor * @see GradientPaint * @see TexturePaint */ public void setPaint(Paint paint) { if (paint instanceof Color) { setColor((Color) paint); return; } if (paint == null || this.paint == paint) { return; } this.paint = paint; if (imageComp == CompositeType.SrcOverNoEa) { // special case where compState depends on opacity of paint if (paint.getTransparency() == Transparency.OPAQUE) { if (compositeState != COMP_ISCOPY) { compositeState = COMP_ISCOPY; } } else { if (compositeState == COMP_ISCOPY) { compositeState = COMP_ALPHA; } } } Class paintClass = paint.getClass(); if (paintClass == GradientPaint.class) { paintState = PAINT_GRADIENT; } else if (paintClass == LinearGradientPaint.class) { paintState = PAINT_LIN_GRADIENT; } else if (paintClass == RadialGradientPaint.class) { paintState = PAINT_RAD_GRADIENT; } else if (paintClass == TexturePaint.class) { paintState = PAINT_TEXTURE; } else { paintState = PAINT_CUSTOM; } validFontInfo = false; invalidatePipe(); } static final int NON_UNIFORM_SCALE_MASK = (AffineTransform.TYPE_GENERAL_TRANSFORM | AffineTransform.TYPE_GENERAL_SCALE); public static final double MinPenSizeAA = sun.java2d.pipe.RenderingEngine.getInstance().getMinimumAAPenSize(); public static final double MinPenSizeAASquared = (MinPenSizeAA * MinPenSizeAA); // Since inaccuracies in the trig package can cause us to // calculated a rotated pen width of just slightly greater // than 1.0, we add a fudge factor to our comparison value // here so that we do not misclassify single width lines as // wide lines under certain rotations. public static final double MinPenSizeSquared = 1.000000001; private void validateBasicStroke(BasicStroke bs) { boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON); if (transformState < TRANSFORM_TRANSLATESCALE) { if (aa) { if (bs.getLineWidth() <= MinPenSizeAA) { if (bs.getDashArray() == null) { strokeState = STROKE_THIN; } else { strokeState = STROKE_THINDASHED; } } else { strokeState = STROKE_WIDE; } } else { if (bs == defaultStroke) { strokeState = STROKE_THIN; } else if (bs.getLineWidth() <= 1.0f) { if (bs.getDashArray() == null) { strokeState = STROKE_THIN; } else { strokeState = STROKE_THINDASHED; } } else { strokeState = STROKE_WIDE; } } } else {Lines 501–1000 of 3310. Previous lines Load more
private static final int MINALLOCATED = 8; private static final int TEXTARRSIZE = 17; private static double[][] textTxArr = new double[TEXTARRSIZE][]; private static AffineTransform[] textAtArr = new AffineTransform[TEXTARRSIZE]; static { for (int i=MINALLOCATED;i<TEXTARRSIZE;i++) { textTxArr[i] = new double [] {i, 0, 0, i}; textAtArr[i] = new AffineTransform( textTxArr[i]); } } // cached state for various draw[String,Char,Byte] optimizations public FontInfo checkFontInfo(FontInfo info, Font font, FontRenderContext frc) { /* Do not create a FontInfo object as part of construction of an * SG2D as its possible it may never be needed - ie if no text * is drawn using this SG2D. */ if (info == null) { info = new FontInfo(); } float ptSize = font.getSize2D(); int txFontType; AffineTransform devAt, textAt=null; if (font.isTransformed()) { textAt = font.getTransform(); textAt.scale(ptSize, ptSize); txFontType = textAt.getType(); info.originX = (float)textAt.getTranslateX(); info.originY = (float)textAt.getTranslateY(); textAt.translate(-info.originX, -info.originY); if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); devAt = new AffineTransform(info.devTx); textAt.preConcatenate(devAt); } else { info.devTx = IDENT_MATRIX; devAt = IDENT_ATX; } textAt.getMatrix(info.glyphTx = new double[4]); double shearx = textAt.getShearX(); double scaley = textAt.getScaleY(); if (shearx != 0) { scaley = Math.sqrt(shearx * shearx + scaley * scaley); } info.pixelHeight = (int)(Math.abs(scaley)+0.5); } else { txFontType = AffineTransform.TYPE_IDENTITY; info.originX = info.originY = 0; if (transformState >= TRANSFORM_TRANSLATESCALE) { transform.getMatrix(info.devTx = new double[4]); devAt = new AffineTransform(info.devTx); info.glyphTx = new double[4]; for (int i = 0; i < 4; i++) { info.glyphTx[i] = info.devTx[i] * ptSize; } textAt = new AffineTransform(info.glyphTx); double shearx = transform.getShearX(); double scaley = transform.getScaleY(); if (shearx != 0) { scaley = Math.sqrt(shearx * shearx + scaley * scaley); } info.pixelHeight = (int)(Math.abs(scaley * ptSize)+0.5); } else { /* If the double represents a common integral, we * may have pre-allocated objects. * A "sparse" array be seems to be as fast as a switch * even for 3 or 4 pt sizes, and is more flexible. * This should perform comparably in single-threaded * rendering to the old code which synchronized on the * class and scale better on MP systems. */ int pszInt = (int)ptSize; if (ptSize == pszInt && pszInt >= MINALLOCATED && pszInt < TEXTARRSIZE) { info.glyphTx = textTxArr[pszInt]; textAt = textAtArr[pszInt]; info.pixelHeight = pszInt; } else { info.pixelHeight = (int)(ptSize+0.5); } if (textAt == null) { info.glyphTx = new double[] {ptSize, 0, 0, ptSize}; textAt = new AffineTransform(info.glyphTx); } info.devTx = IDENT_MATRIX; devAt = IDENT_ATX; } } info.font2D = FontUtilities.getFont2D(font); int fmhint = fractionalMetricsHint; if (fmhint == SunHints.INTVAL_FRACTIONALMETRICS_DEFAULT) { fmhint = SunHints.INTVAL_FRACTIONALMETRICS_OFF; } info.lcdSubPixPos = false; // conditionally set true in LCD mode. /* The text anti-aliasing hints that are set by the client need * to be interpreted for the current state and stored in the * FontInfo.aahint which is what will actually be used and * will be one of OFF, ON, LCD_HRGB or LCD_VRGB. * This is what pipe selection code should typically refer to, not * textAntialiasHint. This means we are now evaluating the meaning * of "default" here. Any pipe that really cares about that will * also need to consult that variable. * Otherwise these are being used only as args to getStrike, * and are encapsulated in that object which is part of the * FontInfo, so we do not need to store them directly as fields * in the FontInfo object. * That could change if FontInfo's were more selectively * revalidated when graphics state changed. Presently this * method re-evaluates all fields in the fontInfo. * The strike doesn't need to know the RGB subpixel order. Just * if its H or V orientation, so if an LCD option is specified we * always pass in the RGB hint to the strike. * frc is non-null only if this is a GlyphVector. For reasons * which are probably a historical mistake the AA hint in a GV * is honoured when we render, overriding the Graphics setting. */ int aahint; if (frc == null) { aahint = textAntialiasHint; } else { aahint = ((SunHints.Value)frc.getAntiAliasingHint()).getIndex(); } if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_DEFAULT) { if (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF; } } else { /* If we are in checkFontInfo because a rendering hint has been * set then all pipes are revalidated. But we can also * be here because setFont() has been called when the 'gasp' * hint is set, as then the font size determines the text pipe. * See comments in SunGraphics2d.setFont(Font). */ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP) { if (info.font2D.useAAForPtSize(info.pixelHeight)) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_OFF; } } else if (aahint >= SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB) { /* loops for default rendering modes are installed in the SG2D * constructor. If there are none this will be null. * Not all compositing modes update the render loops, so * we also test that this is a mode we know should support * this. One minor issue is that the loops aren't necessarily * installed for a new rendering mode until after this * method is called during pipeline validation. So it is * theoretically possible that it was set to null for a * compositing mode, the composite is then set back to Src, * but the loop is still null when this is called and AA=ON * is installed instead of an LCD mode. * However this is done in the right order in SurfaceData.java * so this is not likely to be a problem - but not * guaranteed. */ if ( !surfaceData.canRenderLCDText(this) // loops.drawGlyphListLCDLoop == null || // compositeState > COMP_ISCOPY || // paintState > PAINT_ALPHACOLOR ) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_ON; } else { info.lcdRGBOrder = true; /* Collapse these into just HRGB or VRGB. * Pipe selection code needs only to test for these two. * Since these both select the same pipe anyway its * tempting to collapse into one value. But they are * different strikes (glyph caches) so the distinction * needs to be made for that purpose. */ if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HBGR) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB; info.lcdRGBOrder = false; } else if (aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VBGR) { aahint = SunHints.INTVAL_TEXT_ANTIALIAS_LCD_VRGB; info.lcdRGBOrder = false; } /* Support subpixel positioning only for the case in * which the horizontal resolution is increased */ info.lcdSubPixPos = fmhint == SunHints.INTVAL_FRACTIONALMETRICS_ON && aahint == SunHints.INTVAL_TEXT_ANTIALIAS_LCD_HRGB; } } } info.aaHint = aahint; info.fontStrike = info.font2D.getStrike(font, devAt, textAt, aahint, fmhint); return info; } public static boolean isRotated(double [] mtx) { if ((mtx[0] == mtx[3]) && (mtx[1] == 0.0) && (mtx[2] == 0.0) && (mtx[0] > 0.0)) { return false; } return true; } public void setFont(Font font) { /* replacing the reference equality test font != this.font with * !font.equals(this.font) did not yield any measurable difference * in testing, but there may be yet to be identified cases where it * is beneficial. */ if (font != null && font!=this.font/*!font.equals(this.font)*/) { /* In the GASP AA case the textpipe depends on the glyph size * as determined by graphics and font transforms as well as the * font size, and information in the font. But we may invalidate * the pipe only to find that it made no difference. * Deferring pipe invalidation to checkFontInfo won't work because * when called we may already be rendering to the wrong pipe. * So, if the font is transformed, or the graphics has more than * a simple scale, we'll take that as enough of a hint to * revalidate everything. But if they aren't we will * use the font's point size to query the gasp table and see if * what it says matches what's currently being used, in which * case there's no need to invalidate the textpipe. * This should be sufficient for all typical uses cases. */ if (textAntialiasHint == SunHints.INTVAL_TEXT_ANTIALIAS_GASP && textpipe != invalidpipe && (transformState > TRANSFORM_ANY_TRANSLATE || font.isTransformed() || fontInfo == null || // Precaution, if true shouldn't get here (fontInfo.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) != FontUtilities.getFont2D(font). useAAForPtSize(font.getSize()))) { textpipe = invalidpipe; } this.font = font; this.fontMetrics = null; this.validFontInfo = false; } } public FontInfo getFontInfo() { if (!validFontInfo) { this.fontInfo = checkFontInfo(this.fontInfo, font, null); validFontInfo = true; } return this.fontInfo; } /* Used by drawGlyphVector which specifies its own font. */ public FontInfo getGVFontInfo(Font font, FontRenderContext frc) { if (glyphVectorFontInfo != null && glyphVectorFontInfo.font == font && glyphVectorFRC == frc) { return glyphVectorFontInfo; } else { glyphVectorFRC = frc; return glyphVectorFontInfo = checkFontInfo(glyphVectorFontInfo, font, frc); } } public FontMetrics getFontMetrics() { if (this.fontMetrics != null) { return this.fontMetrics; } /* NB the constructor and the setter disallow "font" being null */ return this.fontMetrics = FontDesignMetrics.getMetrics(font, getFontRenderContext()); } public FontMetrics getFontMetrics(Font font) { if ((this.fontMetrics != null) && (font == this.font)) { return this.fontMetrics; } FontMetrics fm = FontDesignMetrics.getMetrics(font, getFontRenderContext()); if (this.font == font) { this.fontMetrics = fm; } return fm; } /** * Checks to see if a Path intersects the specified Rectangle in device * space. The rendering attributes taken into account include the * clip, transform, and stroke attributes. * @param rect The area in device space to check for a hit. * @param p The path to check for a hit. * @param onStroke Flag to choose between testing the stroked or * the filled path. * @return True if there is a hit, false otherwise. * @see #setStroke * @see #fillPath * @see #drawPath * @see #transform * @see #setTransform * @see #clip * @see #setClip */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { if (onStroke) { s = stroke.createStrokedShape(s); } s = transformShape(s); if ((constrainX|constrainY) != 0) { rect = new Rectangle(rect); rect.translate(constrainX, constrainY); } return s.intersects(rect); } /** * Return the ColorModel associated with this Graphics2D. */ public ColorModel getDeviceColorModel() { return surfaceData.getColorModel(); } /** * Return the device configuration associated with this Graphics2D. */ public GraphicsConfiguration getDeviceConfiguration() { return surfaceData.getDeviceConfiguration(); } /** * Return the SurfaceData object assigned to manage the destination * drawable surface of this Graphics2D. */ public final SurfaceData getSurfaceData() { return surfaceData; } /** * Sets the Composite in the current graphics state. Composite is used * in all drawing methods such as drawImage, drawString, drawPath, * and fillPath. It specifies how new pixels are to be combined with * the existing pixels on the graphics device in the rendering process. * @param comp The Composite object to be used for drawing. * @see java.awt.Graphics#setXORMode * @see java.awt.Graphics#setPaintMode * @see AlphaComposite */ public void setComposite(Composite comp) { if (composite == comp) { return; } int newCompState; CompositeType newCompType; if (comp instanceof AlphaComposite) { AlphaComposite alphacomp = (AlphaComposite) comp; newCompType = CompositeType.forAlphaComposite(alphacomp); if (newCompType == CompositeType.SrcOverNoEa) { if (paintState == PAINT_OPAQUECOLOR || (paintState > PAINT_ALPHACOLOR && paint.getTransparency() == Transparency.OPAQUE)) { newCompState = COMP_ISCOPY; } else { newCompState = COMP_ALPHA; } } else if (newCompType == CompositeType.SrcNoEa || newCompType == CompositeType.Src || newCompType == CompositeType.Clear) { newCompState = COMP_ISCOPY; } else if (surfaceData.getTransparency() == Transparency.OPAQUE && newCompType == CompositeType.SrcIn) { newCompState = COMP_ISCOPY; } else { newCompState = COMP_ALPHA; } } else if (comp instanceof XORComposite) { newCompState = COMP_XOR; newCompType = CompositeType.Xor; } else if (comp == null) { throw new IllegalArgumentException("null Composite"); } else { surfaceData.checkCustomComposite(); newCompState = COMP_CUSTOM; newCompType = CompositeType.General; } if (compositeState != newCompState || imageComp != newCompType) { compositeState = newCompState; imageComp = newCompType; invalidatePipe(); validFontInfo = false; } composite = comp; if (paintState <= PAINT_ALPHACOLOR) { validateColor(); } } /** * Sets the Paint in the current graphics state. * @param paint The Paint object to be used to generate color in * the rendering process. * @see java.awt.Graphics#setColor * @see GradientPaint * @see TexturePaint */ public void setPaint(Paint paint) { if (paint instanceof Color) { setColor((Color) paint); return; } if (paint == null || this.paint == paint) { return; } this.paint = paint; if (imageComp == CompositeType.SrcOverNoEa) { // special case where compState depends on opacity of paint if (paint.getTransparency() == Transparency.OPAQUE) { if (compositeState != COMP_ISCOPY) { compositeState = COMP_ISCOPY; } } else { if (compositeState == COMP_ISCOPY) { compositeState = COMP_ALPHA; } } } Class paintClass = paint.getClass(); if (paintClass == GradientPaint.class) { paintState = PAINT_GRADIENT; } else if (paintClass == LinearGradientPaint.class) { paintState = PAINT_LIN_GRADIENT; } else if (paintClass == RadialGradientPaint.class) { paintState = PAINT_RAD_GRADIENT; } else if (paintClass == TexturePaint.class) { paintState = PAINT_TEXTURE; } else { paintState = PAINT_CUSTOM; } validFontInfo = false; invalidatePipe(); } static final int NON_UNIFORM_SCALE_MASK = (AffineTransform.TYPE_GENERAL_TRANSFORM | AffineTransform.TYPE_GENERAL_SCALE); public static final double MinPenSizeAA = sun.java2d.pipe.RenderingEngine.getInstance().getMinimumAAPenSize(); public static final double MinPenSizeAASquared = (MinPenSizeAA * MinPenSizeAA); // Since inaccuracies in the trig package can cause us to // calculated a rotated pen width of just slightly greater // than 1.0, we add a fudge factor to our comparison value // here so that we do not misclassify single width lines as // wide lines under certain rotations. public static final double MinPenSizeSquared = 1.000000001; private void validateBasicStroke(BasicStroke bs) { boolean aa = (antialiasHint == SunHints.INTVAL_ANTIALIAS_ON); if (transformState < TRANSFORM_TRANSLATESCALE) { if (aa) { if (bs.getLineWidth() <= MinPenSizeAA) { if (bs.getDashArray() == null) { strokeState = STROKE_THIN; } else { strokeState = STROKE_THINDASHED; } } else { strokeState = STROKE_WIDE; } } else { if (bs == defaultStroke) { strokeState = STROKE_THIN; } else if (bs.getLineWidth() <= 1.0f) { if (bs.getDashArray() == null) { strokeState = STROKE_THIN; } else { strokeState = STROKE_THINDASHED; } } else { strokeState = STROKE_WIDE; } } } else {