diff options
author | Sven Gothel <[email protected]> | 2023-08-28 22:57:53 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-08-28 22:57:53 +0200 |
commit | 920e529516bb264f04138ed1caca80d4925e3773 (patch) | |
tree | 498bce9425f880cab2cd36b4251eaa4e231e912c /src/jogl/classes | |
parent | 733cc5272cfed10fa07b707e29fd756f44581508 (diff) |
Graph Font + Glyph: More robust detetection and API definition of non-contour/whitespace Glyphs (detect and allow to skip 'em)
We also drop shapes for both, but for id 0 (unknown).
Diffstat (limited to 'src/jogl/classes')
6 files changed, 171 insertions, 63 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java index c282a4b92..d25ab4311 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -188,7 +188,9 @@ public abstract class GLRegion extends Region { final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() {
@Override
public final void visit(final char symbol, final Font.Glyph glyph) {
- Region.countOutlineShape(glyph.getShape(), vertIndexCount);
+ if( !glyph.isNonContour() ) {
+ Region.countOutlineShape(glyph.getShape(), vertIndexCount);
+ }
} };
font.processString(visitor, str);
return GLRegion.create(glp, renderModes, colorTexSeq, vertIndexCount[0], vertIndexCount[1]);
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 62a1ecb95..0090480bb 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java @@ -145,10 +145,9 @@ public class TextRegionUtil { final Font.GlyphVisitor visitor = new Font.GlyphVisitor() { @Override public void visit(final char symbol, final Glyph glyph, final AffineTransform t) { - if( glyph.isWhiteSpace() ) { - return; + if( !glyph.isNonContour() ) { + region.addOutlineShape(glyph.getShape(), t, rgbaColor); } - region.addOutlineShape(glyph.getShape(), t, rgbaColor); } }; if( preGrowRegion ) { @@ -175,7 +174,9 @@ public class TextRegionUtil { final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() { @Override public final void visit(final char symbol, final Font.Glyph glyph) { - Region.countOutlineShape(glyph.getShape(), vertIndexCount); + if( !glyph.isNonContour() ) { + Region.countOutlineShape(glyph.getShape(), vertIndexCount); + } } }; font.processString(visitor, str); return vertIndexCount; diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index 314040adf..ef1179153 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -175,13 +175,44 @@ public interface Font { /** Return the glyph's name, source from `post` table */ String getName(); - /** Return true if the underlying {@link #getShape()} is a whitespace, otherwise false. */ - boolean isWhiteSpace(); + /** + * Return true if the glyph is a whitespace, otherwise false. + * <p> + * A whitespace glyph has no {@link #getShape()}, but a valid {@link #getBounds()} and {@link #getAdvance()}. + * </p> + * Being a whitespace glyph excludes {@link #isUndefined()}. + * @see #isUndefined() + * @see #isNonContour() + */ + boolean isWhitespace(); - /** Return true if the Glyph denotes an undefined {@link #getID()} symbol, i.e. {@link #getName()} == `.notdef`. */ + /** + * Return true if the Glyph denotes an undefined {@link #getID()} symbol as follows + * <ul> + * <li>it's glyph index is {@link #ID_UNKNOWN}, i.e. {@code 0x00}</li> + * <li>has the {@link #getName() name} `.notdef`, `NULL`, `null` or `.null`</li> + * <li>has no original underlying shape</li> + * </ul> + * <p> + * An undefined glyph has no {@link #getShape()} if glyph index is not {@link #ID_UNKNOWN}. + * </p> + * <p> + * An undefined glyph has a default {@link #getBounds()} and {@link #getAdvance()}. + * </p> + * Being an undefined shape excludes {@link #isWhitespace()}. + * @see #isWhitespace() + * @see #isNonContour() + */ boolean isUndefined(); /** + * Returns true if {@link #isWhitespace()} or {@link #isUndefined()}. + * @see #isWhitespace() + * @see #isUndefined() + */ + boolean isNonContour(); + + /** * Return the AABBox in font-units, borrowing internal instance. */ AABBox getBoundsFU(); diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java index 4e7dc308a..1720caf0e 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java @@ -784,6 +784,26 @@ public class AABBox { return high.z() - low.z(); } + /** Returns the volume, i.e. width * height * depth */ + public final float getVolume() { + return getWidth() * getHeight() * getDepth(); + } + + /** Return true if {@link #getVolume()} is {@link FloatUtil#isZero(float)}, considering epsilon. */ + public final boolean hasZeroVolume() { + return FloatUtil.isZero(getVolume()); + } + + /** Returns the assumed 2D area, i.e. width * height while assuming low and high lies on same plane. */ + public final float get2DArea() { + return getWidth() * getHeight(); + } + + /** Return true if {@link #get2DArea()} is {@link FloatUtil#isZero(float)}, considering epsilon. */ + public final boolean hasZero2DArea() { + return FloatUtil.isZero(get2DArea()); + } + @Override public final boolean equals(final Object obj) { if( obj == this ) { diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java index ce490bdf0..8bc832633 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java @@ -48,9 +48,10 @@ 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 jogamp.opengl.Debug; class TypecastFont implements Font { - static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("graph.font.Font"); // private final OTFontCollection fontset; /* pp */ final TTFont font; @@ -203,34 +204,36 @@ class TypecastFont implements Font { } else { glyph_name = ""; } + final boolean isUndefined = Glyph.ID_UNKNOWN == glyph_id || null == glyph || TypecastGlyph.isUndefName(glyph_name); final int glyph_height = metrics.getAscentFU() - metrics.getDescentFU(); final int glyph_advance; final int glyph_leftsidebearings; + final boolean isWhitespace; final AABBox glyph_bbox; final OutlineShape shape; - final boolean isWhiteSpace; if( null != glyph ) { glyph_advance = glyph.getAdvanceWidth(); glyph_leftsidebearings = glyph.getLeftSideBearing(); final AABBox sb = glyph.getBBox(); - final OutlineShape s = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), glyph); - if( 0 < s.getVertexCount() ) { + final OutlineShape os = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), glyph); + if( 0 < os.getVertexCount() ) { + // Case 1: Either valid contour glyph, undefined or a whitespace (Case 2 with zero-area shape) + isWhitespace = isUndefined ? false : os.getBounds().hasZero2DArea(); glyph_bbox = sb; - shape = s; - isWhiteSpace = false; + shape = ( !isWhitespace && !isUndefined ) || Glyph.ID_UNKNOWN == glyph_id ? os : null; } else { - // non-contour glyph -> whitespace + // Case 2: Non-contour glyph -> whitespace or undefined + isWhitespace = !isUndefined; glyph_bbox = new AABBox(0f,0f,0f, glyph_advance, glyph_height, 0f); - shape = TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox); - isWhiteSpace = true; + shape = Glyph.ID_UNKNOWN == glyph_id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null; } } else { - // non-contour glyph -> whitespace + // Case 3: Non-contour glyph -> undefined glyph_advance = getAdvanceWidthFU(glyph_id); glyph_leftsidebearings = 0; + isWhitespace = false; glyph_bbox = new AABBox(0f,0f,0f, glyph_advance, glyph_height, 0f); - shape = TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox); - isWhiteSpace = true; + shape = Glyph.ID_UNKNOWN == glyph_id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null; } KernSubtable kernSub = null; { @@ -239,8 +242,9 @@ class TypecastFont implements Font { kernSub = kern.getSubtable0(); } } - result = new TypecastGlyph(this, glyph_id, glyph_name, glyph_bbox, glyph_advance, glyph_leftsidebearings, kernSub, shape, isWhiteSpace); - if(DEBUG) { + result = new TypecastGlyph(this, glyph_id, glyph_name, glyph_bbox, glyph_advance, glyph_leftsidebearings, kernSub, shape, + isUndefined, isWhitespace); + if( DEBUG || TypecastRenderer.DEBUG ) { System.err.println("New glyph: " + glyph_id + "/'"+glyph_name+"', shape " + (null != shape)); System.err.println(" tc_glyph "+glyph); System.err.println(" glyph "+result); @@ -340,22 +344,24 @@ class TypecastFont implements Font { temp1.setToIdentity(); final int glyph_id = getGlyphID(character); final Font.Glyph glyph = getGlyph(glyph_id); - final OutlineShape glyphShape = glyph.getShape(); - if( null == glyphShape ) { // also covers 'space' and all non-contour symbols + if( glyph.isUndefined() ) { + // break kerning, drop undefined + advanceTotal += glyph.getAdvanceFU(); + left_glyph = null; + } else if( glyph.isWhitespace() ) { + // break kerning, but include its bounding box space + left_glyph = null; + temp1.translate(advanceTotal, y, temp2); + res.resize(temp1.transform(glyph.getBoundsFU(), temp_box)); + advanceTotal += glyph.getAdvanceFU(); + } else { + // regular contour + if( null != left_glyph ) { + advanceTotal += left_glyph.getKerningFU(glyph_id); + } + temp1.translate(advanceTotal, y, temp2); + res.resize(temp1.transform(glyph.getBoundsFU(), temp_box)); advanceTotal += glyph.getAdvanceFU(); - left_glyph = null; // break kerning - continue; - } else if( glyph.isWhiteSpace() ) { // covers 'space' and all non-contour symbols - left_glyph = null; // break kerning - } - if( null != left_glyph ) { - advanceTotal += left_glyph.getKerningFU(glyph_id); - } - temp1.translate(advanceTotal, y, temp2); - res.resize(temp1.transform(glyph.getBoundsFU(), temp_box)); - - advanceTotal += glyph.getAdvanceFU(); - if( !glyph.isWhiteSpace() ) { left_glyph = glyph; } } @@ -421,23 +427,26 @@ class TypecastFont implements Font { final int glyph_id = getGlyphID(character); final Font.Glyph glyph = getGlyph(glyph_id); - final OutlineShape glyphShape = glyph.getShape(); - - if( null == glyphShape ) { // also covers 'space' and all non-contour symbols + if( glyph.isUndefined() ) { + // break kerning, drop undefined + advanceTotal += glyph.getAdvance(); + left_glyph = null; + } else if( glyph.isWhitespace() ) { + // break kerning, but include its bounding box space and visit the visitor + left_glyph = null; + temp1.translate(advanceTotal, y, temp2); + res.resize(temp1.transform(glyph.getBounds(), temp_box)); + visitor.visit(character, glyph, temp1); + advanceTotal += glyph.getAdvance(); + } else { + // regular contour + if( null != left_glyph ) { + advanceTotal += left_glyph.getKerning(glyph_id); + } + temp1.translate(advanceTotal, y, temp2); + res.resize(temp1.transform(glyph.getShape().getBounds(), temp_box)); + visitor.visit(character, glyph, temp1); advanceTotal += glyph.getAdvance(); - left_glyph = null; // break kerning - continue; - } else if( glyph.isWhiteSpace() ) { // covers 'space' and all non-contour symbols - left_glyph = null; // break kerning - } - if( null != left_glyph ) { - advanceTotal += left_glyph.getKerning(glyph_id); - } - temp1.translate(advanceTotal, y, temp2); - res.resize(temp1.transform(glyphShape.getBounds(), temp_box)); - visitor.visit(character, glyph, temp1); - advanceTotal += glyph.getAdvance(); - if( !glyph.isWhiteSpace() ) { left_glyph = glyph; } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java index d851efc3e..3ace5c480 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java @@ -39,6 +39,25 @@ public final class TypecastGlyph implements Font.Glyph { public static final short INVALID_ID = (short)((1 << 16) - 1); public static final short MAX_ID = (short)((1 << 16) - 2); + private static final String dot_undef_NAME = ".notdef"; + private static final String NULL_NAME = "NULL"; + private static final String null_NAME = "null"; + private static final String dot_null_NAME = ".null"; + + /* pp */ static final boolean isUndefName(final String name) { + if( null != name ) { + if( TypecastGlyph.dot_undef_NAME.equals(name) ) { + return true; + } else if( TypecastGlyph.NULL_NAME.equals(name) ) { + return true; + } else if( TypecastGlyph.null_NAME.equals(name) ) { + return true; + } else if( TypecastGlyph.dot_null_NAME.equals(name) ) { + return true; + } + } + return false; + } private static int[][] growPairArray(final int[][] src) { final int length = src.length; @@ -66,7 +85,8 @@ public final class TypecastGlyph implements Font.Glyph { private final int id; private final String name; - private final boolean isWhiteSpace; + private final boolean isUndefined; + private final boolean isWhitespace; private final TypecastFont font; private final AABBox bbox; // in font-units @@ -90,10 +110,12 @@ public final class TypecastGlyph implements Font.Glyph { */ protected TypecastGlyph(final TypecastFont font, final int id, final String name, final AABBox bbox, final int advance, final int leftSideBearings, - final KernSubtable kernSub, final OutlineShape shape, final boolean isWhiteSpace) { + final KernSubtable kernSub, final OutlineShape shape, + final boolean isUndefined, final boolean isWhiteSpace) { this.id = id; this.name = name; - this.isWhiteSpace = isWhiteSpace; + this.isUndefined = isUndefined; + this.isWhitespace = isWhiteSpace; this.font = font; this.bbox = bbox; this.advance = advance; @@ -139,10 +161,13 @@ public final class TypecastGlyph implements Font.Glyph { public final String getName() { return name; } @Override - public final boolean isWhiteSpace() { return this.isWhiteSpace; } + public final boolean isWhitespace() { return this.isWhitespace; } + + @Override + public final boolean isUndefined() { return this.isUndefined; } @Override - public final boolean isUndefined() { return name == ".notdef"; } + public final boolean isNonContour() { return isUndefined() || isWhitespace(); } @Override public final AABBox getBoundsFU() { return bbox; } @@ -232,21 +257,41 @@ public final class TypecastGlyph implements Font.Glyph { @Override public String toString() { final StringBuilder sb = new StringBuilder(); - final String ws_s = isWhiteSpace() ? ", ws" : ""; - sb.append("Glyph[id ").append(id).append(" '").append(name).append("'").append(ws_s) + final String contour_s; + if( isNonContour() ) { + final String ws_s = isWhitespace() ? "whitespace" : ""; + final String undef_s = isUndefined() ? "undefined" : ""; + contour_s = "non-cont("+ws_s+undef_s+")"; + } else { + contour_s = "contour"; + } + final String name_s = null != name ? name : ""; + final String shape_s = null != shape ? "shape "+shape.getVertexCount()+"v" : "shape null"; + sb.append("Glyph[id ").append(id).append(" '").append(name_s).append("', ").append(contour_s) + .append(", ").append(shape_s) .append(", advance ").append(getAdvanceFU()) .append(", leftSideBearings ").append(getLeftSideBearingsFU()) .append(", kerning[size ").append(kerning.length).append(", horiz ").append(this.isKerningHorizontal()).append(", cross ").append(this.isKerningCrossstream()).append("]") - .append(", shape ").append(null != shape).append("]"); + .append("]"); return sb.toString(); } @Override public String fullString() { final PostTable post = font.getPostTable(); - final String glyph_name = null != post ? post.getGlyphName(id) : "n/a"; final StringBuilder sb = new StringBuilder(); - sb.append("Glyph id ").append(id).append(" '").append(glyph_name).append("'") + final String contour_s; + if( isNonContour() ) { + final String ws_s = isWhitespace() ? "whitespace" : ""; + final String undef_s = isUndefined() ? "undefined" : ""; + contour_s = "non-cont("+ws_s+undef_s+")"; + } else { + contour_s = "contour"; + } + final String name_s = null != name ? name : ""; + final String shape_s = null != shape ? "shape "+shape.getVertexCount()+"v" : "shape null"; + sb.append("Glyph id ").append(id).append(" '").append(name_s).append("', ").append(contour_s) + .append(", shape ").append(shape_s) .append(", advance ").append(getAdvanceFU()) .append(", leftSideBearings ").append(getLeftSideBearingsFU()) .append(", ").append(getBoundsFU()); |