[gtk+/wip/cosimoc/image-surface: 3/3] GtkCssImageLinear: cache linear gradient drawing
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/cosimoc/image-surface: 3/3] GtkCssImageLinear: cache linear gradient drawing
- Date: Sat, 21 Nov 2015 07:35:12 +0000 (UTC)
commit d525d3d4ece5f1ceb167a66625eef64a53fac57a
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Fri Nov 20 23:25:52 2015 -0800
GtkCssImageLinear: cache linear gradient drawing
Under some circumstances, we can end up creating a very large gradient,
and that can trigger the creation of a very large image surface because
of how gtk_css_image_get_surface() works.
Instead, cache the linear gradients into a surface, and return a
reference if we can.
gtk/gtkcssimagelinear.c | 233 ++++++++++++++++++++++++---------------
gtk/gtkcssimagelinearprivate.h | 2 +
2 files changed, 145 insertions(+), 90 deletions(-)
---
diff --git a/gtk/gtkcssimagelinear.c b/gtk/gtkcssimagelinear.c
index 093a308..f3f2db9 100644
--- a/gtk/gtkcssimagelinear.c
+++ b/gtk/gtkcssimagelinear.c
@@ -126,118 +126,164 @@ gtk_css_image_linear_compute_start_point (double angle_in_degrees,
*x = c / (slope - perpendicular);
*y = perpendicular * *x + c;
}
-
+
static void
-gtk_css_image_linear_draw (GtkCssImage *image,
- cairo_t *cr,
- double width,
- double height)
+gtk_css_image_linear_ensure_surface (GtkCssImageLinear *linear,
+ double width,
+ double height)
{
- GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image);
- cairo_pattern_t *pattern;
- double angle; /* actual angle of the gradiant line in degrees */
- double x, y; /* coordinates of start point */
- double length; /* distance in pixels for 100% */
- double start, end; /* position of first/last point on gradient line - with gradient line being [0, 1] */
- double offset;
- int i, last;
-
- if (_gtk_css_number_value_get_unit (linear->angle) == GTK_CSS_NUMBER)
+ if (linear->cached_surface != NULL &&
+ width != cairo_image_surface_get_width (linear->cached_surface) &&
+ height != cairo_image_surface_get_height (linear->cached_surface))
{
- guint side = _gtk_css_number_value_get (linear->angle, 100);
-
- /* special casing the regular cases here so we don't get rounding errors */
- switch (side)
- {
- case 1 << GTK_CSS_RIGHT:
- angle = 90;
- break;
- case 1 << GTK_CSS_LEFT:
- angle = 270;
- break;
- case 1 << GTK_CSS_TOP:
- angle = 0;
- break;
- case 1 << GTK_CSS_BOTTOM:
- angle = 180;
- break;
- default:
- angle = atan2 (side & 1 << GTK_CSS_TOP ? -width : width,
- side & 1 << GTK_CSS_LEFT ? -height : height);
- angle = 180 * angle / G_PI + 90;
- break;
- }
+ cairo_surface_destroy (linear->cached_surface);
+ linear->cached_surface = NULL;
}
- else
- {
- angle = _gtk_css_number_value_get (linear->angle, 100);
- }
-
- gtk_css_image_linear_compute_start_point (angle,
- width, height,
- &x, &y);
- length = sqrt (x * x + y * y);
- gtk_css_image_linear_get_start_end (linear, length, &start, &end);
- pattern = cairo_pattern_create_linear (x * (start - 0.5), y * (start - 0.5),
- x * (end - 0.5), y * (end - 0.5));
- if (linear->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 < linear->stops->len; i++)
+ if (linear->cached_surface == NULL)
{
- GtkCssImageLinearColorStop *stop;
- double pos, step;
-
- stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i);
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ double angle; /* actual angle of the gradiant line in degrees */
+ double x, y; /* coordinates of start point */
+ double length; /* distance in pixels for 100% */
+ double start, end; /* position of first/last point on gradient line - with gradient line being [0, 1]
*/
+ double offset;
+ int i, last;
+
+ if (_gtk_css_number_value_get_unit (linear->angle) == GTK_CSS_NUMBER)
+ {
+ guint side = _gtk_css_number_value_get (linear->angle, 100);
- if (stop->offset == NULL)
+ /* special casing the regular cases here so we don't get rounding errors */
+ switch (side)
+ {
+ case 1 << GTK_CSS_RIGHT:
+ angle = 90;
+ break;
+ case 1 << GTK_CSS_LEFT:
+ angle = 270;
+ break;
+ case 1 << GTK_CSS_TOP:
+ angle = 0;
+ break;
+ case 1 << GTK_CSS_BOTTOM:
+ angle = 180;
+ break;
+ default:
+ angle = atan2 (side & 1 << GTK_CSS_TOP ? -width : width,
+ side & 1 << GTK_CSS_LEFT ? -height : height);
+ angle = 180 * angle / G_PI + 90;
+ break;
+ }
+ }
+ else
{
- if (i == 0)
- pos = 0.0;
- else if (i + 1 == linear->stops->len)
- pos = 1.0;
- else
- continue;
+ angle = _gtk_css_number_value_get (linear->angle, 100);
}
+
+ gtk_css_image_linear_compute_start_point (angle,
+ width, height,
+ &x, &y);
+
+ length = sqrt (x * x + y * y);
+ gtk_css_image_linear_get_start_end (linear, length, &start, &end);
+ pattern = cairo_pattern_create_linear (x * (start - 0.5), y * (start - 0.5),
+ x * (end - 0.5), y * (end - 0.5));
+ if (linear->repeating)
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
else
- pos = _gtk_css_number_value_get (stop->offset, length) / length;
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
- pos = MAX (pos, offset);
- step = (pos - offset) / (i - last);
- for (last = last + 1; last <= i; last++)
+ offset = start;
+ last = -1;
+ for (i = 0; i < linear->stops->len; i++)
{
- const GdkRGBA *rgba;
+ GtkCssImageLinearColorStop *stop;
+ double pos, step;
+
+ stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i);
+
+ if (stop->offset == NULL)
+ {
+ if (i == 0)
+ pos = 0.0;
+ else if (i + 1 == linear->stops->len)
+ pos = 1.0;
+ else
+ continue;
+ }
+ else
+ pos = _gtk_css_number_value_get (stop->offset, length) / length;
+
+ pos = MAX (pos, offset);
+ step = (pos - offset) / (i - last);
+ for (last = last + 1; last <= i; last++)
+ {
+ const GdkRGBA *rgba;
+
+ stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, last);
- stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, last);
+ rgba = _gtk_css_rgba_value_get_rgba (stop->color);
+ offset += step;
- 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);
+ }
- cairo_pattern_add_color_stop_rgba (pattern,
- (offset - start) / (end - start),
- rgba->red,
- rgba->green,
- rgba->blue,
- rgba->alpha);
+ offset = pos;
+ last = i;
}
- offset = pos;
- last = i;
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ width, height);
+ cr = cairo_create (surface);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_translate (cr, width / 2, height / 2);
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ cairo_pattern_destroy (pattern);
+
+ linear->cached_surface = surface;
}
+}
- cairo_rectangle (cr, 0, 0, width, height);
- cairo_translate (cr, width / 2, height / 2);
- cairo_set_source (cr, pattern);
- cairo_fill (cr);
+static void
+gtk_css_image_linear_draw (GtkCssImage *image,
+ cairo_t *cr,
+ double width,
+ double height)
+{
+ GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image);
- cairo_pattern_destroy (pattern);
+ gtk_css_image_linear_ensure_surface (linear, width, height);
+ cairo_set_source_surface (cr, linear->cached_surface, 0, 0);
+ cairo_paint (cr);
}
+static cairo_surface_t *
+gtk_css_image_linear_get_surface (GtkCssImage *image,
+ cairo_surface_t *target,
+ int surface_width,
+ int surface_height)
+{
+ GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image);
+
+ if (linear->cached_surface != NULL &&
+ surface_width == cairo_image_surface_get_width (linear->cached_surface) &&
+ surface_height == cairo_image_surface_get_height (linear->cached_surface))
+ return cairo_surface_reference (linear->cached_surface);
+
+ return GTK_CSS_IMAGE_CLASS (_gtk_css_image_linear_parent_class)->get_surface
+ (image, target, surface_width, surface_height);
+}
static gboolean
gtk_css_image_linear_parse (GtkCssImage *image,
@@ -586,6 +632,12 @@ gtk_css_image_linear_dispose (GObject *object)
linear->angle = NULL;
}
+ if (linear->cached_surface)
+ {
+ cairo_surface_destroy (linear->cached_surface);
+ linear->cached_surface = NULL;
+ }
+
G_OBJECT_CLASS (_gtk_css_image_linear_parent_class)->dispose (object);
}
@@ -595,6 +647,7 @@ _gtk_css_image_linear_class_init (GtkCssImageLinearClass *klass)
GtkCssImageClass *image_class = GTK_CSS_IMAGE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ image_class->get_surface = gtk_css_image_linear_get_surface;
image_class->draw = gtk_css_image_linear_draw;
image_class->parse = gtk_css_image_linear_parse;
image_class->print = gtk_css_image_linear_print;
diff --git a/gtk/gtkcssimagelinearprivate.h b/gtk/gtkcssimagelinearprivate.h
index 6988abe..3c25f89 100644
--- a/gtk/gtkcssimagelinearprivate.h
+++ b/gtk/gtkcssimagelinearprivate.h
@@ -48,6 +48,8 @@ struct _GtkCssImageLinear
GtkCssValue *angle; /* warning: We use GTK_CSS_NUMBER as an enum for the corners */
GArray *stops;
guint repeating :1;
+
+ cairo_surface_t *cached_surface;
};
struct _GtkCssImageLinearClass
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]