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.open...@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 > >