iced-rs / iced

A cross-platform GUI library for Rust, inspired by Elm
https://iced.rs
MIT License
24.34k stars 1.13k forks source link

Dark text on light background is hard to read on non-HiDPI displays #2254

Open CryZe opened 7 months ago

CryZe commented 7 months ago

Is there an existing issue for this?

Is this issue related to iced?

What happened?

I've been checking out iced because 0.12 came out today. But immediately the text stood out as very wrongly rendered. Initially I thought the hinting just isn't very good, but comparing Paint (on the top) to Iced (at the bottom), the problem is much rather an issue with the "apparent font weight": image

This immediately stood out to me as an issue with sRGB linear vs. gamma corrected, so I pulled out RenderDoc where at first glance the textures seem to use the correct color space. Extracting the glyph cache by itself, we actually get a proper rendering of the glyphs, confirming that up until this point everything is done correctly: image

This strongly implied that the shader is causing the problems, and it probably needs to do a sRGB linear to gamma corrected conversion, and lo and behold, the text is now properly rendered: image

My quick fix amounted to the following in glyphon:

fn linear_to_srgb(x: f32) -> f32 {
    if x <= 0.00031308 {
        return 12.92 * x;
    } else {
        return 1.055 * pow(x,(1.0 / 2.4) ) - 0.055;
    }
}
return vec4<f32>(in_frag.color.rgb, in_frag.color.a * linear_to_srgb(textureSampleLevel(mask_atlas_texture, atlas_sampler, in_frag.uv, 0.0).x));

Almost certainly too hacky (although the same hack already exists), but it should give a good starting point for investigation where the color space mess up happens and what the correct fix may be.

I believe the main problem here is that what iced and glyphon are doing is technically the correct color space handling, but no other application interprets the alpha channel of glyphs this way, so while technically being more correct, it ends up looking very washed out and inconsistent with other applications.

The tiny-skia backend does NOT have this problem, solidifying that it's at least a bug in one of the two backends.

image

Interestingly both Paint and tiny-skia are still clearer, but that may just be the hacky sRGB conversion function.

What is the expected behavior?

See above

Version

crates.io release

Operating System

Windows

Do you have any log output?

No response

hecrj commented 7 months ago

I believe the main problem here is that what iced and glyphon are doing is technically the correct color space handling, but no other application interprets the alpha channel of glyphs this way, so while technically being more correct, it ends up looking very washed out and inconsistent with other applications.

Indeed! This is not just a color space issue, but also an alpha blending issue.

Most GUI toolkits operate (e.g. perform alpha blending) on sRGB colors while assuming they are linear. See Web color is still broken.

iced tries to do the correct thing by default. If you want "broken colors", you can enable the web-colors feature.

web-colors disabled

image

web-colors enabled

image
CryZe commented 7 months ago

I'm well aware of those color space issues and I actually really like that you even have a toggle for it. And in fact I did mention that what you do is for sure much more correct than what other applications are doing. I think the question still somewhat remains if it should affect the font rendering to this (negative) degree. Like I believe even without "web colors" it should probably behave this way for blending the fonts.

I think the reason why the wrongly blended text looks better is because for low resolutions (i.e. normally sized text on non HiDPI screens), the higher contrast makes the glyphs look a lot more legible than doing the correct blending.

hecrj commented 7 months ago

Like I believe even without "web colors" it should probably behave this way for blending the fonts.

I'm happy to consider our options here, as long as we know what we are doing.

Gamma correcting an alpha channel seems quite strange. It does improve the final alpha blending result, but it feels like we are just smashing things. How is brighter text on a dark background affected? Is there any previous work on this?

In order to produce the web-colors result only for text, we would not be able to rely on hardware color conversions. We would have to declare every texture as linear RGB and manually do all of the sRGB conversions in the shaders, except for the text one. Or maybe declare a dedicated render pass for every text layer... equally undesirable.

hecrj commented 7 months ago

We could also consider enabling the web-colors feature by default, since it's in the end what will be expected from most users.

It is quite unfortunate that we have to adopt a broken rendering technique just because it's become the norm, though.

TheDecryptor commented 7 months ago

Is there an option of doing something like stem darkening? Freetype experimented with it (as explained here in an article from 8 years ago) as a way to offset the "washed-out" look.

hecrj commented 7 months ago

@TheDecryptor Yes! I remember that paper! I think it would certainly be an interesting feature for glyphon when using ColorMode::Accurate.

cc @grovesNL

grovesNL commented 7 months ago

Yeah that could be useful for sure, I'd like to understand the state-of-the-art for this better before we decide on stem darkening though. If I remember correctly some approaches for this problem don't work well with display scaling (especially macOS versions in the last decade or so) but don't have a reference handy right now.

yump commented 7 months ago

IANA text rendering expert, but I remember someone in Gnome saying RGBA blending can't do the right thing with subpixel AA on both dark and light backgrounds, and per-channel opacity is needed.

Freetype's Harmony renderer has an interesting first-principles approach to AA, where subpixel rendering is seen as a correction for hardware's misaligned color planes, instead of some kind of "triple resolution" hack. It could be implemented directly at some performance cost by rendering color channels separately and swizzling at the end. That handles vertical and triangular subpixel arrangements just as easily as horizontal, although I don't think it's quite right for RGBW which could have color-dependent offsets.

It is quite unfortunate that we have to adopt a broken rendering technique just because it's become the norm, though.

Perhaps text and graphics should be treated separately? The font designer was probably looking dark-on-light output rendered with that broken technique when tuning the small sizes, so in some sense it is ultimately correct. The text renderer could attempt to adapt the appearance of incorrectly-blended dark-on-light to whatever the actual output target is.

And then color emojis are a whole 'nother kettle of fish.

hecrj commented 7 months ago

Perhaps text and graphics should be treated separately? The font designer was probably looking dark-on-light output rendered with that broken technique when tuning the small sizes, so in some sense it is ultimately correct. The text renderer could attempt to adapt the appearance of incorrectly-blended dark-on-light to whatever the actual output target is.

Yes. As I said in my previous comment, we could do it but it seems it would cost us performance and composability when it comes to our rendering primitives. But I'm not a graphics wizard, so maybe there is a way!

And honestly, adding a bunch of complexity to our renderer architecture so we can basically accomodate a broken rendering technique sounds quite painful.