X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkcssshadowvalue.c;h=618288807af8d3b7e9571d013c632880a9f7f67a;hb=563eb60666d9f72c38d7542b0ab37841e6aac488;hp=370e1dba978bf2c4ad6709e4b3701af807aa683f;hpb=645309e98b73e825a232544ee15abf03bb397ea0;p=~andy%2Fgtk diff --git a/gtk/gtkcssshadowvalue.c b/gtk/gtkcssshadowvalue.c index 370e1dba9..618288807 100644 --- a/gtk/gtkcssshadowvalue.c +++ b/gtk/gtkcssshadowvalue.c @@ -21,275 +21,349 @@ #include "gtkcssshadowvalueprivate.h" +#include "gtkcairoblurprivate.h" +#include "gtkcsscolorvalueprivate.h" +#include "gtkcssnumbervalueprivate.h" +#include "gtkcssrgbavalueprivate.h" #include "gtkstylecontextprivate.h" #include "gtkthemingengineprivate.h" #include "gtkpango.h" -typedef struct _GtkShadowElement GtkShadowElement; - -struct _GtkShadowElement { - gint16 hoffset; - gint16 voffset; - gint16 radius; - gint16 spread; +struct _GtkCssValue { + GTK_CSS_VALUE_BASE + guint inset :1; - gboolean inset; + GtkCssValue *hoffset; + GtkCssValue *voffset; + GtkCssValue *radius; + GtkCssValue *spread; - GdkRGBA color; - GtkSymbolicColor *symbolic_color; + GtkCssValue *color; }; -static void -shadow_element_print (GtkShadowElement *element, - GString *str) -{ - gchar *color_str; - - if (element->inset) - g_string_append (str, "inset "); - - g_string_append_printf (str, "%d %d ", - (gint) element->hoffset, - (gint) element->voffset); - - if (element->radius != 0) - g_string_append_printf (str, "%d ", (gint) element->radius); - - if (element->spread != 0) - g_string_append_printf (str, "%d ", (gint) element->spread); - - if (element->symbolic_color != NULL) - color_str = gtk_symbolic_color_to_string (element->symbolic_color); - else - color_str = gdk_rgba_to_string (&element->color); - - g_string_append (str, color_str); - g_free (color_str); -} +static GtkCssValue * gtk_css_shadow_value_new (GtkCssValue *hoffset, + GtkCssValue *voffset, + GtkCssValue *radius, + GtkCssValue *spread, + gboolean inset, + GtkCssValue *color); static void -shadow_element_free (GtkShadowElement *element) +gtk_css_value_shadow_free (GtkCssValue *shadow) { - if (element->symbolic_color != NULL) - gtk_symbolic_color_unref (element->symbolic_color); + _gtk_css_value_unref (shadow->hoffset); + _gtk_css_value_unref (shadow->voffset); + _gtk_css_value_unref (shadow->radius); + _gtk_css_value_unref (shadow->spread); + _gtk_css_value_unref (shadow->color); - g_slice_free (GtkShadowElement, element); + g_slice_free (GtkCssValue, shadow); } -static GtkShadowElement * -shadow_element_new (gdouble hoffset, - gdouble voffset, - gdouble radius, - gdouble spread, - gboolean inset, - GdkRGBA *color, - GtkSymbolicColor *symbolic_color) +static GtkCssValue * +gtk_css_value_shadow_compute (GtkCssValue *shadow, + guint property_id, + GtkStyleProviderPrivate *provider, + GtkCssComputedValues *values, + GtkCssComputedValues *parent_values, + GtkCssDependencies *dependencies) { - GtkShadowElement *retval; - - retval = g_slice_new0 (GtkShadowElement); + GtkCssValue *hoffset, *voffset, *radius, *spread, *color; + GtkCssDependencies child_deps; - retval->hoffset = hoffset; - retval->voffset = voffset; - retval->radius = radius; - retval->spread = spread; - retval->inset = inset; + child_deps = 0; + hoffset = _gtk_css_value_compute (shadow->hoffset, property_id, provider, values, parent_values, &child_deps); + *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps); - if (symbolic_color != NULL) - retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color); + child_deps = 0; + voffset = _gtk_css_value_compute (shadow->voffset, property_id, provider, values, parent_values, &child_deps); + *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps); - if (color != NULL) - retval->color = *color; + child_deps = 0; + radius = _gtk_css_value_compute (shadow->radius, property_id, provider, values, parent_values, &child_deps); + *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps); - return retval; -} + child_deps = 0; + spread = _gtk_css_value_compute (shadow->spread, property_id, provider, values, parent_values, &child_deps), + *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps); -/**************** - * GtkCssValue * - ****************/ + child_deps = 0; + color = _gtk_css_value_compute (shadow->color, property_id, provider, values, parent_values, &child_deps); + *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps); -struct _GtkCssValue { - GTK_CSS_VALUE_BASE - GList *elements; -}; - -static void -gtk_css_value_shadow_free (GtkCssValue *shadow) -{ - g_list_free_full (shadow->elements, - (GDestroyNotify) shadow_element_free); - g_slice_free (GtkCssValue, shadow); + return gtk_css_shadow_value_new (hoffset, voffset, radius, spread, shadow->inset, color); } static gboolean gtk_css_value_shadow_equal (const GtkCssValue *shadow1, const GtkCssValue *shadow2) { - /* FIXME */ - return shadow1 == shadow2; + return shadow1->inset == shadow2->inset + && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset) + && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset) + && _gtk_css_value_equal (shadow1->radius, shadow2->radius) + && _gtk_css_value_equal (shadow1->spread, shadow2->spread) + && _gtk_css_value_equal (shadow1->color, shadow2->color); +} + +static GtkCssValue * +gtk_css_value_shadow_transition (GtkCssValue *start, + GtkCssValue *end, + guint property_id, + double progress) +{ + if (start->inset != end->inset) + return NULL; + + return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, property_id, progress), + _gtk_css_value_transition (start->voffset, end->voffset, property_id, progress), + _gtk_css_value_transition (start->radius, end->radius, property_id, progress), + _gtk_css_value_transition (start->spread, end->spread, property_id, progress), + start->inset, + _gtk_css_value_transition (start->color, end->color, property_id, progress)); } static void gtk_css_value_shadow_print (const GtkCssValue *shadow, GString *string) { - gint length; - GList *l; - - length = g_list_length (shadow->elements); + _gtk_css_value_print (shadow->hoffset, string); + g_string_append_c (string, ' '); + _gtk_css_value_print (shadow->voffset, string); + g_string_append_c (string, ' '); + if (_gtk_css_number_value_get (shadow->radius, 100) != 0 || + _gtk_css_number_value_get (shadow->spread, 100) != 0) + { + _gtk_css_value_print (shadow->radius, string); + g_string_append_c (string, ' '); + } - if (length == 0) + if (_gtk_css_number_value_get (shadow->spread, 100) != 0) { - g_string_append (string, "none"); - return; + _gtk_css_value_print (shadow->spread, string); + g_string_append_c (string, ' '); } - shadow_element_print (shadow->elements->data, string); + _gtk_css_value_print (shadow->color, string); - if (length == 1) - return; + if (shadow->inset) + g_string_append (string, " inset"); - for (l = g_list_next (shadow->elements); l != NULL; l = l->next) - { - g_string_append (string, ", "); - shadow_element_print (l->data, string); - } } static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = { gtk_css_value_shadow_free, + gtk_css_value_shadow_compute, gtk_css_value_shadow_equal, + gtk_css_value_shadow_transition, gtk_css_value_shadow_print }; -static GtkCssValue none_singleton = { >K_CSS_VALUE_SHADOW, 1, NULL }; +static GtkCssValue * +gtk_css_shadow_value_new (GtkCssValue *hoffset, + GtkCssValue *voffset, + GtkCssValue *radius, + GtkCssValue *spread, + gboolean inset, + GtkCssValue *color) +{ + GtkCssValue *retval; + + retval = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW); + + retval->hoffset = hoffset; + retval->voffset = voffset; + retval->radius = radius; + retval->spread = spread; + retval->inset = inset; + retval->color = color; + + return retval; +} GtkCssValue * -_gtk_css_shadow_value_new_none (void) +_gtk_css_shadow_value_new_for_transition (GtkCssValue *target) +{ + GdkRGBA transparent = { 0, 0, 0, 0 }; + + g_return_val_if_fail (target->class == >K_CSS_VALUE_SHADOW, NULL); + + return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX), + _gtk_css_number_value_new (0, GTK_CSS_PX), + _gtk_css_number_value_new (0, GTK_CSS_PX), + _gtk_css_number_value_new (0, GTK_CSS_PX), + target->inset, + _gtk_css_rgba_value_new_from_rgba (&transparent)); +} + +static gboolean +value_is_done_parsing (GtkCssParser *parser) { - return _gtk_css_value_ref (&none_singleton); + return _gtk_css_parser_is_eof (parser) || + _gtk_css_parser_begins_with (parser, ',') || + _gtk_css_parser_begins_with (parser, ';') || + _gtk_css_parser_begins_with (parser, '}'); } GtkCssValue * _gtk_css_shadow_value_parse (GtkCssParser *parser) { - gboolean have_inset, have_color, have_lengths; - gdouble hoffset, voffset, blur, spread; - GtkSymbolicColor *color; - GtkShadowElement *element; - GtkCssValue *shadow; + enum { + HOFFSET, + VOFFSET, + RADIUS, + SPREAD, + COLOR, + N_VALUES + }; + GtkCssValue *values[N_VALUES] = { NULL, }; + gboolean inset; guint i; - if (_gtk_css_parser_try (parser, "none", TRUE)) - return _gtk_css_shadow_value_new_none (); - - shadow = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW); + inset = _gtk_css_parser_try (parser, "inset", TRUE); do + { + if (values[HOFFSET] == NULL && + _gtk_css_parser_has_number (parser)) + { + values[HOFFSET] = _gtk_css_number_value_parse (parser, + GTK_CSS_PARSE_LENGTH + | GTK_CSS_NUMBER_AS_PIXELS); + if (values[HOFFSET] == NULL) + goto fail; + + values[VOFFSET] = _gtk_css_number_value_parse (parser, + GTK_CSS_PARSE_LENGTH + | GTK_CSS_NUMBER_AS_PIXELS); + if (values[VOFFSET] == NULL) + goto fail; + + if (_gtk_css_parser_has_number (parser)) + { + values[RADIUS] = _gtk_css_number_value_parse (parser, + GTK_CSS_PARSE_LENGTH + | GTK_CSS_POSITIVE_ONLY + | GTK_CSS_NUMBER_AS_PIXELS); + if (values[RADIUS] == NULL) + goto fail; + } + else + values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX); + + if (_gtk_css_parser_has_number (parser)) + { + values[SPREAD] = _gtk_css_number_value_parse (parser, + GTK_CSS_PARSE_LENGTH + | GTK_CSS_NUMBER_AS_PIXELS); + if (values[SPREAD] == NULL) + goto fail; + } + else + values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX); + } + else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE)) + { + if (values[HOFFSET] == NULL) + goto fail; + inset = TRUE; + break; + } + else if (values[COLOR] == NULL) + { + values[COLOR] = _gtk_css_color_value_parse (parser); + + if (values[COLOR] == NULL) + goto fail; + } + else + { + /* We parsed everything and there's still stuff left? + * Pretend we didn't notice and let the normal code produce + * a 'junk at end of value' error */ + goto fail; + } + } + while (values[HOFFSET] == NULL || !value_is_done_parsing (parser)); + + if (values[COLOR] == NULL) + values[COLOR] = _gtk_css_color_value_new_current_color (); + + return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET], + values[RADIUS], values[SPREAD], + inset, values[COLOR]); + +fail: + for (i = 0; i < N_VALUES; i++) { - have_inset = have_lengths = have_color = FALSE; - - for (i = 0; i < 3; i++) - { - if (!have_inset && - _gtk_css_parser_try (parser, "inset", TRUE)) - { - have_inset = TRUE; - continue; - } - - if (!have_lengths && - _gtk_css_parser_try_double (parser, &hoffset)) - { - have_lengths = TRUE; - - if (!_gtk_css_parser_try_double (parser, &voffset)) - { - _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required"); - _gtk_css_value_unref (shadow); - return NULL; - } - - if (!_gtk_css_parser_try_double (parser, &blur)) - blur = 0; - - if (!_gtk_css_parser_try_double (parser, &spread)) - spread = 0; - - continue; - } - - if (!have_color) - { - have_color = TRUE; - - /* XXX: the color is optional and UA-defined if it's missing, - * but it doesn't really make sense for us... - */ - color = _gtk_css_parser_read_symbolic_color (parser); - - if (color == NULL) - { - _gtk_css_value_unref (shadow); - return NULL; - } - } - } - - if (!have_color || !have_lengths) - { - _gtk_css_parser_error (parser, "Must specify at least color and offsets"); - _gtk_css_value_unref (shadow); - return NULL; - } - - element = shadow_element_new (hoffset, voffset, - blur, spread, have_inset, - NULL, color); - - shadow->elements = g_list_append (shadow->elements, element); - - gtk_symbolic_color_unref (color); + if (values[i]) + _gtk_css_value_unref (values[i]); + } + + return NULL; +} +static const cairo_user_data_key_t shadow_key; + +static cairo_t * +gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow, + cairo_t *cr) +{ + cairo_rectangle_int_t clip_rect; + cairo_surface_t *surface; + cairo_t *blur_cr; + gdouble radius; + + radius = _gtk_css_number_value_get (shadow->radius, 0); + if (radius == 0.0) + return cr; + + gdk_cairo_get_clip_rectangle (cr, &clip_rect); + + /* Create a larger surface to center the blur. */ + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + clip_rect.width + 2 * radius, + clip_rect.height + 2 * radius); + cairo_surface_set_device_offset (surface, radius - clip_rect.x, radius - clip_rect.y); + blur_cr = cairo_create (surface); + cairo_set_user_data (blur_cr, &shadow_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy); + + if (cairo_has_current_point (cr)) + { + double x, y; + + cairo_get_current_point (cr, &x, &y); + cairo_move_to (blur_cr, x, y); } - while (_gtk_css_parser_try (parser, ",", TRUE)); - return shadow; + return blur_cr; } -GtkCssValue * -_gtk_css_shadow_value_compute (GtkCssValue *shadow, - GtkStyleContext *context) +static cairo_t * +gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow, + cairo_t *cr) { - GtkCssValue *resolved_shadow; - GtkShadowElement *element, *resolved_element; - GdkRGBA color; - GList *l; + gdouble radius; + cairo_t *original_cr; + cairo_surface_t *surface; - resolved_shadow = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW); + radius = _gtk_css_number_value_get (shadow->radius, 0); + if (radius == 0.0) + return cr; - for (l = shadow->elements; l != NULL; l = l->next) - { - element = l->data; - - if (!_gtk_style_context_resolve_color (context, - element->symbolic_color, - &color)) - { - _gtk_css_value_unref (resolved_shadow); - return NULL; - } - - resolved_element = - shadow_element_new (element->hoffset, element->voffset, - element->radius, element->spread, element->inset, - &color, NULL); - - resolved_shadow->elements = - g_list_append (resolved_shadow->elements, resolved_element); - } + surface = cairo_get_target (cr); + original_cr = cairo_get_user_data (cr, &shadow_key); + + /* Blur the surface. */ + _gtk_cairo_blur_surface (surface, radius); + + cairo_set_source_surface (original_cr, surface, 0, 0); + cairo_paint (original_cr); + + cairo_destroy (cr); + cairo_surface_destroy (surface); - return resolved_shadow; + return original_cr; } void @@ -297,52 +371,54 @@ _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow, cairo_t *cr, PangoLayout *layout) { - GList *l; - GtkShadowElement *element; + g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW); if (!cairo_has_current_point (cr)) cairo_move_to (cr, 0, 0); - /* render shadows starting from the last one, - * and the others on top. - */ - for (l = g_list_last (shadow->elements); l != NULL; l = l->prev) - { - element = l->data; + cairo_save (cr); - cairo_save (cr); + cairo_rel_move_to (cr, + _gtk_css_number_value_get (shadow->hoffset, 0), + _gtk_css_number_value_get (shadow->voffset, 0)); - cairo_rel_move_to (cr, element->hoffset, element->voffset); - gdk_cairo_set_source_rgba (cr, &element->color); - _gtk_pango_fill_layout (cr, layout); + cr = gtk_css_shadow_value_start_drawing (shadow, cr); - cairo_rel_move_to (cr, -element->hoffset, -element->voffset); - cairo_restore (cr); - } + gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color)); + _gtk_pango_fill_layout (cr, layout); + + cr = gtk_css_shadow_value_finish_drawing (shadow, cr); + + cairo_rel_move_to (cr, + - _gtk_css_number_value_get (shadow->hoffset, 0), + - _gtk_css_number_value_get (shadow->voffset, 0)); + cairo_restore (cr); } void _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow, cairo_t *cr) { - GList *l; - GtkShadowElement *element; cairo_pattern_t *pattern; - for (l = g_list_last (shadow->elements); l != NULL; l = l->prev) - { - element = l->data; + g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW); - cairo_save (cr); - pattern = cairo_pattern_reference (cairo_get_source (cr)); - gdk_cairo_set_source_rgba (cr, &element->color); + cairo_save (cr); + pattern = cairo_pattern_reference (cairo_get_source (cr)); - cairo_translate (cr, element->hoffset, element->voffset); - cairo_mask (cr, pattern); + cr = gtk_css_shadow_value_start_drawing (shadow, cr); - cairo_restore (cr); - cairo_pattern_destroy (pattern); - } + gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color)); + + cairo_translate (cr, + _gtk_css_number_value_get (shadow->hoffset, 0), + _gtk_css_number_value_get (shadow->voffset, 0)); + cairo_mask (cr, pattern); + + cr = gtk_css_shadow_value_finish_drawing (shadow, cr); + + cairo_restore (cr); + cairo_pattern_destroy (pattern); } void @@ -351,22 +427,22 @@ _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow, gdouble radius, gdouble progress) { - GtkShadowElement *element; - GList *l; + g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW); - for (l = g_list_last (shadow->elements); l != NULL; l = l->prev) - { - element = l->data; + cairo_save (cr); - cairo_save (cr); + cr = gtk_css_shadow_value_start_drawing (shadow, cr); - cairo_translate (cr, element->hoffset, element->voffset); - _gtk_theming_engine_paint_spinner (cr, - radius, progress, - &element->color); + cairo_translate (cr, + _gtk_css_number_value_get (shadow->hoffset, 0), + _gtk_css_number_value_get (shadow->voffset, 0)); + _gtk_theming_engine_paint_spinner (cr, + radius, progress, + _gtk_css_rgba_value_get_rgba (shadow->color)); - cairo_restore (cr); - } + cr = gtk_css_shadow_value_finish_drawing (shadow, cr); + + cairo_restore (cr); } void @@ -374,38 +450,37 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue *shadow, cairo_t *cr, const GtkRoundedBox *padding_box) { - GtkShadowElement *element; - GtkRoundedBox box; - GList *l; + GtkRoundedBox box, clip_box; + double spread, radius; + + g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW); cairo_save (cr); - cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); _gtk_rounded_box_path (padding_box, cr); cairo_clip (cr); - /* render shadows starting from the last one, - * and the others on top. - */ - for (l = g_list_last (shadow->elements); l != NULL; l = l->prev) - { - element = l->data; + box = *padding_box; + _gtk_rounded_box_move (&box, + _gtk_css_number_value_get (shadow->hoffset, 0), + _gtk_css_number_value_get (shadow->voffset, 0)); + spread = _gtk_css_number_value_get (shadow->spread, 0); + _gtk_rounded_box_shrink (&box, spread, spread, spread, spread); - if (!element->inset) - continue; + clip_box = *padding_box; + radius = _gtk_css_number_value_get (shadow->radius, 0); + _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius); - box = *padding_box; - _gtk_rounded_box_move (&box, element->hoffset, element->voffset); - _gtk_rounded_box_shrink (&box, - element->spread, element->spread, - element->spread, element->spread); + cr = gtk_css_shadow_value_start_drawing (shadow, cr); - _gtk_rounded_box_path (&box, cr); - _gtk_rounded_box_clip_path (padding_box, cr); + cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); + _gtk_rounded_box_path (&box, cr); + _gtk_rounded_box_clip_path (&clip_box, cr); - gdk_cairo_set_source_rgba (cr, &element->color); - cairo_fill (cr); - } + gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color)); + cairo_fill (cr); + + cr = gtk_css_shadow_value_finish_drawing (shadow, cr); cairo_restore (cr); }