Getting width of rendered font using stb bindings

Started by Briggybros, August 24, 2016, 18:30:08

Previous topic - Next topic

Briggybros

Hello, I'm trying to calculate the width of a font string when using stb_truetype. I render my font like so:
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:

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?

spasi

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.

Briggybros

So I have this:

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.

Cornix


Briggybros


spasi

Quote from: Briggybros on September 19, 2016, 18:36:07So I have this:

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).

Briggybros

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.

spasi

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.