New to working with TextRegionUtil

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

New to working with TextRegionUtil

dmclean
This post was updated on .
Hello,

I'm trying to write an application that draws basic shapes and text using OpenGL (all 2D). The shapes and text can change at any time - this is for vision / perception experiments. Since these are for experiments the timing between frames is critical and must work at 60 or 120 Hz. I have the v-sync working very well for basic shapes in JOGL, but text is giving me trouble. I'm using JOGL build 2.4.0-rc-20210111.

To look for frame drops I'm just timing the end of the display() call to the end of the next display() call. Anything that is >25ms @ 60 Hz I consider a frame drop.

I had been trying to use TextRenderer. However, using 10 short strings (ex. "5x2cm") with the hardware I need to deploy on will cause 2 or more frames to drop every 10 seconds. I need to be able to draw ~100 short strings. 100 short strings on this hardware leads to 3 frame drops at a time (i.e. 45+ms between display calls). Part of the issue is that for each piece of text I render I have a begin3DRender and end3DRender. Unfortunately, this is required because the data stream I'm reading that tells me what to draw immediately, so I don't have many good options for optimizing the scene.

I started experimenting with the TextRegionUtil and I found that I drop few if any frames even when I'm drawing 100 strings. This is great, but I'm got 2 problems: the text renders strangely and the rest of my shapes will not display after any drawString3D call.

First, here is what my text looks like:


The 'w' is rendered poorly. As well, the text is rendered as though it's in a black box, rather than with a transparent background (like the TextRenderer does). The font I'm using is arial, pulled from my windows\fonts folder.

The second issue likely comes down to my ancient use of OpenGL and a lack of understanding about what is exactly going on. A rough example of the code is:

FontTest.java

In display()

        gl.glClear(GL_COLOR_BUFFER_BIT);

        gl.glColor4f(1, 0, 0, 1);
        gl.glPushMatrix();
        gl.glBegin(GL2.GL_TRIANGLES);
            gl.glVertex3d(0, 0, 0.0);
            gl.glVertex3d(-0.1, -0.2, 0.0);
            gl.glVertex3d(0.1, -0.2, 0.0);
        gl.glEnd();
        gl.glPopMatrix();

            PMVMatrix pmv = renderer.getMatrix();
            pmv.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
            pmv.glLoadIdentity();
            float pixelSize = font.getPixelSize(32, 96);
            pmv.glPushMatrix();
            pmv.glTranslatef((float)pt[0], (float)pt[1], -1);
            util.drawString3D(gl, renderer, font, pixelSize, "Test new", null, new int[]{2});
            pmv.glPopMatrix();
         
If I comment out the drawString3D line then I can see my triangle. If I leave that line in then I see my text, but no triangle.

Any help on either issue (rendering artifacts and missing shapes) would be greatly appreciated.

This behaves basically identically on:

win10
i7-10850H
GeForce GTX 1650

and

Ubuntu 20
Atom E3940
Intel HD 500

Thank you!
Reply | Threaded
Open this post in threaded view
|

Re: New to working with TextRegionUtil

dmclean
After lots of looking around, reading old posts and experimenting with code I've got something that (mostly) works. The main basis for my solution comes from:

awt Glyph vector interpretation
Points on a quad

There are limitations to my solution:

1. It's based on pre-release JOGL code
2. Some fonts will not draw properly without a few hacks (M and W are particularly bad in many fonts).
3. This is aimed mainly at ASCII characters. I'm sure it will work for many other characters in the latin set. I'm sure it will not work for many other languages as is (mainly to do with the hacks required for some characters).
4. Works with the GL2 object.

This example is based on Courier Bold, it's the first font I found that did not require extra hacks for any characters. I was also able to get Arial to work with a few tricks. I can't tell if the rendering issues are with my glyph interpretation or the OutlineShape class. I assume it's something I'm doing wrong.

Rendering speed appears reasonable, at least it meets my requirements. There are definitely ways to improve the implementation to make it much faster for huge amounts of text. The important points of the solution are how to interpret the font glyph iterator and specifically plotting the quadratic sections of the glyphs.  

Here is the class for a single character. The full example class is attached. Expanding the example code to draw strings is just a matter of getting the glyph bounds from the GlyphVector and using the bounds to translate for each character.

    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);
        }
    }
FontTest2.java
courbd.ttf