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:
parent
03757c1fcb
commit
779d5462f2
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user