static void gtk_css_image_radial_draw (GtkCssImage *image, cairo_t *cr, double width, double height) { GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); cairo_pattern_t *pattern; cairo_matrix_t matrix; double x, y; double radius, yscale; double start, end; double r1, r2, r3, r4, r; double offset; int i, last; x = _gtk_css_position_value_get_x (radial->position, width); y = _gtk_css_position_value_get_y (radial->position, height); if (radial->circle) { switch (radial->size) { case GTK_CSS_EXPLICIT_SIZE: radius = _gtk_css_number_value_get (radial->sizes[0], width); break; case GTK_CSS_CLOSEST_SIDE: radius = MIN (MIN (x, width - x), MIN (y, height - y)); break; case GTK_CSS_FARTHEST_SIDE: radius = MAX (MAX (x, width - x), MAX (y, height - y)); break; case GTK_CSS_CLOSEST_CORNER: case GTK_CSS_FARTHEST_CORNER: r1 = x*x + y*y; r2 = x*x + (height - y)*(height - y); r3 = (width - x)*(width - x) + y*y; r4 = (width - x)*(width - x) + (height - y)*(height - y); if (radial->size == GTK_CSS_CLOSEST_CORNER) r = MIN ( MIN (r1, r2), MIN (r3, r4)); else r = MAX ( MAX (r1, r2), MAX (r3, r4)); radius = sqrt (r); break; default: g_assert_not_reached (); } radius = MAX (1.0, radius); yscale = 1.0; } else { double hradius, vradius; switch (radial->size) { case GTK_CSS_EXPLICIT_SIZE: hradius = _gtk_css_number_value_get (radial->sizes[0], width); vradius = _gtk_css_number_value_get (radial->sizes[1], height); break; case GTK_CSS_CLOSEST_SIDE: hradius = MIN (x, width - x); vradius = MIN (y, height - y); break; case GTK_CSS_FARTHEST_SIDE: hradius = MAX (x, width - x); vradius = MAX (y, height - y); break; case GTK_CSS_CLOSEST_CORNER: hradius = M_SQRT2 * MIN (x, width - x); vradius = M_SQRT2 * MIN (y, height - y); break; case GTK_CSS_FARTHEST_CORNER: hradius = M_SQRT2 * MAX (x, width - x); vradius = M_SQRT2 * MAX (y, height - y); break; default: g_assert_not_reached (); } hradius = MAX (1.0, hradius); vradius = MAX (1.0, vradius); radius = hradius; yscale = vradius / hradius; } gtk_css_image_radial_get_start_end (radial, radius, &start, &end); pattern = cairo_pattern_create_radial (0, 0, 0, 0, 0, radius); if (yscale != 1.0) { cairo_matrix_init_scale (&matrix, 1.0, 1.0 / yscale); cairo_pattern_set_matrix (pattern, &matrix); } if (radial->repeating) cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); else cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); offset = start; last = -1; for (i = 0; i < radial->stops->len; i++) { GtkCssImageRadialColorStop *stop; double pos, step; stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, i); if (stop->offset == NULL) { if (i == 0) pos = 0.0; else if (i + 1 == radial->stops->len) pos = 1.0; else continue; } else pos = _gtk_css_number_value_get (stop->offset, radius) / radius; pos = MAX (pos, 0); step = pos / (i - last); for (last = last + 1; last <= i; last++) { const GdkRGBA *rgba; stop = &g_array_index (radial->stops, GtkCssImageRadialColorStop, last); rgba = _gtk_css_rgba_value_get_rgba (stop->color); offset += step; cairo_pattern_add_color_stop_rgba (pattern, (offset - start) / (end - start), rgba->red, rgba->green, rgba->blue, rgba->alpha); } offset = pos; last = i; } cairo_rectangle (cr, 0, 0, width, height); cairo_translate (cr, x, y); cairo_set_source (cr, pattern); cairo_fill (cr); cairo_pattern_destroy (pattern); }
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 = &bg->boxes[ _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; _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); if (image_width <= 0 || image_height <= 0) return; /* optimization */ if (image_width == width) hrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; if (image_height == height) vrepeat = GTK_CSS_REPEAT_STYLE_NO_REPEAT; cairo_save (cr); _gtk_rounded_box_path ( &bg->boxes[ _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); 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 { 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); 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) { 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; } /* if hrepeat or vrepeat is 'space', we create a somewhat larger surface * to store the extra space. */ if (hrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { double n = floor (width / image_width); surface_width = n ? round (width / n) : 0; } else surface_width = round (image_width); if (vrepeat == GTK_CSS_REPEAT_STYLE_SPACE) { 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) { fill_rect.x = _gtk_css_position_value_get_x (pos, width - image_width); fill_rect.width = image_width; } else { fill_rect.x = 0; fill_rect.width = width; } if (vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT) { fill_rect.y = _gtk_css_position_value_get_y (pos, height - image_height); fill_rect.height = image_height; } else { fill_rect.y = 0; fill_rect.height = height; } cairo_rectangle (cr, fill_rect.x, fill_rect.y, fill_rect.width, fill_rect.height); cairo_fill (cr); } cairo_restore (cr); }