[ft-devel] FT_Render_Glyph(...) errors for high pixel-per-em (ppem) valu
From:
Colin Fahey
Subject:
[ft-devel] FT_Render_Glyph(...) errors for high pixel-per-em (ppem) values
Date:
Fri, 20 Oct 2017 08:17:20 -0700
FT_Render_Glyph(...) errors for high pixel-per-em (ppem) values
2017 October 20
INTRODUCTION
For pixels-per-em (ppem) values up to, and including, 1126 ppem, FT_Render_Glyph(...) succeeds for ALL 736,482 glyphs in a collection of 334 TrueType font face files (most of which shipped with Windows 7, and others which ship with popular open-source applications) -- for ALL combinations of FT_Load_Glyph(...) loading-flags values (e.g., specifying hinting algorithm) and FT_Render_Glyph(...) render-mode values (e.g., anti-aliased, light, and monochrome).
However, for ppem values of 1127 ppem and higher, FT_Render_Glyph(...) *FAILS* (with error code "1" (generally supposed to represent "cannot open resource")) for a very small of glyphs -- for most combinations of FT_Load_Glyph(...) loading-flags values and FT_Render_Glyph(...) render-mode values.
The error has *NOT* been observed when the render-mode value is set to FT_RENDER_MODE_MONO.
For *non-MONO* render-mode values, the number of glyphs for which FT_Render_Glyph(...) returns error "1" increases slowly as the ppem value increases.
For example, for 4096 ppem: 363 glyphs, out of the collection of 736,482 glyphs previously mentioned, cause FT_Render_Glyph(...) to return an error. That's only 0.049 % of a vast number of glyphs. But a few of these glyphs are commonly-used Unicode code points in mainstream fonts (e.g., upper-case "T" in Courier Bold, and "emdash" in Times Italic), and many glyphs in each of a few other popular Windows fonts (ARIALUNI.ttf, msjh.ttf, msjhbd.ttf, msyh.ttf, msybd.ttf, simhei.ttf).
Consider the following two non-MONO render-mode options:
Commit date: "2017-10-12 18:48:57 +0800" (i.e., approximately 8 days ago)
BASIC SEQUENCE OF FREETYPE LIBRARY FUNCTION CALLS
(Note: Full test code appears in a later section.)
Test Function: { error = FT_Init_FreeType( &library );
For each face_size in ppem (e.g., 1024, 2048, 4096), and for each load_flags value, and for each render_mode value: { For each font-face file name: { error = FT_New_Face( library, file_name, 0, &face );
For face_size=4096 (ppem), load_flags=FT_LOAD_DEFAULT, render_mode=FT_RENDER_MODE_NORMAL, 363 glyphs out of a collection of 736,482 glyphs found in 334 popular Windows font face files and open-source font face files will cause FT_Render_Glyph(...) to return error code "1".
A rough stack trace (starting from the parent first):
There are deeper calls within "gray_raster_render(...)", but right now the library code I'm testing was compiled with optimizations enabled, and so stepping through the code with the debugger sometimes does not follow the order of statements in the source code. I need to disable optimizations before I can narrow down the code which is ultimately causing an error to be returned.
COMPLETE SOURCE CODE FOR A TEST PROGRAM
The following code can be put in to a simple Windows "console-mode" application which links to the FreeType library (e.g., freetype2\objs\freetype.lib) and specifies the following additional include directory: [...]\freetype2\include\
This test program check various face_size values (in ppem) (1024, 2048, and 4096), showing that no errors occur for 1024 ppem, and some errors happen for 2048 ppem, and many more errors happen for 4096 ppem.
This test program checks several possible load_flags values (a parameter to FT_Load_Glyph(...)): FT_LOAD_NO_HINTING, FT_LOAD_DEFAULT, // Same as (FT_LOAD_DEFAULT | FT_LOAD_TARGET_NORMAL) (FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT), (FT_LOAD_DEFAULT | FT_LOAD_TARGET_MONO)
This test program checks several possible render_mode values (a parameter to FT_Render_Glyph(...)): FT_RENDER_MODE_NORMAL, FT_RENDER_MODE_LIGHT, FT_RENDER_MODE_MONO. No errors ever occur for FT_RENDER_MODE_MONO.
This program checks the following small set of font face files: "C:\\Windows\\fonts\\constan.ttf", "C:\\Windows\\fonts\\courbd.ttf", "C:\\Windows\\fonts\\msjh.ttf", "C:\\Windows\\fonts\\timesi.ttf", "C:\\Windows\\fonts\\webdings.ttf", "C:\\Windows\\fonts\\wingding.ttf", (One or more of these might not be installed with a clean install of Windows 7 Ultimate. You can comment out any of these items to exclude it.) These font face files were selected because I have observed the problem for glyphs in each of these files. The problem occurs for perhaps a dozen of the 334 font face files installed on my PC. But, keep in mind that even across all 334 font face files I tested, the problem only starts for a single non-popular font face file at 1127 ppem! Only when ppem is much higher, like 2048 or 4096 ppem, does the problem start to appear in a few mainstream files for a notable number of glyphs.
If no error entries appear after a (font_size, load_flags, render_mode) combination heading, then no errors occurred for that combination.
This program tests 3 *large* face sizes, 4 load-flag values, and 3 render-mode vaues, and 6 font face files -- a total of 3*4*3*6 = 216 combinations. And tests ALL glyphs in each file for that combination. ==> This program took approximately 55 MINUTES to execute on my PC.
// Set breakpoint here to conveniently see the error happen again! error = FT_Render_Glyph( face->glyph, render_mode );
continue; return( 0 ); } } } } } }
return( 0 ); }
THOUGHTS ABOUT THE ERRORS
Observations:
(O1) The problem doesn't happen for any load or render parameters for 1126 ppem or lower.
(O2) The problem never happens for MONO rendering mode, even for 4096 ppem; only for NORMAL (anti-aliased 8-bit bitmap) and LIGHT (variation similar to NORMAL).
Thus, all of the following assumes non-MONO rendering modes...
(O3) At 1127 ppem, the error first happens for a very small number of glyphs for obscure fonts (not ones tested by the test code above). For higher ppem values, the error happens for gradually more glyphs for more fonts. At 2048 ppem, the error happens for some glyphs in mainstream fonts. At 4096 ppem, the error happens for even more glyphs -- but is still relatively rare (e.g., 363 glyphs out of 736,482 glyphs in my collection!).
(O4) The glyphs which cause the errors are clearly not the widest or the tallest glyphs contained in the same font face file.
(O5) The glyphs which cause the errors are clearly not unusually high complexity compared to other glyphs in the same font face file. In some cases, the errors happen for very simple glyphs!
(O6) The glyphs which cause the errors do not seem to have unusual positions relative to the EM box area (e.g., the glyphs seem relatively contained in the EM box, while there are numerous other glyphs in the same font face file which stray far outside the EM box).
(O7) Some problem cases had a few shared geometric characteristics:
* A long line segment which is very close to being perfectly horizontal, but might be very slightly tilted (by a fraction of a pixel over a 1000-pixel span).
* A long (~500-1000 pixel), nearly-flat quadratic curve, with a tiny deviation (1-2 pixels) from perfect flatness.
Conclusions:
(C1) Because of (O2): The problem is limited to non-MONO rendering modes; i.e., affects gray-scale rendering only.
(C2) Because of (O4), (O5), and (O2): The problem does not seem to be resource exhaustion problem caused by the size or complexity of the glyphs.
(C3) Because of (O3), (O4), (O6): I don't think coordinate roundoff or truncation (due to large coordinate values) is causing the problem.
MY CURRENT GUESS
I think that something about the slope of nearly-flat lines is breaking part of the gray-scale anti-aliasing calculations in "gray_raster_render(...)", or more specifically in "gray_convert_glyph(...)", or some deeper function.
NEXT STEPS
I'm going to try to learn more about the specific causes of this problem. However, I have very little experience with this code, and so I'm hoping someone familiar with the rasterization code can offer feedback and maybe a fix.