Hello Guest

Getting width of rendered font using stb bindings

  • 7 Replies
  • 6682 Views
Getting width of rendered font using stb bindings
« on: August 24, 2016, 18:30:08 »
Hello, I'm trying to calculate the width of a font string when using stb_truetype. I render my font like so:
Code: [Select]
try (MemoryStack stack = stackPush()) {
FloatBuffer x = stack.floats(0.0f);
FloatBuffer y = stack.floats(0.0f);

STBTTAlignedQuad q = STBTTAlignedQuad.mallocStack(stack);

glScalef(this.scale, this.scale,
this.scale);

glBegin(GL_QUADS);
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '\n') {
y.put(0, y.get(0) + FONT_HEIGHT);
x.put(0, 0.0f);
continue;
} else if (c < 32 || 128 <= c)
continue;
stbtt_GetBakedQuad(cdata, BITMAP_W, BITMAP_H, c - 32, x, y, q, true);

glTexCoord2f(q.s0(), q.t0());
glVertex2f(q.x0(), q.y0());

glTexCoord2f(q.s1(), q.t0());
glVertex2f(q.x1(), q.y0());

glTexCoord2f(q.s1(), q.t1());
glVertex2f(q.x1(), q.y1());

glTexCoord2f(q.s0(), q.t1());
glVertex2f(q.x0(), q.y1());
}
glEnd();

glScalef(1f, 1f, 1f);
}

which works fine, the text is rendered in the font.

So I try to work out the width of the string like so:

Code: [Select]
public float getWidth(String text) {
float maxWidth = 0;
try (MemoryStack stack = stackPush()) {
FloatBuffer x = stack.floats(0.0f);
FloatBuffer y = stack.floats(0.0f);

STBTTAlignedQuad q = STBTTAlignedQuad.mallocStack(stack);

for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '\n') {
y.put(0, y.get(0) + FONT_HEIGHT);
x.put(0, 0.0f);
continue;
} else if (c < 32 || 128 <= c)
continue;
stbtt_GetBakedQuad(cdata, BITMAP_W, BITMAP_H, c - 32, x, y, q, true);

if (q.s1() > maxWidth)
maxWidth = q.s1();
}
}
return maxWidth * this.scale;
}

but this returns a value much less than it should. Where am I going wrong here?

*

Offline spasi

  • *****
  • 2261
    • WebHotelier
Re: Getting width of rendered font using stb bindings
« Reply #1 on: August 24, 2016, 23:38:20 »
The quad's .s1() value is a texture coordinate, it doesn't have anything to do with the glyph position. Using .x1() should give a more accurate result...

...but it would still be wrong. You need to use the stbtt_GetCodepointHMetrics function (or the more efficient stbtt_GetGlyphHMetrics) and accumulate the advance of each glyph. The result should be scaled by the font size.

There are many other functions in stb_truetype that return metrics, offsets, bounds and even glyph shapes, so everything you'll need is there.

Re: Getting width of rendered font using stb bindings
« Reply #2 on: September 19, 2016, 18:36:07 »
So I have this:

Code: [Select]
public float getWidth(String text) {
float length = 0f;
for (int i = 0; i < text.length(); i++) {
IntBuffer advancewidth = IntBuffer.allocate(1);
IntBuffer leftsidebearing = IntBuffer.allocate(1);
stbtt_GetCodepointHMetrics(info, (int)text.charAt(i), advancewidth, leftsidebearing);
length += advancewidth.get(0) + leftsidebearing.get(0);
}
return length * scale;
}

but it always just returns 0.

*

Offline Cornix

  • *****
  • 488
Re: Getting width of rendered font using stb bindings
« Reply #3 on: September 19, 2016, 18:54:35 »
Perhaps your "scale" attribute is 0.

Re: Getting width of rendered font using stb bindings
« Reply #4 on: September 19, 2016, 20:50:27 »
I can assure you this is not the case.

*

Offline spasi

  • *****
  • 2261
    • WebHotelier
Re: Getting width of rendered font using stb bindings
« Reply #5 on: September 19, 2016, 20:52:54 »
So I have this:

Code: [Select]
public float getWidth(String text) {
float length = 0f;
for (int i = 0; i < text.length(); i++) {
IntBuffer advancewidth = IntBuffer.allocate(1);
IntBuffer leftsidebearing = IntBuffer.allocate(1);
stbtt_GetCodepointHMetrics(info, (int)text.charAt(i), advancewidth, leftsidebearing);
length += advancewidth.get(0) + leftsidebearing.get(0);
}
return length * scale;
}

but it always just returns 0.

The IntBuffers you allocate are heap buffers. This is not supported, you should use BufferUtils (or MemoryUtil/MemoryStack) to allocate off-heap buffers. The above code does not crash because it so happens that advanceWidth and leftSideBearing are allowed to be null (no value is returned in that case). When LWJGL tries to retrieve the off-heap address of a heap buffer, it always reads 0 (=NULL).

Re: Getting width of rendered font using stb bindings
« Reply #6 on: September 20, 2016, 01:01:28 »
While this does now return a number that number is approximately 100x too large; But not exactly (When divided by 100 it's still not correct.

*

Offline spasi

  • *****
  • 2261
    • WebHotelier
Re: Getting width of rendered font using stb bindings
« Reply #7 on: September 20, 2016, 10:46:23 »
That's because all metrics are returned in unscaled coordinates (this is mentioned in the Javadoc). You can use stbtt_ScaleForPixelHeight to compute an appropriate scale factor.