Bug 28549 - Gamma-corrected alpha blending for text output
Summary: Gamma-corrected alpha blending for text output
Status: RESOLVED MOVED
Alias: None
Product: cairo
Classification: Unclassified
Component: freetype font backend (show other bugs)
Version: 1.9.9
Hardware: All Linux (All)
: medium enhancement
Assignee: David Turner
QA Contact: cairo-bugs mailing list
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-06-15 05:21 UTC by Antti S. Lankila
Modified: 2018-08-25 13:25 UTC (History)
5 users (show)

See Also:
i915 platform:
i915 features:


Attachments
Modification of pixman-glyph.c to compile with current pixman (237.85 KB, patch)
2015-08-08 07:58 UTC, madigens
Details | Splinter Review

Description Antti S. Lankila 2010-06-15 05:21:11 UTC
I'm soliciting interest for improving text rendering on the Linux desktop by applying gamma correction for the text. I have written a couple of JavaScript demos (for use in chromium or firefox) that can demonstrate the problem and what I think should be done about it.

To view these demos, a TFT display with RGB subpixel order is required. Chromium is preferred because of faster JavaScript execution. I have not tested the demo on other browsers.

Firstly, a screenshot highlighting the difference between Linux and Windows 7 font rendering. The gnome font settings are set to 'no hinting', and LCD with RGB subpixel order. I've also installed the msttcorefonts to have the Arial font used on the demo:

http://bel.fi/~alankila/lcd/lcd.png

The Linux browser's rendering on the bottom left shows a dirty halo around the text on black-on-white, clear color fringing on white-on-black, and significant darkening of the green-on-violet test cases. In summary, the javascript results are similar on both linux and windows, and the windows 7 output resembles the javascript output much more than linux output. Subjectively, the linux results are far worse than the others.

The actual demo can be played with here:

http://bel.fi/~alankila/lcd/

The demo renders the text anew on changes to text field input value. For instance, replacing the gamma 2.0 value with neutral gamma 1.0 produces javascript results that are distinctly similar to those given by linux browsers. Therefore it seems likely that lacking gamma correction on text output is the reason for the poor results on Linux.

As additional evidence, executing "xgamma -gamma 2.0" makes the Linux browser results seem fairly similar as the javascript results are with normal gamma value, indicating that Linux font rendering can be remedied by use of linear gamma display.

From this, it is fair to conclude that the key problem is the assumption that alpha blending can be performed as linear blend between color values in sRGB space. This is only true if the sRGB space maps linearly to physical intensity ("screen space") of the pixel on the display surface, in other words, only when gamma is 1.0. When it is not 1.0, it is necessary to raise the color component to the gamma, then perform blending linearly, and then raise to 1/gamma to get back to sRGB space.

Typical monitors use gamma values from 1.8 to 2.2, and therefore all alpha blending operations should, in principle, compensate for gamma. However, that may be unacceptably slow, so a compromise might be to fix only text, where this problem matters most. In addition, the difference between 1.8 or 2.2 to 2.0 is not very large, indicating that it may be acceptable to treat the gamma value as fixed constant, rather than user-specified variable.

To demonstrate the screen space alpha blending, I have made another page which works as a measuring tool for display system's gamma value:

http://bel.fi/~alankila/lcd/gamma.html

it is based on the color blending idea which is used on the LCD test page to show how fonts can be put on screen without color fringing artifacts. For instance, on the leftmost blocks calibrated for gamma 2.0, the middle square is 0xb4b4b4 in color. This is because the light intensity midpoint between 0x000 and 0xfff is not 0x888, but it is actually given by:

desired_intensity^(1/gamma) = 0.5^(1/2.0) = 0.707,

which mapped to 0 .. 255 range gives 0.707 * 255 = 0xb4.

This provides the clue to understanding why Linux results on the green-on-violet text seem much too dark. The combination of 0xf0f and 0x0f0 tends to produce the value 0x888 when gamma is being ignored, but it is much too dark. The same problem is responsible for the color fringing in the other cases as well, of course.
Comment 1 Bill Spitzak 2010-06-15 11:13:23 UTC
I did a lot of work with gamma correction (though I normally call it 
"linear" as the numbers are in a linear relationship with light). A few 
corrections:

First gamma correction is much MORE useful for thick filled shapes. Text 
and thin lines are actually the problem cases, not the best ones as you 
claim. I believe Cairo should do gamma correct antialising of all 
shapes, and would prefer lines be attacked before letters if necessary.

There is a HUGE problem that for thin lines and text, users expect the 
reverse to be perceptually inverted, not light-inverted. Linear 
white-on-black letters look much too thin compared to the same image 
drawn black-on-white. This is not an antialising issue, users actually 
accept negative film of such text as being the correct reverse image, 
despite being on a medium with a large gamma correction.

In addition the same font and lines drawn black-on-white looks much to 
thin when drawn gamma-corrected (drawn white-on-black looks thicker but 
appears to be less objectionable to users, as your demo shows). Windows 
solution, judging by your screen shots, appears to be to disable 
antialising of black-on-white by turning up the hinting excessively, I 
think this looks like crap and would prefer a different approach. 
Unfortunatly black-on-white text is far more common than any other so 
solutions may involve strategic thickening of letters and lines before 
rendering so the gamma-corrected output looks the same weight as the 
non-corrected output, and also so inverse images seem perceptually linear.

Another problem is ARGB image composites. It definitely looks FAR better 
when gamma corrected, adding images looks precisely like a double 
exposure, for instance, and rendered antialised edges of objects 
composite correctly. The problem is that a huge number of ARGB images 
are simply "painted to composite correctly" when done in gamma space, 
and identifying these is very difficult. One idea is to add 
linear-correct compositing as new operations, leaving OVER etc, as they 
are. This is ugly because now there is a different operation for images 
than for other Cairo drawing (assuming we want gamma correction to be 
the default). Alternative is to analyze the image and identify sharp 
edges which want gamma correction, but leave large partially-transparent 
areas alone. Another alternative is to just do it but people are going 
to bitch because their output looks different than Windows.

I am unsure if there is a good solution other than making 
gamma-correction be a new set of composite operations and not making 
them the default. Then at least programs can know when they are 
requesting this. I would not do the Windows crap output of text.

Implementation can be done by just assuming gamma is 2.0. This is plenty 
close enough, well below the threshold of other errors in the hardware 
and perceptual environment. It has the advantage that the gamma 
calculation is x^2 or sqrt(x), meaning that expressions can often be 
simplified greatly. A tiny lookup table for sqrt() for all 8-bit values 
might help.

My pages on gamma correct blending:

http://mysite.verizon.net/spitzak/conversion/index.html

Page describing when gamma correction does not work, including screen 
shot of reversed text showing the weight problems:

http://mysite.verizon.net/spitzak/conversion/notlinear.html

Bill Spitzak
Comment 2 Antti S. Lankila 2010-06-15 12:52:46 UTC
Thank you for your comments, Bill.

The reason why I claim thin shapes to be the most important is that in context of fonts, which are rarely transparent, the opaque, filled-in areas will work and all problems are at boundaries.

I do not want to currently complicate the matter by talking about what is seen as perceptively equivalent thickness given white-on-black vs. black-on-white. I will personally be satisfied with the removal of color fringing at the present time.

What Windows 7 does doesn't matter to me. I'm not proposing that we emulate windows 7 strong hinting. I just want to show an OS that does things better than Linux does, to give a sense of perspective on how it could be improves. OS X screenshot would have served this function just as well, but I do not own an Apple computer.

As to the discussion of new set of OVER primitives, etc. I have no real personal interest on matter. The idea of providing new set of operators (or a flag, or whatever) for gamma-corrected alpha blending seems fine by me. However, I believe the way Linux currently does text is wrong and does not need preserving. Linux should definitely do it more like the other OS's do. I believe this point is both practical and tenable, so I pick this battle. I would like to leave discussion of image blending, or changes to RENDER extension, or whatever it entails to another time and another bug report. :)

Your image processing pages were fascinating. I liked the improved blur, especially the one that compensated for saturated pixels. I am somewhat distressed by what the sample in notlinear.html looked like. I had expected it to look better. Perhaps I will take it as a starting point for future improvement. However, right now, I really want to see the gamma-corrected OVER for text. Later, maybe, improvements that relate to perception but aren't physically obvious.
Comment 3 madigens 2015-08-08 07:58:25 UTC
Created attachment 117586 [details] [review]
Modification of pixman-glyph.c to compile with current pixman

Trying to revive this discussion...

Build pixman with this patch, then do:

LD_PRELOAD=pixman/.libs/libpixman-1.so \
CAIRO_DEBUG=xrender-version=-1 \
some-application-like-firefox

Firefox screenshots: http://imgur.com/a/Xb1at
Before/after with autohinted Liberation Sans, before/after with Adobe-CFF-engine-hinted Source Sans Pro. Use wheel thingy the corner of a preview to download full size image.

Text renders much cleaner.
Comment 4 madigens 2015-08-08 08:05:50 UTC
(In case it's unclear, I did not write the patch, Antti did, I just made it compile for 0.32.6. Original source is https://bel.fi/alankila/lcd/alpcor.html)
Comment 5 octoploid 2015-08-08 09:55:11 UTC
I think the consensus is that this is not a freetype bug.

If you use qt-5 (KDE) you can get nice alpha blended fonts if you 
set  QPlatformIntegration::FontSmoothingGamma to 1.8.

diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index 258359d20f28..8181aeaf5c8b 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -357,7 +357,7 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
         break; // Not implemented, use defaults
     case QPlatformIntegration::FontSmoothingGamma:
         // Match Qt 4.8 text rendering, and rendering of other X11 toolkits.
-        return qreal(1.0);
+        return qreal(1.8);
     case QPlatformIntegration::StartDragDistance: {
         // The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but
         // on a high-resolution screen it makes sense to increase it.

Similarly for Chromium you can get alpha blended fonts with a simple patch:

diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index d08f68a1d91c..97fec8b47826 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -389,12 +389,9 @@
 
 //////////////////////////////////////////////////////////////////////
 
-#if defined(SK_GAMMA_EXPONENT) && defined(SK_GAMMA_SRGB)
-#  error "cannot define both SK_GAMMA_EXPONENT and SK_GAMMA_SRGB"
-#elif defined(SK_GAMMA_SRGB)
-#  define SK_GAMMA_EXPONENT (0.0f)
-#elif !defined(SK_GAMMA_EXPONENT)
-#  define SK_GAMMA_EXPONENT (2.2f)
-#endif
+#undef SK_GAMMA_EXPONENT
+#undef SK_GAMMA_CONTRAST
+#  define SK_GAMMA_EXPONENT (1.8f)
+#  define SK_GAMMA_CONTRAST (0.0f)
 
 #endif // SkPostConfig_DEFINED

The reason this is not the default is because most people use crappy
TrueType fonts. And for them a gamma of 1.8 will make look fonts too light.

You really have to use freetype's new CFF engine to get good results.
Comment 6 octoploid 2015-08-08 12:19:17 UTC
Forgot to mention the Chromium patch needs to be applied in:
chromium/src/third_party/skia
Comment 7 GitLab Migration User 2018-08-25 13:25:47 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/cairo/cairo/issues/2.


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.