Bug 102661

Summary: Treat color glyph alpha as mask instead of source alpha
Product: cairo Reporter: Jan Alexander Steffens (heftig) <jan.steffens>
Component: freetype font backendAssignee: David Turner <david>
Status: RESOLVED MOVED QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: normal    
Priority: medium CC: andyrtr, apodtele, freedesktop, wl
Version: unspecified   
Hardware: All   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: [PATCH] Hacky hack: Use color glyph alpha as mask

Description Jan Alexander Steffens (heftig) 2017-09-11 18:04:45 UTC
Created attachment 134165 [details] [review]
[PATCH] Hacky hack: Use color glyph alpha as mask

Context:

After upgrading to cairo 1.15.8, i3 suddenly cleared its surfaces before drawing color glyphs, because it uses the SOURCE operator for text drawing:

https://github.com/i3/i3/pull/2908

My patch to use OVER instead was reverted as another user uses a fork with alpha source to "punch holes":

https://github.com/i3/i3/pull/2925

Looking for an alternative solution, the attached (very hacky) patch alters cairo's handling of color glyphs:

- When loading a color glyph, separate the alpha channel and save it as
  the non-color surface.
- Use the non-color surface as the mask when compositing color glyphs.
- Plug a leak of patterns in composite_one_color_glyph

=> Drawing glyphs with CAIRO_OPERATOR_SOURCE no longer clears dest. In
   general the handling of color glyphs should match the handling of
   normal glyphs now, except for the source color.

=> Possible extension: Let the user deactivate color glyph handling (in
   the context? in the pattern?) in order to draw with the chosen source
   instead, for things like text shadows. Since the normal surface is the
   alpha mask now, this handles color glyphs nicely.

   To do that, just have it override the check of
   _cairo_scaled_font_has_color_glyphs to be false.

My ignorance of cairo and pixman internals means this is probably
terrible code.
Comment 2 Jan Alexander Steffens (heftig) 2017-09-12 08:18:11 UTC
I think using alpha as the mask but leaving it in the source would do weird things to the destination alpha, as it becomes src * src + dst * (1 - src), i.e. you have a quadratic curve.
Comment 3 Behdad Esfahbod 2017-09-12 13:31:00 UTC
(In reply to Jan Alexander Steffens (heftig) from comment #2)
> I think using alpha as the mask but leaving it in the source would do weird
> things to the destination alpha, as it becomes src * src + dst * (1 - src),
> i.e. you have a quadratic curve.

Yeah I agree we are double-multiplying...
Comment 4 Behdad Esfahbod 2017-09-12 14:03:01 UTC
Reopening to discuss double-multiplying.  I'm willing to let it go though.

Note that the proposed hacky hack patch that separate alpha and rgb suffers from the same problem IMO.
Comment 5 Jan Alexander Steffens (heftig) 2017-09-12 14:07:29 UTC
Does it? My patch turns the color surface into RGB24 format, so the alpha is dropped.
Comment 6 Behdad Esfahbod 2017-09-12 14:08:17 UTC
(In reply to Jan Alexander Steffens (heftig) from comment #5)
> Does it? My patch turns the color surface into RGB24 format, so the alpha is
> dropped.

Yes, but RGB that get in there are premultiplied...
Comment 7 Jan Alexander Steffens (heftig) 2017-09-12 14:20:18 UTC
Ah, I missed that detail. And the colors coming out of FreeType are already premultiplied. Well, there goes the easy solution.

I now agree this is probably not worth worrying about.
Comment 8 Behdad Esfahbod 2017-09-12 14:29:20 UTC
(In reply to Jan Alexander Steffens (heftig) from comment #7)
> Ah, I missed that detail. And the colors coming out of FreeType are already
> premultiplied. Well, there goes the easy solution.

Exactly.  The PNG is NOT premultiplied, but we premultiply in FreeType before returning...

> I now agree this is probably not worth worrying about.
Comment 9 Alexei Podtelezhnikov 2018-07-31 03:03:03 UTC
Ahh. I wish I knew about premultiplication and objected as much as I could. Now we are about to release layered fonts with the premultiplication... Screw everyone who cares about gamma correction too!

At least Direct2D offers a choice
https://docs.microsoft.com/en-us/windows/desktop/api/dcommon/ne-dcommon-d2d1_alpha_mode
Comment 10 Bill Spitzak 2018-08-01 22:48:06 UTC
Cairo was designed to act like the letters were the *mask*. No pixels outside the letters should be changed no matter what the compositing operation is or what character is drawn.

Users expect that by default (which means the compositing op is OVER) the emoji are filled in with their colors.

It is also useful to be able to fill the shapes of the emoji with solid colors, for effects such as drop shadows. This cannot be done in one step right now.

My recommendation:

* If the op is OVER, then it does what it is doing now.

* If the op is not OVER, then the color of the emoji is ignored. Instead the alpha of each of them is merged into the mask with the rest of the glyphs, and then font rendering continues as normal. In particular this allows SOURCE to be used to fill with a solid color or image.

A different recommendation. This is more consistent, but much harder to implement, and may be less useful. To draw an emoji produce exactly the same result as if you did this:

* Intersect the mask with the alpha of the emoji
* Set the source to an opaque *unpremultiplied* version of the emoji's color
* Fill a rectangle that is bigger than the mask.

This produces the same result for OVER as it does now. SOURCE also produces the same result.
Comment 11 GitLab Migration User 2018-08-25 13:58:24 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/296.

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.