LWJGL Forum

Please login or register.

Login with username, password and session length

Author Topic: Using stb_truetype  (Read 192 times)

Cornix

  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 464
Using stb_truetype
« on: August 02, 2017, 11:45:09 »

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:
Code: [Select]
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.
Logged

Cornix

  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 464
Re: Using stb_truetype
« Reply #1 on: August 03, 2017, 15:51:03 »

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!)
Code: [Select]
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?
Logged

bobjob

  • Prolific Timewaster
  • ****
  • Offline Offline
  • Posts: 394
  • LWJGL: WOW SO GOOD
Re: Using stb_truetype
« Reply #2 on: August 03, 2017, 16:06:40 »

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:
Code: [Select]
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.
Logged

spasi

  • Administrator
  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 1837
Re: Using stb_truetype
« Reply #3 on: August 03, 2017, 20:40:43 »

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

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

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?

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.

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

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

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

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.

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

Cornix

  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 464
Re: Using stb_truetype
« Reply #4 on: August 03, 2017, 21:45:06 »

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

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

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?

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.

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

spasi

  • Administrator
  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 1837
Re: Using stb_truetype
« Reply #5 on: August 03, 2017, 22:27:52 »

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.

Em (typography).

Also, this is what stb_truetype says about points:

Quote
The 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.

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

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

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

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.

See above.

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.

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

Cornix

  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 464
Re: Using stb_truetype
« Reply #6 on: August 03, 2017, 23:06:17 »

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.

Em (typography).

Also, this is what stb_truetype says about points:

Quote
The 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.

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.

See above.
Got it, thanks!

But 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?

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

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

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.

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

spasi

  • Administrator
  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 1837
Re: Using stb_truetype
« Reply #7 on: August 04, 2017, 07:29:25 »

stbtt_GetGlyphBox is slightly faster because it does not need to translate from codepoint to glyph, correct?

Correct.

I will anticipate your demo code

Using the following loop:

Code: [Select]
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:

Quote
0 - 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:

Quote
0 - © 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.
Logged

Cornix

  • Nerdus Imperius
  • *****
  • Offline Offline
  • Posts: 464
Re: Using stb_truetype
« Reply #8 on: August 04, 2017, 20:59:22 »

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