136 public class StandardGlyphVector extends GlyphVector {
137 private Font font;
138 private FontRenderContext frc;
139 private int[] glyphs; // always
140 private int[] userGlyphs; // used to return glyphs to the client.
141 private float[] positions; // only if not default advances
142 private int[] charIndices; // only if interesting
143 private int flags; // indicates whether positions, charIndices is interesting
144
145 private static final int UNINITIALIZED_FLAGS = -1;
146
147 // transforms information
148 private GlyphTransformInfo gti; // information about per-glyph transforms
149
150 // !!! can we get rid of any of this extra stuff?
151 private AffineTransform ftx; // font transform without translation
152 private AffineTransform dtx; // device transform used for strike calculations, no translation
153 private AffineTransform invdtx; // inverse of dtx or null if dtx is identity
154 private AffineTransform frctx; // font render context transform, wish we could just share it
155 private Font2D font2D; // basic strike-independent stuff
156 private SoftReference fsref; // font strike reference for glyphs with no per-glyph transform
157
158 /////////////////////////////
159 // Constructors and Factory methods
160 /////////////////////////////
161
162 public StandardGlyphVector(Font font, String str, FontRenderContext frc) {
163 init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS);
164 }
165
166 public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) {
167 init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS);
168 }
169
170 public StandardGlyphVector(Font font, char[] text, int start, int count,
171 FontRenderContext frc) {
172 init(font, text, start, count, frc, UNINITIALIZED_FLAGS);
173 }
174
175 private float getTracking(Font font) {
176 if (font.hasLayoutAttributes()) {
509 public float[] getGlyphPositions(int start, int count, float[] result) {
510 if (count < 0) {
511 throw new IllegalArgumentException("count = " + count);
512 }
513 if (start < 0) {
514 throw new IndexOutOfBoundsException("start = " + start);
515 }
516 if (start > glyphs.length + 1 - count) { // watch for overflow
517 throw new IndexOutOfBoundsException("start + count = " + (start + count));
518 }
519
520 return internalGetGlyphPositions(start, count, 0, result);
521 }
522
523 public Shape getGlyphLogicalBounds(int ix) {
524 if (ix < 0 || ix >= glyphs.length) {
525 throw new IndexOutOfBoundsException("ix = " + ix);
526 }
527
528 Shape[] lbcache;
529 if (lbcacheRef == null || (lbcache = (Shape[])lbcacheRef.get()) == null) {
530 lbcache = new Shape[glyphs.length];
531 lbcacheRef = new SoftReference(lbcache);
532 }
533
534 Shape result = lbcache[ix];
535 if (result == null) {
536 setFRCTX();
537 initPositions();
538
539 // !!! ought to return a rectangle2d for simple cases, though the following works for all
540
541 // get the position, the tx offset, and the x,y advance and x,y adl. The
542 // shape is the box formed by adv (width) and adl (height) offset by
543 // the position plus the tx offset minus the ascent.
544
545 ADL adl = new ADL();
546 GlyphStrike gs = getGlyphStrike(ix);
547 gs.getADL(adl);
548
549 Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]);
550
551 float wx = adv.x;
552 float wy = adv.y;
553 float hx = adl.descentX + adl.leadingX + adl.ascentX;
554 float hy = adl.descentY + adl.leadingY + adl.ascentY;
555 float x = positions[ix*2] + gs.dx - adl.ascentX;
556 float y = positions[ix*2+1] + gs.dy - adl.ascentY;
557
558 GeneralPath gp = new GeneralPath();
559 gp.moveTo(x, y);
560 gp.lineTo(x + wx, y + wy);
561 gp.lineTo(x + wx + hx, y + wy + hy);
562 gp.lineTo(x + hx, y + hy);
563 gp.closePath();
564
565 result = new DelegatingShape(gp);
566 lbcache[ix] = result;
567 }
568
569 return result;
570 }
571 private SoftReference lbcacheRef;
572
573 public Shape getGlyphVisualBounds(int ix) {
574 if (ix < 0 || ix >= glyphs.length) {
575 throw new IndexOutOfBoundsException("ix = " + ix);
576 }
577
578 Shape[] vbcache;
579 if (vbcacheRef == null || (vbcache = (Shape[])vbcacheRef.get()) == null) {
580 vbcache = new Shape[glyphs.length];
581 vbcacheRef = new SoftReference(vbcache);
582 }
583
584 Shape result = vbcache[ix];
585 if (result == null) {
586 result = new DelegatingShape(getGlyphOutlineBounds(ix));
587 vbcache[ix] = result;
588 }
589
590 return result;
591 }
592 private SoftReference vbcacheRef;
593
594 public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) {
595 return getGlyphsPixelBounds(renderFRC, x, y, index, 1);
596 }
597
598 public GlyphMetrics getGlyphMetrics(int ix) {
599 if (ix < 0 || ix >= glyphs.length) {
600 throw new IndexOutOfBoundsException("ix = " + ix);
601 }
602
603 Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D();
604 Point2D pt = getGlyphPosition(ix);
605 vb.setRect(vb.getMinX() - pt.getX(),
606 vb.getMinY() - pt.getY(),
607 vb.getWidth(),
608 vb.getHeight());
609 Point2D.Float adv =
610 getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]);
611 GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y,
612 vb,
1213 Point2D.Float pt = new Point.Float();
1214 int n = start * 2;
1215 while (--count >= 0) {
1216 pt.x = x + positions[n++];
1217 pt.y = y + positions[n++];
1218 tx.transform(pt, pt);
1219 fs.getGlyphImageBounds(glyphs[start++], pt, r);
1220 if (!r.isEmpty()) {
1221 if (result == null) {
1222 result = new Rectangle(r);
1223 } else {
1224 result.add(r);
1225 }
1226 }
1227 }
1228 return result != null ? result : r;
1229 }
1230
1231 private void clearCaches(int ix) {
1232 if (lbcacheRef != null) {
1233 Shape[] lbcache = (Shape[])lbcacheRef.get();
1234 if (lbcache != null) {
1235 lbcache[ix] = null;
1236 }
1237 }
1238
1239 if (vbcacheRef != null) {
1240 Shape[] vbcache = (Shape[])vbcacheRef.get();
1241 if (vbcache != null) {
1242 vbcache[ix] = null;
1243 }
1244 }
1245 }
1246
1247 private void clearCaches() {
1248 lbcacheRef = null;
1249 vbcacheRef = null;
1250 }
1251
1252 // internal use only for possible future extension
1253
1254 /**
1255 * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses
1256 * a vertical baseline.
1257 */
1258 public static final int FLAG_USES_VERTICAL_BASELINE = 128;
1259
1260 /**
1340 */
1341 private void clearFlags(int clearedFlags) {
1342 flags = getLayoutFlags() & ~clearedFlags;
1343 }
1344
1345 // general utility methods
1346
1347 // encapsulate the test to check whether we have per-glyph transforms
1348 private GlyphStrike getGlyphStrike(int ix) {
1349 if (gti == null) {
1350 return getDefaultStrike();
1351 } else {
1352 return gti.getStrike(ix);
1353 }
1354 }
1355
1356 // encapsulate access to cached default glyph strike
1357 private GlyphStrike getDefaultStrike() {
1358 GlyphStrike gs = null;
1359 if (fsref != null) {
1360 gs = (GlyphStrike)fsref.get();
1361 }
1362 if (gs == null) {
1363 gs = GlyphStrike.create(this, dtx, null);
1364 fsref = new SoftReference(gs);
1365 }
1366 return gs;
1367 }
1368
1369
1370 /////////////////////
1371 // Internal utility classes
1372 /////////////////////
1373
1374 // !!! I have this as a separate class instead of just inside SGV,
1375 // but I previously didn't bother. Now I'm trying this again.
1376 // Probably still not worth it, but I'd like to keep sgv's small in the common case.
1377
1378 static final class GlyphTransformInfo {
1379 StandardGlyphVector sgv; // reference back to glyph vector - yuck
1380 int[] indices; // index into unique strikes
1381 double[] transforms; // six doubles per unique transform, because AT is a pain to manipulate
1382 SoftReference strikesRef; // ref to unique strikes, one per transform
1383 boolean haveAllStrikes; // true if the strike array has been filled by getStrikes().
1384
1385 // used when first setting a transform
1386 GlyphTransformInfo(StandardGlyphVector sgv) {
1387 this.sgv = sgv;
1388 }
1389
1390 // used when cloning a glyph vector, need to set back link
1391 GlyphTransformInfo(StandardGlyphVector sgv, GlyphTransformInfo rhs) {
1392 this.sgv = sgv;
1393
1394 this.indices = rhs.indices == null ? null : rhs.indices.clone();
1395 this.transforms = rhs.transforms == null ? null : rhs.transforms.clone();
1396 this.strikesRef = null; // can't share cache, so rather than clone, we just null out
1397 }
1398
1399 // used in sgv equality
1400 public boolean equals(GlyphTransformInfo rhs) {
1401 if (rhs == null) {
1402 return false;
1636
1637 private GlyphStrike[] getAllStrikes() {
1638 if (indices == null) {
1639 return null;
1640 }
1641
1642 GlyphStrike[] strikes = getStrikeArray();
1643 if (!haveAllStrikes) {
1644 for (int i = 0; i < strikes.length; ++i) {
1645 getStrikeAtIndex(strikes, i);
1646 }
1647 haveAllStrikes = true;
1648 }
1649
1650 return strikes;
1651 }
1652
1653 private GlyphStrike[] getStrikeArray() {
1654 GlyphStrike[] strikes = null;
1655 if (strikesRef != null) {
1656 strikes = (GlyphStrike[])strikesRef.get();
1657 }
1658 if (strikes == null) {
1659 haveAllStrikes = false;
1660 strikes = new GlyphStrike[transformCount() + 1];
1661 strikesRef = new SoftReference(strikes);
1662 }
1663
1664 return strikes;
1665 }
1666
1667 private GlyphStrike getStrikeAtIndex(GlyphStrike[] strikes, int strikeIndex) {
1668 GlyphStrike strike = strikes[strikeIndex];
1669 if (strike == null) {
1670 if (strikeIndex == 0) {
1671 strike = sgv.getDefaultStrike();
1672 } else {
1673 int ix = (strikeIndex - 1) * 6;
1674 AffineTransform gtx = new AffineTransform(transforms[ix],
1675 transforms[ix+1],
1676 transforms[ix+2],
1677 transforms[ix+3],
1678 transforms[ix+4],
1679 transforms[ix+5]);
1680
1681 strike = GlyphStrike.create(sgv, sgv.dtx, gtx);
|
136 public class StandardGlyphVector extends GlyphVector {
137 private Font font;
138 private FontRenderContext frc;
139 private int[] glyphs; // always
140 private int[] userGlyphs; // used to return glyphs to the client.
141 private float[] positions; // only if not default advances
142 private int[] charIndices; // only if interesting
143 private int flags; // indicates whether positions, charIndices is interesting
144
145 private static final int UNINITIALIZED_FLAGS = -1;
146
147 // transforms information
148 private GlyphTransformInfo gti; // information about per-glyph transforms
149
150 // !!! can we get rid of any of this extra stuff?
151 private AffineTransform ftx; // font transform without translation
152 private AffineTransform dtx; // device transform used for strike calculations, no translation
153 private AffineTransform invdtx; // inverse of dtx or null if dtx is identity
154 private AffineTransform frctx; // font render context transform, wish we could just share it
155 private Font2D font2D; // basic strike-independent stuff
156 private SoftReference<GlyphStrike> fsref; // font strike reference for glyphs with no per-glyph transform
157
158 /////////////////////////////
159 // Constructors and Factory methods
160 /////////////////////////////
161
162 public StandardGlyphVector(Font font, String str, FontRenderContext frc) {
163 init(font, str.toCharArray(), 0, str.length(), frc, UNINITIALIZED_FLAGS);
164 }
165
166 public StandardGlyphVector(Font font, char[] text, FontRenderContext frc) {
167 init(font, text, 0, text.length, frc, UNINITIALIZED_FLAGS);
168 }
169
170 public StandardGlyphVector(Font font, char[] text, int start, int count,
171 FontRenderContext frc) {
172 init(font, text, start, count, frc, UNINITIALIZED_FLAGS);
173 }
174
175 private float getTracking(Font font) {
176 if (font.hasLayoutAttributes()) {
509 public float[] getGlyphPositions(int start, int count, float[] result) {
510 if (count < 0) {
511 throw new IllegalArgumentException("count = " + count);
512 }
513 if (start < 0) {
514 throw new IndexOutOfBoundsException("start = " + start);
515 }
516 if (start > glyphs.length + 1 - count) { // watch for overflow
517 throw new IndexOutOfBoundsException("start + count = " + (start + count));
518 }
519
520 return internalGetGlyphPositions(start, count, 0, result);
521 }
522
523 public Shape getGlyphLogicalBounds(int ix) {
524 if (ix < 0 || ix >= glyphs.length) {
525 throw new IndexOutOfBoundsException("ix = " + ix);
526 }
527
528 Shape[] lbcache;
529 if (lbcacheRef == null || (lbcache = lbcacheRef.get()) == null) {
530 lbcache = new Shape[glyphs.length];
531 lbcacheRef = new SoftReference<>(lbcache);
532 }
533
534 Shape result = lbcache[ix];
535 if (result == null) {
536 setFRCTX();
537 initPositions();
538
539 // !!! ought to return a rectangle2d for simple cases, though the following works for all
540
541 // get the position, the tx offset, and the x,y advance and x,y adl. The
542 // shape is the box formed by adv (width) and adl (height) offset by
543 // the position plus the tx offset minus the ascent.
544
545 ADL adl = new ADL();
546 GlyphStrike gs = getGlyphStrike(ix);
547 gs.getADL(adl);
548
549 Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]);
550
551 float wx = adv.x;
552 float wy = adv.y;
553 float hx = adl.descentX + adl.leadingX + adl.ascentX;
554 float hy = adl.descentY + adl.leadingY + adl.ascentY;
555 float x = positions[ix*2] + gs.dx - adl.ascentX;
556 float y = positions[ix*2+1] + gs.dy - adl.ascentY;
557
558 GeneralPath gp = new GeneralPath();
559 gp.moveTo(x, y);
560 gp.lineTo(x + wx, y + wy);
561 gp.lineTo(x + wx + hx, y + wy + hy);
562 gp.lineTo(x + hx, y + hy);
563 gp.closePath();
564
565 result = new DelegatingShape(gp);
566 lbcache[ix] = result;
567 }
568
569 return result;
570 }
571 private SoftReference<Shape[]> lbcacheRef;
572
573 public Shape getGlyphVisualBounds(int ix) {
574 if (ix < 0 || ix >= glyphs.length) {
575 throw new IndexOutOfBoundsException("ix = " + ix);
576 }
577
578 Shape[] vbcache;
579 if (vbcacheRef == null || (vbcache = vbcacheRef.get()) == null) {
580 vbcache = new Shape[glyphs.length];
581 vbcacheRef = new SoftReference<>(vbcache);
582 }
583
584 Shape result = vbcache[ix];
585 if (result == null) {
586 result = new DelegatingShape(getGlyphOutlineBounds(ix));
587 vbcache[ix] = result;
588 }
589
590 return result;
591 }
592 private SoftReference<Shape[]> vbcacheRef;
593
594 public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) {
595 return getGlyphsPixelBounds(renderFRC, x, y, index, 1);
596 }
597
598 public GlyphMetrics getGlyphMetrics(int ix) {
599 if (ix < 0 || ix >= glyphs.length) {
600 throw new IndexOutOfBoundsException("ix = " + ix);
601 }
602
603 Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D();
604 Point2D pt = getGlyphPosition(ix);
605 vb.setRect(vb.getMinX() - pt.getX(),
606 vb.getMinY() - pt.getY(),
607 vb.getWidth(),
608 vb.getHeight());
609 Point2D.Float adv =
610 getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]);
611 GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y,
612 vb,
1213 Point2D.Float pt = new Point.Float();
1214 int n = start * 2;
1215 while (--count >= 0) {
1216 pt.x = x + positions[n++];
1217 pt.y = y + positions[n++];
1218 tx.transform(pt, pt);
1219 fs.getGlyphImageBounds(glyphs[start++], pt, r);
1220 if (!r.isEmpty()) {
1221 if (result == null) {
1222 result = new Rectangle(r);
1223 } else {
1224 result.add(r);
1225 }
1226 }
1227 }
1228 return result != null ? result : r;
1229 }
1230
1231 private void clearCaches(int ix) {
1232 if (lbcacheRef != null) {
1233 Shape[] lbcache = lbcacheRef.get();
1234 if (lbcache != null) {
1235 lbcache[ix] = null;
1236 }
1237 }
1238
1239 if (vbcacheRef != null) {
1240 Shape[] vbcache = vbcacheRef.get();
1241 if (vbcache != null) {
1242 vbcache[ix] = null;
1243 }
1244 }
1245 }
1246
1247 private void clearCaches() {
1248 lbcacheRef = null;
1249 vbcacheRef = null;
1250 }
1251
1252 // internal use only for possible future extension
1253
1254 /**
1255 * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses
1256 * a vertical baseline.
1257 */
1258 public static final int FLAG_USES_VERTICAL_BASELINE = 128;
1259
1260 /**
1340 */
1341 private void clearFlags(int clearedFlags) {
1342 flags = getLayoutFlags() & ~clearedFlags;
1343 }
1344
1345 // general utility methods
1346
1347 // encapsulate the test to check whether we have per-glyph transforms
1348 private GlyphStrike getGlyphStrike(int ix) {
1349 if (gti == null) {
1350 return getDefaultStrike();
1351 } else {
1352 return gti.getStrike(ix);
1353 }
1354 }
1355
1356 // encapsulate access to cached default glyph strike
1357 private GlyphStrike getDefaultStrike() {
1358 GlyphStrike gs = null;
1359 if (fsref != null) {
1360 gs = fsref.get();
1361 }
1362 if (gs == null) {
1363 gs = GlyphStrike.create(this, dtx, null);
1364 fsref = new SoftReference<>(gs);
1365 }
1366 return gs;
1367 }
1368
1369
1370 /////////////////////
1371 // Internal utility classes
1372 /////////////////////
1373
1374 // !!! I have this as a separate class instead of just inside SGV,
1375 // but I previously didn't bother. Now I'm trying this again.
1376 // Probably still not worth it, but I'd like to keep sgv's small in the common case.
1377
1378 static final class GlyphTransformInfo {
1379 StandardGlyphVector sgv; // reference back to glyph vector - yuck
1380 int[] indices; // index into unique strikes
1381 double[] transforms; // six doubles per unique transform, because AT is a pain to manipulate
1382 SoftReference<GlyphStrike[]> strikesRef; // ref to unique strikes, one per transform
1383 boolean haveAllStrikes; // true if the strike array has been filled by getStrikes().
1384
1385 // used when first setting a transform
1386 GlyphTransformInfo(StandardGlyphVector sgv) {
1387 this.sgv = sgv;
1388 }
1389
1390 // used when cloning a glyph vector, need to set back link
1391 GlyphTransformInfo(StandardGlyphVector sgv, GlyphTransformInfo rhs) {
1392 this.sgv = sgv;
1393
1394 this.indices = rhs.indices == null ? null : rhs.indices.clone();
1395 this.transforms = rhs.transforms == null ? null : rhs.transforms.clone();
1396 this.strikesRef = null; // can't share cache, so rather than clone, we just null out
1397 }
1398
1399 // used in sgv equality
1400 public boolean equals(GlyphTransformInfo rhs) {
1401 if (rhs == null) {
1402 return false;
1636
1637 private GlyphStrike[] getAllStrikes() {
1638 if (indices == null) {
1639 return null;
1640 }
1641
1642 GlyphStrike[] strikes = getStrikeArray();
1643 if (!haveAllStrikes) {
1644 for (int i = 0; i < strikes.length; ++i) {
1645 getStrikeAtIndex(strikes, i);
1646 }
1647 haveAllStrikes = true;
1648 }
1649
1650 return strikes;
1651 }
1652
1653 private GlyphStrike[] getStrikeArray() {
1654 GlyphStrike[] strikes = null;
1655 if (strikesRef != null) {
1656 strikes = strikesRef.get();
1657 }
1658 if (strikes == null) {
1659 haveAllStrikes = false;
1660 strikes = new GlyphStrike[transformCount() + 1];
1661 strikesRef = new SoftReference<>(strikes);
1662 }
1663
1664 return strikes;
1665 }
1666
1667 private GlyphStrike getStrikeAtIndex(GlyphStrike[] strikes, int strikeIndex) {
1668 GlyphStrike strike = strikes[strikeIndex];
1669 if (strike == null) {
1670 if (strikeIndex == 0) {
1671 strike = sgv.getDefaultStrike();
1672 } else {
1673 int ix = (strikeIndex - 1) * 6;
1674 AffineTransform gtx = new AffineTransform(transforms[ix],
1675 transforms[ix+1],
1676 transforms[ix+2],
1677 transforms[ix+3],
1678 transforms[ix+4],
1679 transforms[ix+5]);
1680
1681 strike = GlyphStrike.create(sgv, sgv.dtx, gtx);
|