Compare commits

...

5 Commits

Author SHA1 Message Date
Cosimo Cecchi
d525d3d4ec 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.
2015-11-20 23:33:58 -08:00
Cosimo Cecchi
a240d2b3cd GtkRenderBorder: make one function static 2015-11-20 23:33:58 -08:00
Cosimo Cecchi
ec1bc09183 GtkRenderBorder: optimize GtkCssImageSurface of same size
We don't need to draw a surface again if GtkCssImageSurface already
holds a cairo surface of the right size.
2015-11-20 23:33:58 -08:00
Cosimo Cecchi
5e69a9dfb1 GtkCssImage: add a class method to return a surface
This will be useful in a later commit when we will avoid recreating a
surface in some cases.
2015-11-20 21:26:00 -08:00
Cosimo Cecchi
973ad918b9 GtkCssImage: formatting fixes 2015-11-20 21:20:48 -08:00
7 changed files with 240 additions and 138 deletions

View File

@@ -60,6 +60,36 @@ gtk_css_image_real_get_aspect_ratio (GtkCssImage *image)
return 0;
}
static cairo_surface_t *
gtk_css_image_real_get_surface (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height)
{
cairo_surface_t *result;
cairo_t *cr;
g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL);
g_return_val_if_fail (surface_width > 0, NULL);
g_return_val_if_fail (surface_height > 0, NULL);
if (target)
result = cairo_surface_create_similar (target,
CAIRO_CONTENT_COLOR_ALPHA,
surface_width,
surface_height);
else
result = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
surface_width,
surface_height);
cr = cairo_create (result);
_gtk_css_image_draw (image, cr, surface_width, surface_height);
cairo_destroy (cr);
return result;
}
static GtkCssImage *
gtk_css_image_real_compute (GtkCssImage *image,
guint property_id,
@@ -99,6 +129,7 @@ _gtk_css_image_class_init (GtkCssImageClass *klass)
klass->get_width = gtk_css_image_real_get_width;
klass->get_height = gtk_css_image_real_get_height;
klass->get_aspect_ratio = gtk_css_image_real_get_aspect_ratio;
klass->get_surface = gtk_css_image_real_get_surface;
klass->compute = gtk_css_image_real_compute;
klass->equal = gtk_css_image_real_equal;
klass->transition = gtk_css_image_real_transition;
@@ -145,6 +176,21 @@ _gtk_css_image_get_aspect_ratio (GtkCssImage *image)
return klass->get_aspect_ratio (image);
}
cairo_surface_t *
_gtk_css_image_get_surface (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height)
{
GtkCssImageClass *klass;
g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL);
klass = GTK_CSS_IMAGE_GET_CLASS (image);
return klass->get_surface (image, target, surface_width, surface_height);
}
GtkCssImage *
_gtk_css_image_compute (GtkCssImage *image,
guint property_id,
@@ -377,39 +423,6 @@ _gtk_css_image_get_concrete_size (GtkCssImage *image,
}
}
cairo_surface_t *
_gtk_css_image_get_surface (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height)
{
cairo_surface_t *result;
cairo_t *cr;
g_return_val_if_fail (GTK_IS_CSS_IMAGE (image), NULL);
g_return_val_if_fail (surface_width > 0, NULL);
g_return_val_if_fail (surface_height > 0, NULL);
if (target)
{
result = cairo_surface_create_similar (target,
CAIRO_CONTENT_COLOR_ALPHA,
surface_width,
surface_height);
}
else
result = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
surface_width,
surface_height);
cr = cairo_create (result);
_gtk_css_image_draw (image, cr, surface_width, surface_height);
cairo_destroy (cr);
return result;
}
static GType
gtk_css_image_get_parser_type (GtkCssParser *parser)
{

View File

@@ -126,7 +126,135 @@ 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_ensure_surface (GtkCssImageLinear *linear,
double width,
double height)
{
if (linear->cached_surface != NULL &&
width != cairo_image_surface_get_width (linear->cached_surface) &&
height != cairo_image_surface_get_height (linear->cached_surface))
{
cairo_surface_destroy (linear->cached_surface);
linear->cached_surface = NULL;
}
if (linear->cached_surface == NULL)
{
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);
/* 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
{
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++)
{
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);
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;
}
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;
}
}
static void
gtk_css_image_linear_draw (GtkCssImage *image,
cairo_t *cr,
@@ -134,110 +262,28 @@ gtk_css_image_linear_draw (GtkCssImage *image,
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)
{
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;
}
}
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++)
{
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);
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, width / 2, height / 2);
cairo_set_source (cr, pattern);
cairo_fill (cr);
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;

View File

@@ -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

View File

@@ -54,6 +54,12 @@ struct _GtkCssImageClass
/* aspect ratio (width / height) of image or 0 if it has no aspect ratio (optional) */
double (* get_aspect_ratio) (GtkCssImage *image);
/* returns a surface for specified width and height */
cairo_surface_t * (* get_surface) (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height);
/* create "computed value" in CSS terms, returns a new reference */
GtkCssImage *(* compute) (GtkCssImage *image,
guint property_id,

View File

@@ -39,6 +39,22 @@ gtk_css_image_surface_get_height (GtkCssImage *image)
return cairo_image_surface_get_height (surface->surface);
}
static cairo_surface_t *
gtk_css_image_surface_get_surface (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height)
{
GtkCssImageSurface *surface = GTK_CSS_IMAGE_SURFACE (image);
if (surface_width == cairo_image_surface_get_width (surface->surface) &&
surface_height == cairo_image_surface_get_height (surface->surface))
return cairo_surface_reference (surface->surface);
return GTK_CSS_IMAGE_CLASS (_gtk_css_image_surface_parent_class)->get_surface
(image, target, surface_width, surface_height);
}
static void
gtk_css_image_surface_draw (GtkCssImage *image,
cairo_t *cr,
@@ -112,6 +128,7 @@ _gtk_css_image_surface_class_init (GtkCssImageSurfaceClass *klass)
image_class->get_width = gtk_css_image_surface_get_width;
image_class->get_height = gtk_css_image_surface_get_height;
image_class->get_surface = gtk_css_image_surface_get_surface;
image_class->draw = gtk_css_image_surface_draw;
image_class->print = gtk_css_image_surface_print;

View File

@@ -108,6 +108,18 @@ gtk_css_image_url_get_aspect_ratio (GtkCssImage *image)
return _gtk_css_image_get_aspect_ratio (gtk_css_image_url_load_image (url));
}
static cairo_surface_t *
gtk_css_image_url_get_surface (GtkCssImage *image,
cairo_surface_t *target,
int surface_width,
int surface_height)
{
GtkCssImageUrl *url = GTK_CSS_IMAGE_URL (image);
return _gtk_css_image_get_surface (gtk_css_image_url_load_image (url),
target, surface_width, surface_height);
}
static void
gtk_css_image_url_draw (GtkCssImage *image,
cairo_t *cr,
@@ -173,6 +185,7 @@ _gtk_css_image_url_class_init (GtkCssImageUrlClass *klass)
image_class->get_width = gtk_css_image_url_get_width;
image_class->get_height = gtk_css_image_url_get_height;
image_class->get_aspect_ratio = gtk_css_image_url_get_aspect_ratio;
image_class->get_surface = gtk_css_image_url_get_surface;
image_class->compute = gtk_css_image_url_compute;
image_class->draw = gtk_css_image_url_draw;
image_class->parse = gtk_css_image_url_parse;

View File

@@ -247,7 +247,7 @@ gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
sizes[1].offset = sizes[0].size;
}
void
static void
gtk_border_image_render (GtkBorderImage *image,
const double border_width[4],
cairo_t *cr,
@@ -267,8 +267,6 @@ gtk_border_image_render (GtkBorderImage *image,
width, height,
&source_width, &source_height);
/* XXX: Optimize for (source_width == width && source_height == height) */
surface = _gtk_css_image_get_surface (image->source,
cairo_get_target (cr),
source_width, source_height);