The crisp fonts saga
Thiago Milczarek Sayão
thiago.sayao at gmail.com
Tue Dec 12 16:11:38 UTC 2023
The dates don't match since the change is from 4 months ago and 23.04 was
released on April. So it was some other change.
Em ter., 12 de dez. de 2023 13:03, Thiago Milczarek Sayão <
thiago.sayao at gmail.com> escreveu:
> Fonts started to look better on Ubuntu 23.04
>
> https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/6190
>
> May be related to this change.
>
> Em ter., 12 de dez. de 2023 11:10, Mark Raynsford <org.openjdk at io7m.com>
> escreveu:
>
>> Hello!
>>
>> I've never been particularly satisfied with the font rendering in
>> JavaFX. In particular, on Linux, the text always appears very soft and
>> blurry compared to non-JavaFX applications on the same system. Even
>> applications that render antialiased text with Java2D seem to look
>> better.
>>
>> I decided to take a look into it to see if anything could be done about
>> this, and I have some questions. I'm only looking at the Freetype
>> implementation in Prism currently, as that's all I can realistically
>> test on at the moment.
>>
>> For reference, here's how text rendered at 16px using Terminus TTF
>> looks today:
>>
>> https://ataxia.io7m.com/2023/12/12/hinting_nobitmaps_normal.png
>>
>> I'm not sure if I'm alone on this, but I find this almost migraine-
>> inducing. No other application on my system, including those that use
>> Freetype, using that font at that size will render as blurry as that.
>>
>> Looking at
>> modules/javafx.graphics/src/main/java/com/sun/javafx/font/freetype/FTFo
>> ntFile.java, I see this in initGlyph():
>>
>> ```
>> int flags = OSFreetype.FT_LOAD_RENDER | OSFreetype.FT_LOAD_NO_HINTING |
>> OSFreetype.FT_LOAD_NO_BITMAP;
>> ```
>>
>> Additionally, the code might also add the FT_LOAD_TARGET_NORMAL
>> or FT_LOAD_TARGET_LCD flags later, but I'll assume
>> FT_LOAD_TARGET_NORMAL for the sake of avoiding combinatorial explosions
>> in testing at this point.
>>
>> Essentially, we discard hinting information, and we discard bitmap
>> information. I'm not sure why we do either of these things. I decided
>> to try different combinations of flags to see what would happen.
>>
>> Here's FT_LOAD_RENDER | FT_LOAD_NO_BITMAP (no bitmaps, but using
>> hinting data):
>>
>> https://ataxia.io7m.com/2023/12/12/hinting_nobitmaps_normal.png
>>
>> That's no real improvement. Here's FT_LOAD_RENDER | FT_LOAD_NO_HINTING
>> (ignore hinting data, but use bitmaps if they are included):
>>
>> https://ataxia.io7m.com/2023/12/12/nohinting_bitmaps_normal.png
>>
>> That, to my poor suffering eyes, is already a _vast_ improvement.
>>
>> Let's try including both hinting and bitmaps (FT_LOAD_RENDER):
>>
>> https://ataxia.io7m.com/2023/12/12/hinting_bitmaps_normal.png
>>
>> Inspecting that in an image editor shows the pixels of the text to be
>> identical.
>>
>> So, clearly, Terminus TTF includes bitmaps for smaller text sizes.
>> Let's try another font such as Droid Sans that renders crisply at ~10pt
>> sizes on my system, and that I'm reasonably confident doesn't include
>> any bitmaps.
>>
>> Here's the JavaFX default (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP):
>>
>> https://ataxia.io7m.com/2023/12/12/droid_12_nohinting_nobitmaps.png
>>
>> That's pretty nasty. Let's enable hinting (FT_LOAD_NO_BITMAP):
>>
>> https://ataxia.io7m.com/2023/12/12/droid_12_hinting_nobitmaps.png
>>
>> That's already a lot better. If you overlay the two images in an image
>> editor, it's clear that the glyph shapes are not quite the same (with
>> hinting, some glyphs are ever-so-slightly taller).
>>
>> For completeness, let's allow bitmaps:
>>
>> https://ataxia.io7m.com/2023/12/12/droid_12_hinting_bitmaps.png
>>
>> The rendered glyphs are pixel-identical.
>>
>> Now, most modern desktops have options to disable antialiasing for text
>> under a given size. Antialiasing on 10pt text is rarely an improvement
>> over just not having it as there are so few pixels to work with. I
>> decided to experiment a bit with turning off antialiasing. This
>> requires setting the load target to FT_LOAD_TARGET_MONO so that
>> Freetype returns a monochrome image instead of what amounts to an alpha
>> coverage map. Unfortunately, this does also change the format of the
>> image returned to a 1bpp image instead of an 8bpp greyscale image, and
>> JavaFX isn't equipped to handle that. However, we can do the conversion
>> manually if we see that bitmap.pixel_mode == 1, and then the rest of
>> JavaFX doesn't need to care about it:
>>
>> ```
>> if (bitmap.pixel_mode == 1) {
>> byte[] newBuffer = new byte[width * height];
>> for (int y = 0; y < height; y++) {
>> final var rowOffset = y * width;
>> for (int x = 0; x < width; x++) {
>> final var byteOffset = rowOffset + x;
>> newBuffer[byteOffset] = bitAt(buffer, x, y, pitch);
>> }
>> }
>> buffer = newBuffer;
>> }
>>
>> private static byte bitAt(byte[] buffer, int x, int y, int pitch)
>> {
>> final var byteOffset = (y * pitch) + (x / 8);
>> final var bitOffset = 7 - (x % 8);
>> final var bit = (buffer[byteOffset] >>> bitOffset) & 1;
>> return (byte) (bit == 1 ? 0xff : 0x00);
>> }
>> ```
>>
>> Here's the JavaFX default of (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)
>> combined with FT_LOAD_TARGET_MONO:
>>
>> https://ataxia.io7m.com/2023/12/12/droid_12_nohinting_nobitmaps_mono.png
>>
>> That's not a typeface even a mother could love. :)
>>
>> However, what happens if we enable hinting?
>>
>> Here's (FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_MONO):
>>
>> https://ataxia.io7m.com/2023/12/12/droid_12_hinting_nobitmaps_mono.png
>>
>> I mean, it's not exactly wonderful for Droid Sans 12 (the O is a little
>> mangled), but that's more an issue with the font itself. It's certainly
>> better than the result _without_ hinting.
>>
>> Amusingly, here's DejaVu Sans at 7pt, (FT_LOAD_NO_BITMAP |
>> FT_LOAD_TARGET_MONO):
>>
>> https://ataxia.io7m.com/2023/12/12/dejavu_12_hinting_nobitmaps_mono.png
>>
>> That, to my eyes, looks pretty good. The JavaFX defaults for the same
>> font are not good:
>>
>>
>> https://ataxia.io7m.com/2023/12/12/dejavu_12_nohinting_nobitmaps_normal.png
>>
>> I've tried on multiple systems (all Linux, however), and I've yet to be
>> able to contrive a situation where the JavaFX defaults give better
>> rendering results with any combinations of font sizes, with or without
>> AA. A brief inspection of the JDK's Java2D sources show that it does
>> conditionally use FT_LOAD_NO_HINTING depending on various settings
>> (there are comments about FT_LOAD_NO_HINTING yielding constant-sized
>> glyphs, which supposedly can make things easier in some cases). The
>> Java2D results on the systems I tested are consistently better.
>>
>> So I guess my questions are:
>>
>> * Why do we discard hinting information?
>> * Why do we discard bitmaps?
>> * Would JavaFX accept patches to allow hinting, bitmaps, and
>> FT_LOAD_TARGET_MONO? Ideally this should be developer-controlled, so I
>> would need to come up with a pleasant API for it.
>>
>> My experience has been that most JavaFX applications tend to bundle
>> fonts rather than relying on anything the system has. I suspect that,
>> given that developers are including their own fonts, they are the best
>> equipped to answer questions about hinting and AA, rather than just
>> setting values and hoping that the font they get will work well, so an
>> explicit API might be fine.
>>
>> --
>> Mark Raynsford | https://www.io7m.com
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20231212/bcb49a51/attachment.htm>
More information about the openjfx-dev
mailing list