StThemeDrawing: clip background to border

Previously, trying to use a background image and border on
the same node resulted in the background drawing over the border.

This commit adds support for background images to

st_theme_node_render_background_with_border

and changes the code to call that function when appropriate.

https://bugzilla.gnome.org/show_bug.cgi?id=636976
This commit is contained in:
Ray Strode 2010-12-10 17:15:39 -05:00
parent 03757c1fcb
commit 779d5462f2

View File

@ -485,9 +485,233 @@ create_cairo_pattern_of_background_gradient (StThemeNode *node)
return pattern; return pattern;
} }
static cairo_pattern_t *
create_cairo_pattern_of_background_image (StThemeNode *node,
gboolean *needs_background_fill)
{
cairo_surface_t *surface;
cairo_pattern_t *pattern;
cairo_content_t content;
cairo_matrix_t matrix;
const char *file;
double height_ratio, width_ratio;
int file_width;
int file_height;
StTextureCache *texture_cache;
file = st_theme_node_get_background_image (node);
texture_cache = st_texture_cache_get_default ();
surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file);
if (surface == NULL)
return NULL;
g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
content = cairo_surface_get_content (surface);
pattern = cairo_pattern_create_for_surface (surface);
file_width = cairo_image_surface_get_width (surface);
file_height = cairo_image_surface_get_height (surface);
height_ratio = file_height / node->alloc_height;
width_ratio = file_width / node->alloc_width;
*needs_background_fill = TRUE;
if ((file_width > node->alloc_width || file_height > node->alloc_height)
&& !node->background_position_set)
{
double scale_factor;
double x_offset, y_offset;
if (width_ratio > height_ratio)
{
double scaled_height;
/* center vertically */
scale_factor = width_ratio;
scaled_height = file_height / scale_factor;
x_offset = 0.;
y_offset = - (node->alloc_height / 2. - scaled_height / 2.);
}
else
{
double scaled_width;
/* center horizontally */
scale_factor = height_ratio;
scaled_width = file_width / scale_factor;
y_offset = 0.;
x_offset = - (node->alloc_width / 2. - scaled_width / 2.);
}
cairo_matrix_init_translate (&matrix, x_offset, y_offset);
cairo_matrix_scale (&matrix, scale_factor, scale_factor);
cairo_pattern_set_matrix (pattern, &matrix);
/* If it's opaque, and when scaled, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA && width_ratio == height_ratio)
*needs_background_fill = FALSE;
}
else
{
double x_offset, y_offset;
if (node->background_position_set)
{
x_offset = -node->background_position_x;
y_offset = -node->background_position_y;
}
else
{
if (node->alloc_width > file_width)
x_offset = - (node->alloc_width / 2.0 - file_width / 2.0);
else
x_offset = - (file_width / 2.0 - node->alloc_width / 2.0);
if (node->alloc_height > file_height)
y_offset = - (node->alloc_height / 2.0 - file_height / 2.0);
else
y_offset = - (file_height / 2.0 - node->alloc_height / 2.0);
}
/* If it's opaque, and when translated, fills up the entire allocated
* area, then don't bother doing a background fill first
*/
if (content != CAIRO_CONTENT_COLOR_ALPHA
&& -x_offset <= 0
&& -x_offset + file_width >= node->alloc_width
&& -y_offset <= 0
&& -y_offset + file_height >= node->alloc_height)
*needs_background_fill = FALSE;
cairo_matrix_init_translate (&matrix, x_offset, y_offset);
cairo_pattern_set_matrix (pattern, &matrix);
}
return pattern;
}
static void
paint_background_image_shadow_to_cairo_context (StThemeNode *node,
StShadow *shadow_spec,
cairo_pattern_t *pattern,
cairo_t *cr,
cairo_path_t *interior_path,
cairo_path_t *outline_path,
int x,
int y,
int width,
int height)
{
cairo_pattern_t *shadow_pattern;
g_assert (shadow_spec != NULL);
g_assert (pattern != NULL);
if (outline_path != NULL)
{
cairo_surface_t *clipped_surface;
cairo_pattern_t *clipped_pattern;
cairo_t *temp_cr;
/* Prerender the pattern to a temporary surface,
* so it's properly clipped before we create a shadow from it
*/
clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
temp_cr = cairo_create (clipped_surface);
cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (temp_cr);
cairo_set_operator (temp_cr, CAIRO_OPERATOR_SOURCE);
if (interior_path != NULL)
{
cairo_append_path (temp_cr, interior_path);
cairo_clip (temp_cr);
}
cairo_append_path (temp_cr, outline_path);
cairo_translate (temp_cr, x, y);
cairo_set_source (temp_cr, pattern);
cairo_clip (temp_cr);
cairo_paint (temp_cr);
cairo_destroy (temp_cr);
clipped_pattern = cairo_pattern_create_for_surface (clipped_surface);
cairo_surface_destroy (clipped_surface);
shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
clipped_pattern);
cairo_pattern_destroy (clipped_pattern);
}
else
{
shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
pattern);
}
/* Stamp the shadow pattern out in the appropriate color
* in a new layer
*/
cairo_push_group (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_rgba (cr,
shadow_spec->color.red / 255.0,
shadow_spec->color.green / 255.0,
shadow_spec->color.blue / 255.0,
shadow_spec->color.alpha / 255.0);
cairo_paint (cr);
cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
cairo_set_source (cr, shadow_pattern);
cairo_paint (cr);
cairo_pattern_destroy (shadow_pattern);
cairo_pop_group_to_source (cr);
/* mask and merge the shadow
*/
cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_save (cr);
if (interior_path != NULL)
{
/* If there are borders, clip the shadow to the interior
* of the borders
*/
cairo_append_path (cr, interior_path);
cairo_clip (cr);
}
else if (outline_path != NULL)
{
/* If there is a visible outline, clip the shadow to
* that outline
*/
cairo_append_path (cr, outline_path);
cairo_clip (cr);
}
cairo_paint (cr);
cairo_restore (cr);
}
/* In order for borders to be smoothly blended with non-solid backgrounds, /* In order for borders to be smoothly blended with non-solid backgrounds,
* we need to use cairo. This function is a slow fallback path for those * we need to use cairo. This function is a slow fallback path for those
* cases. It currently only supports gradients, however. * cases (gradients, background images, etc).
*/ */
static CoglHandle static CoglHandle
st_theme_node_render_background_with_border (StThemeNode *node) st_theme_node_render_background_with_border (StThemeNode *node)
@ -497,21 +721,54 @@ st_theme_node_render_background_with_border (StThemeNode *node)
int radius[4], i; int radius[4], i;
cairo_t *cr; cairo_t *cr;
cairo_surface_t *surface; cairo_surface_t *surface;
StShadow *shadow_spec;
cairo_pattern_t *pattern = NULL; cairo_pattern_t *pattern = NULL;
gboolean draw_solid_background; cairo_path_t *outline_path = NULL;
gboolean draw_solid_background = TRUE;
gboolean draw_background_image_shadow = FALSE;
gboolean has_visible_outline;
ClutterColor border_color; ClutterColor border_color;
int border_width[4]; int border_width[4];
guint rowstride; guint rowstride;
guchar *data; guchar *data;
ClutterActorBox actor_box;
ClutterActorBox paint_box;
cairo_path_t *interior_path = NULL;
border_image = st_theme_node_get_border_image (node); border_image = st_theme_node_get_border_image (node);
rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, node->alloc_width); shadow_spec = st_theme_node_get_background_image_shadow (node);
data = g_new0 (guchar, node->alloc_height * rowstride);
actor_box.x1 = 0;
actor_box.x2 = node->alloc_width;
actor_box.y1 = 0;
actor_box.y2 = node->alloc_height;
/* If there's a background image shadow, we
* may need to create an image bigger than the nodes
* allocation
*/
st_theme_node_get_paint_box (node, &actor_box, &paint_box);
/* translate the boxes so the paint box is at 0,0
*/
actor_box.x1 += - paint_box.x1;
actor_box.x2 += - paint_box.x1;
actor_box.y1 += - paint_box.y1;
actor_box.y2 += - paint_box.y1;
paint_box.x2 += - paint_box.x1;
paint_box.x1 += - paint_box.x1;
paint_box.y2 += - paint_box.y1;
paint_box.y1 += - paint_box.y1;
rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
paint_box.x2 - paint_box.x1);
data = g_new0 (guchar, (paint_box.y2 - paint_box.y1) * rowstride);
surface = cairo_image_surface_create_for_data (data, surface = cairo_image_surface_create_for_data (data,
CAIRO_FORMAT_ARGB32, CAIRO_FORMAT_ARGB32,
node->alloc_width, paint_box.x2 - paint_box.x1,
node->alloc_height, paint_box.y2 - paint_box.y1,
rowstride); rowstride);
cr = cairo_create (surface); cr = cairo_create (surface);
@ -525,6 +782,9 @@ st_theme_node_render_background_with_border (StThemeNode *node)
radius[i] = st_theme_node_get_border_radius (node, i); radius[i] = st_theme_node_get_border_radius (node, i);
} }
/* Note we don't support translucent background images on top
* of gradients. It's strictly either/or.
*/
if (node->background_gradient_type != ST_GRADIENT_NONE) if (node->background_gradient_type != ST_GRADIENT_NONE)
{ {
pattern = create_cairo_pattern_of_background_gradient (node); pattern = create_cairo_pattern_of_background_gradient (node);
@ -532,38 +792,54 @@ st_theme_node_render_background_with_border (StThemeNode *node)
} }
else else
{ {
g_warning ("st_theme_node_render_background_with_border called with non-gradient background (which isn't yet supported). Falling back to solid background color."); const char *background_image;
draw_solid_background = TRUE;
background_image = st_theme_node_get_background_image (node);
if (background_image != NULL)
{
pattern = create_cairo_pattern_of_background_image (node,
&draw_solid_background);
if (shadow_spec && pattern != NULL)
draw_background_image_shadow = TRUE;
}
} }
if (pattern == NULL)
draw_solid_background = TRUE;
has_visible_outline = st_theme_node_has_visible_outline (node);
/* Create a path for the background's outline first */ /* Create a path for the background's outline first */
if (radius[ST_CORNER_TOPLEFT] > 0) if (radius[ST_CORNER_TOPLEFT] > 0)
cairo_arc (cr, cairo_arc (cr,
radius[ST_CORNER_TOPLEFT], actor_box.x1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT], actor_box.y1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2); radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
else else
cairo_move_to (cr, 0, 0); cairo_move_to (cr, actor_box.x1, actor_box.y1);
cairo_line_to (cr, node->alloc_width - radius[ST_CORNER_TOPRIGHT], 0); cairo_line_to (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1);
if (radius[ST_CORNER_TOPRIGHT] > 0) if (radius[ST_CORNER_TOPRIGHT] > 0)
cairo_arc (cr, cairo_arc (cr,
node->alloc_width - radius[ST_CORNER_TOPRIGHT], actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT], actor_box.x1 + radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI); radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]); cairo_line_to (cr, actor_box.x2, actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT]);
if (radius[ST_CORNER_BOTTOMRIGHT] > 0) if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
cairo_arc (cr, cairo_arc (cr,
node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT], actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT], actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2); radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height); cairo_line_to (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2);
if (radius[ST_CORNER_BOTTOMLEFT] > 0) if (radius[ST_CORNER_BOTTOMLEFT] > 0)
cairo_arc (cr, cairo_arc (cr,
radius[ST_CORNER_BOTTOMLEFT], actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
node->alloc_height - radius[ST_CORNER_BOTTOMLEFT], actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI); radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
cairo_close_path (cr); cairo_close_path (cr);
outline_path = cairo_copy_path (cr);
/* If we have a solid border, we fill the outline shape with the border /* If we have a solid border, we fill the outline shape with the border
* color and create the inline shape for the background; * color and create the inline shape for the background;
* otherwise the outline shape is filled with the background * otherwise the outline shape is filled with the background
@ -585,68 +861,80 @@ st_theme_node_render_background_with_border (StThemeNode *node)
if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP], if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
border_width[ST_SIDE_LEFT])) border_width[ST_SIDE_LEFT]))
elliptical_arc (cr, elliptical_arc (cr,
radius[ST_CORNER_TOPLEFT], actor_box.x1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT], actor_box.y1 + radius[ST_CORNER_TOPLEFT],
radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT], radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP], radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
M_PI, 3 * M_PI / 2); M_PI, 3 * M_PI / 2);
else else
cairo_move_to (cr, cairo_move_to (cr,
border_width[ST_SIDE_LEFT], actor_box.x1 + border_width[ST_SIDE_LEFT],
border_width[ST_SIDE_TOP]); actor_box.y1 + border_width[ST_SIDE_TOP]);
cairo_line_to (cr, cairo_line_to (cr,
node->alloc_width - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]), actor_box.x2 - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
border_width[ST_SIDE_TOP]); actor_box.y1 + border_width[ST_SIDE_TOP]);
if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP], if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
border_width[ST_SIDE_RIGHT])) border_width[ST_SIDE_RIGHT]))
elliptical_arc (cr, elliptical_arc (cr,
node->alloc_width - radius[ST_CORNER_TOPRIGHT], actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT], actor_box.y1 + radius[ST_CORNER_TOPRIGHT],
radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT], radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP], radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
3 * M_PI / 2, 2 * M_PI); 3 * M_PI / 2, 2 * M_PI);
else else
cairo_line_to (cr, cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT], actor_box.x2 - border_width[ST_SIDE_RIGHT],
border_width[ST_SIDE_TOP]); actor_box.y1 + border_width[ST_SIDE_TOP]);
cairo_line_to (cr, cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT], actor_box.x2 - border_width[ST_SIDE_RIGHT],
node->alloc_height - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM])); actor_box.y2 - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM], if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
border_width[ST_SIDE_RIGHT])) border_width[ST_SIDE_RIGHT]))
elliptical_arc (cr, elliptical_arc (cr,
node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT], actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT], actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT], radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM], radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
0, M_PI / 2); 0, M_PI / 2);
else else
cairo_line_to (cr, cairo_line_to (cr,
node->alloc_width - border_width[ST_SIDE_RIGHT], actor_box.x2 - border_width[ST_SIDE_RIGHT],
node->alloc_height - border_width[ST_SIDE_BOTTOM]); actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
cairo_line_to (cr, cairo_line_to (cr,
MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]), MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
node->alloc_height - border_width[ST_SIDE_BOTTOM]); actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM], if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
border_width[ST_SIDE_LEFT])) border_width[ST_SIDE_LEFT]))
elliptical_arc (cr, elliptical_arc (cr,
radius[ST_CORNER_BOTTOMLEFT], actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
node->alloc_height - radius[ST_CORNER_BOTTOMLEFT], actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT], radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM], radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
M_PI / 2, M_PI); M_PI / 2, M_PI);
else else
cairo_line_to (cr, cairo_line_to (cr,
border_width[ST_SIDE_LEFT], actor_box.x1 + border_width[ST_SIDE_LEFT],
node->alloc_height - border_width[ST_SIDE_BOTTOM]); actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
cairo_close_path (cr); cairo_close_path (cr);
interior_path = cairo_copy_path (cr);
/* clip drawing to the region inside of the borders
*/
cairo_clip (cr);
/* But fill the pattern as if it started at the edge of outline,
* behind the borders. This is similar to
* background-clip: border-box; semantics.
*/
cairo_append_path (cr, outline_path);
} }
if (draw_solid_background) if (draw_solid_background)
@ -659,6 +947,23 @@ st_theme_node_render_background_with_border (StThemeNode *node)
cairo_fill_preserve (cr); cairo_fill_preserve (cr);
} }
if (draw_background_image_shadow)
{
paint_background_image_shadow_to_cairo_context (node,
shadow_spec,
pattern,
cr,
interior_path,
has_visible_outline? outline_path : NULL,
actor_box.x1,
actor_box.y1,
paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1);
cairo_append_path (cr, outline_path);
}
cairo_translate (cr, actor_box.x1, actor_box.y1);
if (pattern != NULL) if (pattern != NULL)
{ {
cairo_set_source (cr, pattern); cairo_set_source (cr, pattern);
@ -666,7 +971,14 @@ st_theme_node_render_background_with_border (StThemeNode *node)
cairo_pattern_destroy (pattern); cairo_pattern_destroy (pattern);
} }
texture = cogl_texture_new_from_data (node->alloc_width, node->alloc_height, if (outline_path != NULL)
cairo_path_destroy (outline_path);
if (interior_path != NULL)
cairo_path_destroy (interior_path);
texture = cogl_texture_new_from_data (paint_box.x2 - paint_box.x1,
paint_box.y2 - paint_box.y1,
COGL_TEXTURE_NONE, COGL_TEXTURE_NONE,
#if G_BYTE_ORDER == G_LITTLE_ENDIAN #if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE, COGL_PIXEL_FORMAT_BGRA_8888_PRE,
@ -745,6 +1057,8 @@ st_theme_node_render_resources (StThemeNode *node,
{ {
StTextureCache *texture_cache; StTextureCache *texture_cache;
StBorderImage *border_image; StBorderImage *border_image;
gboolean has_border;
gboolean has_border_radius;
StShadow *box_shadow_spec; StShadow *box_shadow_spec;
StShadow *background_image_shadow_spec; StShadow *background_image_shadow_spec;
const char *background_image; const char *background_image;
@ -765,9 +1079,26 @@ st_theme_node_render_resources (StThemeNode *node,
box_shadow_spec = st_theme_node_get_box_shadow (node); box_shadow_spec = st_theme_node_get_box_shadow (node);
/* Load referenced images from disk and draw anything we need with cairo now */ if (node->border_width[ST_SIDE_TOP] > 0 ||
node->border_width[ST_SIDE_LEFT] > 0 ||
node->border_width[ST_SIDE_RIGHT] > 0 ||
node->border_width[ST_SIDE_BOTTOM] > 0)
has_border = TRUE;
else
has_border = FALSE;
if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
has_border_radius = TRUE;
else
has_border_radius = FALSE;
/* Load referenced images from disk and draw anything we need with cairo now */
background_image = st_theme_node_get_background_image (node);
border_image = st_theme_node_get_border_image (node); border_image = st_theme_node_get_border_image (node);
if (border_image) if (border_image)
{ {
const char *filename; const char *filename;
@ -777,14 +1108,23 @@ st_theme_node_render_resources (StThemeNode *node,
node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename); node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
} }
if (node->background_gradient_type != ST_GRADIENT_NONE)
node->prerendered_texture = st_theme_node_render_background_with_border (node);
if (node->border_slices_texture) if (node->border_slices_texture)
node->border_slices_material = _st_create_texture_material (node->border_slices_texture); node->border_slices_material = _st_create_texture_material (node->border_slices_texture);
else else
node->border_slices_material = COGL_INVALID_HANDLE; node->border_slices_material = COGL_INVALID_HANDLE;
/* Use cairo to prerender the node if there is a gradient, or
* background image with borders and/or rounded corners,
* since we can't do those things easily with cogl.
*
* FIXME: if we could figure out ahead of time that a
* background image won't overlap with the node borders,
* then we could use cogl for that case.
*/
if ((node->background_gradient_type != ST_GRADIENT_NONE)
|| (background_image && (has_border || has_border_radius)))
node->prerendered_texture = st_theme_node_render_background_with_border (node);
if (node->prerendered_texture) if (node->prerendered_texture)
node->prerendered_material = _st_create_texture_material (node->prerendered_texture); node->prerendered_material = _st_create_texture_material (node->prerendered_texture);
else else
@ -798,11 +1138,7 @@ st_theme_node_render_resources (StThemeNode *node,
else if (node->prerendered_texture != COGL_INVALID_HANDLE) else if (node->prerendered_texture != COGL_INVALID_HANDLE)
node->box_shadow_material = _st_create_shadow_material (box_shadow_spec, node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
node->prerendered_texture); node->prerendered_texture);
else if (node->background_color.alpha > 0 || else if (node->background_color.alpha > 0 || has_border)
node->border_width[ST_SIDE_TOP] > 0 ||
node->border_width[ST_SIDE_LEFT] > 0 ||
node->border_width[ST_SIDE_RIGHT] > 0 ||
node->border_width[ST_SIDE_BOTTOM] > 0)
{ {
CoglHandle buffer, offscreen; CoglHandle buffer, offscreen;
@ -829,10 +1165,8 @@ st_theme_node_render_resources (StThemeNode *node,
} }
} }
background_image = st_theme_node_get_background_image (node);
background_image_shadow_spec = st_theme_node_get_background_image_shadow (node); background_image_shadow_spec = st_theme_node_get_background_image_shadow (node);
if (background_image != NULL && !has_border && !has_border_radius)
if (background_image != NULL)
{ {
CoglHandle texture; CoglHandle texture;
@ -1339,7 +1673,9 @@ st_theme_node_paint (StThemeNode *node,
* not supported; the background color will be drawn with square * not supported; the background color will be drawn with square
* corners. * corners.
* - The background image is drawn above the border color, not below it. * - The background image is drawn above the border color, not below it.
* - We don't clip the background image to the (rounded) border area. * - We clip the background image to the inside edges of the border
* instead of the outside edges of the border (but position the image
* such that it's aligned to the outside edges)
*/ */
if (node->box_shadow_material) if (node->box_shadow_material)
@ -1352,7 +1688,15 @@ st_theme_node_paint (StThemeNode *node,
node->border_slices_material != COGL_INVALID_HANDLE) node->border_slices_material != COGL_INVALID_HANDLE)
{ {
if (node->prerendered_material != COGL_INVALID_HANDLE) if (node->prerendered_material != COGL_INVALID_HANDLE)
paint_material_with_opacity (node->prerendered_material, &allocation, paint_opacity); {
ClutterActorBox paint_box;
st_theme_node_get_paint_box (node, &allocation, &paint_box);
paint_material_with_opacity (node->prerendered_material,
&paint_box,
paint_opacity);
}
if (node->border_slices_material != COGL_INVALID_HANDLE) if (node->border_slices_material != COGL_INVALID_HANDLE)
st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity); st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity);
@ -1367,8 +1711,6 @@ st_theme_node_paint (StThemeNode *node,
if (node->background_texture != COGL_INVALID_HANDLE) if (node->background_texture != COGL_INVALID_HANDLE)
{ {
ClutterActorBox background_box; ClutterActorBox background_box;
get_background_position (node, &allocation, &background_box);
gboolean has_visible_outline; gboolean has_visible_outline;
/* If the background doesn't have a border or opaque background, /* If the background doesn't have a border or opaque background,
@ -1377,6 +1719,8 @@ st_theme_node_paint (StThemeNode *node,
*/ */
has_visible_outline = st_theme_node_has_visible_outline (node); has_visible_outline = st_theme_node_has_visible_outline (node);
get_background_position (node, &allocation, &background_box);
if (has_visible_outline) if (has_visible_outline)
cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2); cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2);