BitmapFont producing incorrect values for ascent, descent, and cap height
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 25.4k
- Forks
- 6.5k
- Avg merge
- 1d 16h
- Merged PRs (30d)
- 5
Description
I am loading a BitmapFont from png and fnt file (uploaded as a txt because of restrictions) like so:
public class SSCCE implements ApplicationAdapter{
public static void main(String[] args) throws Throwable{
new LwjglApplication(new SSCCE());
}
public void create(){
BitmapFont font = new BitmapFont(Gdx.files.internal("cnc.fnt"), Gdx.files.internal("cnc.png"), false);
System.out.println("Line Height: " + font.getLineHeight());
System.out.println("Cap Height: " + font.getCapHeight());
System.out.println("Ascent: " + font.getAscent());
System.out.println("Descent: " + font.getDescent());
}
public void render(){}
}
The fnt file includes the following:
- lineHeight=13
- base=11
- height=9 (on all characters)
- yoffset=-2 (on all characters)
Issue 1: Incorrect getDescent
Expectation: According to the docs, getDescent() is described as "the distance from the bottom of the glyph that extends the lowest to the baseline. This number is negative". All characters have a -2 yoffset so are 2 pixels below the baseline, and I expect a descent of -2.
Result: font.getDescent() produces 0, which is incorrect. If I change all yoffsets from -2 to 2 font.getDescent() still produces 0.
Notes: This needed a few changes because of flip, and also because if all yoffsets are positive, then all glyphs are above the baseline and descent will be positive - therefore it cannot be initialised at 0.
Change L548 to: descent = Integer.MAX_VALUE;
Change L596 to: if (glyph.width > 0 && glyph.height > 0) descent = Math.min((flip ? 0 : glyph.height) + glyph.yoffset, descent);
Add Before L598: if(descent == Integer.MAX_VALUE) descent = 0;
With this change the docs should probably be clarified to say "This number is negative if the lowest glyph is below the baseline", or otherwise just don't implement the MAX_VALUE lines.
Edit: actually I was thinking about this a bit more and the fnt format seems to generally work with variables considering 0,0 to be in the top left with y down as positive. My yoffset maybe should be 2 not -2, and the above code might need a change. Either way its broken though.
Issue 2: Incorrect getCapHeight
Expectation: According to the docs, getCapHeight() is described as "the distance from the top of most uppercase characters to the baseline". Every character is height 9 and yoffset -2, so I expect a cap height of 7.
Result: font.getCapHeight() produces 9, which is incorrect.
Notes: I can see in the code that cap height does not incorporate yoffset at all. In most fonts capitals will always be at baseline, and my example file has 2 pixels of blank space below each character which arguably makes my configuration incorrect, but its not impossible for a font to exist with elaborate capitals (like cursives) that have details below the baseline requring a yoffset. Additionally I can see from the code that where the docs say "most uppercase characters" it should probably say "a typical uppercase character".
Issue 3: Incorrect getAscent
Expectation: According to the docs, getAscent() is described as "the distance from the cap height to the top of the tallest glyph". Every character is the same height, so my capitals are equal to the tallest glyphs, and I expect an ascent of 0.
Result: font.getAscent() produces 2, which is incorrect.
Notes: I can see in the code at L660 that ascent is actually calculated as:
ascent = baseLine - capHeight;
Which is the distance from the cap height to the top of the line. In this case, either:
- The docs are correct. I don't think it's this because in most fonts, the capitals will be the tallest characters anyway, and therefore this value will very often just be 0.
- The code is correct. I don't think it's this because then getAscent will no longer be the opposite of getDescent (i.e. calculating a distance within the height of the glyphs)
- Both are incorrect, and it should be the difference between the top of lower case letters and the tallest character. (i.e the diff between xHeight and capHeight... I don't know maybe)
Related mini feature request:
getAscent and getDescent return (according to their current docs at least) vertical spacing within the glyph. It would be good to add something like:
getAscentBuffer(): return the distance between cap height or maybe the tallest glyph, and the top of the line (i.e. current behaviour of getAscent())
getDescentBuffer(): return the distance between the bottom of lowest glyph and the bottom of the line (i.e. opposite of above)
Version of LibGDX and/or relevant dependencies
Latest commit ac4c5331a2d646efcf0911c51964eff0e99ea31a
However, this appears to have been the case for a long time.
Affected platforms
- Android
- iOS (robovm)
- iOS (MOE)
- HTML/GWT
- Windows
- Linux
- MacOS
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by inspecting BitmapFont's ascent, descent, and cap-height calculations and the related API documentation. Clarify the intended coordinate convention and metric definitions first, then add focused regression tests for the reported font data; done means the documented values and test expectations agree.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- computer-graphics, game-dev
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100