Although this seems simple on the surface, after a bit of investigation this change raises some difficult issues:
Essentially, ClearType rendering is unsuitable when
the render target has translucent pixels underneath rendered text, or
the render ouput is rotated, scaled, subpixel-translated, or otherwise transformed before display.
This necessitates avoiding ClearType when rendering into transparent bitmaps. Unfortunately, a commmon use-case for transparent bitmaps is buffered component images. If ClearType were enabled for opaque targets but disabled otherwise, ClearType text could then end up displaying next to non-ClearType text rendered to an intermediate texture, leading to an inconsistent appearance.
In short, Iām inclined to revert the antialiasing change, and to go back to using grayscale rendering unconditionally. This approach is guaranteed to give a consistent appearance whether rendering directly to the screen or to a bitmap. My feeling is that consistent grayscale rendering is preferable to mixing ClearType and grayscale - does that sound like a reasonable assumption?
Are you sure of that?
My whole main component is scaled (resizable UI) and the subpixel coloring seems to be working as expected.
Or do you mean when specifically rendered to an image?
Could this be settable so we can choose to use it or not? Ideally for a given context, but this could also be a simple define.
Iām talking about rendering to an image. Subpixel antialiasing relies on the exact locations of the subpixels, so if the image is transformed after rendering then it may no longer line up exactly with the expected subpixels, leading to colour fringing.
Iāve discussed my preference for runtime options over buildtime flags earlier in this thread. I also donāt think a global flag is a good fit. Even if we allow ClearType in opaque windows, users may then want to toggle it when rendering to images depending on whether the text drawn into those images falls on opaque or transparent background pixels.
Iām struggling a bit with how a runtime API would look. Intuitively, weād add a setTextAntiAliasingKind function onto Graphics, but this wouldnāt be very usable. To modify the antialiasing kind of an existing component, a user would need to modify or override the paint function of that component to update the antialiasing mode before drawing each piece of text. This would quickly get unweildy for deep trees of components.
Given that:
mixing grayscale and ClearType is likely to look bad,
adding a new antialiasing API is likely to require large changes to the framework in order to be usable,
the only anti-aliasing option available previously on Windows was grayscale (not subpixel), and
the only anti-aliasing mode available on other platforms (macOS, iOS, Linux, Android) is grayscale, so a new API would only affect rendering on Windows,
Iām inclined to say that we should stick with grayscale rendering and avoid ClearType.
This is very bad news as finally, JUCE text rendering started to look okay on Windows. Instead of just reverting the changes, please make them at least optional for those of use who care about how text looks next to ānativeā apps on the Windows platform.
Or you could just fork, make the change and continuously (automated with a GitHub workflow) merge any JUCE changes into your fork. Then you have an always up to date version with your cleartype change.
If the image is scaled then the font will be a blurry mess anyway.
By the way this is exactly what happens when using bufferedToImage with a scaled UI: fonts are often blurry due to rounding errors forcing a resize of the image. This forced me to get rid of bufferedToImage components altogether, and Direct2D made this possible while maintaining good (even better) performance.
Regarding integration challenges in the framework, I understand your point, but I allow me to try and convince you otherwise
JUCE7 was indeed using greyscale but I would say the new hinting method in JUCE8 make the issues more visible.
I donāt know how cleartype integration could be addressed, but speaking for myself I would be happy with a global setting (be it a define or a runtime thing) that would do the right thing when enabled (ie using cleartype when it is set in the OS, and resort to greyscale for images).
Font rendering is an important consideration, and I would love to see JUCE8 improve over JUCE7 on this. I feel like cleartype support would really bring something to the table and should warrant a define, like we already have for other font aspects for woth Windows and MacOS (eg JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING and JUCE_USE_DIRECTWRITE).
A scaled-up image with ClearType text can look objectively worse due to the colored subpixel fringes being more visible. Scaling down an image even slightly can cause these subpixels to misalign.
Microsoft has shifted away from using ClearType in many of its native apps in Windows 11. For instance, the redesigned Windows Terminal and Paint apps in Windows 11 do not use ClearType, nor do apps such as Calendar, Calculator, Store, and Photos. Even the Start menu in the latest Windows 11 also lacks ClearType. Microsoft has also removed ClearType as the default option in Office apps such as Word and Excel since the 2013 versions.
Here are some screenshots of various apps mentioned above, showing they do not use ClearType. Iāve verified that ClearType was enabled and confirmed this on another system with a fresh install of Windows 11 as well.
The problem is that ādoing the right thingā is a difficult problem, and is evidently subjective.
Iāve already explained that a global setting is not a good fit for this feature. Using grayscale unconditionally when rendering to an image is not a good solution, because itās likely that cleartype and non-cleartype text will end up displayed in close proximity when buffering component images. To cover all use-cases, users would end up needing to toggle ClearType on an image-by-image basis, or even for each run of text inside an image.
JUCE_USE_DIRECTWRITE no longer has any effect. JUCE_DISABLE_COREGRAPHICS_FONT_SMOOTHING is less situational than ClearType: text rendering to screen and to image will work and look consistent, whether rendering to screen or to image, and whether this option is set or not.
I agree, and to allow such a situational decision, weād ideally have some way of enabling/disabling ClearType before rendering any given piece of text. However, adding such an API is very difficult to justify given that it would require large changes to the framework for a feature that would only have an effect on Windows.
This isnāt an easy decision; it feels like there are trade-offs to every potential solution here. However, I think that sticking to grayscale rendering everywhere is the least bad option for all of the reasons Iāve previously mentioned.
Can we get a warning from JUCE in case the client project that uses JUCE still #defines it?
Many developers like me share the same desire of the JUCE team to reduce the dependency upon compile time macros. It would be nice if we could get a warning when a compile time macro for JUCE gets phased out and setting it in the client project has no longer any effect. Thanks
One thing I realized: most of my comparisons (software vs d2d with or without cleartype) have been done with a standalone app and a 200% DPI setting, and with a scalable UI (ie scale transform on the main component based on window size).
For some reason it looks like in this context VST3 does not suffer the same problems, at least not to the same amount: software and d2d are much closer.
Even more suprising: it looks like d2d SVG rendering is also worse in standalone mode whereas it is pretty similar (and better) to software in VST3 mode.
Could this be due to UI scale (both affine transforme and OS DPI) not being interpreted correctly in standalone?
The main difference between Standalone and VST3 scaling is that VST3s are dependent on the host to both provide a scaling-aware window, and to provide an appropriate scale factor to use. A JUCE Standalone will normally take the displayās native scale into account automatically. Iām assuming youāre testing in a host that renders the plugin view at the displayās scale (if in Live, ensure auto-scaling of plugin windows is off).
I think that JUCE plugins on Windows always treat the display as having a scale of 1 (96 DPI). I wonder whether Direct2D adjusts anti-aliasing or other aspects of text rendering based on the target DPI. I havenāt investigated this, so this is just a theory, but it might account for differences between Standalone and VST3 rendering. If this were happening, then Iād expect the Standalone to be passing the correct screen DPI into the D2D context, so the results should be better there.
This is surprising. Are you able to provide some side-by-side examples of rendering in the Standalone and VST3 so that we can see the differences? āworseā is subjective, so itād be helpful to see exactly what is different between the two targets.
I am using Reaper which correctly handle DPI settings.
I will try to build an example, but from what I remember there is a difference in what g.getInternalContext().getPhysicalPixelScaleFactor() reports in standalone vs VST3. It should be the product of the affine transform scale and DPI setting, but this is not the case for all formats IIRC.