1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2011 Red Hat, Inc.
4 * Author: Cosimo Cecchi <cosimoc@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 #include "gtkcssshadowvalueprivate.h"
26 #include "gtkcairoblurprivate.h"
27 #include "gtkcssnumbervalueprivate.h"
28 #include "gtkcssrgbavalueprivate.h"
29 #include "gtkstylecontextprivate.h"
30 #include "gtksymboliccolorprivate.h"
31 #include "gtkthemingengineprivate.h"
46 static GtkCssValue * gtk_css_shadow_value_new (GtkCssValue *hoffset,
54 gtk_css_value_shadow_free (GtkCssValue *shadow)
56 _gtk_css_value_unref (shadow->hoffset);
57 _gtk_css_value_unref (shadow->voffset);
58 _gtk_css_value_unref (shadow->radius);
59 _gtk_css_value_unref (shadow->spread);
60 _gtk_css_value_unref (shadow->color);
62 g_slice_free (GtkCssValue, shadow);
66 gtk_css_value_shadow_compute (GtkCssValue *shadow,
68 GtkStyleContext *context,
69 GtkCssDependencies *dependencies)
71 GtkCssValue *hoffset, *voffset, *radius, *spread, *color;
72 GtkCssDependencies child_deps;
75 hoffset = _gtk_css_value_compute (shadow->hoffset, property_id, context, &child_deps);
76 *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
79 voffset = _gtk_css_value_compute (shadow->voffset, property_id, context, &child_deps);
80 *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
83 radius = _gtk_css_value_compute (shadow->radius, property_id, context, &child_deps);
84 *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
87 spread = _gtk_css_value_compute (shadow->spread, property_id, context, &child_deps),
88 *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
91 color = _gtk_css_value_compute (shadow->color, property_id, context, &child_deps);
92 *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
94 return gtk_css_shadow_value_new (hoffset, voffset, radius, spread, shadow->inset, color);
98 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
99 const GtkCssValue *shadow2)
101 return shadow1->inset == shadow2->inset
102 && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset)
103 && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset)
104 && _gtk_css_value_equal (shadow1->radius, shadow2->radius)
105 && _gtk_css_value_equal (shadow1->spread, shadow2->spread)
106 && _gtk_css_value_equal (shadow1->color, shadow2->color);
110 gtk_css_value_shadow_transition (GtkCssValue *start,
115 if (start->inset != end->inset)
118 return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, property_id, progress),
119 _gtk_css_value_transition (start->voffset, end->voffset, property_id, progress),
120 _gtk_css_value_transition (start->radius, end->radius, property_id, progress),
121 _gtk_css_value_transition (start->spread, end->spread, property_id, progress),
123 _gtk_css_value_transition (start->color, end->color, property_id, progress));
127 gtk_css_value_shadow_print (const GtkCssValue *shadow,
130 _gtk_css_value_print (shadow->hoffset, string);
131 g_string_append_c (string, ' ');
132 _gtk_css_value_print (shadow->voffset, string);
133 g_string_append_c (string, ' ');
134 if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
135 _gtk_css_number_value_get (shadow->spread, 100) != 0)
137 _gtk_css_value_print (shadow->radius, string);
138 g_string_append_c (string, ' ');
141 if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
143 _gtk_css_value_print (shadow->spread, string);
144 g_string_append_c (string, ' ');
147 _gtk_css_value_print (shadow->color, string);
150 g_string_append (string, " inset");
154 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
155 gtk_css_value_shadow_free,
156 gtk_css_value_shadow_compute,
157 gtk_css_value_shadow_equal,
158 gtk_css_value_shadow_transition,
159 gtk_css_value_shadow_print
163 gtk_css_shadow_value_new (GtkCssValue *hoffset,
164 GtkCssValue *voffset,
172 retval = _gtk_css_value_new (GtkCssValue, >K_CSS_VALUE_SHADOW);
174 retval->hoffset = hoffset;
175 retval->voffset = voffset;
176 retval->radius = radius;
177 retval->spread = spread;
178 retval->inset = inset;
179 retval->color = color;
185 _gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
187 GdkRGBA transparent = { 0, 0, 0, 0 };
189 g_return_val_if_fail (target->class == >K_CSS_VALUE_SHADOW, NULL);
191 return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX),
192 _gtk_css_number_value_new (0, GTK_CSS_PX),
193 _gtk_css_number_value_new (0, GTK_CSS_PX),
194 _gtk_css_number_value_new (0, GTK_CSS_PX),
196 _gtk_css_rgba_value_new_from_rgba (&transparent));
200 value_is_done_parsing (GtkCssParser *parser)
202 return _gtk_css_parser_is_eof (parser) ||
203 _gtk_css_parser_begins_with (parser, ',') ||
204 _gtk_css_parser_begins_with (parser, ';') ||
205 _gtk_css_parser_begins_with (parser, '}');
209 _gtk_css_shadow_value_parse (GtkCssParser *parser)
219 GtkCssValue *values[N_VALUES] = { NULL, };
223 inset = _gtk_css_parser_try (parser, "inset", TRUE);
227 if (values[HOFFSET] == NULL &&
228 _gtk_css_parser_has_number (parser))
230 values[HOFFSET] = _gtk_css_number_value_parse (parser,
232 | GTK_CSS_NUMBER_AS_PIXELS);
233 if (values[HOFFSET] == NULL)
236 values[VOFFSET] = _gtk_css_number_value_parse (parser,
238 | GTK_CSS_NUMBER_AS_PIXELS);
239 if (values[VOFFSET] == NULL)
242 if (_gtk_css_parser_has_number (parser))
244 values[RADIUS] = _gtk_css_number_value_parse (parser,
246 | GTK_CSS_POSITIVE_ONLY
247 | GTK_CSS_NUMBER_AS_PIXELS);
248 if (values[RADIUS] == NULL)
252 values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
254 if (_gtk_css_parser_has_number (parser))
256 values[SPREAD] = _gtk_css_number_value_parse (parser,
258 | GTK_CSS_NUMBER_AS_PIXELS);
259 if (values[SPREAD] == NULL)
263 values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
265 else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE))
267 if (values[HOFFSET] == NULL)
272 else if (values[COLOR] == NULL)
274 values[COLOR] = _gtk_css_symbolic_value_new (parser);
276 if (values[COLOR] == NULL)
281 /* We parsed everything and there's still stuff left?
282 * Pretend we didn't notice and let the normal code produce
283 * a 'junk at end of value' error */
287 while (values[HOFFSET] == NULL || !value_is_done_parsing (parser));
289 if (values[COLOR] == NULL)
290 values[COLOR] = _gtk_css_symbolic_value_new_take_symbolic_color (
291 gtk_symbolic_color_ref (
292 _gtk_symbolic_color_get_current_color ()));
294 return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET],
295 values[RADIUS], values[SPREAD],
296 inset, values[COLOR]);
299 for (i = 0; i < N_VALUES; i++)
302 _gtk_css_value_unref (values[i]);
308 static const cairo_user_data_key_t shadow_key;
311 gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
314 cairo_rectangle_t clip;
315 cairo_surface_t *surface;
319 radius = _gtk_css_number_value_get (shadow->radius, 0);
323 radius = ceil (radius);
324 cairo_clip_extents (cr, &clip.x, &clip.y, &clip.width, &clip.height);
327 clip.width += 2 * radius;
328 clip.height += 2 * radius;
330 /* Create a larger surface to center the blur. */
331 surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, &clip);
332 cairo_surface_set_device_offset (surface, - clip.x, - clip.y);
333 blur_cr = cairo_create (surface);
334 cairo_set_user_data (blur_cr, &shadow_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
336 if (cairo_has_current_point (cr))
340 cairo_get_current_point (cr, &x, &y);
341 cairo_move_to (blur_cr, x, y);
348 gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow,
352 cairo_t *original_cr, *image_cr;
353 cairo_surface_t *recording_surface, *image_surface;
354 cairo_rectangle_t ink;
356 radius = _gtk_css_number_value_get (shadow->radius, 0);
360 recording_surface = cairo_get_target (cr);
361 original_cr = cairo_get_user_data (cr, &shadow_key);
363 /* Blur the surface. */
364 cairo_recording_surface_ink_extents (recording_surface, &ink.x, &ink.y, &ink.width, &ink.height);
365 ink.width = ceil (ink.width + ink.x - floor (ink.x)) + 2 * ceil (radius);
366 ink.height = ceil (ink.height + ink.y - floor (ink.y)) + 2 * ceil (radius);
367 ink.x = floor (ink.x) + ceil (radius);
368 ink.y = floor (ink.y) + ceil (radius);
370 image_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ink.width, ink.height);
371 cairo_surface_set_device_offset (image_surface, - ink.x, - ink.y);
373 image_cr = cairo_create (image_surface);
374 cairo_set_source_surface (image_cr, recording_surface, 0, 0);
375 cairo_paint (image_cr);
376 cairo_destroy (image_cr);
378 _gtk_cairo_blur_surface (image_surface, radius);
380 cairo_set_source_surface (original_cr, image_surface, 0, 0);
381 cairo_paint (original_cr);
384 cairo_surface_destroy (image_surface);
390 _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
394 g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
396 if (!cairo_has_current_point (cr))
397 cairo_move_to (cr, 0, 0);
401 cairo_rel_move_to (cr,
402 _gtk_css_number_value_get (shadow->hoffset, 0),
403 _gtk_css_number_value_get (shadow->voffset, 0));
405 cr = gtk_css_shadow_value_start_drawing (shadow, cr);
407 gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
408 _gtk_pango_fill_layout (cr, layout);
410 cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
412 cairo_rel_move_to (cr,
413 - _gtk_css_number_value_get (shadow->hoffset, 0),
414 - _gtk_css_number_value_get (shadow->voffset, 0));
419 _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
422 cairo_pattern_t *pattern;
424 g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
427 pattern = cairo_pattern_reference (cairo_get_source (cr));
429 cr = gtk_css_shadow_value_start_drawing (shadow, cr);
431 gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
434 _gtk_css_number_value_get (shadow->hoffset, 0),
435 _gtk_css_number_value_get (shadow->voffset, 0));
436 cairo_mask (cr, pattern);
438 cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
441 cairo_pattern_destroy (pattern);
445 _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
450 g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
454 cr = gtk_css_shadow_value_start_drawing (shadow, cr);
457 _gtk_css_number_value_get (shadow->hoffset, 0),
458 _gtk_css_number_value_get (shadow->voffset, 0));
459 _gtk_theming_engine_paint_spinner (cr,
461 _gtk_css_rgba_value_get_rgba (shadow->color));
463 cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
469 _gtk_css_shadow_value_paint_box (const GtkCssValue *shadow,
471 const GtkRoundedBox *padding_box)
473 GtkRoundedBox box, clip_box;
474 double spread, radius;
476 g_return_if_fail (shadow->class == >K_CSS_VALUE_SHADOW);
480 _gtk_rounded_box_path (padding_box, cr);
484 _gtk_rounded_box_move (&box,
485 _gtk_css_number_value_get (shadow->hoffset, 0),
486 _gtk_css_number_value_get (shadow->voffset, 0));
487 spread = _gtk_css_number_value_get (shadow->spread, 0);
488 _gtk_rounded_box_shrink (&box, spread, spread, spread, spread);
490 clip_box = *padding_box;
491 radius = _gtk_css_number_value_get (shadow->radius, 0);
492 _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius);
494 cr = gtk_css_shadow_value_start_drawing (shadow, cr);
496 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
497 _gtk_rounded_box_path (&box, cr);
498 _gtk_rounded_box_clip_path (&clip_box, cr);
500 gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
503 cr = gtk_css_shadow_value_finish_drawing (shadow, cr);