aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl
diff options
context:
space:
mode:
Diffstat (limited to 'src/jogl')
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java119
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/Font.java250
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/FontScale.java70
-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
6 files changed, 783 insertions, 258 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
index 344e1e51f..5aa2be258 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
@@ -74,41 +74,41 @@ public class TextRegionUtil {
/**
* Visit each {@link Font.Glyph}'s {@link OutlineShape} with the given {@link ShapeVisitor}
- * additionally passing the progressed {@link AffineTransform}.
- * The latter reflects the given font metric, pixelSize and hence character position.
+ * additionally passing the progressed {@link AffineTransform} in font-units.
+ * The latter reflects the given font metric in font-units and hence character position.
* @param visitor
* @param transform optional given transform
* @param font the target {@link Font}
- * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
* @param str string text
* @param temp1 temporary AffineTransform storage, mandatory, will be passed to {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} and can be modified.
* @param temp2 temporary AffineTransform storage, mandatory, can be re-used in {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} by user code.
*/
public static void processString(final ShapeVisitor visitor, final AffineTransform transform,
- final Font font, final float pixelSize, final CharSequence str,
+ final Font font, final CharSequence str,
final AffineTransform temp1, final AffineTransform temp2) {
final int charCount = str.length();
// region.setFlipped(true);
final Font.Metrics metrics = font.getMetrics();
- final float lineHeight = font.getLineHeight(pixelSize);
+ final int lineHeight = font.getLineHeightFU();
- final float scale = metrics.getScale(pixelSize);
-
- float y = 0;
- float advanceTotal = 0;
+ int y = 0;
+ int advanceTotal = 0;
+ char left_character = 0;
+ int left_glyphid = 0;
for(int i=0; i< charCount; i++) {
final char character = str.charAt(i);
if( '\n' == character ) {
y -= lineHeight;
advanceTotal = 0;
+ left_glyphid = 0;
+ left_character = 0;
} else if (character == ' ') {
- advanceTotal += font.getAdvanceWidth(Glyph.ID_SPACE, pixelSize);
+ advanceTotal += font.getAdvanceWidthFU(Glyph.ID_SPACE);
+ left_glyphid = 0;
+ left_character = character;
} else {
- if(Region.DEBUG_INSTANCE) {
- System.err.println("XXXXXXXXXXXXXXx char: "+character+", scale: "+scale+"; translate: "+advanceTotal+", "+y);
- }
// reset transform
if( null != transform ) {
temp1.setTransform(transform);
@@ -116,51 +116,65 @@ public class TextRegionUtil {
temp1.setToIdentity();
}
temp1.translate(advanceTotal, y, temp2);
- temp1.scale(scale, scale, temp2);
final Font.Glyph glyph = font.getGlyph(character);
final OutlineShape glyphShape = glyph.getShape();
if( null == glyphShape ) {
+ left_glyphid = 0;
continue;
}
visitor.visit(glyphShape, temp1);
-
- advanceTotal += glyph.getAdvance(pixelSize, true);
+ final int right_glyphid = glyph.getID();
+ final int kern = font.getKerningFU(left_glyphid, right_glyphid);
+ final int advance = glyph.getAdvanceFU();
+ advanceTotal += advance + kern;
+ if( Region.DEBUG_INSTANCE && 0 != left_character && kern > 0f ) {
+ System.err.println(": '"+left_character+"'/"+left_glyphid+" -> '"+character+"'/"+right_glyphid+
+ ": a "+advance+"px + k ["+kern+"em, "+kern+"px = "+(advance+kern)+"px -> "+advanceTotal+"px, y "+y);
+ }
+ // advanceTotal += glyph.getAdvance(pixelSize, true)
+ // + font.getKerning(left_glyphid, right_glyphid);
+ left_glyphid = right_glyphid;
+ left_character = character;
}
}
}
/**
- * Add the string in 3D space w.r.t. the font and pixelSize at the end of the {@link GLRegion}.
+ * Add the string in 3D space w.r.t. the font using font-units at the end of the {@link GLRegion}.
+ * <p>
+ * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+ * </p>
* @param region the {@link GLRegion} sink
* @param vertexFactory vertex impl factory {@link Factory}
* @param font the target {@link Font}
- * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
* @param str string text
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param temp1 temporary AffineTransform storage, mandatory
* @param temp2 temporary AffineTransform storage, mandatory
*/
public static void addStringToRegion(final GLRegion region, final Factory<? extends Vertex> vertexFactory,
- final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor,
+ final Font font, final CharSequence str, final float[] rgbaColor,
final AffineTransform temp1, final AffineTransform temp2) {
final ShapeVisitor visitor = new ShapeVisitor() {
+ @Override
public final void visit(final OutlineShape shape, final AffineTransform t) {
region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null);
} };
- processString(visitor, null, font, pixelSize, str, temp1, temp2);
+ processString(visitor, null, font, str, temp1, temp2);
}
/**
- * Render the string in 3D space w.r.t. the font and pixelSize
- * using a cached {@link GLRegion} for reuse.
+ * Render the string in 3D space w.r.t. the font using font-units at the end of an internally cached {@link GLRegion}.
+ * <p>
+ * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+ * </p>
* <p>
* Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory.
* </p>
* @param gl the current GL state
* @param renderer TODO
* @param font {@link Font} to be used
- * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
* @param str text to be rendered
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -168,34 +182,35 @@ public class TextRegionUtil {
* @throws Exception if TextRenderer not initialized
*/
public void drawString3D(final GL2ES2 gl,
- final RegionRenderer renderer, final Font font, final float pixelSize,
- final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount) {
+ final RegionRenderer renderer, final Font font, final CharSequence str,
+ final float[] rgbaColor, final int[/*1*/] sampleCount) {
if( !renderer.isInitialized() ) {
throw new GLException("TextRendererImpl01: not initialized!");
}
final int special = 0;
- GLRegion region = getCachedRegion(font, str, pixelSize, special);
+ GLRegion region = getCachedRegion(font, str, special);
if(null == region) {
region = GLRegion.create(renderModes, null);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, tempT1, tempT2);
- addCachedRegion(gl, font, str, pixelSize, special, region);
+ addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, tempT1, tempT2);
+ addCachedRegion(gl, font, str, special, region);
}
region.draw(gl, renderer, sampleCount);
}
/**
- * Render the string in 3D space w.r.t. the font and pixelSize
- * using a temporary {@link GLRegion}, which will be destroyed afterwards.
+ * Render the string in 3D space w.r.t. the font using font-units at the end of an internally temporary {@link GLRegion}.
+ * <p>
+ * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+ * </p>
* <p>
* In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion}
* is a huge performance impact.
- * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, float, CharSequence, float[], int[], AffineTransform, AffineTransform)}
+ * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)}
* instead.
* </p>
* @param gl the current GL state
* @param renderModes TODO
* @param font {@link Font} to be used
- * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
* @param str text to be rendered
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -205,24 +220,26 @@ public class TextRegionUtil {
* @throws Exception if TextRenderer not initialized
*/
public static void drawString3D(final GL2ES2 gl, final int renderModes,
- final RegionRenderer renderer, final Font font, final float pixelSize,
- final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount,
- final AffineTransform temp1, final AffineTransform temp2) {
+ final RegionRenderer renderer, final Font font, final CharSequence str,
+ final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform temp1,
+ final AffineTransform temp2) {
if(!renderer.isInitialized()){
throw new GLException("TextRendererImpl01: not initialized!");
}
final GLRegion region = GLRegion.create(renderModes, null);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2);
+ addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
region.draw(gl, renderer, sampleCount);
region.destroy(gl);
}
/**
- * Render the string in 3D space w.r.t. the font and pixelSize
- * using the given {@link GLRegion}, which will {@link GLRegion#clear(GL2ES2) cleared} beforehand.
+ * Render the string in 3D space w.r.t. the font using font-units at the end of the given {@link GLRegion},
+ * which will {@link GLRegion#clear(GL2ES2) cleared} beforehand.
+ * <p>
+ * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+ * </p>
* @param gl the current GL state
* @param font {@link Font} to be used
- * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
* @param str text to be rendered
* @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
* @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -232,14 +249,14 @@ public class TextRegionUtil {
* @throws Exception if TextRenderer not initialized
*/
public static void drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer,
- final Font font, final float pixelSize, final CharSequence str,
- final float[] rgbaColor, final int[/*1*/] sampleCount,
- final AffineTransform temp1, final AffineTransform temp2) {
+ final Font font, final CharSequence str, final float[] rgbaColor,
+ final int[/*1*/] sampleCount, final AffineTransform temp1,
+ final AffineTransform temp2) {
if(!renderer.isInitialized()){
throw new GLException("TextRendererImpl01: not initialized!");
}
region.clear(gl);
- addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2);
+ addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
region.draw(gl, renderer, sampleCount);
}
@@ -297,13 +314,13 @@ public class TextRegionUtil {
}
}
- protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final float pixelSize, final int special) {
- return stringCacheMap.get(getKey(font, str, pixelSize, special));
+ protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final int special) {
+ return stringCacheMap.get(getKey(font, str, special));
}
- protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final float pixelSize, final int special, final GLRegion glyphString) {
+ protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int special, final GLRegion glyphString) {
if ( 0 != getCacheLimit() ) {
- final String key = getKey(font, str, pixelSize, special);
+ final String key = getKey(font, str, special);
final GLRegion oldRegion = stringCacheMap.put(key, glyphString);
if ( null == oldRegion ) {
// new entry ..
@@ -313,8 +330,8 @@ public class TextRegionUtil {
}
}
- protected final void removeCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int pixelSize, final int special) {
- final String key = getKey(font, str, pixelSize, special);
+ protected final void removeCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int special) {
+ final String key = getKey(font, str, special);
final GLRegion region = stringCacheMap.remove(key);
if(null != region) {
region.destroy(gl);
@@ -332,10 +349,10 @@ public class TextRegionUtil {
}
}
- protected final String getKey(final Font font, final CharSequence str, final float pixelSize, final int special) {
+ protected final String getKey(final Font font, final CharSequence str, final int special) {
final StringBuilder sb = new StringBuilder();
return font.getName(sb, Font.NAME_UNIQUNAME)
- .append(".").append(str.hashCode()).append(".").append(Float.floatToIntBits(pixelSize)).append(special).toString();
+ .append(".").append(str.hashCode()).append(".").append(special).toString();
}
/** Default cache limit, see {@link #setCacheLimit(int)} */
diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java
index 597a7e76f..9f25b5481 100644
--- a/src/jogl/classes/com/jogamp/graph/font/Font.java
+++ b/src/jogl/classes/com/jogamp/graph/font/Font.java
@@ -37,7 +37,7 @@ import com.jogamp.opengl.math.geom.AABBox;
* TrueType Font Specification:
* <ul>
* <li>http://www.freetype.org/freetype2/documentation.html</li>
- * <li>http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html</li>
+ * <li>https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html</li>
* <li>http://www.microsoft.com/typography/SpecificationsOverview.mspx</li>
* <li>http://www.microsoft.com/typography/otspec/</li>
* </ul>
@@ -76,20 +76,54 @@ public interface Font {
* Depending on the font's direction, horizontal or vertical,
* the following tables shall be used:
*
- * Vertical http://developer.apple.com/fonts/TTRefMan/RM06/Chap6vhea.html
- * Horizontal http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html
+ * Vertical https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6vhea.html
+ * Horizontal https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hhea.html
*/
public interface Metrics {
- float getAscent(final float pixelSize);
- float getDescent(final float pixelSize);
- float getLineGap(final float pixelSize);
- float getMaxExtend(final float pixelSize);
- float getScale(final float pixelSize);
+ /**
+ * @return ascent in font-units to be divided by {@link #getUnitsPerEM()}
+ */
+ int getAscentFU();
+
+ /**
+ * @return descent in font-units to be divided by {@link #getUnitsPerEM()}
+ */
+ int getDescentFU();
+
+ /**
+ * @return line-gap in font-units to be divided by {@link #getUnitsPerEM()}
+ */
+ int getLineGapFU();
+
+ /**
+ * @return max-extend in font-units to be divided by {@link #getUnitsPerEM()}
+ */
+ int getMaxExtendFU();
+
+ /** Returns the font's units per EM from the 'head' table. One em square covers one glyph. */
+ int getUnitsPerEM();
+
+ /**
+ * Return fractional font em-size [0..1], i.e. funits divided by {@link #getUnitsPerEM()}, i.e.
+ * <pre>
+ * return funits / head.unitsPerEM;
+ * </pre>
+ * @param funits smallest font unit, where {@link #getUnitsPerEM()} square covers whole glyph
+ * @return fractional font em-size [0..1]
+ */
+ float getScale(final int funits);
+
+ /**
+ * @param dest AABBox instance set to this metrics boundary in font-units
+ * @return the given and set AABBox 'dest' in font units
+ */
+ AABBox getBBox(final AABBox dest);
+
/**
* @param dest AABBox instance set to this metrics boundary w/ given pixelSize
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
* @param tmpV3 caller provided temporary 3-component vector
- * @return the given and set AABBox 'dest'
+ * @return the given and set AABBox 'dest' in pixel size
*/
AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3);
}
@@ -108,118 +142,192 @@ public interface Font {
public static final int ID_CR = 2;
public static final int ID_SPACE = 3;
- public Font getFont();
- public char getSymbol();
- public short getID();
- public AABBox getBBox();
+ Font getFont();
+ char getSymbol();
+ int getID();
+
/**
- *
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
- * @return
+ * Return fractional font em-size [0..1], i.e. funits divided by {@link #getUnitsPerEM()}, i.e.
+ * <pre>
+ * return funits / head.unitsPerEM;
+ * </pre>
+ * @param funits smallest font unit, where {@link #getUnitsPerEM()} square covers whole glyph
+ * @return fractional font em-size [0..1]
*/
- public float getScale(final float pixelSize);
+ float getScale(final int funits);
+
/**
* @param dest AABBox instance set to this metrics boundary w/ given pixelSize
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
* @param tmpV3 caller provided temporary 3-component vector
- * @return the given and set AABBox 'dest'
+ * @return the given and set AABBox 'dest' in pixel size
+ */
+ AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3);
+
+ /**
+ * Return the AABBox in font-units to be divided by unitsPerEM
+ * @param dest AABBox instance set to this metrics boundary in font-units
+ * @return the given and set AABBox 'dest' in font-units
+ */
+ AABBox getBBoxFU(final AABBox dest);
+
+ /**
+ * Return the AABBox in font-units to be divided by unitsPerEM
*/
- public AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3);
+ AABBox getBBoxFU();
+
+ /** Return advance in font units to be divided by unitsPerEM */
+ int getAdvanceFU();
+
+ /** Return advance in font em-size [0..1] */
+ float getAdvance();
+
/**
- *
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
- * @param useFrationalMetrics
- * @return
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+ * @return pixel size of advance
*/
- public float getAdvance(final float pixelSize, boolean useFrationalMetrics);
- public OutlineShape getShape();
- public int hashCode();
+ float getAdvance(final float pixelSize);
+
+ OutlineShape getShape();
+
+ @Override
+ int hashCode();
}
- public String getName(final int nameIndex);
- public StringBuilder getName(final StringBuilder string, final int nameIndex);
+ String getName(final int nameIndex);
+ StringBuilder getName(final StringBuilder string, final int nameIndex);
/** Shall return the family and subfamily name, separated a dash.
* <p>{@link #getName(StringBuilder, int)} w/ {@link #NAME_FAMILY} and {@link #NAME_SUBFAMILY}</p>
* <p>Example: "{@code Ubuntu-Regular}"</p> */
- public StringBuilder getFullFamilyName(final StringBuilder buffer);
+ StringBuilder getFullFamilyName(final StringBuilder buffer);
+
+ StringBuilder getAllNames(final StringBuilder string, final String separator);
- public StringBuilder getAllNames(final StringBuilder string, final String separator);
+ /**
+ *
+ * @param glyphID
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+ * @return pixel size of advance width
+ */
+ float getAdvanceWidth(final int glyphID, final float pixelSize);
/**
- * <pre>
- Font Scale Formula:
- inch: 25.4 mm
- pointSize: [point] = [1/72 inch]
-
- [1] Scale := pointSize * resolution / ( 72 points per inch * units_per_em )
- [2] PixelSize := pointSize * resolution / ( 72 points per inch )
- [3] Scale := PixelSize / units_per_em
- * </pre>
- * @param fontSize in point-per-inch
- * @param resolution display resolution in dots-per-inch
- * @return pixel-per-inch, pixelSize scale factor for font operations.
+ * Return advance-width of given glyphID in font em-size [0..1]
+ * @param glyphID
*/
- public float getPixelSize(final float fontSize /* points per inch */, final float resolution);
+ float getAdvanceWidth(final int glyphID);
/**
- *
+ * Return advance-width of given glyphID in font-units to be divided by unitsPerEM
* @param glyphID
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
- * @return
*/
- public float getAdvanceWidth(final int glyphID, final float pixelSize);
- public Metrics getMetrics();
- public Glyph getGlyph(final char symbol);
- public int getNumGlyphs();
+ int getAdvanceWidthFU(final int glyphID);
/**
+ * Returns the optional kerning inter-glyph distance within words in fractional font em-size [0..1].
*
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
- * @return
+ * @param left_glyphid left glyph code id
+ * @param right_glyphid right glyph code id
+ * @return fractional font em-size distance [0..1]
*/
- public float getLineHeight(final float pixelSize);
+ float getKerning(final int left_glyphid, final int right_glyphid);
+
+ /**
+ * Returns the optional kerning inter-glyph distance within words in fractional font-units to be divided by unitsPerEM
+ *
+ * @param left_glyphid left glyph code id
+ * @param right_glyphid right glyph code id
+ * @return font-units to be divided by unitsPerEM
+ */
+ int getKerningFU(final int left_glyphid, final int right_glyphid);
+
+ Metrics getMetrics();
+ int getGlyphID(final char symbol);
+ Glyph getGlyph(final char symbol);
+ int getNumGlyphs();
+
+ /**
+ *
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+ * @return pixel size of line height
+ */
+ float getLineHeight(final float pixelSize);
+
+ /**
+ * Return line height in font em-size [0..1]
+ */
+ float getLineHeight();
+
+ /**
+ * Return line height in font-units to be divided by unitsPerEM
+ */
+ int getLineHeightFU();
+
/**
*
* @param string
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
- * @return
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+ * @return pixel size of metric width
*/
- public float getMetricWidth(final CharSequence string, final float pixelSize);
+ float getMetricWidth(final CharSequence string, final float pixelSize);
+
+ /** Return metric-width in font em-size */
+ float getMetricWidth(final CharSequence string);
+
+ /** Return metric-width in font-units */
+ int getMetricWidthFU(final CharSequence string);
+
/**
*
* @param string
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
- * @param tmp
- * @return
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+ * @return pixel size of metric height
*/
- public float getMetricHeight(final CharSequence string, final float pixelSize, final AABBox tmp);
+ float getMetricHeight(final CharSequence string, final float pixelSize);
+
+ /** Return metric-height in font em-size */
+ float getMetricHeight(final CharSequence string);
+
+ /** Return metric-height in font-units */
+ int getMetricHeightFU(final CharSequence string);
+
/**
* Return the <i>layout</i> bounding box as computed by each glyph's metrics.
- * The result is not pixel correct, bit reflects layout specific metrics.
+ * The result is not pixel correct, but reflects layout specific metrics.
* <p>
* See {@link #getPointsBounds(AffineTransform, CharSequence, float, AffineTransform, AffineTransform)} for pixel correct results.
* </p>
* @param string string text
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
*/
- public AABBox getMetricBounds(final CharSequence string, final float pixelSize);
+ AABBox getMetricBounds(final CharSequence string, final float pixelSize);
+
+ /** Return layout metric-bounds in font em-size, see {@link #getMetricBounds(CharSequence, float)} */
+ AABBox getMetricBounds(final CharSequence string);
+
+ /** Return layout metric-bounds in font-units, see {@link #getMetricBounds(CharSequence, float)} */
+ AABBox getMetricBoundsFU(final CharSequence string);
/**
* Return the bounding box by taking each glyph's point-based bounding box into account.
* @param transform optional given transform
* @param string string text
- * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
- * @param temp1 temporary AffineTransform storage, mandatory
- * @param temp2 temporary AffineTransform storage, mandatory
+ * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
*/
- public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize,
- final AffineTransform temp1, final AffineTransform temp2);
+ AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize);
+
+ AABBox getPointsBounds(final AffineTransform transform, final CharSequence string);
- public boolean isPrintableChar(final char c);
+ AABBox getPointsBoundsFU(final AffineTransform transform, final CharSequence string);
+
+ boolean isPrintableChar(final char c);
/** Shall return {@link #getFullFamilyName()} */
@Override
public String toString();
+
+ /** Return all font details as string. */
+ String fullString();
}
diff --git a/src/jogl/classes/com/jogamp/graph/font/FontScale.java b/src/jogl/classes/com/jogamp/graph/font/FontScale.java
new file mode 100644
index 000000000..a0f2ef4e0
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/graph/font/FontScale.java
@@ -0,0 +1,70 @@
+package com.jogamp.graph.font;
+
+/**
+ * Simple static font scale methods for unit conversion.
+ */
+public class FontScale {
+ /**
+ * Converts typical font size in points (per-inch) and screen resolution in dpi to font size in pixels (per-inch),
+ * which can be used for pixel-size font scaling operations.
+ * <p>
+ * Note that 1em == size of the selected font.<br/>
+ * In case the pixel per em size is required (advance etc),
+ * the resulting pixel-size (per-inch) of this method shall be used if rendering directly into the screen resolution!
+ * </p>
+ * <pre>
+ Font Scale Formula:
+ 1 inch = 25.4 mm
+
+ 1 inch = 72 points
+ pointSize: [point] = [1/72 inch]
+
+ [1] Scale := pointSize * resolution / ( 72 points per inch * units_per_em )
+ [2] PixelSize := pointSize * resolution / ( 72 points per inch )
+ [3] Scale := PixelSize / units_per_em
+ * </pre>
+ * @param font_sz_pt in points (per-inch)
+ * @param res_dpi display resolution in dots-per-inch
+ * @return pixel-per-inch, pixelSize scale factor for font operations.
+ * @see #toPixels2(float, float)
+ */
+ public static float toPixels(final float font_sz_pt /* points per inch */, final float res_dpi /* dots per inch */) {
+ return ( font_sz_pt / 72f /* points per inch */ ) * res_dpi;
+ }
+
+ /**
+ * Converts typical font size in points-per-inch and screen resolution in points-per-mm to font size in pixels (per-inch),
+ * which can be used for pixel-size font scaling operations.
+ *
+ * @param font_sz_pt in points (per-inch)
+ * @param res_ppmm display resolution in dots-per-mm
+ * @return pixel-per-inch, pixelSize scale factor for font operations.
+ * @see #toPixels(float, float)
+ */
+ public static float toPixels2(final float font_sz_pt /* points per inch */, final float res_ppmm /* pixels per mm */) {
+ return ( font_sz_pt / 72f /* points per inch */ ) * ( res_ppmm * 25.4f /* mm per inch */ ) ;
+ }
+
+ /**
+ * Converts [1/mm] to [1/inch] in place
+ * @param ppmm float[2] [1/mm] value
+ * @return return [1/inch] value
+ */
+ public static float[/*2*/] perMMToPerInch(final float[/*2*/] ppmm) {
+ ppmm[0] *= 25.4f;
+ ppmm[1] *= 25.4f;
+ return ppmm;
+ }
+
+ /**
+ * Converts [1/mm] to [1/inch] into res storage
+ * @param ppmm float[2] [1/mm] value
+ * @param res the float[2] result storage
+ * @return return [1/inch] value, i.e. the given res storage
+ */
+ public static float[/*2*/] ppmmToPPI(final float[/*2*/] ppmm, final float[/*2*/] res) {
+ res[0] = ppmm[0] * 25.4f;
+ res[1] = ppmm[1] * 25.4f;
+ return res;
+ }
+}
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
+}