diff options
author | Sven Gothel <[email protected]> | 2014-02-24 13:32:34 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-02-24 13:32:34 +0100 |
commit | c3621221b9a563495b4f54fe60e18e8db8cc57fb (patch) | |
tree | 00aded20f3582e517372c12f58e19d3524582099 /src/jogl/classes/jogamp/graph/font | |
parent | f69df875d0b9f969a816d143ed589b25e50cd9e7 (diff) |
Bug 802: Graph TextRenderer Performance Part-1 (incomplete, rendering artifacts)
Strategy Change:
- Font.Glyph itself holds it's OutlineShape
with it's default scaling.
Triangulation is done only once per glyph!
- A CharSequence produces a Region
by translating and scaling each Glyphs's OutlineShape.
This removes the need for re-triangulate - see above.
See: TextRendererUtil
- The indices of re-added Triangles are
offset to the new vertices (FIXME, seems not be be accurate yet).
- OutlineShape's vertices and triangles are reused if 'clean'.
- Simplified code
- Reduced copies
API Changes:
- OutlineShape, Region, ...: See above
- Removed TextRenderer, GlyphShape and GlyphString: Redundant
- Added TextRendererUtil to produce the Region from CharSequence
Result:
- Over 600 fps while changing text for each frame.
Previously only ~60fps max.
TODO:
- Region shall not hold the triangles itself,
but the indices instead.
This will remove the need to swizzle w/ vertices in the Region Renderer impl
and easies reusage of OutlineShapes.
Diffstat (limited to 'src/jogl/classes/jogamp/graph/font')
4 files changed, 157 insertions, 25 deletions
diff --git a/src/jogl/classes/jogamp/graph/font/FontInt.java b/src/jogl/classes/jogamp/graph/font/FontInt.java index 1f4eaf20d..f87a00251 100644 --- a/src/jogl/classes/jogamp/graph/font/FontInt.java +++ b/src/jogl/classes/jogamp/graph/font/FontInt.java @@ -31,6 +31,9 @@ import jogamp.graph.geom.plane.Path2D; import com.jogamp.graph.font.Font; +/** + * @deprecated + */ public interface FontInt extends Font { public interface GlyphInt extends Font.Glyph { diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java index 250778f06..41fa6c223 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java @@ -44,19 +44,20 @@ import com.jogamp.common.util.IntObjectHashMap; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.font.Font; import com.jogamp.graph.font.FontFactory; +import com.jogamp.graph.geom.SVertex; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; import com.jogamp.opengl.math.geom.AABBox; class TypecastFont implements FontInt { static final boolean DEBUG = false; + private static final Vertex.Factory<SVertex> vertexFactory = SVertex.factory(); final OTFontCollection fontset; final OTFont font; TypecastHMetrics metrics; final CmapFormat cmapFormat; int cmapentries; - // FIXME: Add cache size to limit memory usage ?? IntObjectHashMap char2Glyph; @@ -201,8 +202,10 @@ class TypecastFont implements FontInt { if(null == glyph) { throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+code); } - Path2D path = TypecastRenderer.buildPath(glyph); - result = new TypecastGlyph(this, symbol, code, glyph.getBBox(), glyph.getAdvanceWidth(), path); + final OutlineShape shape = TypecastRenderer.buildShape(glyph, vertexFactory); + // FIXME: Remove Path2D + final Path2D path = TypecastRenderer.buildPath(glyph); + result = new TypecastGlyph(this, symbol, code, glyph.getBBox(), glyph.getAdvanceWidth(), path, shape); if(DEBUG) { System.err.println("New glyph: " + (int)symbol + " ( " + symbol +" ) -> " + code + ", contours " + glyph.getPointCount() + ": " + path); } @@ -225,11 +228,7 @@ class TypecastFont implements FontInt { return result; } - @Override - public OutlineShape getOutlineShape(Glyph glyph, Factory<? extends Vertex> vertexFactory) { - return TypecastRenderer.getOutlineShape(this, glyph, vertexFactory); - } - + // FIXME: Remove altogether @Override public List<OutlineShape> getOutlineShapes(List<OutlineShape> shapes, CharSequence string, float pixelSize, Factory<? extends Vertex> vertexFactory) { AffineTransform transform = new AffineTransform(vertexFactory); diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java index 302366647..40727826b 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java @@ -33,6 +33,7 @@ import jogamp.graph.font.FontInt; import jogamp.graph.geom.plane.AffineTransform; import jogamp.graph.geom.plane.Path2D; +import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.font.Font; import com.jogamp.opengl.math.geom.AABBox; @@ -136,30 +137,36 @@ public class TypecastGlyph implements FontInt.GlyphInt { public static final short MAX_ID = (short)((1 << 16) - 2); private final Font font; + protected final char symbol; + protected final OutlineShape shape; // in EM units + + protected final Path2D path; // in EM units FIXME remove! - char symbol; short id; int advance; Metrics metrics; - protected Path2D path; // in EM units - protected Path2D pathSized; - protected float numberSized; + protected Path2D pathSized; // FIXME remove! + protected float numberSized; // FIXME remove! protected TypecastGlyph(Font font, char symbol) { this.font = font; this.symbol = symbol; + this.shape = null; + this.path = null; } protected TypecastGlyph(Font font, - char symbol, short id, AABBox bbox, int advance, Path2D path) { + char symbol, short id, AABBox bbox, int advance, Path2D path, OutlineShape shape) { this.font = font; this.symbol = symbol; + this.shape = shape; + this.path = path; + this.advance = advance; init(id, bbox, advance); - this.path = path; this.pathSized = null; this.numberSized = 0.0f; } @@ -170,10 +177,11 @@ public class TypecastGlyph implements FontInt.GlyphInt { this.metrics = new Metrics(this.font, bbox, this.advance); } + /** public void reset(Path2D path) { this.path = path; this.metrics.reset(); - } + } */ @Override public Font getFont() { @@ -223,6 +231,11 @@ public class TypecastGlyph implements FontInt.GlyphInt { } @Override + public OutlineShape getShape() { + return this.shape; + } + + @Override public Path2D getPath() { return this.path; } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java index d7f8497b3..83ccadab3 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java @@ -55,25 +55,27 @@ public class TypecastRenderer { if (string == null) { return; } - Font.Metrics metrics = font.getMetrics(); - float advanceTotal = 0; - float lineGap = metrics.getLineGap(pixelSize) ; - float ascent = metrics.getAscent(pixelSize) ; - float descent = metrics.getDescent(pixelSize) ; + final Font.Metrics metrics = font.getMetrics(); + final float lineGap = metrics.getLineGap(pixelSize) ; + final float ascent = metrics.getAscent(pixelSize) ; + final float descent = metrics.getDescent(pixelSize) ; + final float advanceY = lineGap - descent + ascent; + final float scale = metrics.getScale(pixelSize); if (transform == null) { transform = new AffineTransform(); } - AffineTransform t = new AffineTransform(); + final AffineTransform t = new AffineTransform(transform.getFactory()); final int len = string.length(); - float advanceY = lineGap - descent + ascent; + + float advanceTotal = 0; float y = 0; + for (int i=0; i<len; i++) { p[i] = new Path2D(); p[i].reset(); - t.setTransform(transform); - char character = string.charAt(i); + final char character = string.charAt(i); if (character == '\n') { y += advanceY; advanceTotal = 0; @@ -82,7 +84,7 @@ public class TypecastRenderer { } else { final Glyph glyph = font.getGlyph(character); final Path2D gp = ((GlyphInt)glyph).getPath(); - final float scale = metrics.getScale(pixelSize); + t.setTransform(transform); // reset transform t.translate(advanceTotal, y); t.scale(scale, scale); p[i].append(gp.iterator(t), false); @@ -92,6 +94,7 @@ public class TypecastRenderer { } public static OutlineShape getOutlineShape(TypecastFont font, Glyph glyph, Factory<? extends Vertex> vertexFactory) { + // FIXME: Remove Path2D Path2D path = ((GlyphInt)glyph).getPath(); AffineTransform transform = new AffineTransform(vertexFactory); OutlineShape shape = new OutlineShape(vertexFactory); @@ -109,6 +112,8 @@ public class TypecastRenderer { } public static List<OutlineShape> getOutlineShapes(List<OutlineShape> shapes, TypecastFont font, CharSequence string, float pixelSize, AffineTransform transform, Factory<? extends Vertex> vertexFactory) { + // FIXME: Remove Path2D + // FIXME: Remove altogether Path2D[] paths = new Path2D[string.length()]; getPaths(font, string, pixelSize, transform, paths); @@ -161,6 +166,118 @@ public class TypecastRenderer { } } + private static void addShapeMoveTo(final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1) { + shape.closeLastOutline(); + shape.addEmptyOutline(); + shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, true)); // p1.onCurve)); + // shape.addVertex(0, vertexFactory.create(coords, 0, 2, true)); + } + private static void addShapeLineTo(final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1) { + shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, true)); // p1.onCurve)); + // shape.addVertex(0, vertexFactory.create(coords, 0, 2, true)); + } + private static void addShapeQuadTo(final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1, Point p2) { + shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, false)); // p1.onCurve)); + shape.addVertex(0, vertexFactory.create(p2.x, p2.y, 0, true)); // p2.onCurve)); + // shape.addVertex(0, vertexFactory.create(coords, 0, 2, false)); + // shape.addVertex(0, vertexFactory.create(coords, 2, 2, true)); + } + private static void addShapeQuadTo(final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1, int p2x, int p2y) { + shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, false)); // p1.onCurve)); + shape.addVertex(0, vertexFactory.create(p2x, p2y, 0, true)); // p2.onCurve)); + // shape.addVertex(0, vertexFactory.create(coords, 0, 2, false)); + // shape.addVertex(0, vertexFactory.create(coords, 2, 2, true)); + } + /** + private static void addShapeCubicTo(final OutlineShape shape, Factory<? extends Vertex> vertexFactory, Point p1, Point p2, Point p3) { + shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, false)); // p1.onCurve)); + shape.addVertex(0, vertexFactory.create(p2.x, p2.y, 0, false)); // p2.onCurve)); + shape.addVertex(0, vertexFactory.create(p3.x, p3.y, 0, true)); // p3.onCurve)); + // shape.addVertex(0, vertexFactory.create(coords, 0, 2, false)); + // shape.addVertex(0, vertexFactory.create(coords, 2, 2, false)); + // shape.addVertex(0, vertexFactory.create(coords, 4, 2, true)); + + } + private static void addShapeClose(final OutlineShape shape, Factory<? extends Vertex> vertexFactory) { + shape.closeLastOutline(); + } */ + + public static OutlineShape buildShape(OTGlyph glyph, Factory<? extends Vertex> vertexFactory) { + + if (glyph == null) { + return null; + } + + final OutlineShape shape = new OutlineShape(vertexFactory); + + // Iterate through all of the points in the glyph. Each time we find a + // contour end point, add the point range to the path. + int startIndex = 0; + int count = 0; + for (int i = 0; i < glyph.getPointCount(); i++) { + count++; + if (glyph.getPoint(i).endOfContour) { + { + int offset = 0; + while (offset < count) { + final Point point = glyph.getPoint(startIndex + offset%count); + final Point point_plus1 = glyph.getPoint(startIndex + (offset+1)%count); + final Point point_plus2 = glyph.getPoint(startIndex + (offset+2)%count); + if(offset == 0) + { + addShapeMoveTo(shape, vertexFactory, point); + // gp.moveTo(point.x, point.y); + } + + if (point.onCurve) { + if (point_plus1.onCurve) { + // s = new Line2D.Float(point.x, point.y, point_plus1.x, point_plus1.y); + addShapeLineTo(shape, vertexFactory, point_plus1); + // gp.lineTo( point_plus1.x, point_plus1.y ); + offset++; + } else { + if (point_plus2.onCurve) { + // s = new QuadCurve2D.Float( point.x, point.y, point_plus1.x, point_plus1.y, point_plus2.x, point_plus2.y); + addShapeQuadTo(shape, vertexFactory, point_plus1, point_plus2); + // gp.quadTo(point_plus1.x, point_plus1.y, point_plus2.x, point_plus2.y); + offset+=2; + } else { + // s = new QuadCurve2D.Float(point.x,point.y,point_plus1.x,point_plus1.y, + // midValue(point_plus1.x, point_plus2.x), midValue(point_plus1.y, point_plus2.y)); + addShapeQuadTo(shape, vertexFactory, point_plus1, midValue(point_plus1.x, point_plus2.x), midValue(point_plus1.y, point_plus2.y)); + // gp.quadTo(point_plus1.x, point_plus1.y, midValue(point_plus1.x, point_plus2.x), midValue(point_plus1.y, point_plus2.y)); + offset+=2; + } + } + } else { + if (point_plus1.onCurve) { + // s = new QuadCurve2D.Float(midValue(point_minus1.x, point.x), midValue(point_minus1.y, point.y), + // point.x, point.y, point_plus1.x, point_plus1.y); + //gp.curve3(point_plus1.x, point_plus1.y, point.x, point.y); + addShapeQuadTo(shape, vertexFactory, point, point_plus1); + // gp.quadTo(point.x, point.y, point_plus1.x, point_plus1.y); + offset++; + + } else { + // s = new QuadCurve2D.Float(midValue(point_minus1.x, point.x), midValue(point_minus1.y, point.y), point.x, point.y, + // midValue(point.x, point_plus1.x), midValue(point.y, point_plus1.y)); + //gp.curve3(midValue(point.x, point_plus1.x), midValue(point.y, point_plus1.y), point.x, point.y); + addShapeQuadTo(shape, vertexFactory, point, midValue(point.x, point_plus1.x), midValue(point.y, point_plus1.y)); + // gp.quadTo(point.x, point.y, midValue(point.x, point_plus1.x), midValue(point.y, point_plus1.y)); + offset++; + } + } + } + + } + startIndex = i + 1; + count = 0; + } + } + shape.closeLastOutline(); + return shape; + } + /** * Build a {@link com.jogamp.graph.geom.Path2D Path2D} from a * {@link jogamp.graph.font.typecast.ot.OTGlyph Glyph}. This glyph path can then |