/* * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; import java.awt.Font;import java.awt.Graphics2D;import java.awt.Point;import java.awt.Rectangle;import static java.awt.RenderingHints.*;import java.awt.Shape;import java.awt.font.FontRenderContext;import java.awt.font.GlyphMetrics;import java.awt.font.GlyphJustificationInfo;import java.awt.font.GlyphVector;import java.awt.font.LineMetrics;import java.awt.font.TextAttribute;import java.awt.geom.AffineTransform;import java.awt.geom.GeneralPath;import java.awt.geom.NoninvertibleTransformException;import java.awt.geom.PathIterator;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.lang.ref.SoftReference;import java.text.CharacterIterator; import sun.awt.SunHints;import sun.java2d.loops.FontInfo; /** * Standard implementation of GlyphVector used by Font, GlyphList, and * SunGraphics2D. * * The main issues involve the semantics of the various transforms * (font, glyph, device) and their effect on rendering and metrics. * * Very, very unfortunately, the translation component of the font * transform affects where the text gets rendered. It offsets the * rendering origin. None of the other metrics of the glyphvector * are affected, making them inconsistent with the rendering behavior. * I think the translation component of the font would be better * interpreted as the translation component of a per-glyph transform, * but I don't know if this is possible to change. * * After the font transform is applied, the glyph transform is * applied. This makes glyph transforms relative to font transforms, * if the font transform changes, the glyph transform will have the * same (relative) effect on the outline of the glyph. The outline * and logical bounds are passed through the glyph transform before * being returned. The glyph metrics ignore the glyph transform, but * provide the outline bounds and the advance vector of the glyph (the * latter will be rotated if the font is rotated). The default layout * places each glyph at the end of the advance vector of the previous * glyph, and since the glyph transform translates the advance vector, * this means a glyph transform affects the positions of all * subsequent glyphs if defaultLayout is called after setting a glyph * transform. In the glyph info array, the bounds are the outline * bounds including the glyph transform, and the positions are as * computed, and the advances are the deltas between the positions. * * (There's a bug in the logical bounds of a rotated glyph for * composite fonts, it's not to spec (in 1.4.0, 1.4.1, 1.4.2). The * problem is that the rotated composite doesn't handle the multiple * ascents and descents properly in both x and y. You end up with * a rotated advance vector but an unrotated ascent and descent.) * * Finally, the whole thing is transformed by the device transform to * position it on the page. * * Another bug: The glyph outline seems to ignore fractional point * size information, but the images (and advances) don't ignore it. * * Small fonts drawn at large magnification have odd advances when * fractional metrics is off-- that's because the advances depend on * the frc. When the frc is scaled appropriately, the advances are * fine. FM or a large frc (high numbers) make the advances right. * * The buffer aa flag doesn't affect rendering, the glyph vector * renders as AA if aa is set in its frc, and as non-aa if aa is not * set in its frc. * * font rotation, baseline, vertical etc. * * Font rotation and baseline Line metrics should be measured along a * unit vector pi/4 cc from the baseline vector. For 'horizontal' * fonts the baseline vector is the x vector passed through the font * transform (ignoring translation), for 'vertical' it is the y * vector. This definition makes ascent, descent, etc independent of * shear, so shearing can be used to simulate italic. This means no * fonts have 'negative ascents' or 'zero ascents' etc. * * Having a coordinate system with orthogonal axes where one is * parallel to the baseline means we could use rectangles and interpret * them in terms of this coordinate system. Unfortunately there * is support for rotated fonts in the jdk already so maintaining * the semantics of existing code (getlogical bounds, etc) might * be difficult. * * A font transform transforms both the baseline and all the glyphs * in the font, so it does not rotate the glyph w.r.t the baseline. * If you do want to rotate individual glyphs, you need to apply a * glyph transform. If performDefaultLayout is called after this, * the transformed glyph advances will affect the glyph positions. * * useful additions * - select vertical metrics - glyphs are rotated pi/4 cc and vertical * metrics are used to align them to the baseline. * - define baseline for font (glyph rotation not linked to baseline) * - define extra space (delta between each glyph along baseline) * - define offset (delta from 'true' baseline, impacts ascent and * descent as these are still computed from true basline and pinned * to zero, used in superscript). */public class StandardGlyphVector extends GlyphVector { private Font font; private FontRenderContext frc; private int[] glyphs; // always private int[] userGlyphs; // used to return glyphs to the client. private float[] positions; // only if not default advances private int[] charIndices; // only if interesting private int flags; // indicates whether positions, charIndices is interesting private static final int UNINITIALIZED_FLAGS = -1; // transforms information private GlyphTransformInfo gti; // information about per-glyph transforms // !!! can we get rid of any of this extra stuff? private AffineTransform ftx; // font transform without translation private AffineTransform dtx; // device transform used for strike calculations, no translation private AffineTransform invdtx; // inverse of dtx or null if dtx is identity private AffineTransform frctx; // font render context transform, wish we could just share it private Font2D font2D; // basic strike-independent stuff private SoftReference fsref; // font strike reference for glyphs with no per-glyph transform ///////////////////////////// // Constructors and Factory methods ///////////////////////////// public StandardGlyphVector(Font font, String str, FontRenderContext frc) { init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) { init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, int start, int count, FontRenderContext frc) { init(font, text, start, count, frc, UNINITIALIZED_FLAGS); } private float getTracking(Font font) { if (font.hasLayoutAttributes()) { AttributeValues values = ((AttributeMap)font.getAttributes()).getValues(); return values.getTracking(); } return 0; } // used by GlyphLayout to construct a glyphvector public StandardGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { initGlyphVector(font, frc, glyphs, positions, indices, flags); // this code should go into layout float track = getTracking(font); if (track != 0) { track *= font.getSize2D(); Point2D.Float trackPt = new Point2D.Float(track, 0); // advance delta if (font.isTransformed()) { AffineTransform at = font.getTransform(); at.deltaTransform(trackPt, trackPt); } // how do we know its a base glyph // for now, it is if the natural advance of the glyph is non-zero Font2D f2d = FontUtilities.getFont2D(font); FontStrike strike = f2d.getStrike(font, frc); float[] deltas = { trackPt.x, trackPt.y }; for (int j = 0; j < deltas.length; ++j) { float inc = deltas[j]; if (inc != 0) { float delta = 0; for (int i = j, n = 0; n < glyphs.length; i += 2) { if (strike.getGlyphAdvance(glyphs[n++]) != 0) { // might be an inadequate test positions[i] += delta; delta += inc; } } positions[positions.length-2+j] += delta; } } } } public void initGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { this.font = font; this.frc = frc; this.glyphs = glyphs; this.userGlyphs = glyphs; // no need to check this.positions = positions; this.charIndices = indices; this.flags = flags; initFontData(); } public StandardGlyphVector(Font font, CharacterIterator iter, FontRenderContext frc) { int offset = iter.getBeginIndex(); char[] text = new char [iter.getEndIndex() - offset]; for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { text[iter.getIndex() - offset] = c; } init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, int[] glyphs, FontRenderContext frc) { // !!! find callers of this // should be able to fully init from raw data, e.g. charmap, flags too. this.font = font; this.frc = frc; this.flags = UNINITIALIZED_FLAGS; initFontData(); this.userGlyphs = glyphs; this.glyphs = getValidatedGlyphs(this.userGlyphs); } /* This is called from the rendering loop. FontInfo is supplied * because a GV caches a strike and glyph images suitable for its FRC. * LCD text isn't currently supported on all surfaces, in which case * standard AA must be used. This is most likely to occur when LCD text * is requested and the surface is some non-standard type or hardward * surface for which there are no accelerated loops. * We can detect this as being AA=="ON" in the FontInfo and AA!="ON" * and AA!="GASP" in the FRC - since this only occurs for LCD text we don't * need to check any more precisely what value is in the FRC. */ public static StandardGlyphVector getStandardGV(GlyphVector gv, FontInfo info) { if (info.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) { Object aaHint = gv.getFontRenderContext().getAntiAliasingHint(); if (aaHint != VALUE_TEXT_ANTIALIAS_ON && aaHint != VALUE_TEXT_ANTIALIAS_GASP) { /* We need to create a new GV with AA==ON for rendering */ FontRenderContext frc = gv.getFontRenderContext(); frc = new FontRenderContext(frc.getTransform(), VALUE_TEXT_ANTIALIAS_ON, frc.getFractionalMetricsHint()); return new StandardGlyphVector(gv, frc); } } if (gv instanceof StandardGlyphVector) { return (StandardGlyphVector)gv; } return new StandardGlyphVector(gv, gv.getFontRenderContext()); } ///////////////////////////// // GlyphVector API ///////////////////////////// public Font getFont() { return this.font; } public FontRenderContext getFontRenderContext() { return this.frc; } public void performDefaultLayout() { positions = null; if (getTracking(font) == 0) { clearFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } } public int getNumGlyphs() { return glyphs.length; } public int getGlyphCode(int glyphIndex) { return userGlyphs[glyphIndex]; } public int[] getGlyphCodes(int start, int count, int[] result) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } if (start < 0) { throw new IndexOutOfBoundsException("start = " + start); } if (start > glyphs.length - count) { // watch out for overflow if index + count overlarge throw new IndexOutOfBoundsException("start + count = " + (start + count)); } if (result == null) { result = new int[count]; } // if arraycopy were faster, we wouldn't code this for (int i = 0; i < count; ++i) { result[i] = userGlyphs[i + start]; } return result; } public int getGlyphCharIndex(int ix) { if (ix < 0 && ix >= glyphs.length) { throw new IndexOutOfBoundsException("" + ix); } if (charIndices == null) { if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) { return glyphs.length - 1 - ix; } return ix; } return charIndices[ix]; } public int[] getGlyphCharIndices(int start, int count, int[] result) { if (start < 0 || count < 0 || (count > glyphs.length - start)) { throw new IndexOutOfBoundsException("" + start + ", " + count); } if (result == null) { result = new int[count]; } if (charIndices == null) { if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) { for (int i = 0, n = glyphs.length - 1 - start; i < count; ++i, --n) { result[i] = n; } } else { for (int i = 0, n = start; i < count; ++i, ++n) { result[i] = n; } } } else { for (int i = 0; i < count; ++i) { result[i] = charIndices[i + start]; } } return result; } // !!! not cached, assume TextLayout will cache if necessary // !!! reexamine for per-glyph-transforms // !!! revisit for text-on-a-path, vertical public Rectangle2D getLogicalBounds() { setFRCTX(); initPositions(); LineMetrics lm = font.getLineMetrics("", frc); float minX, minY, maxX, maxY; // horiz only for now... minX = 0; minY = -lm.getAscent(); maxX = 0; maxY = lm.getDescent() + lm.getLeading(); if (glyphs.length > 0) { maxX = positions[positions.length - 2]; } return new Rectangle2D.Float(minX, minY, maxX - minX, maxY - minY); } // !!! not cached, assume TextLayout will cache if necessary public Rectangle2D getVisualBounds() { Rectangle2D result = null; for (int i = 0; i < glyphs.length; ++i) { Rectangle2D glyphVB = getGlyphVisualBounds(i).getBounds2D(); if (!glyphVB.isEmpty()) { if (result == null) { result = glyphVB; } else { Rectangle2D.union(result, glyphVB, result); } } } if (result == null) { result = new Rectangle2D.Float(0, 0, 0, 0); } return result; } // !!! not cached, assume TextLayout will cache if necessary // !!! fontStrike needs a method for this public Rectangle getPixelBounds(FontRenderContext renderFRC, float x, float y) { return getGlyphsPixelBounds(renderFRC, x, y, 0, glyphs.length); } public Shape getOutline() { return getGlyphsOutline(0, glyphs.length, 0, 0); } public Shape getOutline(float x, float y) { return getGlyphsOutline(0, glyphs.length, x, y); } // relative to gv origin public Shape getGlyphOutline(int ix) { return getGlyphsOutline(ix, 1, 0, 0); } // relative to gv origin offset by x, y public Shape getGlyphOutline(int ix, float x, float y) { return getGlyphsOutline(ix, 1, x, y); } public Point2D getGlyphPosition(int ix) { initPositions(); ix *= 2; return new Point2D.Float(positions[ix], positions[ix + 1]); } public void setGlyphPosition(int ix, Point2D pos) { initPositions(); int ix2 = ix << 1; positions[ix2] = (float)pos.getX(); positions[ix2 + 1] = (float)pos.getY(); clearCaches(ix); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } public AffineTransform getGlyphTransform(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } if (gti != null) { return gti.getGlyphTransform(ix); } return null; // spec'd as returning null } public void setGlyphTransform(int ix, AffineTransform newTX) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } if (gti == null) { if (newTX == null || newTX.isIdentity()) { return; } gti = new GlyphTransformInfo(this); } gti.setGlyphTransform(ix, newTX); // sets flags if (gti.transformCount() == 0) { gti = null; } } public int getLayoutFlags() { if (flags == UNINITIALIZED_FLAGS) { flags = 0; if (charIndices != null && glyphs.length > 1) { boolean ltr = true; boolean rtl = true; int rtlix = charIndices.length; // rtl index for (int i = 0; i < charIndices.length && (ltr || rtl); ++i) { int cx = charIndices[i]; ltr = ltr && (cx == i); rtl = rtl && (cx == --rtlix); } Lines 1–500 of 1935. Load more
/* * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import static java.awt.RenderingHints.*; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.GlyphMetrics; import java.awt.font.GlyphJustificationInfo; import java.awt.font.GlyphVector; import java.awt.font.LineMetrics; import java.awt.font.TextAttribute; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.lang.ref.SoftReference; import java.text.CharacterIterator; import sun.awt.SunHints; import sun.java2d.loops.FontInfo; /** * Standard implementation of GlyphVector used by Font, GlyphList, and * SunGraphics2D. * * The main issues involve the semantics of the various transforms * (font, glyph, device) and their effect on rendering and metrics. * * Very, very unfortunately, the translation component of the font * transform affects where the text gets rendered. It offsets the * rendering origin. None of the other metrics of the glyphvector * are affected, making them inconsistent with the rendering behavior. * I think the translation component of the font would be better * interpreted as the translation component of a per-glyph transform, * but I don't know if this is possible to change. * * After the font transform is applied, the glyph transform is * applied. This makes glyph transforms relative to font transforms, * if the font transform changes, the glyph transform will have the * same (relative) effect on the outline of the glyph. The outline * and logical bounds are passed through the glyph transform before * being returned. The glyph metrics ignore the glyph transform, but * provide the outline bounds and the advance vector of the glyph (the * latter will be rotated if the font is rotated). The default layout * places each glyph at the end of the advance vector of the previous * glyph, and since the glyph transform translates the advance vector, * this means a glyph transform affects the positions of all * subsequent glyphs if defaultLayout is called after setting a glyph * transform. In the glyph info array, the bounds are the outline * bounds including the glyph transform, and the positions are as * computed, and the advances are the deltas between the positions. * * (There's a bug in the logical bounds of a rotated glyph for * composite fonts, it's not to spec (in 1.4.0, 1.4.1, 1.4.2). The * problem is that the rotated composite doesn't handle the multiple * ascents and descents properly in both x and y. You end up with * a rotated advance vector but an unrotated ascent and descent.) * * Finally, the whole thing is transformed by the device transform to * position it on the page. * * Another bug: The glyph outline seems to ignore fractional point * size information, but the images (and advances) don't ignore it. * * Small fonts drawn at large magnification have odd advances when * fractional metrics is off-- that's because the advances depend on * the frc. When the frc is scaled appropriately, the advances are * fine. FM or a large frc (high numbers) make the advances right. * * The buffer aa flag doesn't affect rendering, the glyph vector * renders as AA if aa is set in its frc, and as non-aa if aa is not * set in its frc. * * font rotation, baseline, vertical etc. * * Font rotation and baseline Line metrics should be measured along a * unit vector pi/4 cc from the baseline vector. For 'horizontal' * fonts the baseline vector is the x vector passed through the font * transform (ignoring translation), for 'vertical' it is the y * vector. This definition makes ascent, descent, etc independent of * shear, so shearing can be used to simulate italic. This means no * fonts have 'negative ascents' or 'zero ascents' etc. * * Having a coordinate system with orthogonal axes where one is * parallel to the baseline means we could use rectangles and interpret * them in terms of this coordinate system. Unfortunately there * is support for rotated fonts in the jdk already so maintaining * the semantics of existing code (getlogical bounds, etc) might * be difficult. * * A font transform transforms both the baseline and all the glyphs * in the font, so it does not rotate the glyph w.r.t the baseline. * If you do want to rotate individual glyphs, you need to apply a * glyph transform. If performDefaultLayout is called after this, * the transformed glyph advances will affect the glyph positions. * * useful additions * - select vertical metrics - glyphs are rotated pi/4 cc and vertical * metrics are used to align them to the baseline. * - define baseline for font (glyph rotation not linked to baseline) * - define extra space (delta between each glyph along baseline) * - define offset (delta from 'true' baseline, impacts ascent and * descent as these are still computed from true basline and pinned * to zero, used in superscript). */ public class StandardGlyphVector extends GlyphVector { private Font font; private FontRenderContext frc; private int[] glyphs; // always private int[] userGlyphs; // used to return glyphs to the client. private float[] positions; // only if not default advances private int[] charIndices; // only if interesting private int flags; // indicates whether positions, charIndices is interesting private static final int UNINITIALIZED_FLAGS = -1; // transforms information private GlyphTransformInfo gti; // information about per-glyph transforms // !!! can we get rid of any of this extra stuff? private AffineTransform ftx; // font transform without translation private AffineTransform dtx; // device transform used for strike calculations, no translation private AffineTransform invdtx; // inverse of dtx or null if dtx is identity private AffineTransform frctx; // font render context transform, wish we could just share it private Font2D font2D; // basic strike-independent stuff private SoftReference fsref; // font strike reference for glyphs with no per-glyph transform ///////////////////////////// // Constructors and Factory methods ///////////////////////////// public StandardGlyphVector(Font font, String str, FontRenderContext frc) { init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) { init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, char[] text, int start, int count, FontRenderContext frc) { init(font, text, start, count, frc, UNINITIALIZED_FLAGS); } private float getTracking(Font font) { if (font.hasLayoutAttributes()) { AttributeValues values = ((AttributeMap)font.getAttributes()).getValues(); return values.getTracking(); } return 0; } // used by GlyphLayout to construct a glyphvector public StandardGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { initGlyphVector(font, frc, glyphs, positions, indices, flags); // this code should go into layout float track = getTracking(font); if (track != 0) { track *= font.getSize2D(); Point2D.Float trackPt = new Point2D.Float(track, 0); // advance delta if (font.isTransformed()) { AffineTransform at = font.getTransform(); at.deltaTransform(trackPt, trackPt); } // how do we know its a base glyph // for now, it is if the natural advance of the glyph is non-zero Font2D f2d = FontUtilities.getFont2D(font); FontStrike strike = f2d.getStrike(font, frc); float[] deltas = { trackPt.x, trackPt.y }; for (int j = 0; j < deltas.length; ++j) { float inc = deltas[j]; if (inc != 0) { float delta = 0; for (int i = j, n = 0; n < glyphs.length; i += 2) { if (strike.getGlyphAdvance(glyphs[n++]) != 0) { // might be an inadequate test positions[i] += delta; delta += inc; } } positions[positions.length-2+j] += delta; } } } } public void initGlyphVector(Font font, FontRenderContext frc, int[] glyphs, float[] positions, int[] indices, int flags) { this.font = font; this.frc = frc; this.glyphs = glyphs; this.userGlyphs = glyphs; // no need to check this.positions = positions; this.charIndices = indices; this.flags = flags; initFontData(); } public StandardGlyphVector(Font font, CharacterIterator iter, FontRenderContext frc) { int offset = iter.getBeginIndex(); char[] text = new char [iter.getEndIndex() - offset]; for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) { text[iter.getIndex() - offset] = c; } init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS); } public StandardGlyphVector(Font font, int[] glyphs, FontRenderContext frc) { // !!! find callers of this // should be able to fully init from raw data, e.g. charmap, flags too. this.font = font; this.frc = frc; this.flags = UNINITIALIZED_FLAGS; initFontData(); this.userGlyphs = glyphs; this.glyphs = getValidatedGlyphs(this.userGlyphs); } /* This is called from the rendering loop. FontInfo is supplied * because a GV caches a strike and glyph images suitable for its FRC. * LCD text isn't currently supported on all surfaces, in which case * standard AA must be used. This is most likely to occur when LCD text * is requested and the surface is some non-standard type or hardward * surface for which there are no accelerated loops. * We can detect this as being AA=="ON" in the FontInfo and AA!="ON" * and AA!="GASP" in the FRC - since this only occurs for LCD text we don't * need to check any more precisely what value is in the FRC. */ public static StandardGlyphVector getStandardGV(GlyphVector gv, FontInfo info) { if (info.aaHint == SunHints.INTVAL_TEXT_ANTIALIAS_ON) { Object aaHint = gv.getFontRenderContext().getAntiAliasingHint(); if (aaHint != VALUE_TEXT_ANTIALIAS_ON && aaHint != VALUE_TEXT_ANTIALIAS_GASP) { /* We need to create a new GV with AA==ON for rendering */ FontRenderContext frc = gv.getFontRenderContext(); frc = new FontRenderContext(frc.getTransform(), VALUE_TEXT_ANTIALIAS_ON, frc.getFractionalMetricsHint()); return new StandardGlyphVector(gv, frc); } } if (gv instanceof StandardGlyphVector) { return (StandardGlyphVector)gv; } return new StandardGlyphVector(gv, gv.getFontRenderContext()); } ///////////////////////////// // GlyphVector API ///////////////////////////// public Font getFont() { return this.font; } public FontRenderContext getFontRenderContext() { return this.frc; } public void performDefaultLayout() { positions = null; if (getTracking(font) == 0) { clearFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } } public int getNumGlyphs() { return glyphs.length; } public int getGlyphCode(int glyphIndex) { return userGlyphs[glyphIndex]; } public int[] getGlyphCodes(int start, int count, int[] result) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } if (start < 0) { throw new IndexOutOfBoundsException("start = " + start); } if (start > glyphs.length - count) { // watch out for overflow if index + count overlarge throw new IndexOutOfBoundsException("start + count = " + (start + count)); } if (result == null) { result = new int[count]; } // if arraycopy were faster, we wouldn't code this for (int i = 0; i < count; ++i) { result[i] = userGlyphs[i + start]; } return result; } public int getGlyphCharIndex(int ix) { if (ix < 0 && ix >= glyphs.length) { throw new IndexOutOfBoundsException("" + ix); } if (charIndices == null) { if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) { return glyphs.length - 1 - ix; } return ix; } return charIndices[ix]; } public int[] getGlyphCharIndices(int start, int count, int[] result) { if (start < 0 || count < 0 || (count > glyphs.length - start)) { throw new IndexOutOfBoundsException("" + start + ", " + count); } if (result == null) { result = new int[count]; } if (charIndices == null) { if ((getLayoutFlags() & FLAG_RUN_RTL) != 0) { for (int i = 0, n = glyphs.length - 1 - start; i < count; ++i, --n) { result[i] = n; } } else { for (int i = 0, n = start; i < count; ++i, ++n) { result[i] = n; } } } else { for (int i = 0; i < count; ++i) { result[i] = charIndices[i + start]; } } return result; } // !!! not cached, assume TextLayout will cache if necessary // !!! reexamine for per-glyph-transforms // !!! revisit for text-on-a-path, vertical public Rectangle2D getLogicalBounds() { setFRCTX(); initPositions(); LineMetrics lm = font.getLineMetrics("", frc); float minX, minY, maxX, maxY; // horiz only for now... minX = 0; minY = -lm.getAscent(); maxX = 0; maxY = lm.getDescent() + lm.getLeading(); if (glyphs.length > 0) { maxX = positions[positions.length - 2]; } return new Rectangle2D.Float(minX, minY, maxX - minX, maxY - minY); } // !!! not cached, assume TextLayout will cache if necessary public Rectangle2D getVisualBounds() { Rectangle2D result = null; for (int i = 0; i < glyphs.length; ++i) { Rectangle2D glyphVB = getGlyphVisualBounds(i).getBounds2D(); if (!glyphVB.isEmpty()) { if (result == null) { result = glyphVB; } else { Rectangle2D.union(result, glyphVB, result); } } } if (result == null) { result = new Rectangle2D.Float(0, 0, 0, 0); } return result; } // !!! not cached, assume TextLayout will cache if necessary // !!! fontStrike needs a method for this public Rectangle getPixelBounds(FontRenderContext renderFRC, float x, float y) { return getGlyphsPixelBounds(renderFRC, x, y, 0, glyphs.length); } public Shape getOutline() { return getGlyphsOutline(0, glyphs.length, 0, 0); } public Shape getOutline(float x, float y) { return getGlyphsOutline(0, glyphs.length, x, y); } // relative to gv origin public Shape getGlyphOutline(int ix) { return getGlyphsOutline(ix, 1, 0, 0); } // relative to gv origin offset by x, y public Shape getGlyphOutline(int ix, float x, float y) { return getGlyphsOutline(ix, 1, x, y); } public Point2D getGlyphPosition(int ix) { initPositions(); ix *= 2; return new Point2D.Float(positions[ix], positions[ix + 1]); } public void setGlyphPosition(int ix, Point2D pos) { initPositions(); int ix2 = ix << 1; positions[ix2] = (float)pos.getX(); positions[ix2 + 1] = (float)pos.getY(); clearCaches(ix); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } public AffineTransform getGlyphTransform(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } if (gti != null) { return gti.getGlyphTransform(ix); } return null; // spec'd as returning null } public void setGlyphTransform(int ix, AffineTransform newTX) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } if (gti == null) { if (newTX == null || newTX.isIdentity()) { return; } gti = new GlyphTransformInfo(this); } gti.setGlyphTransform(ix, newTX); // sets flags if (gti.transformCount() == 0) { gti = null; } } public int getLayoutFlags() { if (flags == UNINITIALIZED_FLAGS) { flags = 0; if (charIndices != null && glyphs.length > 1) { boolean ltr = true; boolean rtl = true; int rtlix = charIndices.length; // rtl index for (int i = 0; i < charIndices.length && (ltr || rtl); ++i) { int cx = charIndices[i]; ltr = ltr && (cx == i); rtl = rtl && (cx == --rtlix); }