| Constructor: |
public StandardGlyphVector(Font font,
String str,
FontRenderContext frc) {
// font strike reference for glyphs with no per-glyph transform
/////////////////////////////
// Constructors and Factory methods
/////////////////////////////
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,
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);
}
|
public StandardGlyphVector(Font font,
char[] text,
int start,
int count,
FontRenderContext frc) {
init(font, text, start, count, frc, UNINITIALIZED_FLAGS);
}
|
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 = FontManager.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;
}
}
}
}
|
| Method from sun.font.StandardGlyphVector Detail: |
StringBuffer appendString(StringBuffer buf) {
if (buf == null) {
buf = new StringBuffer();
}
try {
buf.append("SGV{font: ");
buf.append(font.toString());
buf.append(", frc: ");
buf.append(frc.toString());
buf.append(", glyphs: (");
buf.append(glyphs.length);
buf.append(")[");
for (int i = 0; i < glyphs.length; ++i) {
if (i > 0) {
buf.append(", ");
}
buf.append(Integer.toHexString(glyphs[i]));
}
buf.append("]");
if (positions != null) {
buf.append(", positions: (");
buf.append(positions.length);
buf.append(")[");
for (int i = 0; i < positions.length; i += 2) {
if (i > 0) {
buf.append(", ");
}
buf.append(positions[i]);
buf.append("@");
buf.append(positions[i+1]);
}
buf.append("]");
}
if (charIndices != null) {
buf.append(", indices: (");
buf.append(charIndices.length);
buf.append(")[");
for (int i = 0; i < charIndices.length; ++i) {
if (i > 0) {
buf.append(", ");
}
buf.append(charIndices[i]);
}
buf.append("]");
}
buf.append(", flags:");
if (getLayoutFlags() == 0) {
buf.append(" default");
} else {
if ((flags & FLAG_HAS_TRANSFORMS) != 0) {
buf.append(" tx");
}
if ((flags & FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {
buf.append(" pos");
}
if ((flags & FLAG_RUN_RTL) != 0) {
buf.append(" rtl");
}
if ((flags & FLAG_COMPLEX_GLYPHS) != 0) {
buf.append(" complex");
}
}
}
catch(Exception e) {
buf.append(" " + e.getMessage());
}
buf.append("}");
return buf;
}
|
public Object clone() {
// positions, gti are mutable so we have to clone them
// font2d can be shared
// fsref is a cache and can be shared
try {
StandardGlyphVector result = (StandardGlyphVector)super.clone();
result.clearCaches();
if (positions != null) {
result.positions = (float[])positions.clone();
}
if (gti != null) {
result.gti = new GlyphTransformInfo(result, gti);
}
return result;
}
catch (CloneNotSupportedException e) {
}
return this;
}
As a concrete subclass of GlyphVector, this must implement clone. |
public StandardGlyphVector copy() {
return (StandardGlyphVector)clone();
}
Sometimes I wish java had covariant return types... |
public boolean equals(GlyphVector rhs) {
if (this == rhs) {
return true;
}
if (rhs == null) {
return false;
}
try {
StandardGlyphVector other = (StandardGlyphVector)rhs;
if (glyphs.length != other.glyphs.length) {
return false;
}
for (int i = 0; i < glyphs.length; ++i) {
if (glyphs[i] != other.glyphs[i]) {
return false;
}
}
if (!font.equals(other.font)) {
return false;
}
if (!frc.equals(other.frc)) {
return false;
}
if ((other.positions == null) != (positions == null)) {
if (positions == null) {
initPositions();
} else {
other.initPositions();
}
}
if (positions != null) {
for (int i = 0; i < positions.length; ++i) {
if (positions[i] != other.positions[i]) {
return false;
}
}
}
if (gti == null) {
return other.gti == null;
} else {
return gti.equals(other.gti);
}
}
catch (ClassCastException e) {
// assume they are different simply by virtue of the class difference
return false;
}
}
|
public boolean equals(Object rhs) {
try {
return equals((GlyphVector)rhs);
}
catch (ClassCastException e) {
return false;
}
}
Since we implement equality comparisons for GlyphVector, we implement
the inherited Object.equals(Object) as well. GlyphVector should do
this, and define two glyphvectors as not equal if the classes differ. |
public Font getFont() {
return this.font;
}
|
public FontRenderContext getFontRenderContext() {
return this.frc;
}
|
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;
}
|
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 float[] getGlyphInfo() {
setFRCTX();
initPositions();
float[] result = new float[glyphs.length * 8];
for (int i = 0, n = 0; i < glyphs.length; ++i, n += 8) {
float x = positions[i*2];
float y = positions[i*2+1];
result[n] = x;
result[n+1] = y;
int glyphID = glyphs[i];
GlyphStrike s = getGlyphStrike(i);
Point2D.Float adv = s.strike.getGlyphMetrics(glyphID);
result[n+2] = adv.x;
result[n+3] = adv.y;
Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D();
result[n+4] = (float)(vb.getMinX());
result[n+5] = (float)(vb.getMinY());
result[n+6] = (float)(vb.getWidth());
result[n+7] = (float)(vb.getHeight());
}
return result;
}
For each glyph return posx, posy, advx, advy, visx, visy, visw, vish. |
public GlyphJustificationInfo getGlyphJustificationInfo(int ix) {
if (ix < 0 || ix >= glyphs.length) {
throw new IndexOutOfBoundsException("ix = " + ix);
}
// currently we don't have enough information to do this right. should
// get info from the font and use real OT/GX justification. Right now
// sun/font/ExtendedTextSourceLabel assigns one of three infos
// based on whether the char is kanji, space, or other.
return null;
}
|
public Shape getGlyphLogicalBounds(int ix) {
if (ix < 0 || ix >= glyphs.length) {
throw new IndexOutOfBoundsException("ix = " + ix);
}
Shape[] lbcache;
if (lbcacheRef == null || (lbcache = (Shape[])lbcacheRef.get()) == null) {
lbcache = new Shape[glyphs.length];
lbcacheRef = new SoftReference(lbcache);
}
Shape result = lbcache[ix];
if (result == null) {
setFRCTX();
initPositions();
// !!! ought to return a rectangle2d for simple cases, though the following works for all
// get the position, the tx offset, and the x,y advance and x,y adl. The
// shape is the box formed by adv (width) and adl (height) offset by
// the position plus the tx offset minus the ascent.
ADL adl = new ADL();
GlyphStrike gs = getGlyphStrike(ix);
gs.getADL(adl);
Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]);
float wx = adv.x;
float wy = adv.y;
float hx = adl.descentX + adl.leadingX + adl.ascentX;
float hy = adl.descentY + adl.leadingY + adl.ascentY;
float x = positions[ix*2] + gs.dx - adl.ascentX;
float y = positions[ix*2+1] + gs.dy - adl.ascentY;
GeneralPath gp = new GeneralPath();
gp.moveTo(x, y);
gp.lineTo(x + wx, y + wy);
gp.lineTo(x + wx + hx, y + wy + hy);
gp.lineTo(x + hx, y + hy);
gp.closePath();
result = new DelegatingShape(gp);
lbcache[ix] = result;
}
return result;
}
|
public GlyphMetrics getGlyphMetrics(int ix) {
if (ix < 0 || ix >= glyphs.length) {
throw new IndexOutOfBoundsException("ix = " + ix);
}
Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D();
Point2D pt = getGlyphPosition(ix);
vb.setRect(vb.getMinX() - pt.getX(),
vb.getMinY() - pt.getY(),
vb.getWidth(),
vb.getHeight());
Point2D.Float adv =
getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]);
GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y,
vb,
GlyphMetrics.STANDARD);
return gm;
}
|
public Shape getGlyphOutline(int ix) {
return getGlyphsOutline(ix, 1, 0, 0);
}
|
public Shape getGlyphOutline(int ix,
float x,
float y) {
return getGlyphsOutline(ix, 1, x, y);
}
|
public Rectangle getGlyphPixelBounds(int index,
FontRenderContext renderFRC,
float x,
float y) {
return getGlyphsPixelBounds(renderFRC, x, y, index, 1);
}
|
public Point2D getGlyphPosition(int ix) {
initPositions();
ix *= 2;
return new Point2D.Float(positions[ix], positions[ix + 1]);
}
|
public float[] getGlyphPositions(float[] result) {
return internalGetGlyphPositions(0, glyphs.length + 1, 0, result);
}
This is a convenience overload that gets all the glyph positions, which
is what you usually want to do if you're getting more than one.
!!! should I bother taking result parameter? |
public float[] getGlyphPositions(int start,
int count,
float[] result) {
if (count < 0) {
throw new IllegalArgumentException("count = " + count);
}
if (start < 0) {
throw new IndexOutOfBoundsException("start = " + start);
}
if (start > glyphs.length + 1 - count) { // watch for overflow
throw new IndexOutOfBoundsException("start + count = " + (start + count));
}
return internalGetGlyphPositions(start, count, 0, result);
}
|
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 AffineTransform[] getGlyphTransforms() {
return getGlyphTransforms(0, glyphs.length, null);
}
Convenience overload for getGlyphTransforms(int, int, AffineTransform[], int); |
public AffineTransform[] getGlyphTransforms(int start,
int count,
AffineTransform[] result) {
if (start < 0 || count < 0 || start + count > glyphs.length) {
throw new IllegalArgumentException("start: " + start + " count: " + count);
}
if (gti == null) {
return null;
}
if (result == null) {
result = new AffineTransform[count];
}
for (int i = 0; i < count; ++i, ++start) {
result[i] = gti.getGlyphTransform(start);
}
return result;
}
Get transform information for the requested range of glyphs.
If no glyphs have a transform, return null.
If a glyph has no transform (or is the identity transform) its entry in the result array will be null.
If the passed-in result is null an array will be allocated for the caller.
Each transform instance in the result array will unique, and independent of the GlyphVector's transform. |
public Shape getGlyphVisualBounds(int ix) {
if (ix < 0 || ix >= glyphs.length) {
throw new IndexOutOfBoundsException("ix = " + ix);
}
Shape[] vbcache;
if (vbcacheRef == null || (vbcache = (Shape[])vbcacheRef.get()) == null) {
vbcache = new Shape[glyphs.length];
vbcacheRef = new SoftReference(vbcache);
}
Shape result = vbcache[ix];
if (result == null) {
result = new DelegatingShape(getGlyphOutlineBounds(ix));
vbcache[ix] = result;
}
return result;
}
|
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);
}
if (rtl) flags |= FLAG_RUN_RTL;
if (!rtl && !ltr) flags |= FLAG_COMPLEX_GLYPHS;
}
}
return flags;
}
|
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);
}
|
public int getNumGlyphs() {
return 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);
}
|
public Rectangle getPixelBounds(FontRenderContext renderFRC,
float x,
float y) {
return getGlyphsPixelBounds(renderFRC, x, y, 0, glyphs.length);
}
|
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());
}
|
int[] getValidatedGlyphs(int[] oglyphs) {
int len = oglyphs.length;
int[] vglyphs = new int[len];
for (int i=0; i< len; i++) {
if (oglyphs[i] == 0xFFFE || oglyphs[i] == 0xFFFF) {
vglyphs[i] = oglyphs[i];
} else {
vglyphs[i] = font2D.getValidatedGlyphCode(oglyphs[i]);
}
}
return vglyphs;
}
|
public Rectangle2D getVisualBounds() {
if (glyphs.length == 0) {
return new Rectangle2D.Float(0, 0, 0, 0);
}
Rectangle2D result = getGlyphVisualBounds(0).getBounds2D();
for (int i = 1; i < glyphs.length; ++i) {
Rectangle2D.union(result, getGlyphVisualBounds(i).getBounds2D(), result);
}
return result;
}
|
public int hashCode() {
return font.hashCode() ^ glyphs.length;
}
As a concrete subclass of Object that implements equality, this must
implement hashCode. |
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();
}
|
boolean needsPositions(double[] devTX) {
return gti != null ||
(getLayoutFlags() & FLAG_HAS_POSITION_ADJUSTMENTS) != 0 ||
!matchTX(devTX, frctx);
}
|
public void performDefaultLayout() {
positions = null;
if (getTracking(font) == 0) {
clearFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
}
}
|
public void pixellate(FontRenderContext renderFRC,
Point2D loc,
Point pxResult) {
if (renderFRC == null) {
renderFRC = frc;
}
// it is a total pain that you have to copy the transform.
AffineTransform at = renderFRC.getTransform();
at.transform(loc, loc);
pxResult.x = (int)loc.getX(); // but must not behave oddly around zero
pxResult.y = (int)loc.getY();
loc.setLocation(pxResult.x, pxResult.y);
try {
at.inverseTransform(loc, loc);
}
catch (NoninvertibleTransformException e) {
throw new IllegalArgumentException("must be able to invert frc transform");
}
}
!!! not used currently, but might be by getPixelbounds? |
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 void setGlyphPositions(float[] srcPositions) {
int requiredLength = glyphs.length * 2 + 2;
if (srcPositions.length != requiredLength) {
throw new IllegalArgumentException("srcPositions.length != " + requiredLength);
}
positions = (float[])srcPositions.clone();
clearCaches();
addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
}
Set all the glyph positions, including the 'after last glyph' position.
The srcPositions array must be of length (numGlyphs + 1) * 2. |
public void setGlyphPositions(float[] srcPositions,
int srcStart,
int start,
int count) {
if (count < 0) {
throw new IllegalArgumentException("count = " + count);
}
initPositions();
for (int i = start * 2, e = i + count * 2, p = srcStart; i < e; ++i, ++p) {
positions[i] = srcPositions[p];
}
clearCaches();
addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
}
|
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 void setGlyphTransforms(AffineTransform[] srcTransforms) {
setGlyphTransforms(srcTransforms, 0, 0, glyphs.length);
}
Convenience overload of setGlyphTransforms(AffineTransform[], int, int, int). |
public void setGlyphTransforms(AffineTransform[] srcTransforms,
int srcStart,
int start,
int count) {
for (int i = start, e = start + count; i < e; ++i) {
setGlyphTransform(i, srcTransforms[srcStart + i]);
}
}
Set a number of glyph transforms.
Original transforms are unchanged. The array may contain nulls, and also may
contain multiple references to the same transform instance. |
Object setupGlyphImages(long[] images,
float[] positions,
double[] devTX) {
initPositions(); // FIRST ensure we have positions based on our frctx
setRenderTransform(devTX); // THEN make sure we are using the desired devTX
if (gti != null) {
return gti.setupGlyphImages(images, positions, dtx);
}
GlyphStrike gs = getDefaultStrike();
gs.strike.getGlyphImagePtrs(glyphs, images, glyphs.length);
if (positions != null) {
if (dtx.isIdentity()) {
System.arraycopy(this.positions, 0, positions, 0, glyphs.length * 2);
} else {
dtx.transform(this.positions, 0, positions, 0, glyphs.length);
}
}
return gs;
}
|
public String toString() {
return appendString(null).toString();
}
|