summaryrefslogtreecommitdiff
path: root/gtk/gtkrenderbackground.c
diff options
context:
space:
mode:
authorGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>2016-07-01 09:54:23 -0300
committerMatthias Clasen <mclasen@redhat.com>2016-07-03 17:24:47 -0400
commit369db4a40650bbbcd6125b9b97f486925d93541e (patch)
tree2030f4424121e6ac886800c0b32fcebc0aae55fb /gtk/gtkrenderbackground.c
parentc8a74a1f5021fe47c9eaf199390a43a16e9809dd (diff)
downloadgtk+-369db4a40650bbbcd6125b9b97f486925d93541e.tar.gz
css: add background-blend-mode support
CSS supports blend modes, in which a series of layers are merged together according to the given operation or set of operations. Support for blend modes landed on Cairo, which exposes all the commons and also the exquisites blend modes available. Adding support for blend modes, then, is just a matter of using the available Cairo operations. This patch adds the background-blend-mode CSS enum property, and adapts the background rendering code to blend the backgrounds using the available blend modes when they're set. https://bugzilla.gnome.org/show_bug.cgi?id=768305
Diffstat (limited to 'gtk/gtkrenderbackground.c')
-rw-r--r--gtk/gtkrenderbackground.c88
1 files changed, 85 insertions, 3 deletions
diff --git a/gtk/gtkrenderbackground.c b/gtk/gtkrenderbackground.c
index a9a80f1636..b41d3a5876 100644
--- a/gtk/gtkrenderbackground.c
+++ b/gtk/gtkrenderbackground.c
@@ -72,10 +72,45 @@ _gtk_theming_background_paint_color (GtkThemingBackground *bg,
cairo_fill (cr);
}
+static gboolean
+_gtk_theming_background_needs_push_group (GtkCssStyle *style)
+{
+ const GdkRGBA *bg_color;
+ GtkCssValue *background_color;
+ GtkCssValue *blend_modes;
+ gint i;
+
+ background_color = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_COLOR);
+ bg_color = _gtk_css_rgba_value_get_rgba (background_color);
+
+ /* An opaque background-color means we don't need to push the group */
+ if (bg_color->alpha == 1)
+ return FALSE;
+
+ blend_modes = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE);
+
+ /*
+ * If we have any blend mode different than NORMAL, we'll need to
+ * push a group in order to correctly apply the blend modes.
+ */
+ for (i = _gtk_css_array_value_get_n_values (blend_modes); i > 0; i--)
+ {
+ GtkCssBlendMode blend_mode;
+
+ blend_mode = _gtk_css_blend_mode_value_get (_gtk_css_array_value_get_nth (blend_modes, i - 1));
+
+ if (blend_mode != GTK_CSS_BLEND_MODE_NORMAL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
_gtk_theming_background_paint_layer (GtkThemingBackground *bg,
guint idx,
- cairo_t *cr)
+ cairo_t *cr,
+ GtkCssBlendMode blend_mode)
{
GtkCssRepeatStyle hrepeat, vrepeat;
const GtkCssValue *pos, *repeat;
@@ -134,6 +169,13 @@ _gtk_theming_background_paint_layer (GtkThemingBackground *bg,
cairo_translate (cr, origin->box.x, origin->box.y);
+ /*
+ * Apply the blend mode, if any.
+ */
+ if (G_UNLIKELY (_gtk_css_blend_mode_get_operator (blend_mode) != cairo_get_operator (cr)))
+ cairo_set_operator (cr, _gtk_css_blend_mode_get_operator (blend_mode));
+
+
if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT && vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT)
{
cairo_translate (cr,
@@ -249,6 +291,12 @@ _gtk_theming_background_paint_layer (GtkThemingBackground *bg,
cairo_fill (cr);
}
+ /*
+ * Since this cairo_t can be shared with other widgets,
+ * we must reset the operator after all the backgrounds
+ * are properly rendered.
+ */
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_restore (cr);
}
@@ -304,10 +352,14 @@ gtk_css_style_render_background (GtkCssStyle *style,
GtkThemingBackground bg;
gint idx;
GtkCssValue *background_image;
+ GtkCssValue *blend_modes;
GtkCssValue *box_shadow;
const GdkRGBA *bg_color;
+ gboolean needs_push_group;
+ gint number_of_layers;
background_image = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_IMAGE);
+ blend_modes = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE);
bg_color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_COLOR));
box_shadow = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BOX_SHADOW);
@@ -324,6 +376,22 @@ gtk_css_style_render_background (GtkCssStyle *style,
cairo_save (cr);
cairo_translate (cr, x, y);
+ /*
+ * When we have a blend mode set for the background, we cannot blend the current
+ * widget's drawing with whatever the content that the Cairo context may have.
+ * Because of that, push the drawing to a new group before drawing the background
+ * layers, and paint the resulting image back after.
+ */
+ needs_push_group = _gtk_theming_background_needs_push_group (style);
+
+ if (needs_push_group)
+ {
+ cairo_save (cr);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+ cairo_push_group (cr);
+ }
+
/* Outset shadows */
_gtk_css_shadows_value_paint_box (box_shadow,
cr,
@@ -332,9 +400,15 @@ gtk_css_style_render_background (GtkCssStyle *style,
_gtk_theming_background_paint_color (&bg, cr, bg_color, background_image);
- for (idx = _gtk_css_array_value_get_n_values (background_image) - 1; idx >= 0; idx--)
+ number_of_layers = _gtk_css_array_value_get_n_values (background_image);
+
+ for (idx = number_of_layers - 1; idx >= 0; idx--)
{
- _gtk_theming_background_paint_layer (&bg, idx, cr);
+ GtkCssBlendMode blend_mode;
+
+ blend_mode = _gtk_css_blend_mode_value_get (_gtk_css_array_value_get_nth (blend_modes, idx));
+
+ _gtk_theming_background_paint_layer (&bg, idx, cr, blend_mode);
}
/* Inset shadows */
@@ -343,6 +417,14 @@ gtk_css_style_render_background (GtkCssStyle *style,
&bg.boxes[GTK_CSS_AREA_PADDING_BOX],
TRUE);
+ /* Paint back the resulting surface */
+ if (needs_push_group)
+ {
+ cairo_pop_group_to_source (cr);
+ cairo_paint (cr);
+ cairo_restore (cr);
+ }
+
cairo_restore (cr);
}