Using stb_truetype

Started by Cornix, August 02, 2017, 11:45:09

Previous topic - Next topic

Cornix

Hello all,

I am trying to get into the stbtt_* functions but was very disappointed when I realized there is hardly any information about those methods on the internet. At least I couldnt find anything.

I wanted to calculate the size of a string when rendered, my research only got me to this:
https://stackoverflow.com/questions/39129632/stb-truetype-calculating-the-dimensions-of-a-string

which does not contain a satisfactory answer. So I ask here, hopeful that somebody else may have figured these things out before:

1) How do I get the exact size of a string when rendered with a font?

2) Which data should I cache and which data can I throw away?

3) Can I reliably query the font name, the style (italic, bold, etc), the point size (like with the Font class from AWT) with stbtt?

4) Can I load system fonts from the OS without needing to load a .ttf file?

5) What data do I need to manually release to avoid memory leaks?

6) When I try to read the VMetrics of a font I get very suspicious values which can not be correct. Code:
try (MemoryStack stack = stackPush()) {
				IntBuffer bufAscent = stack.ints(0);
				IntBuffer bufDescent = stack.ints(0);
				IntBuffer bufLineGap = stack.ints(0);
				
				STBTTFontinfo fontInfo = STBTTFontinfo.mallocStack(stack);
				stbtt_InitFont(fontInfo, ttfFileData); // ttfFileData is the same data I would pass to stbtt_BakeFontBitmap as first parameter
				stbtt_GetFontVMetrics(fontInfo, bufAscent, bufDescent, bufLineGap);
				
				ascent = bufAscent.get(0);
				descent = bufDescent.get(0);
				lineGap = bufLineGap.get(0);
				System.out.println("ascent="+ascent);
				System.out.println("descent="+descent);
				System.out.println("lineGap="+lineGap);
			}


7) What exactly does "stb_easy_font_print" do? Does it render each glyph as a sequence of untextured quads? The demo doesnt seem to use any shaders or textures or anything.

I am very thankful for any answers.

Cornix

I think I managed to solve problem No.6. The following code seems to calculate the size of rendered text correctly (although it does have problems with whitespace!)
try (MemoryStack stack = MemoryStack.stackPush()) {
				STBTTFontinfo fontInfo = STBTTFontinfo.mallocStack(stack);
				stbtt_InitFont(fontInfo, ttfFileData);
				float pixelScale = stbtt_ScaleForPixelHeight(fontInfo, fontHeight);
				
				IntBuffer bufX0 = stack.ints(0);
				IntBuffer bufX1 = stack.ints(0);
				IntBuffer bufY0 = stack.ints(0);
				IntBuffer bufY1 = stack.ints(0);
				for (int codePoint = BAKE_FONT_FIRST_CHAR;
						codePoint <= BAKE_FONT_LAST_CHAR;
						codePoint++)
				{
					stbtt_GetCodepointBox(fontInfo, codePoint, bufX0, bufY0, bufX1, bufY1);
					float x0 = pixelScale * bufX0.get(0);
					float y0 = pixelScale * bufY0.get(0);
					float x1 = pixelScale * bufX1.get(0);
					float y1 = pixelScale * bufY1.get(0);
					float glyphW = Math.abs(x0 - x1);
					float glyphH = Math.abs(y0 - y1);
				}
			}

How do I find out how large a whitespace is for a given font?

bobjob

Here is an example of a font class that I believe answers your question about rendered string size (see attached file):

there is an alignment option, to align from the right.

the way this is possible is by getting the width of a line of text up untill the new line character.

I used the function:
public float getLineWidth(String text, int startChar) {

		xb.put(0, 0);
		yb.put(0, 0);

		for (int i = startChar; i < text.length(); i++) {
			if (text.charAt(i) == '\n')
				break;
			STBTruetype.stbtt_GetPackedQuad(chardata, BITMAP_W, BITMAP_H, text.charAt(i), xb, yb, q,
					(font == 0 && integer_align));
		}
		return q.x1();
	}


this is in the TrueTypeFont.java file

keep in mind that this only checks the "width" of the line of text not the height.
it also only gets the line width up until a newline character.

spasi

Quote from: Cornix on August 02, 2017, 11:45:091) How do I get the exact size of a string when rendered with a font?

You loop over each codepoint and sum the advance returned by stbtt_GetCodepointHMetrics. You may also use stbtt_GetGlyphKernAdvance for fonts that include kerning information. You use stbtt_GetFontVMetrics to calculate the height.

All metrics returned are in "unscaled" coordinates. You must scale them for the specific point/pixel size you're rendering at. Use stbtt_ScaleForPixelHeight and stbtt_ScaleForMappingEmToPixels to get the scaling factor.

Quote from: Cornix on August 02, 2017, 11:45:092) Which data should I cache and which data can I throw away?

In theory you could cache everything and query a Java data structure instead of calling JNI methods. Not sure if it's worth the effort, but certainly doable if you notice a performance bottleneck there.

Quote from: Cornix on August 02, 2017, 11:45:093) Can I reliably query the font name, the style (italic, bold, etc), the point size (like with the Font class from AWT) with stbtt?

See stbtt_GetFontNameString and follow the javadoc links to learn more about the name table.

.ttf fonts can be rendered to any point size. It's not something you query, you specify it when creating a texture atlas.

Quote from: Cornix on August 02, 2017, 11:45:094) Can I load system fonts from the OS without needing to load a .ttf file?

I don't think so. But you can use paths to known OS fonts.

Quote from: Cornix on August 02, 2017, 11:45:095) What data do I need to manually release to avoid memory leaks?

Only those that have a stbtt_FreeX function. And the .ttf buffer of course and the STBTTFontinfo struct, depending on how they've been allocated.

Quote from: Cornix on August 02, 2017, 11:45:096) When I try to read the VMetrics of a font I get very suspicious values which can not be correct.

See 1) about unscaled coordinates. Provide more information if that's not it.

Quote from: Cornix on August 02, 2017, 11:45:097) What exactly does "stb_easy_font_print" do? Does it render each glyph as a sequence of untextured quads? The demo doesnt seem to use any shaders or textures or anything.

Yes, stb_easy_font generates simple (blocky) geometry for each character. That's the easiest way to get text on screen, hence the name.

Cornix

Quote from: spasi on August 03, 2017, 20:40:43
Quote from: Cornix on August 02, 2017, 11:45:091) How do I get the exact size of a string when rendered with a font?

You loop over each codepoint and sum the advance returned by stbtt_GetCodepointHMetrics. You may also use stbtt_GetGlyphKernAdvance for fonts that include kerning information. You use stbtt_GetFontVMetrics to calculate the height.

All metrics returned are in "unscaled" coordinates. You must scale them for the specific point/pixel size you're rendering at. Use stbtt_ScaleForPixelHeight and stbtt_ScaleForMappingEmToPixels to get the scaling factor.
What is the difference between ScaleForPixelHeight and ScaleForMappingEmToPixels? The documentation really doesnt say much about it and I cant figure out what the "Em" is standing for.
If you take a look at my second post in this thread (sorry for the double post) you will see that I kinda did what you suggested. But the numbers I get are still wrong. The width is slightly too small and the whitespace does not have a width at all. Do you have any ideas what I did wrong? The font I am using is Arial by the way so I doubt its a problem with the font.

Edit: Did some further testing. The method "stbtt_GetCodepointBox" is definitely not returning the correct values to calculate the width of glyphs with. The values returned by "stbtt_GetCodepointHMetrics" seem to be much better. But there does not seem to be a way to get the height of a glyph since there is no "stbtt_GetCodepointVMetrics" method.

Quote from: spasi on August 03, 2017, 20:40:43
Quote from: Cornix on August 02, 2017, 11:45:092) Which data should I cache and which data can I throw away?

In theory you could cache everything and query a Java data structure instead of calling JNI methods. Not sure if it's worth the effort, but certainly doable if you notice a performance bottleneck there.
Sorry for being vague with that question. What I meant to ask is: What data (if any) is "expensive" to calculate and is recommended to be cached.

Quote from: spasi on August 03, 2017, 20:40:43
Quote from: Cornix on August 02, 2017, 11:45:093) Can I reliably query the font name, the style (italic, bold, etc), the point size (like with the Font class from AWT) with stbtt?

See stbtt_GetFontNameString and follow the javadoc links to learn more about the name table.

.ttf fonts can be rendered to any point size. It's not something you query, you specify it when creating a texture atlas.
I couldnt get the GetFontNameString method to work unfortunately. Do you have any example code for this? I even looked at the official stb_truetype repo but they don't seem to have any examples for this either. The parameters are very confusing with hardly any useful documentation. The javadoc does not even mention what nameID is supposed to be but it doesnt quite explain what the other arguments do either.

And as far as I understood stb_truetype uses pixel size and not point size for the font size. Those are not the same. Is there any way to get the point size? Its not a problem if there isnt, I was just curious.

Quote from: spasi on August 03, 2017, 20:40:43
Quote from: Cornix on August 02, 2017, 11:45:095) What data do I need to manually release to avoid memory leaks?

Only those that have a stbtt_FreeX function. And the .ttf buffer of course and the STBTTFontinfo struct, depending on how they've been allocated.
There is a "stbtt_FreeBitmap" method but I believe that is not the same bitmap as the one in "stbtt_BakeFontBitmap", right? Just want to make sure I understand this correctly.


Thank you for the help, I really appreciate the awesome work you are doing.

spasi

Quote from: Cornix on August 03, 2017, 21:45:06What is the difference between ScaleForPixelHeight and ScaleForMappingEmToPixels? The documentation really doesnt say much about it and I cant figure out what the "Em" is standing for.

Em (typography).

Also, this is what stb_truetype says about points:

QuoteThe preferred interface for specifying font sizes in stb_truetype is to specify how tall the font's vertical extent should be in pixels. If that sounds good enough, skip the next paragraph.

Most font APIs instead use "points", which are a common typographic measurement for describing font size, defined as 72 points per inch. stb_truetype  provides a point API for compatibility. However, true "per inch" conventions don't make much sense on computer displays since different monitors have different number of pixels per inch. For example, Windows traditionally uses a convention that there are 96 pixels per inch, thus making 'inch' measurements have nothing to do with inches, and thus effectively defining a point to be 1.333 pixels. Additionally, the TrueType font data provides an explicit scale factor to scale a given font's glyphs to points, but the author has observed that this scale factor is often wrong for non-commercial fonts, thus making fonts scaled in points according to the TrueType spec incoherently sized in practice.

Quote from: Cornix on August 03, 2017, 21:45:06But there does not seem to be a way to get the height of a glyph since there is no "stbtt_GetCodepointVMetrics" method.

There are no per-glyph vertical metrics. They apply to the entire font. If you need the tight bounds of a glyph (very rare), you can use stbtt_GetGlyphBox or stbtt_GetGlyphShape.

Quote from: Cornix on August 03, 2017, 21:45:06What data (if any) is "expensive" to calculate and is recommended to be cached.

Anything that bakes/rasterizes bitmaps. The metric queries should be cheap. If I were doing things in tight loops, I would measure performance and inspect stb_truetype.h to get an idea of the amount of work involved in each function. If it looks complex enough, then caching that data might be worth the effort.

Quote from: Cornix on August 03, 2017, 21:45:06The javadoc does not even mention what nameID is supposed to be but it doesnt quite explain what the other arguments do either.

Did you read the TrueType Reference Manual? Specifically the Name Identifiers table. I actually haven't used stbtt_GetFontNameString yet, I'll test it and prepare some example code tomorrow.

Quote from: Cornix on August 03, 2017, 21:45:06And as far as I understood stb_truetype uses pixel size and not point size for the font size. Those are not the same. Is there any way to get the point size? Its not a problem if there isnt, I was just curious.

See above.

Quote from: Cornix on August 03, 2017, 21:45:06There is a "stbtt_FreeBitmap" method but I believe that is not the same bitmap as the one in "stbtt_BakeFontBitmap", right? Just want to make sure I understand this correctly.

See the javadoc: "Frees a bitmap allocated by stbtt_GetCodepointBitmap, stbtt_GetCodepointBitmapSubpixel, stbtt_GetGlyphBitmap or stbtt_GetGlyphBitmapSubpixel."

Cornix

Quote from: spasi on August 03, 2017, 22:27:52
Quote from: Cornix on August 03, 2017, 21:45:06What is the difference between ScaleForPixelHeight and ScaleForMappingEmToPixels? The documentation really doesnt say much about it and I cant figure out what the "Em" is standing for.

Em (typography).

Also, this is what stb_truetype says about points:

QuoteThe preferred interface for specifying font sizes in stb_truetype is to specify how tall the font's vertical extent should be in pixels. If that sounds good enough, skip the next paragraph.

Most font APIs instead use "points", which are a common typographic measurement for describing font size, defined as 72 points per inch. stb_truetype  provides a point API for compatibility. However, true "per inch" conventions don't make much sense on computer displays since different monitors have different number of pixels per inch. For example, Windows traditionally uses a convention that there are 96 pixels per inch, thus making 'inch' measurements have nothing to do with inches, and thus effectively defining a point to be 1.333 pixels. Additionally, the TrueType font data provides an explicit scale factor to scale a given font's glyphs to points, but the author has observed that this scale factor is often wrong for non-commercial fonts, thus making fonts scaled in points according to the TrueType spec incoherently sized in practice.

Quote from: Cornix on August 03, 2017, 21:45:06And as far as I understood stb_truetype uses pixel size and not point size for the font size. Those are not the same. Is there any way to get the point size? Its not a problem if there isnt, I was just curious.

See above.
Got it, thanks!

Quote from: spasi on August 03, 2017, 22:27:52
Quote from: Cornix on August 03, 2017, 21:45:06But there does not seem to be a way to get the height of a glyph since there is no "stbtt_GetCodepointVMetrics" method.

There are no per-glyph vertical metrics. They apply to the entire font. If you need the tight bounds of a glyph (very rare), you can use stbtt_GetGlyphBox or stbtt_GetGlyphShape.
I assume stbtt_GetGlyphBox and stbtt_GetCodepointBox do the same thing with the exception, that stbtt_GetGlyphBox is slightly faster because it does not need to translate from codepoint to glyph, correct?

Quote from: spasi on August 03, 2017, 22:27:52
Quote from: Cornix on August 03, 2017, 21:45:06What data (if any) is "expensive" to calculate and is recommended to be cached.

Anything that bakes/rasterizes bitmaps. The metric queries should be cheap. If I were doing things in tight loops, I would measure performance and inspect stb_truetype.h to get an idea of the amount of work involved in each function. If it looks complex enough, then caching that data might be worth the effort.
Understood. Thanks. I will look into it.

Quote from: spasi on August 03, 2017, 22:27:52
Quote from: Cornix on August 03, 2017, 21:45:06The javadoc does not even mention what nameID is supposed to be but it doesnt quite explain what the other arguments do either.

Did you read the TrueType Reference Manual? Specifically the Name Identifiers table. I actually haven't used stbtt_GetFontNameString yet, I'll test it and prepare some example code tomorrow.
I did not read the reference manual, thank you for the link. I will play around with it and try to make something work. I will anticipate your demo code, thank you for the effort you put into this.

Quote from: spasi on August 03, 2017, 22:27:52
Quote from: Cornix on August 03, 2017, 21:45:06There is a "stbtt_FreeBitmap" method but I believe that is not the same bitmap as the one in "stbtt_BakeFontBitmap", right? Just want to make sure I understand this correctly.

See the javadoc: "Frees a bitmap allocated by stbtt_GetCodepointBitmap, stbtt_GetCodepointBitmapSubpixel, stbtt_GetGlyphBitmap or stbtt_GetGlyphBitmapSubpixel."
Yeah, I saw that. I just wanted to make sure this is accurate. I had bad experiences with descriptions like these in third party API's before. Thank you.

spasi

Quote from: Cornix on August 03, 2017, 23:06:17stbtt_GetGlyphBox is slightly faster because it does not need to translate from codepoint to glyph, correct?

Correct.

Quote from: Cornix on August 03, 2017, 23:06:17I will anticipate your demo code

Using the following loop:

for (int i = 0; i <= Short.MAX_VALUE; i++) {
    ByteBuffer value = stbtt_GetFontNameString(fontInfo, STBTT_PLATFORM_ID_MICROSOFT, STBTT_MS_EID_UNICODE_BMP, STBTT_MS_LANG_ENGLISH, i);
    if (value != null) {
        System.out.println(i + " - " + memUTF16(value.order(ByteOrder.BIG_ENDIAN)));
    }
}


I get this output for demo/FiraSans.ttf:

Quote0 - Digitized data copyright 2012-2016, The Mozilla Foundation and Telefonica S.A.
1 - Fira Sans
2 - Regular
3 - 4.203;CTDB;FiraSans-Regular
4 - Fira Sans Regular
5 - Version 4.203
6 - FiraSans-Regular
7 - Fira Sans is a trademark of The Mozilla Corporation.
8 - Carrois Corporate GbR & Edenspiekermann AG
9 - Carrois Corporate & Edenspiekermann AG
11 - http://www.carrois.com
12 - http://www.carrois.com
13 - Licensed under the Open Font License, version 1.1 or later
14 - http://scripts.sil.org/OFL

and this for C:/Windows/Fonts/Arial.ttf:

Quote0 - Ã,© 2015 The Monotype Corporation. All Rights Reserved.

Hebrew OpenType Layout logic copyright Ã,© 2003 & 2007, Ralph Hancock & John Hudson. This layout logic for Biblical Hebrew is open source software under the MIT License; see embedded license description for details.
1 - Arial
2 - Regular
3 - Monotype:Arial Regular (Microsoft)
4 - Arial
5 - Version 6.90
6 - ArialMT
7 - Arial is a trademark of The Monotype Corporation.
8 - The Monotype Corporation
9 - Monotype Type Drawing Office - Robin Nicholas, Patricia Saunders 1982
13 - Microsoft supplied font. You may use this font to create, display, and print content as permitted by the license terms or terms of use, of the Microsoft product, service, or content in which this font was included. You may only (i) embed this font in content as permitted by the embedding restrictions included in this font; and (ii) temporarily download this font to a printer or other output device to help print content. Any other use is prohibited.

The following license, based on the MIT license (http://en.wikipedia.org/wiki/MIT_License), applies to the OpenType Layout logic for Biblical Hebrew ìLayout Logicî as jointly developed by Ralph Hancock and John Hudson.

Permission is hereby granted, free of charge, to any person obtaining a copy of the OpenType Layout logic for Biblical Hebrew and associated documentation files (the ìLayout Logic Softwareî), to deal in the Layout Logic Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Layout Logic Software, and to permit persons to whom the Layout Logic Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Layout Logic Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Cornix

Thank you very much for the quick, thorough and professional help. All my questions are answered now.