[OpenJDK 2D-Dev] [PATCH] 8236996: Incorrect Roboto font rendering on Windows with subpixel antialiasing

Sergey Bylokhov Sergey.Bylokhov at oracle.com
Tue Jan 21 05:05:19 UTC 2020


Looks fine.
Thank you for contribution!

On 1/20/20 12:14 am, Dmitry Batrak wrote:
>  > Ok approved. Seems it is making a few things better if not ideal, but nothing worse.
> 
> Thanks!
> Anyone else volunteering to review?
> 
> Best regards,
> Dmitry Batrak
> 
> On Tue, Jan 14, 2020 at 8:58 PM Phil Race <philip.race at oracle.com <mailto:philip.race at oracle.com>> wrote:
> 
>     Ok approved. Seems it is making a few things better if not ideal, but nothing worse.
> 
>     -phil.
> 
>     On 1/14/20 8:14 AM, Dmitry Batrak wrote:
>>     > So this is a workaround for a buggy font that doesn't play well with GDI ?
>>
>>     This is a workaround for all cases (or the vast majority of them) of broken
>>     rendering reported by our customers. The case with Roboto is just the one we
>>     have steps to reproduce for. There can be other cases where GDI's logic is not
>>     matched by JDK. Even if all of them are caused by 'mis-constructed' fonts, I'm
>>     afraid, this will not be considered as a good excuse by our customers, as only
>>     Java applications have such problems with these fonts.
>>
>>     See JDK-8192972, still unsolved in OpenJDK, as an example of the problems which
>>     will be, at least partially, solved with this fix (correct glyphs will be
>>     rendered, albeit using FreeType).
>>
>>     I did test the fix with fonts preinstalled in Windows 10. Fallback was actually
>>     triggered for one font (bold italic 'Segoe UI Semibold'), which is not a 'false'
>>     positive, but actually a manifestation of another JDK bug from the same family -
>>     I've just raised https://bugs.openjdk.java.net/browse/JDK-8237085 for it. That's
>>     yet another example of an issue which will be (mostly) solved by the proposed
>>     fix.
>>
>>     Using file length as a 'checksum' certainly doesn't guarantee we choose the
>>     right font, but the probability of error is very low, and this value seems to be
>>     the best candidate in our circumstances in terms of cost vs. benefit. Even if
>>     the validation mistreats a different font (having the same length) as a correct
>>     one, we'll not be in a worse position than before.
>>
>>     Of course, there's a certain risk that rendering for unaffected fonts might
>>     change, but, given quite straightforward contract of GetFontData function, I
>>     would consider it very low.
>>
>>     > Since you aren't retrieving the data, just asking what the size is, I'd expect
>>     > it to be unmeasurable.
>>
>>     Well, we don't know how GetFontData works exactly, but it does seem to add some
>>     overhead. On my Windows 10 machine OpenJDK with the proposed fix yields about 7%
>>     larger result for the following benchmark program. The reported value does
>>     fluctuate from run to run, but the impact of the fix seems to be larger than the
>>     fluctuations.
>>
>>     --- Benchmark source code ----
>>     import java.awt.*;
>>     import java.awt.font.GlyphVector;
>>     import java.awt.image.BufferedImage;
>>
>>     public class PerfTestOneFont {
>>         private static final Font FONT = new Font("Segoe UI", Font.PLAIN, 12);
>>
>>         public static void main(String[] args) {
>>             FONT.getFamily(); // preload font
>>
>>             BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
>>             Graphics2D g = image.createGraphics();
>>     g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
>>     RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
>>             int glyphCount = FONT.getNumGlyphs();
>>             long startTime = System.currentTimeMillis();
>>             for (int glyphCode = 0; glyphCode < glyphCount; glyphCode++) {
>>                 GlyphVector gv = FONT.createGlyphVector(g.getFontRenderContext(),
>>                                            new int[]{glyphCode});
>>                 g.drawGlyphVector(gv, 0, 0);
>>             }
>>             long endTime = System.currentTimeMillis();
>>             g.dispose();
>>             System.out.println(endTime - startTime);
>>         }
>>     }
>>     ------------------------------
>>
>>     Best regards,
>>     Dmitry Batrak
>>
>>     On Mon, Jan 13, 2020 at 11:09 PM Phil Race <philip.race at oracle.com <mailto:philip.race at oracle.com>> wrote:
>>
>>         So this is a workaround for a buggy font that doesn't play well with GDI ?
>>
>>         It does rely on the fonts always being different sizes which is highly
>>         likely if not guaranteed.
>>         I suppose it is OK so long as we aren't getting any "false" positives.
>>
>>         What I mean is that almost no one will have these Roboto fonts
>>         installed, so the fix
>>         is solving a problem they don't have, but if it is wrong in some way,
>>         then they could lose
>>         GDI rendering of LCD glyphs and that could affect a lot of people.
>>
>>         So have you tested this with the full set of Windows 10 fonts -
>>         including Indic, CJK, etc  - to be sure
>>         there are no cases where it fails for these or other spurious failures.
>>
>>          > As for performance impact, during testing I didn't observe average
>>         glyph generation time increase of more than 15%.
>>
>>         Since you aren't retrieving the data, just asking what the size is, I'd
>>         expect it to be unmeasurable.
>>
>>         -phil.
>>
>>         On 1/13/20 1:25 AM, Dmitry Batrak wrote:
>>         > Hello,
>>         >
>>         > I'd like to submit a patch for JDK-8236996. I'm not a Committer, so
>>         > I'll need someone to sponsor this change.
>>         >
>>         > Issue: https://bugs.openjdk.java.net/browse/JDK-8236996
>>         > Webrev: http://cr.openjdk.java.net/~dbatrak/8236996/webrev.00/
>>         >
>>         > The problem described in JDK-8236996 is from a group of issues (see
>>         > also e.g. JDK-8078382 and JDK-8192972), where JDK
>>         > uses one font to perform char-to-glyph conversion, but GDI, when asked
>>         > to render the glyph is picking a different font,
>>         > leading to completely random glyphs being rendered, as char-to-glyph
>>         > mapping obviously differs for different fonts.
>>         >
>>         > Specific version of Roboto font, mentioned in JDK-8236996, is most
>>         > probably causing the issue because it's not following
>>         > the naming guidelines from OpenType specification
>>         > (https://docs.microsoft.com/en-us/typography/opentype/spec/name),
>>         > having more than 4 variants (regular, bold, italic and bold italic)
>>         > with the same 'Font Family name' (name ID = 1). So,
>>         > GDI gets confused and picks Roboto Black for rendering, when asked to
>>         > choose a regular font from Roboto family (Roboto
>>         > Black having weight of 400, just like Roboto Regular, probably adds to
>>         > the confusion).
>>         >
>>         > But the reasoning, given above, about the issue cause is only a guess.
>>         > GDI is not an open-source subsystem, so we cannot
>>         > know for sure how it selects the font for rendering, and cannot
>>         > implement matching logic in JDK. Ideally, we'd want to
>>         > select the font by specifying its file path, but that's not possible
>>         > with GDI. Luckily, it allows us to query file data
>>         > for the selected font using GetFontData function
>>         > (https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getfontdata),
>>         > which we can use to validate that the
>>         > selected font is the one we need.
>>         >
>>         > The proposed solution is to check the file size of the font, selected
>>         > by GDI, before using it for rendering. If a mismatch
>>         > is detected, fallback to FreeType is performed. It can produce a
>>         > somewhat different glyph representation, but, at least,
>>         > the correct glyph will be rendered. For members of font collections,
>>         > file size for validation is calculated in a special
>>         > way, in accordance with GetFontData logic described in the
>>         > documentation. I've verified that it works for font collections
>>         > bundled with Windows 10.
>>         >
>>         > As for performance impact, during testing I didn't observe average
>>         > glyph generation time increase of more than 15%.
>>         > Taking glyph caching into account, it shouldn't be that significant
>>         > for typical UI applications, I think. Performance
>>         > impact can be made even smaller - by performing the validation only
>>         > once per font, but, I believe, having a Java
>>         > application always render correct glyphs (even if fonts are added or
>>         > removed while application is running) is more
>>         > important.
>>         >
>>         > Proposed patch doesn't add any tests, as reproducing the issue
>>         > requires installation of fonts. Existing automated
>>         > OpenJDK tests pass after the fix. Proposed approach has been used in
>>         > JetBrains Runtime without known issues for about 3
>>         > months in testing and for about 1 month in production.
>>         >
>>         > Best regards,
>>         > Dmitry Batrak
>>
>>
> 
> 
> 


-- 
Best regards, Sergey.


More information about the 2d-dev mailing list