/** * Copyright 2011 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package jogamp.graph.font.typecast; import jogamp.graph.font.typecast.ot.OTGlyph; import jogamp.graph.font.typecast.ot.Point; import jogamp.opengl.Debug; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.geom.Vertex; import com.jogamp.graph.geom.Vertex.Factory; /** * Factory to build an {@link OutlineShape} from * {@link jogamp.graph.font.typecast.ot.OTGlyph Glyph}s. * * http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html * http://walon.org/pub/ttf/ttf_glyphs.htm */ public class TypecastRenderer { private static final boolean DEBUG = Debug.debug("graph.font.Renderer"); private static void addShapeMoveTo(final OutlineShape shape, final Factory<? extends Vertex> vertexFactory, final Point p1) { if( DEBUG ) { System.err.println("Shape.MoveTo: "+p1); } shape.closeLastOutline(false); shape.addEmptyOutline(); shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, p1.onCurve)); } private static void addShapeLineTo(final OutlineShape shape, final Factory<? extends Vertex> vertexFactory, final Point p1) { if( DEBUG ) { System.err.println("Shape.LineTo: "+p1); } shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, p1.onCurve)); } private static void addShapeQuadTo(final OutlineShape shape, final Factory<? extends Vertex> vertexFactory, final Point p1, final Point p2) { if( DEBUG ) { System.err.println("Shape.QuadTo: "+p1+", "+p2); } shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, p1.onCurve)); shape.addVertex(0, vertexFactory.create(p2.x, p2.y, 0, p2.onCurve)); } private static void addShapeQuadTo(final OutlineShape shape, final Factory<? extends Vertex> vertexFactory, final Point p1, final float p2x, final float p2y, final boolean p2OnCurve) { if( DEBUG ) { System.err.println("Shape.QuadTo: "+p1+", p2 "+p2x+", "+p2y+", onCurve "+p2OnCurve); } shape.addVertex(0, vertexFactory.create(p1.x, p1.y, 0, p1.onCurve)); shape.addVertex(0, vertexFactory.create(p2x, p2y, 0, p2OnCurve)); } /** 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, p1.onCurve)); shape.addVertex(0, vertexFactory.create(p2.x, p2.y, 0, p2.onCurve)); shape.addVertex(0, vertexFactory.create(p3.x, p3.y, 0, p3.onCurve)); } */ public static OutlineShape buildShape(final char symbol, final OTGlyph glyph, final Factory<? extends Vertex> vertexFactory) { // // See Typecast: GlyphPathFactory.addContourToPath(..) // if (glyph == null) { return null; } final OutlineShape shape = new OutlineShape(vertexFactory); buildShapeImpl(shape, symbol, glyph, vertexFactory); shape.setIsQuadraticNurbs(); return shape; } /** private static void buildShapeImpl02(final OutlineShape shape, char symbol, OTGlyph glyph, Factory<? extends Vertex> 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 ) { for(int j=0; j<count; j++) { final Point p = glyph.getPoint(startIndex + j); shape.addVertex(0, vertexFactory.create(p.x, p.y, 0, p.onCurve)); } shape.closeLastOutline(false); startIndex = i + 1; count = 0; } } } */ private static void buildShapeImpl(final OutlineShape shape, final char symbol, final OTGlyph glyph, final Factory<? extends Vertex> 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; final int totalPoints = glyph.getPointCount(); for (int i = 0; i < totalPoints; i++) { count++; if ( glyph.getPoint(i).endOfContour ) { int offset = 0; while ( offset < count - 1 ) { // require at least +1 point (last one is end-of-contour) final Point p0 = glyph.getPoint(startIndex + offset%count); final Point p1 = glyph.getPoint(startIndex + (offset+1)%count); final Point p2 = glyph.getPoint(startIndex + (offset+2)%count); final Point p3 = offset+3 < count ? glyph.getPoint(startIndex + offset+3) : null; if( DEBUG ) { System.err.println("GlyphShape<"+symbol+">: offset "+offset+" of "+count+"/"+totalPoints+" points"); final int pMIdx= (offset==0) ? startIndex+count-1 : startIndex+(offset-1)%count; final Point pM = glyph.getPoint(pMIdx); final int p0Idx = startIndex + offset%count; final int p1Idx = startIndex + (offset+1)%count; final int p2Idx = startIndex + (offset+2)%count; final int p3Idx = startIndex + (offset+3)%count; System.err.println("\t pM["+pMIdx+"] "+pM); System.err.println("\t p0["+p0Idx+"] "+p0); System.err.println("\t p1["+p1Idx+"] "+p1); System.err.println("\t p2["+p2Idx+"] "+p2); System.err.println("\t p3["+p3Idx+"] "+p3); } if(offset == 0) { addShapeMoveTo(shape, vertexFactory, p0); // gp.moveTo(point.x, point.y); } if( p0.endOfContour ) { // Branch-0: EOC ** SHALL NEVER HAPPEN ** if( DEBUG ) { System.err.println("B0 .. end-of-contour **** EOC"); } shape.closeLastOutline(false); break; } else if (p0.onCurve) { if (p1.onCurve) { // Branch-1: point.onCurve && p1.onCurve if( DEBUG ) { System.err.println("B1 .. line-to p0-p1"); } // s = new Line2D.Float(point.x, point.y, p1.x, p1.y); // gp.lineTo( p1.x, p1.y ); addShapeLineTo(shape, vertexFactory, p1); offset++; } else { if (p2.onCurve) { // Branch-2: point.onCurve && !p1.onCurve && p2.onCurve if( DEBUG ) { System.err.println("B2 .. quad-to p0-p1-p2"); } // s = new QuadCurve2D.Float( point.x, point.y, p1.x, p1.y, p2.x, p2.y); // gp.quadTo(p1.x, p1.y, p2.x, p2.y); addShapeQuadTo(shape, vertexFactory, p1, p2); offset+=2; } else { if (null != p3 && p3.onCurve) { // Branch-3: point.onCurve && !p1.onCurve && !p2.onCurve && p3.onCurve if( DEBUG ) { System.err.println("B3 .. 2-quad p0-p1-p1_2, p1_2-p2-p3 **** 2QUAD"); } // addShapeCubicTo(shape, vertexFactory, p1, p2, p3); addShapeQuadTo(shape, vertexFactory, p1, midValue(p1.x, p2.x), midValue(p1.y, p2.y), true); addShapeQuadTo(shape, vertexFactory, p2, p3); offset+=3; } else { // Branch-4: point.onCurve && !p1.onCurve && !p2.onCurve && !p3.onCurve if( DEBUG ) { System.err.println("B4 .. quad-to p0-p1-p2h **** MID"); } // s = new QuadCurve2D.Float(point.x,point.y,p1.x,p1.y, // midValue(p1.x, p2.x), midValue(p1.y, p2.y)); // gp.quadTo(p1.x, p1.y, midValue(p1.x, p2.x), midValue(p1.y, p2.y)); addShapeQuadTo(shape, vertexFactory, p1, midValue(p1.x, p2.x), midValue(p1.y, p2.y), true); offset+=2; // Skip p2 as done in Typecast } } } } else { if (!p1.onCurve) { // Branch-5: !point.onCurve && !p1.onCurve if( DEBUG ) { System.err.println("B5 .. quad-to pMh-p0-p1h ***** MID"); } // s = new QuadCurve2D.Float(midValue(pM.x, point.x), midValue(pM.y, point.y), // point.x, point.y, // midValue(point.x, p1.x), midValue(point.y, p1.y)); addShapeQuadTo(shape, vertexFactory, p0, midValue(p0.x, p1.x), midValue(p0.y, p1.y), true); offset++; } else { // Branch-6: !point.onCurve && p1.onCurve if( DEBUG ) { System.err.println("B6 .. quad-to pMh-p0-p1"); } // s = new QuadCurve2D.Float(midValue(pM.x, point.x), midValue(pM.y, point.y), // point.x, point.y, p1.x, p1.y); // gp.quadTo(point.x, point.y, p1.x, p1.y); addShapeQuadTo(shape, vertexFactory, p0, p1); offset++; } } } shape.closeLastOutline(false); startIndex = i + 1; count = 0; } } } private static float midValue(final float a, final float b) { return a + (b - a)/2f; } }