libsdl-org / SDL_ttf

Support for TrueType (.ttf) font files with Simple Directmedia Layer.
zlib License
402 stars 131 forks source link

API: Support non-rounded outlines in TTF_SetFontOutline #422

Open rubenlg opened 1 month ago

rubenlg commented 1 month ago

TTF_SetFontOutline always produces rounded outlines, which looks great for most fonts, but not for pixel art fonts. Here is an example: image

It would be great to support non rounded outlines too, configurable with a flag, to get blocky outlines instead in those cases. This is how it should ideally look when the non-ronded flag is ON: image

As a workaround, the same effect can be achieved by rendering the text 9 times, but SDL_ttf has an opportunity to do it more efficiently by controlling FT_Stroker_LineJoin and FT_Stroker_LineCap when calling FT_Stroker_Set.

An alternative could be to expose functions that allow controlling the line join and line cap of the outline, allowing all the variants supported by the freetype library.

slouken commented 1 month ago

I think passing through the stroker parameters is reasonable. What are the set of parameters you would need here?

rubenlg commented 1 month ago

That's a good question. I'll try a few combinations with the freetype library directly to understand which combination works best for pixel fonts, and circle back with the results. My suspicion is that FT_STROKER_LINEJOIN_MITER should be enough, but I need to test it.

rubenlg commented 1 month ago

I tried all 12 combinations of linecap and linejoin and none of them render the pixel outline properly. I end up with either round corners or angle corners. I'll have to fall back to rendering the text 9 times instead. I'll close this bug since there isn't much SDL_ttf can do here. Sorry for the noise :disappointed:

rubenlg commented 1 month ago

I just realized I forgot to change the miter parameter when calling FT_Stroker_Set. If I set it correctly, to sqrt(2) (so that all the way up to right angles, you get sharp corners), then it works for all combinations that use either FT_STROKER_LINEJOIN_MITER_VARIABLE or FT_STROKER_LINEJOIN_MITER_FIXED. The linecap doesn't matter, all of them work correctly for pixel fonts.

So this would be the correct code to get pixel fonts rendering sharply:

#define RIGHT_ANGLE_MITER_LIMIT 92682  // sqrt(2) in 16.16 fixed-point format
[...]
FT_Stroker_Set(
    font->stroker, outline * 64,
    FT_STROKER_LINECAP_ROUND, // This one doesn't matter.
    FT_STROKER_LINEJOIN_MITER_FIXED,
    RIGHT_ANGLE_MITER_LIMIT);
[...]
slouken commented 1 month ago

Interesting, I'll see if I can add something for SDL_ttf 3.0. Thanks!

rubenlg commented 1 month ago

Thanks!

One last thing: I tested these options with non-pixel fonts, and while I know it's mostly a matter of preference, I also prefer the straight corners on HD fonts:

FT_STROKER_LINEJOIN_MITER_FIXED: image

FT_STROKER_LINEJOIN_ROUND: image

The rounded version feels a bit less formal to me, which could work well in some contexts, but not for what I need.