import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.geom.SVertex; import com.jogamp.graph.geom.Triangle; import com.jogamp.graph.geom.Vertex; import com.jogamp.nativewindow.WindowClosingProtocol; import com.jogamp.newt.NewtFactory; import com.jogamp.newt.Screen; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.*; import com.jogamp.opengl.awt.GLCanvas; import com.jogamp.opengl.util.Animator; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.PathIterator; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import static com.jogamp.opengl.GL.*; import static com.jogamp.opengl.GL2.GL_COMPILE; public class FontTest2 implements GLEventListener { GLWindow window; Animator animator; HashMap m_allCharacters = new HashMap<>(); public static void main(String[] args) { new FontTest2(); } public FontTest2() { final GLProfile profile = GLProfile.get(GLProfile.GL2); GLCapabilities capabilities = new GLCapabilities(profile); capabilities.setDoubleBuffered(true); capabilities.setHardwareAccelerated(true); capabilities.setSampleBuffers(true); capabilities.setNumSamples(4); GLCanvas glcanvas = new GLCanvas(capabilities); glcanvas.addGLEventListener(this); com.jogamp.newt.Display dpy = NewtFactory.createDisplay(null); Screen screen = NewtFactory.createScreen(dpy, 1); window = GLWindow.create(screen, capabilities); window.addGLEventListener(this); window.setVisible(true); window.setSize(800, 800); window.setDefaultCloseOperation(WindowClosingProtocol.WindowClosingMode.DISPOSE_ON_CLOSE); animator = new Animator(window); animator.setRunAsFastAsPossible(true); animator.start(); } @Override public void init(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); gl.setSwapInterval(1); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gl.glEnable(GL_MULTISAMPLE); try (FileInputStream ff = new FileInputStream("c:/windows/fonts/courbd.ttf")) { Font font = java.awt.Font.createFont(java.awt.Font.PLAIN, ff); font = font.deriveFont(5f); for (int n = 32; n < 127; ++n) { char c = Character.toString(n).charAt(0); m_allCharacters.put(c, new CharacterDraw(gl, font, c)); } } catch (IOException | FontFormatException ex) { ex.printStackTrace(); } } @Override public void dispose(GLAutoDrawable glAutoDrawable) { m_allCharacters.values().forEach(c -> c.destroy(glAutoDrawable.getGL().getGL2())); } @Override public void display(GLAutoDrawable drawable) { GL2 gl = drawable.getGL().getGL2(); gl.glClear(GL_COLOR_BUFFER_BIT); gl.glColor4f(1, 1, 1, 1); double x = -1; double y = .75; double scale = 0.04; for (CharacterDraw cd : m_allCharacters.values()) { gl.glPushMatrix(); gl.glTranslated(x, y, 0); gl.glScaled(scale, -scale, 1); cd.draw(gl); gl.glPopMatrix(); x += cd.advance * scale; if (x > .9) { x = - 1; y -= 0.25f; } } } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) { } static class CharacterDraw { float advance; int glListID; CharacterDraw(GL2 gl, java.awt.Font f, char c) { GlyphVector v = f.createGlyphVector(new FontRenderContext(null, true, false), Character.toString(c)); Shape glyph = v.getGlyphOutline(0); PathIterator pi = glyph.getPathIterator(null); OutlineShape shape = new OutlineShape(SVertex.factory()); double[] coords = new double[6]; double[] prev = new double[2]; advance = v.getGlyphMetrics(0).getAdvanceX(); while(!pi.isDone()) { int lineType = pi.currentSegment(coords); switch(lineType) { case PathIterator.SEG_CLOSE: break; case PathIterator.SEG_CUBICTO: break; case PathIterator.SEG_LINETO: shape.addVertex((float) coords[0], (float) coords[1], true); System.arraycopy(coords, 0, prev, 0, 2); break; case PathIterator.SEG_MOVETO: shape.addEmptyOutline(); shape.addVertex((float) coords[0], (float) coords[1], true); System.arraycopy(coords, 0, prev, 0, 2); break; case PathIterator.SEG_QUADTO: updateQuad(shape, prev, coords); System.arraycopy(coords, 2, prev, 0, 2); break; } pi.next(); } glListID = gl.glGenLists(1); gl.glNewList(glListID, GL_COMPILE); gl.glBegin(GL_TRIANGLES); for (Triangle t : shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS)) { for (Vertex vert : t.getVertices()) gl.glVertex2f(vert.getX(), vert.getY()); } gl.glEnd(); gl.glEndList(); } void updateQuad(OutlineShape shape, double[] prev, double[] coords) { double xStart = prev[0], xEnd = coords[2], xCtrl = coords[0]; double yStart = prev[1], yEnd = coords[3], yCtrl = coords[1]; //Draws curs as 3 sections - which generally appears to be enough //No need to draw the first point of the curve, it has already been added by now. for (int n = 1; n < 3; ++n) { //https://stackoverflow.com/questions/7920804/calculating-a-quad-curve-between-two-xy-points double f = n * (1/3.0); double x = (((xEnd-xCtrl)*f+xCtrl)-((xCtrl-xStart)*f+xStart))*f+((xCtrl-xStart)*f+xStart); double y = (((yEnd-yCtrl)*f+yCtrl)-((yCtrl-yStart)*f+yStart))*f+((yCtrl-yStart)*f+yStart); shape.addVertex((float)x, (float)y, true); } shape.addVertex((float)coords[2], (float)coords[3], true); } void draw(GL2 gl) { gl.glCallList(glListID); } void destroy(GL2 gl) { gl.glDeleteLists(glListID, 1); } } }