After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 697758 - support for themes with non-trivial mask/alpha channel (semi-transparent UI)
support for themes with non-trivial mask/alpha channel (semi-transparent UI)
Status: RESOLVED OBSOLETE
Product: mutter
Classification: Core
Component: general
3.8.x
Other Linux
: Normal normal
: ---
Assigned To: mutter-maint
mutter-maint
Depends on:
Blocks:
 
 
Reported: 2013-04-10 20:04 UTC by Simon McVittie
Modified: 2014-12-30 02:17 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Let the UI layer (via the core) construct the frame mask (13.10 KB, patch)
2013-04-10 20:05 UTC, Simon McVittie
none Details | Review
Add support for alpha-blended window titles, etc. (6.90 KB, patch)
2013-04-10 20:06 UTC, Simon McVittie
none Details | Review
MetaFrames: factor out MetaUIFrame accessors for borders, corner radiuses (3.54 KB, patch)
2013-04-11 16:09 UTC, Simon McVittie
committed Details | Review
Let the UI layer (via the core) construct the frame mask (10.96 KB, patch)
2013-04-11 16:11 UTC, Simon McVittie
reviewed Details | Review
Add support for alpha-blended window titles, etc. (7.25 KB, patch)
2013-04-11 16:12 UTC, Simon McVittie
none Details | Review
edited version of Adwaita with semi-transparent window titles (2.15 KB, patch)
2013-04-11 16:14 UTC, Simon McVittie
rejected Details | Review
screenshot of the edited Adwaita theme (81.34 KB, image/png)
2013-04-11 16:15 UTC, Simon McVittie
  Details
Track changes to window frames' masks (5.89 KB, patch)
2013-04-11 17:29 UTC, Simon McVittie
none Details | Review
Let the UI layer (via the core) construct the frame mask (14.67 KB, patch)
2013-04-12 17:02 UTC, Simon McVittie
committed Details | Review
Track changes to window frames' masks (6.02 KB, patch)
2013-04-17 15:55 UTC, Simon McVittie
none Details | Review
Add support for alpha-blended window titles, etc. (4.74 KB, patch)
2013-04-17 15:58 UTC, Simon McVittie
none Details | Review
demonstration of correct alpha-blending (36.45 KB, image/png)
2013-04-17 15:59 UTC, Simon McVittie
  Details

Description Simon McVittie 2013-04-10 20:04:46 UTC
I've been asked to implement a Mutter theme with semi-transparent window titlebars. X compositing is done in terms of pixmaps with no alpha channel, and Mutter's alpha-mask implementation currently only supports circular corners, so this turned out to entail more yak-shaving in Mutter than I'd expected.

My implementation lets themes draw UI elements with whatever alpha is desired, so you can do things like fully-opaque text and buttons on a semi-transparent titlebar (or even "holes" in the titlebar, if you like that sort of thing). It doesn't allow non-rectangular window borders from an input point of view, though: if part of the window border is invisible, it will still respond to right-clicks, dragging, etc. as normal.

Separating the colours from the mask involves some extra Cairo drawing, so I made it opt-in via a <info><uses-alpha/></info> flag and bumped the theme format to 3.5. Themes without the uses-alpha flag will continue to use what's essentially the current code path.
Comment 1 Simon McVittie 2013-04-10 20:05:55 UTC
Created attachment 241209 [details] [review]
Let the UI layer (via the core) construct the frame mask

Having the compositor understand rounded corners seems like a pretty
unfortunate special case: the theme should be able to draw arbitrarily
fancy transparency.
Comment 2 Simon McVittie 2013-04-10 20:06:10 UTC
Created attachment 241210 [details] [review]
Add support for alpha-blended window titles, etc.
Comment 3 Simon McVittie 2013-04-11 16:09:36 UTC
Created attachment 241271 [details] [review]
MetaFrames: factor out MetaUIFrame accessors for  borders, corner radiuses

This makes it a bit simpler for other functions on a MetaUIFrame to
get this information.

---

Previously part of Attachment #241209 [details].
Comment 4 Simon McVittie 2013-04-11 16:11:03 UTC
Created attachment 241272 [details] [review]
Let the UI layer (via the core) construct the frame mask

This essentially just moves install_corners() from the compositor, through
the core, into the UI layer where it arguably should have been anyway,
leaving behind stub functions which call through the various layers. This
removes the compositor's special knowledge of how rounded corners work,
replacing it with "ask the UI for an alpha mask".

The computation of border widths and heights changes a bit, because the
width and height used in install_corners() are the
meta_window_get_outer_rect() (which includes the visible borders but not
the invisible ones), whereas the more readily-available rectangle is the
MetaFrame.rect (which includes both). Computing the same width and height
as meta_window_get_outer_rect() involves compensating for the invisible
borders, but the UI layer is the authority on those anyway, so it seems
clearer to have it do the calculations from scratch.

---

Essentially the same as Attachment #241209 [details], but with a better commit message (it was getting late when I submitted the patch), and with Attachment #241271 [details] split out to appear earlier.
Comment 5 Simon McVittie 2013-04-11 16:12:36 UTC
Created attachment 241274 [details] [review]
Add support for alpha-blended window titles, etc.

X compositing always uses opaque pixmaps, so we end up with the colour
and shape of the window taking different routes through the system.
The colour (opaque RGB) goes from the UI layer through Gdk into X,
then back from X into the compositor as a pixmap. Meanwhile, the alpha
channel goes directly from the UI layer to the compositor without
leaving the Mutter process.

The easiest way to make this happen is to call meta_frames_paint()
once for the colours (drawing onto an opaque black background turns out
to be correct), and calling it again, separately, for the
alpha channel. 

This does mean spending twice as long in Cairo, so I added a
<uses-alpha/> flag in the theme to switch between the current
code-path and the full-alpha code path.

---

Simplified from Attachment #241210 [details] to avoid going via a temporary 32-bit texture, and avoid some duplication.
Comment 6 Simon McVittie 2013-04-11 16:14:27 UTC
Created attachment 241275 [details] [review]
edited version of Adwaita with semi-transparent window titles

The active window border ought to be more opaque than this, but it demonstrates the general idea. (Marking this as pre-rejected, it's not intended for actual application.)
Comment 7 Simon McVittie 2013-04-11 16:15:21 UTC
Created attachment 241276 [details]
screenshot of the edited Adwaita theme
Comment 8 Simon McVittie 2013-04-11 16:36:46 UTC
(In reply to comment #5)
> The easiest way to make this happen is to call meta_frames_paint()
> once for the colours (drawing onto an opaque black background turns out
> to be correct), and calling it again, separately, for the
> alpha channel. 

This doesn't work quite right, unfortunately: when the titlebar gets redrawn, we need to invalidate the mask. At the moment Mutter is relying on titlebars' masks not changing except via changes to window geometry.
Comment 9 Simon McVittie 2013-04-11 17:29:04 UTC
Created attachment 241284 [details] [review]
Track changes to window frames' masks

We have to notice when frames are repainted, and pick up an updated
mask. Otherwise, we draw the RGB of the new frame with the alpha channel
of the old frame: this is particularly visible if you have opaque
window-title text on a semi-transparent frame, and use something whose
window title changes regularly, like a web browser.
Comment 10 Owen Taylor 2013-04-11 19:15:14 UTC
Thanks for the patches, Simon!

I think the make your trick with painting over a black background fully work you need to change the pipeline a bit in meta_shaped_texture_paint() - right now it does:

              cogl_pipeline_set_layer_combine (pipeline_template, 1,
                                           "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
                                           NULL);

But I think you want 'RGB = PREVIOUS; A = TEXTURE[A]' for the titlebar. (Blend string syntax is made up - I forget the details.) Maybe I'm missing something because your screenshot doesn't look like the colors are double-premultiplied, but it seems to me that is how the math is going to work out.

Unfortunately, this is going to break window shaping, since *do* need to multiple the RGB values with the 0 alpha for parts of the window that were shaped away - we can't assume that they'll be black. You actually want 'RGB = MIN(PREVIOUS, TEXTURE[A]); A = TEXTURE[A])' You can't do that as a blend string with current Cogl, but you could use a shader.

Other approaches:
 
 - Just draw the window in two passes - one pass for the titlebar, one part for the contents. (Another thing that we might want to do eventually is make MetaShapedTexture turn off blending for opaque regions, which gets into the multiple-pass thing as well.)

 - Draw client-side *once* into a client side buffer and separate out mask and color client-side, rather than drawing twice. Unpremultiplying is a little nasty for performance with a divide per pixel, but I think it's manageable, or the shader as described above would work.

But at a higher level - the problem we have with this patch set is that we really want to move to using GTK+ CSS theming for the window borders, and stop subjecting our artists to an esoteric and ad-hoc XML theme format. This also gives us consistency with client-side-decorations for applications that use custom decorations, and as we move to Wayland.

So there's going to be a pretty big rework to this area coming for 3.10 anyways, so there's not really a place for these patches to go. :-(
Comment 11 Simon McVittie 2013-04-12 11:11:01 UTC
(In reply to comment #10)
> Maybe I'm missing something
> because your screenshot doesn't look like the colors are double-premultiplied,
> but it seems to me that is how the math is going to work out.

For what it's worth, the screenshot was with an earlier version that rendered to a 32-bit RGBA image, then copied it into the real X drawable with CAIRO_OPERATOR_ADD to an opaque black background. I'm not sure whether that makes a difference or not.

I'll need to make sure the maths is right - I got a bit confused about what was expected to be premultiplied and what wasn't!

Since the frame's colours and alpha currently take different routes through the system and get drawn at different times, I'm tempted to try an approach like this:

- do the Cairo frame drawing into a 32-bit RGBA texture, with the clip area ensuring the middle is blank (and either copy it into the frame's X window, or leave the outer parts of the frame's X window blank)
- make the mask for the frame's X window only take the client area
- when we draw the window in the compositor, only draw the client area with the MetaShapedTexture, and add the 32-bit RGBA frame in the same "layer" (in terms of code structure, not the texture stacking!) as the shadow

Does that sound viable?

I don't suppose you happen to know a convenient example of a simple X demo with a non-trivial shape (xeyes?), and one with a non-trivial alpha channel?

> But at a higher level - the problem we have with this patch set is that we
> really want to move to using GTK+ CSS theming for the window borders

I'd have preferred that - I agree that the Mutter theme format is pretty opaque to non-experts like me - but this doesn't help me to apply the desired theme to a 3.8-based system, so I'm going to have to keep working on this as a branch anyway.

I'll keep putting patches here in case they're useful to anyone else, but if this is never going to be merged upstream anyway, I'll cut out the change to the theme format and make it take the <use-alpha/> code path unconditionally.
Comment 12 Simon McVittie 2013-04-12 13:33:55 UTC
(In reply to comment #10)
> I think the make your trick with painting over a black background fully work
> you need to change the pipeline a bit in meta_shaped_texture_paint()

Yes, you're right. If I set a titlebar to have rgba(240, 0, 0, 0.25) and rgba(80, 0, 0, 0.75) rectangles and put it over a black window, they're visibly different: the result should have a red channel of 60 in both cases, but the actual result looks as though it might well be r=15 and r=45 (which is what I'd expect from double-applying alpha).

> But at a higher level - the problem we have with this patch set is that we
> really want to move to using GTK+ CSS theming for the window borders, and stop
> subjecting our artists to an esoteric and ad-hoc XML theme format.

How do you want the window borders to be drawn? If I can make my compositor-side changes work the way you want, then you can at least use those as a prototype, even if my changes to the UI side are thrown away.

(Or are you relying on every X application having client-side decorations?)
Comment 13 Jasper St. Pierre (not reading bugmail) 2013-04-12 15:10:45 UTC
Review of attachment 241271 [details] [review]:

I will ACK this patch and the other one, as it's a nice cleanup that will help us for CSS borders.
Comment 14 Jasper St. Pierre (not reading bugmail) 2013-04-12 15:11:30 UTC
Review of attachment 241272 [details] [review]:

You should be able to remove meta_frame_get_corner_radiuses.
Comment 15 Simon McVittie 2013-04-12 17:00:39 UTC
Comment on attachment 241271 [details] [review]
MetaFrames: factor out MetaUIFrame accessors for  borders, corner radiuses

c7c122539
Comment 16 Simon McVittie 2013-04-12 17:02:13 UTC
Created attachment 241372 [details] [review]
Let the UI layer (via the core) construct the frame mask

---

v2 of Attachment #241272 [details]: also deletes meta_frame_get_corner_radiuses, meta_ui_get_corner_radiuses as requested.
Comment 17 Jasper St. Pierre (not reading bugmail) 2013-04-12 17:09:28 UTC
Review of attachment 241372 [details] [review]:

OK. I think in my CSS branch I called it "paint_mask", which I think is a bit clearer, but that's up to you.
Comment 18 Simon McVittie 2013-04-17 12:39:20 UTC
Comment on attachment 241372 [details] [review]
Let the UI layer (via the core) construct the frame mask

c2a9ccb.

(In reply to comment #17)
> OK. I think in my CSS branch I called it "paint_mask", which I think is a bit
> clearer, but that's up to you.

I've kept it as-is (since this is the version I tested, and the version you gave positive review for :-) but feel free to rename it if you prefer the other name.
Comment 19 Simon McVittie 2013-04-17 15:55:46 UTC
Created attachment 241757 [details] [review]
Track changes to window frames' masks

We have to notice when frames are repainted, and pick up an updated
mask. Otherwise, we draw the RGB of the new frame with the alpha channel
of the old frame: this is particularly visible if you have opaque
window-title text on a semi-transparent frame, and use something whose
window title changes regularly, like a web browser.

---

No changes, I don't think, but it's had some third-party review now.
Comment 20 Simon McVittie 2013-04-17 15:58:17 UTC
Created attachment 241758 [details] [review]
Add support for alpha-blended window titles, etc.

X compositing always uses opaque pixmaps, so we end up with the colour
and shape of the window taking different routes through the system.
The colour (opaque RGB) goes from the UI layer through Gdk into X,
then back from X into the compositor as a pixmap. Meanwhile, the alpha
channel goes directly from the UI layer to the compositor without
leaving the Mutter process.

The easiest way to make this happen is to call meta_frames_paint()
once for the colours, then call it again, separately, for the
alpha channel. Unfortunately, when we do that, we do have to compensate
for Cairo and Clutter's use of premultiplied alpha, otherwise the colour
channels get multiplied by the alpha and everything semitransparent
comes out darker than it ought to be.

---

This is a more streamlined version: I dropped the "we have an interesting alpha channel" flag in favour of always taking the "interesting alpha channel" path.

Now with division by alpha, to get the colours right.
Comment 21 Simon McVittie 2013-04-17 15:59:36 UTC
Created attachment 241759 [details]
demonstration of correct alpha-blending

The inactive frames (grey) are somewhat realistic, shown over wallpaper; the active frame (red) demonstrates that dividing colours by alpha works as intended.
Comment 22 Jasper St. Pierre (not reading bugmail) 2014-12-30 02:17:14 UTC
metacity themes don't exist anymore, GTK+ themes support an alpha channel. This should be supported now.