aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-08-28 22:57:53 +0200
committerSven Gothel <[email protected]>2023-08-28 22:57:53 +0200
commit920e529516bb264f04138ed1caca80d4925e3773 (patch)
tree498bce9425f880cab2cd36b4251eaa4e231e912c /src/jogl
parent733cc5272cfed10fa07b707e29fd756f44581508 (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')
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java4
-rw-r--r--src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java9
-rw-r--r--src/jogl/classes/com/jogamp/graph/font/Font.java37
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java20
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java99
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java65
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());