X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkthemingbackground.c;h=579d2be4dea32abe70be40ad73cd70fa39cd87b1;hb=a89d420270d1a856e072ed87c365b0176f102e6c;hp=9e21110a6b7e74a3b50a481e4ca7dba560fd88ad;hpb=9181282d8812e2404400f0a5cdbac582037bdce2;p=~andy%2Fgtk diff --git a/gtk/gtkthemingbackground.c b/gtk/gtkthemingbackground.c index 9e21110a6..579d2be4d 100644 --- a/gtk/gtkthemingbackground.c +++ b/gtk/gtkthemingbackground.c @@ -16,357 +16,271 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ -#include "gtkcsstypesprivate.h" +#include "config.h" + #include "gtkthemingbackgroundprivate.h" + +#include "gtkcssarrayvalueprivate.h" +#include "gtkcssbgsizevalueprivate.h" +#include "gtkcssenumvalueprivate.h" +#include "gtkcssimagevalueprivate.h" +#include "gtkcssshadowsvalueprivate.h" +#include "gtkcsspositionvalueprivate.h" +#include "gtkcssrepeatvalueprivate.h" +#include "gtkcsstypesprivate.h" #include "gtkthemingengineprivate.h" +#include + #include -static void -_gtk_theming_background_apply_window_background (GtkThemingBackground *bg, - cairo_t *cr) +/* this is in case round() is not provided by the compiler, + * such as in the case of C89 compilers, like MSVC + */ +#include "fallback-c89.c" + +static const GtkRoundedBox * +gtk_theming_background_get_box (GtkThemingBackground *bg, + GtkCssArea area) { - if (gtk_theming_engine_has_class (bg->engine, "background")) + switch (area) { - cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */ - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - } + case GTK_CSS_AREA_BORDER_BOX: + return &bg->border_box; + case GTK_CSS_AREA_PADDING_BOX: + return &bg->padding_box; + case GTK_CSS_AREA_CONTENT_BOX: + return &bg->content_box; + default: + g_return_val_if_reached (&bg->border_box); + } } static void -_gtk_theming_background_apply_running_transformation (GtkThemingBackground *bg, - cairo_t *cr) +_gtk_theming_background_paint_color (GtkThemingBackground *bg, + cairo_t *cr, + GtkCssValue *background_image) { - gboolean running; - gdouble progress; - cairo_pattern_t *other_pattern; - GtkStateFlags other_flags; - GdkRGBA other_bg; - cairo_pattern_t *new_pattern = NULL; + gint n_values = _gtk_css_array_value_get_n_values (background_image); + GtkCssArea clip = _gtk_css_area_value_get + (_gtk_css_array_value_get_nth + (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_CLIP), + n_values - 1)); - running = gtk_theming_engine_state_is_running (bg->engine, GTK_STATE_PRELIGHT, &progress); + cairo_save (cr); + _gtk_rounded_box_path (gtk_theming_background_get_box (bg, clip), cr); + cairo_clip (cr); - if (!running) - return; + gdk_cairo_set_source_rgba (cr, &bg->bg_color); + cairo_paint (cr); - if (bg->flags & GTK_STATE_FLAG_PRELIGHT) - { - other_flags = bg->flags & ~(GTK_STATE_FLAG_PRELIGHT); - progress = 1 - progress; - } - else - other_flags = bg->flags | GTK_STATE_FLAG_PRELIGHT; + cairo_restore (cr); +} - gtk_theming_engine_get_background_color (bg->engine, other_flags, &other_bg); - gtk_theming_engine_get (bg->engine, other_flags, - "background-image", &other_pattern, - NULL); +static void +_gtk_theming_background_paint_layer (GtkThemingBackground *bg, + guint idx, + cairo_t *cr) +{ + GtkCssRepeatStyle hrepeat, vrepeat; + const GtkCssValue *pos, *repeat; + GtkCssImage *image; + const GtkRoundedBox *origin; + double image_width, image_height; + double width, height; + + pos = _gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_POSITION), idx); + repeat = _gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_REPEAT), idx); + hrepeat = _gtk_css_background_repeat_value_get_x (repeat); + vrepeat = _gtk_css_background_repeat_value_get_y (repeat); + image = _gtk_css_image_value_get_image ( + _gtk_css_array_value_get_nth ( + _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_IMAGE), + idx)); + origin = gtk_theming_background_get_box ( + bg, + _gtk_css_area_value_get ( + _gtk_css_array_value_get_nth ( + _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_ORIGIN), + idx))); + width = origin->box.width; + height = origin->box.height; + + if (image == NULL || width <= 0 || height <= 0) + return; - if (bg->pattern && other_pattern) - { - cairo_pattern_type_t type, other_type; - gint n0, n1; + _gtk_css_bg_size_value_compute_size (_gtk_css_array_value_get_nth (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_SIZE), idx), + image, + width, + height, + &image_width, + &image_height); - cairo_pattern_get_color_stop_count (bg->pattern, &n0); - cairo_pattern_get_color_stop_count (other_pattern, &n1); - type = cairo_pattern_get_type (bg->pattern); - other_type = cairo_pattern_get_type (other_pattern); + if (image_width <= 0 || image_height <= 0) + return; - if (type == other_type && n0 == n1) - { - gdouble offset0, red0, green0, blue0, alpha0; - gdouble offset1, red1, green1, blue1, alpha1; - gdouble x00, x01, y00, y01, x10, x11, y10, y11; - gdouble r00, r01, r10, r11; - guint i; - - if (type == CAIRO_PATTERN_TYPE_LINEAR) - { - cairo_pattern_get_linear_points (bg->pattern, &x00, &y00, &x01, &y01); - cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11); - - new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress, - y00 + (y10 - y00) * progress, - x01 + (x11 - x01) * progress, - y01 + (y11 - y01) * progress); - } - else - { - cairo_pattern_get_radial_circles (bg->pattern, &x00, &y00, &r00, &x01, &y01, &r01); - cairo_pattern_get_radial_circles (other_pattern, &x10, &y10, &r10, &x11, &y11, &r11); - - new_pattern = cairo_pattern_create_radial (x00 + (x10 - x00) * progress, - y00 + (y10 - y00) * progress, - r00 + (r10 - r00) * progress, - x01 + (x11 - x01) * progress, - y01 + (y11 - y01) * progress, - r01 + (r11 - r01) * progress); - } - - cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST); - i = 0; - - /* Blend both gradients into one */ - while (i < n0 && i < n1) - { - cairo_pattern_get_color_stop_rgba (bg->pattern, i, - &offset0, - &red0, &green0, &blue0, - &alpha0); - cairo_pattern_get_color_stop_rgba (other_pattern, i, - &offset1, - &red1, &green1, &blue1, - &alpha1); - - cairo_pattern_add_color_stop_rgba (new_pattern, - offset0 + ((offset1 - offset0) * progress), - red0 + ((red1 - red0) * progress), - green0 + ((green1 - green0) * progress), - blue0 + ((blue1 - blue0) * progress), - alpha0 + ((alpha1 - alpha0) * progress)); - i++; - } - } - else - { - cairo_save (cr); + /* optimization */ + if (image_width == width) + hrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; + if (image_height == height) + vrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; - cairo_rectangle (cr, 0, 0, bg->paint_area.width, bg->paint_area.height); - cairo_clip (cr); - cairo_push_group (cr); + cairo_save (cr); - cairo_scale (cr, bg->paint_area.width, bg->paint_area.height); - cairo_set_source (cr, other_pattern); - cairo_paint_with_alpha (cr, progress); - cairo_set_source (cr, bg->pattern); - cairo_paint_with_alpha (cr, 1.0 - progress); + _gtk_rounded_box_path ( + gtk_theming_background_get_box ( + bg, + _gtk_css_area_value_get ( + _gtk_css_array_value_get_nth ( + _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_CLIP), + idx))), + cr); + cairo_clip (cr); - new_pattern = cairo_pop_group (cr); - cairo_restore (cr); - } + cairo_translate (cr, origin->box.x, origin->box.y); + + if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT && vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) + { + cairo_translate (cr, + _gtk_css_position_value_get_x (pos, width - image_width), + _gtk_css_position_value_get_y (pos, height - image_height)); + /* shortcut for normal case */ + _gtk_css_image_draw (image, cr, image_width, image_height); } - else if (bg->pattern || other_pattern) + else { - cairo_pattern_t *p; - const GdkRGBA *c; - gdouble x0, y0, x1, y1, r0, r1; - gint n, i; + int surface_width, surface_height; + cairo_rectangle_t fill_rect; + cairo_surface_t *surface; + cairo_t *cr2; + + /* If ‘background-repeat’ is ‘round’ for one (or both) dimensions, + * there is a second step. The UA must scale the image in that + * dimension (or both dimensions) so that it fits a whole number of + * times in the background positioning area. In the case of the width + * (height is analogous): + * + * If X ≠ 0 is the width of the image after step one and W is the width + * of the background positioning area, then the rounded width + * X' = W / round(W / X) where round() is a function that returns the + * nearest natural number (integer greater than zero). + * + * If ‘background-repeat’ is ‘round’ for one dimension only and if + * ‘background-size’ is ‘auto’ for the other dimension, then there is + * a third step: that other dimension is scaled so that the original + * aspect ratio is restored. + */ + if (hrepeat == GTK_CSS_REPEAT_STYLE_ROUND) + { + double n = round (width / image_width); - /* Blend a pattern with a color */ - if (bg->pattern) + n = MAX (1, n); + + if (vrepeat != GTK_CSS_REPEAT_STYLE_ROUND + /* && vsize == auto (it is by default) */) + image_height *= width / (image_width * n); + image_width = width / n; + } + if (vrepeat == GTK_CSS_REPEAT_STYLE_ROUND) { - p = bg->pattern; - c = &other_bg; - progress = 1 - progress; + double n = round (height / image_height); + + n = MAX (1, n); + + if (hrepeat != GTK_CSS_REPEAT_STYLE_ROUND + /* && hsize == auto (it is by default) */) + image_width *= height / (image_height * n); + image_height = height / n; } - else + + /* if hrepeat or vrepeat is 'space', we create a somewhat larger surface + * to store the extra space. */ + if (hrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { - p = other_pattern; - c = &bg->bg_color; + double n = floor (width / image_width); + surface_width = n ? round (width / n) : 0; } + else + surface_width = round (image_width); - if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR) + if (vrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { - cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1); - new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1); + double n = floor (height / image_height); + surface_height = n ? round (height / n) : 0; } else + surface_height = round (image_height); + + surface = cairo_surface_create_similar (cairo_get_target (cr), + CAIRO_CONTENT_COLOR_ALPHA, + surface_width, surface_height); + cr2 = cairo_create (surface); + cairo_translate (cr2, + 0.5 * (surface_width - image_width), + 0.5 * (surface_height - image_height)); + _gtk_css_image_draw (image, cr2, image_width, image_height); + cairo_destroy (cr2); + + cairo_set_source_surface (cr, surface, + _gtk_css_position_value_get_x (pos, width - image_width), + _gtk_css_position_value_get_y (pos, height - image_height)); + cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT); + cairo_surface_destroy (surface); + + if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { - cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1); - new_pattern = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1); + fill_rect.x = _gtk_css_position_value_get_x (pos, width - image_width); + fill_rect.width = image_width; } - - cairo_pattern_get_color_stop_count (p, &n); - - for (i = 0; i < n; i++) + else { - gdouble red1, green1, blue1, alpha1; - gdouble offset; - - cairo_pattern_get_color_stop_rgba (p, i, - &offset, - &red1, &green1, &blue1, - &alpha1); - cairo_pattern_add_color_stop_rgba (new_pattern, offset, - c->red + ((red1 - c->red) * progress), - c->green + ((green1 - c->green) * progress), - c->blue + ((blue1 - c->blue) * progress), - c->alpha + ((alpha1 - c->alpha) * progress)); + fill_rect.x = 0; + fill_rect.width = width; } - } - else - { - /* Merge just colors */ - new_pattern = cairo_pattern_create_rgba (CLAMP (bg->bg_color.red + ((other_bg.red - bg->bg_color.red) * progress), 0, 1), - CLAMP (bg->bg_color.green + ((other_bg.green - bg->bg_color.green) * progress), 0, 1), - CLAMP (bg->bg_color.blue + ((other_bg.blue - bg->bg_color.blue) * progress), 0, 1), - CLAMP (bg->bg_color.alpha + ((other_bg.alpha - bg->bg_color.alpha) * progress), 0, 1)); - } - - if (new_pattern) - { - /* Replace pattern to use */ - cairo_pattern_destroy (bg->pattern); - bg->pattern = new_pattern; - } - - if (other_pattern) - cairo_pattern_destroy (other_pattern); -} -static void -_gtk_theming_background_apply_origin (GtkThemingBackground *bg) -{ - GtkCssArea origin; - cairo_rectangle_t image_rect; - - gtk_theming_engine_get (bg->engine, bg->flags, - "background-origin", &origin, - NULL); - - /* The default size of the background image depends on the - background-origin value as this affects the top left - and the bottom right corners. */ - switch (origin) { - case GTK_CSS_AREA_BORDER_BOX: - image_rect.x = 0; - image_rect.y = 0; - image_rect.width = bg->paint_area.width; - image_rect.height = bg->paint_area.height; - break; - case GTK_CSS_AREA_CONTENT_BOX: - image_rect.x = bg->border.left + bg->padding.left; - image_rect.y = bg->border.top + bg->padding.top; - image_rect.width = bg->paint_area.width - bg->border.left - bg->border.right - bg->padding.left - bg->padding.right; - image_rect.height = bg->paint_area.height - bg->border.top - bg->border.bottom - bg->padding.top - bg->padding.bottom; - break; - case GTK_CSS_AREA_PADDING_BOX: - default: - image_rect.x = bg->border.left; - image_rect.y = bg->border.top; - image_rect.width = bg->paint_area.width - bg->border.left - bg->border.right; - image_rect.height = bg->paint_area.height - bg->border.top - bg->border.bottom; - break; - } - - bg->image_rect = image_rect; -} - -static void -_gtk_theming_background_apply_clip (GtkThemingBackground *bg) -{ - GtkCssArea clip; - - gtk_theming_engine_get (bg->engine, bg->flags, - "background-clip", &clip, - NULL); - - if (clip == GTK_CSS_AREA_PADDING_BOX) - { - _gtk_rounded_box_shrink (&bg->clip_box, - bg->border.top, bg->border.right, - bg->border.bottom, bg->border.left); - } - else if (clip == GTK_CSS_AREA_CONTENT_BOX) - { - _gtk_rounded_box_shrink (&bg->clip_box, - bg->border.top + bg->padding.top, - bg->border.right + bg->padding.right, - bg->border.bottom + bg->padding.bottom, - bg->border.left + bg->padding.left); - } -} - -static void -_gtk_theming_background_paint (GtkThemingBackground *bg, - cairo_t *cr) -{ - _gtk_rounded_box_path (&bg->clip_box, cr); - gdk_cairo_set_source_rgba (cr, &bg->bg_color); - - if (bg->pattern) - { - GtkCssBackgroundRepeat *repeat; - cairo_surface_t *surface; - int scale_width, scale_height; - - gtk_theming_engine_get (bg->engine, bg->flags, - "background-repeat", &repeat, - NULL); - - if (cairo_pattern_get_surface (bg->pattern, &surface) != CAIRO_STATUS_SUCCESS) - surface = NULL; - - if (surface && repeat) + if (vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { - scale_width = cairo_image_surface_get_width (surface); - scale_height = cairo_image_surface_get_height (surface); - if (repeat->repeat == GTK_CSS_BACKGROUND_REPEAT_STYLE_REPEAT) - cairo_pattern_set_extend (bg->pattern, CAIRO_EXTEND_REPEAT); - else if (repeat->repeat == GTK_CSS_BACKGROUND_REPEAT_STYLE_NO_REPEAT) - cairo_pattern_set_extend (bg->pattern, CAIRO_EXTEND_NONE); + fill_rect.y = _gtk_css_position_value_get_y (pos, height - image_height); + fill_rect.height = image_height; } else { - cairo_pattern_set_extend (bg->pattern, CAIRO_EXTEND_PAD); - scale_width = bg->image_rect.width; - scale_height = bg->image_rect.height; + fill_rect.y = 0; + fill_rect.height = height; } - if (scale_width && scale_height) - { - /* Fill background color first */ - cairo_fill_preserve (cr); - - cairo_translate (cr, bg->image_rect.x, bg->image_rect.y); - cairo_scale (cr, scale_width, scale_height); - cairo_set_source (cr, bg->pattern); - cairo_scale (cr, 1.0 / scale_width, 1.0 / scale_height); - cairo_translate (cr, -bg->image_rect.x, -bg->image_rect.y); - - g_free (repeat); - - cairo_pattern_destroy (bg->pattern); - bg->pattern = NULL; - } + cairo_rectangle (cr, fill_rect.x, fill_rect.y, + fill_rect.width, fill_rect.height); + cairo_fill (cr); } - cairo_fill (cr); + + cairo_restore (cr); } static void _gtk_theming_background_apply_shadow (GtkThemingBackground *bg, cairo_t *cr) { - GtkShadow *box_shadow; - - gtk_theming_engine_get (bg->engine, bg->flags, - "box-shadow", &box_shadow, - NULL); - - if (box_shadow != NULL) - { - _gtk_box_shadow_render (box_shadow, cr, &bg->padding_box); - _gtk_shadow_unref (box_shadow); - } + _gtk_css_shadows_value_paint_box (_gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BOX_SHADOW), + cr, + &bg->padding_box); } static void -_gtk_theming_background_init_engine (GtkThemingBackground *bg) +_gtk_theming_background_init_context (GtkThemingBackground *bg) { - bg->flags = gtk_theming_engine_get_state (bg->engine); + GtkStateFlags flags = gtk_style_context_get_state (bg->context); + GtkBorder border, padding; - gtk_theming_engine_get_border (bg->engine, bg->flags, &bg->border); - gtk_theming_engine_get_padding (bg->engine, bg->flags, &bg->padding); - gtk_theming_engine_get_background_color (bg->engine, bg->flags, &bg->bg_color); + gtk_style_context_get_border (bg->context, flags, &border); + gtk_style_context_get_padding (bg->context, flags, &padding); + gtk_style_context_get_background_color (bg->context, flags, &bg->bg_color); /* In the CSS box model, by default the background positioning area is * the padding-box, i.e. all the border-box minus the borders themselves, @@ -376,20 +290,18 @@ _gtk_theming_background_init_engine (GtkThemingBackground *bg) * In the future we might want to support different origins or clips, but * right now we just shrink to the default. */ - _gtk_rounded_box_init_rect (&bg->padding_box, 0, 0, bg->paint_area.width, bg->paint_area.height); - _gtk_rounded_box_apply_border_radius (&bg->padding_box, bg->engine, bg->flags, bg->junction); + _gtk_rounded_box_init_rect (&bg->border_box, 0, 0, bg->paint_area.width, bg->paint_area.height); + _gtk_rounded_box_apply_border_radius_for_context (&bg->border_box, bg->context, bg->junction); - bg->clip_box = bg->padding_box; + bg->padding_box = bg->border_box; _gtk_rounded_box_shrink (&bg->padding_box, - bg->border.top, bg->border.right, - bg->border.bottom, bg->border.left); + border.top, border.right, + border.bottom, border.left); - _gtk_theming_background_apply_clip (bg); - _gtk_theming_background_apply_origin (bg); - - gtk_theming_engine_get (bg->engine, bg->flags, - "background-image", &bg->pattern, - NULL); + bg->content_box = bg->padding_box; + _gtk_rounded_box_shrink (&bg->content_box, + padding.top, padding.right, + padding.bottom, padding.left); } void @@ -400,32 +312,59 @@ _gtk_theming_background_init (GtkThemingBackground *bg, gdouble width, gdouble height, GtkJunctionSides junction) +{ + GtkStyleContext *context; + + g_assert (bg != NULL); + + context = _gtk_theming_engine_get_context (engine); + _gtk_theming_background_init_from_context (bg, context, + x, y, width, height, + junction); +} + +void +_gtk_theming_background_init_from_context (GtkThemingBackground *bg, + GtkStyleContext *context, + gdouble x, + gdouble y, + gdouble width, + gdouble height, + GtkJunctionSides junction) { g_assert (bg != NULL); - bg->engine = engine; + bg->context = context; bg->paint_area.x = x; bg->paint_area.y = y; bg->paint_area.width = width; bg->paint_area.height = height; - bg->pattern = NULL; bg->junction = junction; - _gtk_theming_background_init_engine (bg); + _gtk_theming_background_init_context (bg); } void _gtk_theming_background_render (GtkThemingBackground *bg, cairo_t *cr) { + gint idx; + GtkCssValue *background_image; + + background_image = _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_IMAGE); + cairo_save (cr); cairo_translate (cr, bg->paint_area.x, bg->paint_area.y); - _gtk_theming_background_apply_window_background (bg, cr); - _gtk_theming_background_apply_running_transformation (bg, cr); - _gtk_theming_background_paint (bg, cr); + _gtk_theming_background_paint_color (bg, cr, background_image); + + for (idx = _gtk_css_array_value_get_n_values (background_image) - 1; idx >= 0; idx--) + { + _gtk_theming_background_paint_layer (bg, idx, cr); + } + _gtk_theming_background_apply_shadow (bg, cr); cairo_restore (cr); @@ -434,5 +373,12 @@ _gtk_theming_background_render (GtkThemingBackground *bg, gboolean _gtk_theming_background_has_background_image (GtkThemingBackground *bg) { - return (bg->pattern != NULL) ? TRUE : FALSE; + GtkCssImage *image; + GtkCssValue *value = _gtk_style_context_peek_property (bg->context, GTK_CSS_PROPERTY_BACKGROUND_IMAGE); + + if (_gtk_css_array_value_get_n_values (value) == 0) + return FALSE; + + image = _gtk_css_image_value_get_image (_gtk_css_array_value_get_nth (value, 0)); + return (image != NULL); }