aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/graph/font
diff options
context:
space:
mode:
authorSven Gothel <sgothel@jausoft.com>2023-02-10 14:20:35 +0100
committerSven Gothel <sgothel@jausoft.com>2023-02-10 14:20:35 +0100
commitfeb8de27f848b5213423389cf0e19cbd88095682 (patch)
treeec7051b6fec0a78a4f78b1958a3f7999a063c1d9 /src/jogl/classes/jogamp/graph/font
parentbc951476c67282d9676f33ee25fb0f697a4dbe45 (diff)
Font/Graph, {Font, Glyph}/Typecast: Add kerning and expose values in original font-units (FU) to have them scaled later ( fu * pixelScale / unitsPerEM )
Scaling from font-units (funits, or FU) is now performed by the renderer itself, i.e. applying the scale-factor 'fontPixelSize / font.getMetrics().getUnitsPerEM()' to the PMV matrix to render the whole graph GLRegion. This finally provides proper device and resolution independent font utilization. Further, preliminary kerning has been added. +++ Also ... TypecastFont: - getGlyphID(..) getGlyph(..) enforce symbol mapping to Glyph.ID_SPACE Glyph.ID_CR, as some fonts ave an erroneous cmap (FreeSerif-Regular) - add getKerning(..) TODO: Add binary search - Set TypecastFont.USE_PRESCALED_ADVANCE := false, i.e. dropping all prescaled pixel-sized advance values mapped to font pixel-size as we utilize font-units only for later uniform scaling. - Drop virtual getPixelSize() and add static FontScale.toPixels(..) - Add fullString() for debugging purposed, including some font tables
Diffstat (limited to 'src/jogl/classes/jogamp/graph/font')
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java362
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java185
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java55
3 files changed, 466 insertions, 136 deletions
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
index b3c1885d4..885261bf9 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
@@ -34,6 +34,10 @@ import jogamp.graph.font.typecast.ot.table.CmapIndexEntry;
import jogamp.graph.font.typecast.ot.table.CmapTable;
import jogamp.graph.font.typecast.ot.table.HdmxTable;
import jogamp.graph.font.typecast.ot.table.ID;
+import jogamp.graph.font.typecast.ot.table.KernSubtableFormat0;
+import jogamp.graph.font.typecast.ot.table.KernTable;
+import jogamp.graph.font.typecast.ot.table.KerningPair;
+import jogamp.graph.font.typecast.ot.table.PostTable;
import com.jogamp.common.util.IntObjectHashMap;
import com.jogamp.graph.curve.OutlineShape;
@@ -45,6 +49,7 @@ import com.jogamp.graph.geom.plane.AffineTransform;
import com.jogamp.opengl.math.geom.AABBox;
class TypecastFont implements Font {
+ static final boolean USE_PRESCALED_ADVANCE = false;
static final boolean DEBUG = false;
private static final Vertex.Factory<SVertex> vertexFactory = SVertex.factory();
@@ -54,7 +59,6 @@ class TypecastFont implements Font {
private final int cmapentries;
private final IntObjectHashMap char2Glyph;
private final TypecastHMetrics metrics;
- private final float[] tmpV3 = new float[3];
// FIXME: Add cache size to limit memory usage ??
public TypecastFont(final OTFontCollection fontset) {
@@ -169,7 +173,15 @@ class TypecastFont implements Font {
@Override
public float getAdvanceWidth(final int glyphID, final float pixelSize) {
- return font.getHmtxTable().getAdvanceWidth(glyphID) * metrics.getScale(pixelSize);
+ return pixelSize * metrics.getScale( font.getHmtxTable().getAdvanceWidth(glyphID) );
+ }
+ @Override
+ public float getAdvanceWidth(final int glyphID) {
+ return metrics.getScale( font.getHmtxTable().getAdvanceWidth(glyphID) );
+ }
+ @Override
+ public int getAdvanceWidthFU(final int glyphID) {
+ return font.getHmtxTable().getAdvanceWidth(glyphID);
}
@Override
@@ -178,46 +190,104 @@ class TypecastFont implements Font {
}
@Override
+ public final float getKerning(final int left_glyphid, final int right_glyphid) {
+ return metrics.getScale( getKerningFU(left_glyphid, right_glyphid) );
+ }
+
+ @Override
+ public int getKerningFU(final int left_glyphid, final int right_glyphid) {
+ if( 0 == left_glyphid || 0 == right_glyphid ) {
+ return 0;
+ }
+ final KernTable kern = font.getKernTable();
+ if (kern != null) {
+ final int kernSubtableCount = kern.getSubtableCount();
+ if( 0 < kernSubtableCount ) {
+ final KernSubtableFormat0 kst0 = kern.getSubtable0();
+ if( null != kst0 && kst0.areKerningValues() && kst0.isHorizontal() && !kst0.isCrossstream() ) {
+ for (int i = 0; i < kst0.getKerningPairCount(); i++) {
+ final KerningPair kpair = kst0.getKerningPair(i);
+ if( kpair.getLeft() == left_glyphid && kpair.getRight() == right_glyphid ) {
+ return kpair.getValue();
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int getGlyphID(final char symbol) {
+ // enforce mapping as some fonts have an erroneous cmap (FreeSerif-Regular)
+ switch(symbol) {
+ case ' ': return Glyph.ID_SPACE;
+ case '\n': return Glyph.ID_CR;
+ }
+ final int glyphID = cmapFormat.mapCharCode(symbol);
+ if( 0 != glyphID ) {
+ return glyphID;
+ }
+ return Glyph.ID_UNKNOWN;
+ }
+
+ /** pp **/ PostTable getPostTable() {
+ return font.getPostTable();
+ }
+
+ @Override
public Glyph getGlyph(final char symbol) {
TypecastGlyph result = (TypecastGlyph) char2Glyph.get(symbol);
if (null == result) {
- // final short code = (short) char2Code.get(symbol);
- short code = (short) cmapFormat.mapCharCode(symbol);
- if(0 == code && 0 != symbol) {
- // reserved special glyph IDs by convention
- switch(symbol) {
- case ' ': code = Glyph.ID_SPACE; break;
- case '\n': code = Glyph.ID_CR; break;
- default: code = Glyph.ID_UNKNOWN;
- }
- }
+ final int glyph_id = getGlyphID( symbol );
- jogamp.graph.font.typecast.ot.OTGlyph glyph = font.getGlyph(code);
+ jogamp.graph.font.typecast.ot.OTGlyph glyph = font.getGlyph(glyph_id);
+ final int glyph_advance;
+ final AABBox glyph_bbox;
if(null == glyph) {
+ // fallback
glyph = font.getGlyph(Glyph.ID_UNKNOWN);
}
+ switch( glyph_id ) {
+ case Glyph.ID_SPACE:
+ /** fallthrough */
+ case Glyph.ID_CR:
+ glyph_advance = getAdvanceWidthFU(glyph_id);
+ glyph_bbox = new AABBox(0, 0, 0, glyph_advance, getLineHeightFU(), 0);
+ break;
+ default:
+ glyph_advance = glyph.getAdvanceWidth();
+ glyph_bbox = glyph.getBBox();
+ break;
+ }
if(null == glyph) {
- throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+code);
+ throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+glyph_id);
}
final OutlineShape shape = TypecastRenderer.buildShape(symbol, glyph, vertexFactory);
- result = new TypecastGlyph(this, symbol, code, glyph.getBBox(), glyph.getAdvanceWidth(), shape);
+ result = new TypecastGlyph(this, symbol, glyph_id, glyph_bbox, glyph_advance, shape);
if(DEBUG) {
- System.err.println("New glyph: " + (int)symbol + " ( " + symbol +" ) -> " + code + ", contours " + glyph.getPointCount() + ": " + shape);
+ final PostTable post = font.getPostTable();
+ final String glyph_name = null != post ? post.getGlyphName(glyph_id) : "n/a";
+ System.err.println("New glyph: " + (int)symbol + " ( " + symbol +" ) -> " + glyph_id + "/'"+glyph_name+"', contours " + glyph.getPointCount() + ": " + shape);
+ System.err.println(" "+glyph);
+ System.err.println(" "+result);
}
glyph.clearPointData();
- final HdmxTable hdmx = font.getHdmxTable();
- if (null!= result && null != hdmx) {
- /*if(DEBUG) {
- System.err.println("hdmx "+hdmx);
- }*/
- for (int i=0; i<hdmx.getNumberOfRecords(); i++)
- {
- final HdmxTable.DeviceRecord dr = hdmx.getRecord(i);
- result.addAdvance(dr.getWidth(code), dr.getPixelSize());
- /* if(DEBUG) {
- System.err.println("hdmx advance : pixelsize = "+dr.getWidth(code)+" : "+ dr.getPixelSize());
- } */
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ final HdmxTable hdmx = font.getHdmxTable();
+ if (null!= result && null != hdmx) {
+ /*if(DEBUG) {
+ System.err.println("hdmx "+hdmx);
+ }*/
+ for (int i=0; i<hdmx.getNumberOfRecords(); i++)
+ {
+ final HdmxTable.DeviceRecord dr = hdmx.getRecord(i);
+ if(DEBUG) {
+ System.err.println("hdmx advance : pixelsize "+dr.getPixelSize()+" -> advance "+dr.getWidth(glyph_id));
+ }
+ result.addAdvance(dr.getPixelSize(), dr.getWidth(glyph_id));
+ }
}
}
char2Glyph.put(symbol, result);
@@ -226,23 +296,53 @@ class TypecastFont implements Font {
}
@Override
- public final float getPixelSize(final float fontSize /* points per inch */, final float resolution) {
- return fontSize * resolution / ( 72f /* points per inch */ );
+ public float getLineHeight(final float pixelSize) {
+ return pixelSize * metrics.getScale( getLineHeightFU() );
}
@Override
- public float getLineHeight(final float pixelSize) {
+ public float getLineHeight() {
+ return metrics.getScale( getLineHeightFU() );
+ }
+
+ @Override
+ public int getLineHeightFU() {
final Metrics metrics = getMetrics();
- final float lineGap = metrics.getLineGap(pixelSize) ; // negative value!
- final float ascent = metrics.getAscent(pixelSize) ; // negative value!
- final float descent = metrics.getDescent(pixelSize) ; // positive value!
- final float advanceY = lineGap - descent + ascent; // negative value!
+ final int lineGap = metrics.getLineGapFU() ; // negative value!
+ final int ascent = metrics.getAscentFU() ; // negative value!
+ final int descent = metrics.getDescentFU() ; // positive value!
+ final int advanceY = lineGap - descent + ascent; // negative value!
return -advanceY;
}
@Override
public float getMetricWidth(final CharSequence string, final float pixelSize) {
- float width = 0;
+ if( !TypecastFont.USE_PRESCALED_ADVANCE ) {
+ return pixelSize * getMetricWidth(string);
+ } else {
+ float width = 0;
+ final int len = string.length();
+ for (int i=0; i< len; i++) {
+ final char character = string.charAt(i);
+ if (character == '\n') {
+ width = 0;
+ } else {
+ final Glyph glyph = getGlyph(character);
+ width += glyph.getAdvance(pixelSize); // uses pixelSize mapping, different than glyph.getAdvanceFU()
+ }
+ }
+ return width;
+ }
+ }
+
+ @Override
+ public float getMetricWidth(final CharSequence string) {
+ return metrics.getScale( getMetricWidthFU(string) );
+ }
+
+ @Override
+ public int getMetricWidthFU(final CharSequence string) {
+ int width = 0;
final int len = string.length();
for (int i=0; i< len; i++) {
final char character = string.charAt(i);
@@ -250,22 +350,31 @@ class TypecastFont implements Font {
width = 0;
} else {
final Glyph glyph = getGlyph(character);
- width += glyph.getAdvance(pixelSize, false);
+ width += glyph.getAdvanceFU();
}
}
- return (int)(width + 0.5f);
+ return width;
+ }
+
+ @Override
+ public float getMetricHeight(final CharSequence string, final float pixelSize) {
+ return pixelSize * getMetricHeight(string);
+ }
+
+ @Override
+ public float getMetricHeight(final CharSequence string) {
+ return metrics.getScale( getMetricHeightFU(string) );
}
@Override
- public float getMetricHeight(final CharSequence string, final float pixelSize, final AABBox tmp) {
+ public int getMetricHeightFU(final CharSequence string) {
int height = 0;
for (int i=0; i<string.length(); i++) {
final char character = string.charAt(i);
if (character != ' ') {
final Glyph glyph = getGlyph(character);
- final AABBox bbox = glyph.getBBox(tmp, pixelSize, tmpV3);
- height = (int)Math.ceil(Math.max(bbox.getHeight(), height));
+ height = (int)Math.ceil(Math.max(glyph.getBBoxFU().getHeight(), height));
}
}
return height;
@@ -273,14 +382,51 @@ class TypecastFont implements Font {
@Override
public AABBox getMetricBounds(final CharSequence string, final float pixelSize) {
+ if( !TypecastFont.USE_PRESCALED_ADVANCE ) {
+ return getMetricBoundsFU(string).scale(pixelSize/metrics.getUnitsPerEM(), new float[3]);
+ } else {
+ if (string == null) {
+ return new AABBox();
+ }
+ final int charCount = string.length();
+ final float lineHeight = getLineHeight(pixelSize);
+ float totalHeight = 0;
+ float totalWidth = 0;
+ float curLineWidth = 0;
+ for (int i=0; i<charCount; i++) {
+ final char character = string.charAt(i);
+ if (character == '\n') {
+ totalWidth = Math.max(curLineWidth, totalWidth);
+ curLineWidth = 0;
+ totalHeight += lineHeight;
+ continue;
+ }
+ final Glyph glyph = getGlyph(character);
+ curLineWidth += glyph.getAdvance(pixelSize); // uses pixelSize mapping, different than glyph.getAdvanceFU()
+ }
+ if (curLineWidth > 0) {
+ totalHeight += lineHeight;
+ totalWidth = Math.max(curLineWidth, totalWidth);
+ }
+ return new AABBox(0, 0, 0, totalWidth, totalHeight,0);
+ }
+ }
+
+ @Override
+ public AABBox getMetricBounds(final CharSequence string) {
+ return getMetricBoundsFU(string).scale(1.0f/metrics.getUnitsPerEM(), new float[3]);
+ }
+
+ @Override
+ public AABBox getMetricBoundsFU(final CharSequence string) {
if (string == null) {
return new AABBox();
}
final int charCount = string.length();
- final float lineHeight = getLineHeight(pixelSize);
- float totalHeight = 0;
- float totalWidth = 0;
- float curLineWidth = 0;
+ final int lineHeight = getLineHeightFU();
+ int totalHeight = 0;
+ int totalWidth = 0;
+ int curLineWidth = 0;
for (int i=0; i<charCount; i++) {
final char character = string.charAt(i);
if (character == '\n') {
@@ -289,8 +435,7 @@ class TypecastFont implements Font {
totalHeight += lineHeight;
continue;
}
- final Glyph glyph = getGlyph(character);
- curLineWidth += glyph.getAdvance(pixelSize, true);
+ curLineWidth += getAdvanceWidthFU( getGlyphID( character ) );
}
if (curLineWidth > 0) {
totalHeight += lineHeight;
@@ -298,15 +443,72 @@ class TypecastFont implements Font {
}
return new AABBox(0, 0, 0, totalWidth, totalHeight,0);
}
+
+ @Override
+ public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize) {
+ if( !TypecastFont.USE_PRESCALED_ADVANCE ) {
+ return getPointsBoundsFU(transform, string).scale(pixelSize/metrics.getUnitsPerEM(), new float[3]);
+ } else {
+ if (string == null) {
+ return new AABBox();
+ }
+ final AffineTransform temp1 = new AffineTransform();
+ final AffineTransform temp2 = new AffineTransform();
+ final float pixelSize2 = pixelSize / metrics.getUnitsPerEM();
+ final int charCount = string.length();
+ final float lineHeight = getLineHeight(pixelSize);
+ final AABBox tbox = new AABBox();
+ final AABBox res = new AABBox();
+
+ float y = 0;
+ float advanceTotal = 0;
+
+ for(int i=0; i< charCount; i++) {
+ final char character = string.charAt(i);
+ if( '\n' == character ) {
+ y -= lineHeight;
+ advanceTotal = 0;
+ } else if (character == ' ') {
+ advanceTotal += getAdvanceWidth(Glyph.ID_SPACE, pixelSize);
+ } else {
+ // reset transform
+ if( null != transform ) {
+ temp1.setTransform(transform);
+ } else {
+ temp1.setToIdentity();
+ }
+ temp1.translate(advanceTotal, y, temp2);
+ temp1.scale(pixelSize2, pixelSize2, temp2);
+ tbox.reset();
+
+ final Font.Glyph glyph = getGlyph(character);
+ res.resize(temp1.transform(glyph.getBBoxFU(), tbox));
+
+ final OutlineShape glyphShape = glyph.getShape();
+ if( null == glyphShape ) {
+ continue;
+ }
+ advanceTotal += glyph.getAdvance(pixelSize);
+ }
+ }
+ return res;
+ }
+ }
+
@Override
- public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize,
- final AffineTransform temp1, final AffineTransform temp2) {
+ public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string) {
+ return getPointsBoundsFU(transform, string).scale(1.0f/metrics.getUnitsPerEM(), new float[3]);
+ }
+
+ @Override
+ public AABBox getPointsBoundsFU(final AffineTransform transform, final CharSequence string) {
if (string == null) {
return new AABBox();
}
+ final AffineTransform temp1 = new AffineTransform();
+ final AffineTransform temp2 = new AffineTransform();
final int charCount = string.length();
- final float lineHeight = getLineHeight(pixelSize);
- final float scale = getMetrics().getScale(pixelSize);
+ final int lineHeight = getLineHeightFU();
final AABBox tbox = new AABBox();
final AABBox res = new AABBox();
@@ -319,7 +521,7 @@ class TypecastFont implements Font {
y -= lineHeight;
advanceTotal = 0;
} else if (character == ' ') {
- advanceTotal += getAdvanceWidth(Glyph.ID_SPACE, pixelSize);
+ advanceTotal += getAdvanceWidthFU(Glyph.ID_SPACE);
} else {
// reset transform
if( null != transform ) {
@@ -328,17 +530,16 @@ class TypecastFont implements Font {
temp1.setToIdentity();
}
temp1.translate(advanceTotal, y, temp2);
- temp1.scale(scale, scale, temp2);
tbox.reset();
final Font.Glyph glyph = getGlyph(character);
- res.resize(temp1.transform(glyph.getBBox(), tbox));
+ res.resize(temp1.transform(glyph.getBBoxFU(), tbox));
final OutlineShape glyphShape = glyph.getShape();
if( null == glyphShape ) {
continue;
}
- advanceTotal += glyph.getAdvance(pixelSize, true);
+ advanceTotal += glyph.getAdvanceFU();
}
}
return res;
@@ -358,4 +559,55 @@ class TypecastFont implements Font {
public String toString() {
return getFullFamilyName(null).toString();
}
+
+ @Override
+ public String fullString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(toString()).append("[ ").append(font.toString());
+ sb.append("\n").append(font.getHeadTable());
+ sb.append("\n\n").append(font.getHheaTable());
+ if( null != font.getVheaTable() ) {
+ sb.append("\n\n").append(font.getVheaTable());
+ }
+ if( null != font.getKernTable() ) {
+ final PostTable post = font.getPostTable();
+ final KernTable kern = font.getKernTable();
+ sb.append("\n\n").append(kern);
+ final KernSubtableFormat0 ks0 = kern.getSubtable0();
+ if( null != ks0 ) {
+ final int sz = ks0.getKerningPairCount();
+ for(int i=0; i<sz; ++i) {
+ final KerningPair kp = ks0.getKerningPair(i);
+ final int left = kp.getLeft();
+ final int right = kp.getRight();
+ final String leftS;
+ final String rightS;
+ if( null == post ) {
+ leftS = String.valueOf(left);
+ rightS = String.valueOf(left);
+ } else {
+ leftS = post.getGlyphName(left)+"/"+String.valueOf(left);
+ rightS = post.getGlyphName(right)+"/"+String.valueOf(right);
+ }
+ sb.append("\n kp[").append(i).append("]: ").append(leftS).append(" -> ").append(rightS).append(" = ").append(kp.getValue());
+ }
+ }
+ }
+
+ sb.append("\n\n").append(font.getCmapTable());
+ /* if( null != font.getHdmxTable() ) {
+ sb.append("\n").append(font.getHdmxTable()); // too too long
+ } */
+ // glyf
+ // sb.append("\n").append(font.getHmtxTable()); // too long
+ /* if( null != font.getLocaTable() ) {
+ sb.append("\n").append(font.getLocaTable()); // too long
+ } */
+ sb.append("\n").append(font.getMaxpTable());
+ // sb.append("\n\n").append(font.getNameTable()); // no toString()
+ sb.append("\n\n").append(font.getOS2Table());
+ // sb.append("\n\n").append(font.getPostTable()); // too long
+ sb.append("\n]");
+ return sb.toString();
+ }
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
index 97570d605..b36196ee5 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
@@ -32,52 +32,63 @@ import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.font.Font;
import com.jogamp.opengl.math.geom.AABBox;
+import jogamp.graph.font.typecast.ot.table.PostTable;
+
public final class TypecastGlyph implements Font.Glyph {
+
+ /** Scaled hmtx value */
public static final class Advance
{
private final Font font;
- private final float advance;
- private final IntIntHashMap size2advanceI = new IntIntHashMap();
+ private final int advance; // in font-units
+ private final IntIntHashMap size2advanceI;
- public Advance(final Font font, final float advance)
+ public Advance(final Font font, final int advance)
{
this.font = font;
this.advance = advance;
- size2advanceI.setKeyNotFoundValue(0);
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ size2advanceI = new IntIntHashMap();
+ size2advanceI.setKeyNotFoundValue(0);
+ } else {
+ size2advanceI = null;
+ }
}
public final void reset() {
- size2advanceI.clear();
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ size2advanceI.clear();
+ }
}
public final Font getFont() { return font; }
- public final float getScale(final float pixelSize)
+ public final int getUnitsPerEM() { return this.font.getMetrics().getUnitsPerEM(); }
+
+ public final float getScale(final int funits)
{
- return this.font.getMetrics().getScale(pixelSize);
+ return this.font.getMetrics().getScale(funits);
}
- public final void add(final float advance, final float size)
+ public final void add(final float pixelSize, final float advance)
{
- size2advanceI.put(Float.floatToIntBits(size), Float.floatToIntBits(advance));
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ size2advanceI.put(Float.floatToIntBits(pixelSize), Float.floatToIntBits(advance));
+ }
}
- public final float get(final float pixelSize, final boolean useFrationalMetrics)
+ public final float get(final float pixelSize)
{
- final int sI = Float.floatToIntBits(pixelSize);
- final int aI = size2advanceI.get(sI);
- if( 0 != aI ) {
- return Float.intBitsToFloat(aI);
- }
- final float a;
- if ( useFrationalMetrics ) {
- a = this.advance * getScale(pixelSize);
+ if( !TypecastFont.USE_PRESCALED_ADVANCE ) {
+ return pixelSize * font.getMetrics().getScale( advance );
} else {
- // a = Math.ceil(this.advance * getScale(pixelSize));
- a = Math.round(this.advance * getScale(pixelSize)); // TODO: check whether ceil should be used instead?
+ final int sI = Float.floatToIntBits( (float) Math.ceil( pixelSize ) );
+ final int aI = size2advanceI.get(sI);
+ if( 0 != aI ) {
+ return Float.intBitsToFloat(aI);
+ }
+ return pixelSize * font.getMetrics().getScale( advance );
}
- size2advanceI.put(sI, Float.floatToIntBits(a));
- return a;
}
@Override
@@ -91,39 +102,59 @@ public final class TypecastGlyph implements Font.Glyph {
public static final class Metrics
{
- private final AABBox bbox;
- private final Advance advance;
-
- public Metrics(final Font font, final AABBox bbox, final float advance)
+ private final TypecastFont font;
+ private final AABBox bbox; // in font-units
+ private final int advance; // in font-units
+ private final Advance advance2;
+
+ /**
+ *
+ * @param font
+ * @param bbox in font-units
+ * @param advance hmtx value in font-units
+ */
+ public Metrics(final TypecastFont font, final AABBox bbox, final int advance)
{
+ this.font = font;
this.bbox = bbox;
- this.advance = new Advance(font, advance);
+ this.advance = advance;
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ this.advance2 = new Advance(font, advance);
+ } else {
+ this.advance2 = null;
+ }
}
public final void reset() {
- advance.reset();
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ advance2.reset();
+ }
}
- public final Font getFont() { return advance.getFont(); }
+ public final TypecastFont getFont() { return font; }
- public final float getScale(final float pixelSize)
- {
- return this.advance.getScale(pixelSize);
- }
+ public final int getUnitsPerEM() { return font.getMetrics().getUnitsPerEM(); }
- public final AABBox getBBox()
- {
- return this.bbox;
- }
+ public final float getScale(final int funits) { return font.getMetrics().getScale(funits); }
- public final void addAdvance(final float advance, final float size)
- {
- this.advance.add(advance, size);
+ /** in font-units */
+ public final AABBox getBBoxFU() { return this.bbox; }
+
+ /** Return advance in font units to be divided by unitsPerEM */
+ public final int getAdvanceFU() { return this.advance; }
+
+ public final void addAdvance(final float pixelSize, final float advance) {
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ this.advance2.add(pixelSize, advance);
+ }
}
- public final float getAdvance(final float pixelSize, final boolean useFrationalMetrics)
- {
- return this.advance.get(pixelSize, useFrationalMetrics);
+ public final float getAdvance(final float pixelSize) {
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ return this.advance2.get(pixelSize);
+ } else {
+ return pixelSize * font.getMetrics().getScale( advance );
+ }
}
@Override
@@ -131,7 +162,8 @@ public final class TypecastGlyph implements Font.Glyph {
{
return "\nMetrics:"+
"\n bbox: "+this.bbox+
- this.advance;
+ "\n advance: "+this.advance+
+ "\n advance2: "+this.advance2;
}
}
@@ -140,10 +172,19 @@ public final class TypecastGlyph implements Font.Glyph {
private final char symbol;
private final OutlineShape shape; // in EM units
- private final short id;
+ private final int id;
private final Metrics metrics;
- protected TypecastGlyph(final Font font, final char symbol, final short id, final AABBox bbox, final int advance, final OutlineShape shape) {
+ /**
+ *
+ * @param font
+ * @param symbol
+ * @param id
+ * @param bbox in font-units
+ * @param advance from hmtx in font-units
+ * @param shape
+ */
+ protected TypecastGlyph(final TypecastFont font, final char symbol, final int id, final AABBox bbox, final int advance, final OutlineShape shape) {
this.symbol = symbol;
this.shape = shape;
this.id = id;
@@ -160,41 +201,50 @@ public final class TypecastGlyph implements Font.Glyph {
return this.symbol;
}
- final AABBox getBBoxUnsized() {
- return this.metrics.getBBox();
+ public final Metrics getMetrics() {
+ return this.metrics;
}
@Override
- public final AABBox getBBox() {
- return this.metrics.getBBox();
+ public final int getID() {
+ return this.id;
}
- public final Metrics getMetrics() {
- return this.metrics;
+ @Override
+ public final float getScale(final int funits) {
+ return this.metrics.getScale(funits);
}
@Override
- public final short getID() {
- return this.id;
+ public final AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3) {
+ return dest.copy(metrics.getBBoxFU()).scale(pixelSize/metrics.getUnitsPerEM(), tmpV3);
}
@Override
- public final float getScale(final float pixelSize) {
- return this.metrics.getScale(pixelSize);
+ public final AABBox getBBoxFU(final AABBox dest) {
+ return dest.copy(metrics.getBBoxFU());
}
@Override
- public final AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3) {
- return dest.copy(getBBox()).scale(getScale(pixelSize), tmpV3);
+ public final AABBox getBBoxFU() {
+ return metrics.getBBoxFU();
}
- protected final void addAdvance(final float advance, final float size) {
- this.metrics.addAdvance(advance, size);
+ @Override
+ public final int getAdvanceFU() { return metrics.getAdvanceFU(); }
+
+ @Override
+ public float getAdvance() { return getScale( getAdvanceFU() ); }
+
+ protected final void addAdvance(final float pixelSize, final float advance) {
+ if( TypecastFont.USE_PRESCALED_ADVANCE ) {
+ this.metrics.addAdvance(pixelSize, advance);
+ }
}
@Override
- public final float getAdvance(final float pixelSize, final boolean useFrationalMetrics) {
- return this.metrics.getAdvance(pixelSize, useFrationalMetrics);
+ public final float getAdvance(final float pixelSize) {
+ return metrics.getAdvance(pixelSize);
}
@Override
@@ -208,4 +258,15 @@ public final class TypecastGlyph implements Font.Glyph {
final int hash = 31 + getFont().getName(Font.NAME_UNIQUNAME).hashCode();
return ((hash << 5) - hash) + id;
}
+
+ @Override
+ public String toString() {
+ final PostTable post = metrics.getFont().getPostTable();
+ final String glyph_name = null != post ? post.getGlyphName(id) : "n/a";
+ return new StringBuilder()
+ .append("Glyph id ").append(id).append(" '").append(glyph_name).append("'")
+ .append(", advance ").append(getAdvanceFU())
+ .append(", ").append(getBBoxFU())
+ .toString();
+ }
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
index d5e30a500..705246822 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastHMetrics.java
@@ -33,12 +33,13 @@ import jogamp.graph.font.typecast.ot.table.HheaTable;
import com.jogamp.graph.font.Font.Metrics;
import com.jogamp.opengl.math.geom.AABBox;
-class TypecastHMetrics implements Metrics {
+final class TypecastHMetrics implements Metrics {
private final TypecastFont fontImpl;
// HeadTable
private final HeadTable headTable;
- private final float unitsPerEM_Inv;
+ private final int unitsPerEM;
+ private final float unitsPerEM_inv;
private final AABBox bbox;
// HheaTable
private final HheaTable hheaTable;
@@ -50,39 +51,55 @@ class TypecastHMetrics implements Metrics {
headTable = this.fontImpl.font.getHeadTable();
hheaTable = this.fontImpl.font.getHheaTable();
// vheaTable = this.fontImpl.font.getVheaTable();
- unitsPerEM_Inv = 1.0f / ( headTable.getUnitsPerEm() );
+ unitsPerEM = headTable.getUnitsPerEm();
+ unitsPerEM_inv = 1.0f / unitsPerEM;
final int maxWidth = headTable.getXMax() - headTable.getXMin();
final int maxHeight = headTable.getYMax() - headTable.getYMin();
- final float lowx= headTable.getXMin();
- final float lowy = -(headTable.getYMin()+maxHeight);
- final float highx = lowx + maxWidth;
- final float highy = lowy + maxHeight;
+ final int lowx= headTable.getXMin();
+ final int lowy = -(headTable.getYMin()+maxHeight);
+ final int highx = lowx + maxWidth;
+ final int highy = lowy + maxHeight;
bbox = new AABBox(lowx, lowy, 0, highx, highy, 0); // invert
}
@Override
- public final float getAscent(final float pixelSize) {
- return getScale(pixelSize) * -hheaTable.getAscender(); // invert
+ public int getAscentFU() {
+ return -hheaTable.getAscender(); // inverted
}
+
@Override
- public final float getDescent(final float pixelSize) {
- return getScale(pixelSize) * -hheaTable.getDescender(); // invert
+ public int getDescentFU() {
+ return -hheaTable.getDescender(); // inverted
}
+
@Override
- public final float getLineGap(final float pixelSize) {
- return getScale(pixelSize) * -hheaTable.getLineGap(); // invert
+ public int getLineGapFU() {
+ return -hheaTable.getLineGap(); // inverted
}
+
@Override
- public final float getMaxExtend(final float pixelSize) {
- return getScale(pixelSize) * hheaTable.getXMaxExtent();
+ public int getMaxExtendFU() {
+ return hheaTable.getXMaxExtent();
}
+
@Override
- public final float getScale(final float pixelSize) {
- return pixelSize * unitsPerEM_Inv;
+ public final int getUnitsPerEM() {
+ return unitsPerEM;
}
+
+ @Override
+ public final float getScale(final int funits) {
+ return funits * unitsPerEM_inv;
+ }
+
+ @Override
+ public final AABBox getBBox(final AABBox dest) {
+ return dest.copy(bbox);
+ }
+
@Override
public final AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3) {
- return dest.setSize(bbox.getLow(), bbox.getHigh()).scale(getScale(pixelSize), tmpV3);
+ return dest.setSize(bbox.getLow(), bbox.getHigh()).scale(pixelSize*unitsPerEM_inv, tmpV3);
}
-} \ No newline at end of file
+}