2010-02-08 18:40:25 +00:00
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2010-11-10 22:00:45 +00:00
/*
* st - theme - node - drawing . c : Code to draw themed elements
*
* Copyright 2009 , 2010 Red Hat , Inc .
* Copyright 2009 , 2010 Florian Müllner
* Copyright 2010 Intel Corporation .
*
* Contains code derived from :
* rectangle . c : Rounded rectangle .
* Copyright 2008 litl , LLC .
* st - texture - frame . h : Expandible texture actor
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU Lesser General Public License ,
* version 2.1 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT ANY
* WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE . See the GNU Lesser General Public License for
* more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2010-02-08 18:40:25 +00:00
# include <stdlib.h>
# include <math.h>
2010-06-04 13:21:59 +00:00
# include "st-shadow.h"
2010-07-24 15:41:25 +00:00
# include "st-private.h"
2010-02-08 18:40:25 +00:00
# include "st-theme-private.h"
# include "st-theme-context.h"
# include "st-texture-cache.h"
# include "st-theme-node-private.h"
/****
* Rounded corners
* * * */
typedef struct {
ClutterColor color ;
ClutterColor border_color_1 ;
ClutterColor border_color_2 ;
guint radius ;
guint border_width_1 ;
guint border_width_2 ;
} StCornerSpec ;
2010-11-23 14:10:57 +00:00
static void
elliptical_arc ( cairo_t * cr ,
double x_center ,
double y_center ,
double x_radius ,
double y_radius ,
double angle1 ,
double angle2 )
{
cairo_save ( cr ) ;
cairo_translate ( cr , x_center , y_center ) ;
cairo_scale ( cr , x_radius , y_radius ) ;
cairo_arc ( cr , 0 , 0 , 1.0 , angle1 , angle2 ) ;
cairo_restore ( cr ) ;
}
2010-02-08 18:40:25 +00:00
static CoglHandle
2010-10-27 16:41:20 +00:00
create_corner_material ( StCornerSpec * corner )
2010-02-08 18:40:25 +00:00
{
CoglHandle texture ;
cairo_t * cr ;
cairo_surface_t * surface ;
guint rowstride ;
guint8 * data ;
guint size ;
guint max_border_width ;
max_border_width = MAX ( corner - > border_width_2 , corner - > border_width_1 ) ;
size = 2 * MAX ( max_border_width , corner - > radius ) ;
rowstride = size * 4 ;
data = g_new0 ( guint8 , size * rowstride ) ;
surface = cairo_image_surface_create_for_data ( data ,
CAIRO_FORMAT_ARGB32 ,
size , size ,
rowstride ) ;
cr = cairo_create ( surface ) ;
cairo_set_operator ( cr , CAIRO_OPERATOR_SOURCE ) ;
cairo_scale ( cr , size , size ) ;
2010-11-23 14:10:57 +00:00
if ( max_border_width < = corner - > radius )
2010-02-08 18:40:25 +00:00
{
2010-11-23 14:10:57 +00:00
double x_radius , y_radius ;
2010-02-08 18:40:25 +00:00
2010-11-23 14:10:57 +00:00
if ( max_border_width ! = 0 )
2010-02-08 18:40:25 +00:00
{
cairo_set_source_rgba ( cr ,
corner - > border_color_1 . red / 255. ,
corner - > border_color_1 . green / 255. ,
corner - > border_color_1 . blue / 255. ,
corner - > border_color_1 . alpha / 255. ) ;
cairo_arc ( cr , 0.5 , 0.5 , 0.5 , 0 , 2 * M_PI ) ;
cairo_fill ( cr ) ;
}
cairo_set_source_rgba ( cr ,
corner - > color . red / 255. ,
corner - > color . green / 255. ,
corner - > color . blue / 255. ,
corner - > color . alpha / 255. ) ;
2010-11-23 14:10:57 +00:00
x_radius = 0.5 * ( 1.0 - ( double ) corner - > border_width_2 / corner - > radius ) ;
y_radius = 0.5 * ( 1.0 - ( double ) corner - > border_width_1 / corner - > radius ) ;
/* TOPRIGHT */
elliptical_arc ( cr ,
0.5 , 0.5 ,
x_radius , y_radius ,
3 * M_PI / 2 , 2 * M_PI ) ;
/* BOTTOMRIGHT */
elliptical_arc ( cr ,
0.5 , 0.5 ,
x_radius , y_radius ,
0 , M_PI / 2 ) ;
/* TOPLEFT */
elliptical_arc ( cr ,
0.5 , 0.5 ,
x_radius , y_radius ,
M_PI , 3 * M_PI / 2 ) ;
/* BOTTOMLEFT */
elliptical_arc ( cr ,
0.5 , 0.5 ,
x_radius , y_radius ,
M_PI / 2 , M_PI ) ;
2010-02-08 18:40:25 +00:00
cairo_fill ( cr ) ;
}
else
{
double radius ;
2010-11-23 14:10:57 +00:00
radius = ( gdouble ) corner - > radius / max_border_width ;
2010-02-08 18:40:25 +00:00
cairo_set_source_rgba ( cr ,
corner - > border_color_1 . red / 255. ,
corner - > border_color_1 . green / 255. ,
corner - > border_color_1 . blue / 255. ,
corner - > border_color_1 . alpha / 255. ) ;
cairo_arc ( cr , radius , radius , radius , M_PI , 3 * M_PI / 2 ) ;
cairo_line_to ( cr , 1.0 - radius , 0.0 ) ;
cairo_arc ( cr , 1.0 - radius , radius , radius , 3 * M_PI / 2 , 2 * M_PI ) ;
cairo_line_to ( cr , 1.0 , 1.0 - radius ) ;
cairo_arc ( cr , 1.0 - radius , 1.0 - radius , radius , 0 , M_PI / 2 ) ;
cairo_line_to ( cr , radius , 1.0 ) ;
cairo_arc ( cr , radius , 1.0 - radius , radius , M_PI / 2 , M_PI ) ;
cairo_fill ( cr ) ;
}
cairo_destroy ( cr ) ;
cairo_surface_destroy ( surface ) ;
texture = cogl_texture_new_from_data ( size , size ,
COGL_TEXTURE_NONE ,
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
COGL_PIXEL_FORMAT_BGRA_8888_PRE ,
# else
COGL_PIXEL_FORMAT_ARGB_8888_PRE ,
# endif
COGL_PIXEL_FORMAT_ANY ,
rowstride ,
data ) ;
g_free ( data ) ;
g_assert ( texture ! = COGL_INVALID_HANDLE ) ;
return texture ;
}
static char *
corner_to_string ( StCornerSpec * corner )
{
return g_strdup_printf ( " st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u " ,
corner - > color . red , corner - > color . blue , corner - > color . green , corner - > color . alpha ,
corner - > border_color_1 . red , corner - > border_color_1 . green , corner - > border_color_1 . blue , corner - > border_color_1 . alpha ,
corner - > border_color_2 . red , corner - > border_color_2 . green , corner - > border_color_2 . blue , corner - > border_color_2 . alpha ,
corner - > radius ,
corner - > border_width_1 ,
corner - > border_width_2 ) ;
}
typedef struct {
StThemeNode * node ;
StCornerSpec * corner ;
} LoadCornerData ;
static CoglHandle
load_corner ( StTextureCache * cache ,
const char * key ,
void * datap ,
GError * * error )
{
LoadCornerData * data = datap ;
2010-10-27 16:41:20 +00:00
return create_corner_material ( data - > corner ) ;
2010-02-08 18:40:25 +00:00
}
/* To match the CSS specification, we want the border to look like it was
* drawn over the background . But actually drawing the border over the
* background will produce slightly bad antialiasing at the edges , so
* compute the effective border color instead .
*/
# define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
# define MULT(c,a) NORM(c*a)
static void
premultiply ( ClutterColor * color )
{
guint t ;
color - > red = MULT ( color - > red , color - > alpha ) ;
color - > green = MULT ( color - > green , color - > alpha ) ;
color - > blue = MULT ( color - > blue , color - > alpha ) ;
}
static void
unpremultiply ( ClutterColor * color )
{
if ( color - > alpha ! = 0 )
{
color - > red = ( color - > red * 255 + 127 ) / color - > alpha ;
color - > green = ( color - > green * 255 + 127 ) / color - > alpha ;
color - > blue = ( color - > blue * 255 + 127 ) / color - > alpha ;
}
}
static void
over ( const ClutterColor * source ,
const ClutterColor * destination ,
ClutterColor * result )
{
guint t ;
ClutterColor src = * source ;
ClutterColor dst = * destination ;
premultiply ( & src ) ;
premultiply ( & dst ) ;
result - > alpha = src . alpha + NORM ( ( 255 - src . alpha ) * dst . alpha ) ;
result - > red = src . red + NORM ( ( 255 - src . alpha ) * dst . red ) ;
result - > green = src . green + NORM ( ( 255 - src . alpha ) * dst . green ) ;
result - > blue = src . blue + NORM ( ( 255 - src . alpha ) * dst . blue ) ;
unpremultiply ( result ) ;
}
2010-11-23 14:10:57 +00:00
static void
st_theme_node_get_corner_border_widths ( StThemeNode * node ,
StCorner corner_id ,
guint * border_width_1 ,
guint * border_width_2 )
{
switch ( corner_id )
{
case ST_CORNER_TOPLEFT :
if ( border_width_1 )
* border_width_1 = node - > border_width [ ST_SIDE_TOP ] ;
if ( border_width_2 )
* border_width_2 = node - > border_width [ ST_SIDE_LEFT ] ;
break ;
case ST_CORNER_TOPRIGHT :
if ( border_width_1 )
* border_width_1 = node - > border_width [ ST_SIDE_TOP ] ;
if ( border_width_2 )
* border_width_2 = node - > border_width [ ST_SIDE_RIGHT ] ;
break ;
case ST_CORNER_BOTTOMRIGHT :
if ( border_width_1 )
* border_width_1 = node - > border_width [ ST_SIDE_BOTTOM ] ;
if ( border_width_2 )
* border_width_2 = node - > border_width [ ST_SIDE_RIGHT ] ;
break ;
case ST_CORNER_BOTTOMLEFT :
if ( border_width_1 )
* border_width_1 = node - > border_width [ ST_SIDE_BOTTOM ] ;
if ( border_width_2 )
* border_width_2 = node - > border_width [ ST_SIDE_LEFT ] ;
break ;
}
}
2010-02-08 18:40:25 +00:00
static CoglHandle
st_theme_node_lookup_corner ( StThemeNode * node ,
StCorner corner_id )
{
2010-10-27 16:41:20 +00:00
CoglHandle texture , material ;
2010-02-08 18:40:25 +00:00
char * key ;
StTextureCache * cache ;
StCornerSpec corner ;
LoadCornerData data ;
if ( node - > border_radius [ corner_id ] = = 0 )
return COGL_INVALID_HANDLE ;
cache = st_texture_cache_get_default ( ) ;
corner . radius = node - > border_radius [ corner_id ] ;
corner . color = node - > background_color ;
2010-11-23 14:10:57 +00:00
st_theme_node_get_corner_border_widths ( node , corner_id ,
& corner . border_width_1 ,
& corner . border_width_2 ) ;
2010-02-08 18:40:25 +00:00
switch ( corner_id )
{
case ST_CORNER_TOPLEFT :
over ( & node - > border_color [ ST_SIDE_TOP ] , & corner . color , & corner . border_color_1 ) ;
over ( & node - > border_color [ ST_SIDE_LEFT ] , & corner . color , & corner . border_color_2 ) ;
break ;
case ST_CORNER_TOPRIGHT :
over ( & node - > border_color [ ST_SIDE_TOP ] , & corner . color , & corner . border_color_1 ) ;
over ( & node - > border_color [ ST_SIDE_RIGHT ] , & corner . color , & corner . border_color_2 ) ;
break ;
case ST_CORNER_BOTTOMRIGHT :
over ( & node - > border_color [ ST_SIDE_BOTTOM ] , & corner . color , & corner . border_color_1 ) ;
over ( & node - > border_color [ ST_SIDE_RIGHT ] , & corner . color , & corner . border_color_2 ) ;
break ;
case ST_CORNER_BOTTOMLEFT :
over ( & node - > border_color [ ST_SIDE_BOTTOM ] , & corner . color , & corner . border_color_1 ) ;
over ( & node - > border_color [ ST_SIDE_LEFT ] , & corner . color , & corner . border_color_2 ) ;
break ;
}
2010-11-13 13:11:03 +00:00
if ( corner . color . alpha = = 0 & &
corner . border_color_1 . alpha = = 0 & &
corner . border_color_2 . alpha = = 0 )
return COGL_INVALID_HANDLE ;
2010-02-08 18:40:25 +00:00
key = corner_to_string ( & corner ) ;
data . node = node ;
data . corner = & corner ;
texture = st_texture_cache_load ( cache , key , ST_TEXTURE_CACHE_POLICY_NONE , load_corner , & data , NULL ) ;
2010-10-27 16:41:20 +00:00
material = _st_create_texture_material ( texture ) ;
cogl_handle_unref ( texture ) ;
2010-02-08 18:40:25 +00:00
g_free ( key ) ;
2010-10-27 16:41:20 +00:00
return material ;
2010-02-08 18:40:25 +00:00
}
static void
get_background_position ( StThemeNode * self ,
const ClutterActorBox * allocation ,
ClutterActorBox * result )
{
gfloat w , h ;
result - > x1 = result - > y1 = 0 ;
result - > x2 = allocation - > x2 - allocation - > x1 ;
result - > y2 = allocation - > y2 - allocation - > y1 ;
w = cogl_texture_get_width ( self - > background_texture ) ;
h = cogl_texture_get_height ( self - > background_texture ) ;
2010-09-01 16:13:43 +00:00
/* scale the background into the allocated bounds, when not being absolutely positioned */
if ( ( w > result - > x2 | | h > result - > y2 ) & & ! self - > background_position_set )
2010-02-08 18:40:25 +00:00
{
gint new_h , new_w , offset ;
gint box_w , box_h ;
box_w = ( int ) result - > x2 ;
box_h = ( int ) result - > y2 ;
/* scale to fit */
new_h = ( int ) ( ( h / w ) * ( ( gfloat ) box_w ) ) ;
new_w = ( int ) ( ( w / h ) * ( ( gfloat ) box_h ) ) ;
if ( new_h > box_h )
{
/* center for new width */
offset = ( ( box_w ) - new_w ) * 0.5 ;
result - > x1 = offset ;
result - > x2 = offset + new_w ;
result - > y2 = box_h ;
}
else
{
/* center for new height */
offset = ( ( box_h ) - new_h ) * 0.5 ;
result - > y1 = offset ;
result - > y2 = offset + new_h ;
result - > x2 = box_w ;
}
}
else
{
2010-09-01 16:13:43 +00:00
/* honor the specified position if any */
if ( self - > background_position_set )
{
result - > x1 = self - > background_position_x ;
result - > y1 = self - > background_position_y ;
}
else
{
/* center the background on the widget */
result - > x1 = ( int ) ( ( ( allocation - > x2 - allocation - > x1 ) / 2 ) - ( w / 2 ) ) ;
result - > y1 = ( int ) ( ( ( allocation - > y2 - allocation - > y1 ) / 2 ) - ( h / 2 ) ) ;
}
2010-02-08 18:40:25 +00:00
result - > x2 = result - > x1 + w ;
result - > y2 = result - > y1 + h ;
}
}
/* Use of this function marks code which doesn't support
2010-11-23 14:10:57 +00:00
* non - uniform colors .
2010-02-08 18:40:25 +00:00
*/
2010-11-23 14:10:57 +00:00
static void
get_arbitrary_border_color ( StThemeNode * node ,
ClutterColor * color )
2010-02-08 18:40:25 +00:00
{
2010-11-23 14:10:57 +00:00
if ( color )
st_theme_node_get_border_color ( node , ST_SIDE_TOP , color ) ;
2010-02-08 18:40:25 +00:00
}
2011-01-22 02:52:18 +00:00
static gboolean
st_theme_node_has_visible_outline ( StThemeNode * node )
{
if ( node - > background_color . alpha > 0 )
return TRUE ;
if ( node - > background_gradient_end . alpha > 0 )
return TRUE ;
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 )
return TRUE ;
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 )
return TRUE ;
return FALSE ;
}
2010-12-12 20:41:41 +00:00
static cairo_pattern_t *
create_cairo_pattern_of_background_gradient ( StThemeNode * node )
{
cairo_pattern_t * pattern ;
g_return_val_if_fail ( node - > background_gradient_type ! = ST_GRADIENT_NONE ,
NULL ) ;
if ( node - > background_gradient_type = = ST_GRADIENT_VERTICAL )
pattern = cairo_pattern_create_linear ( 0 , 0 , 0 , node - > alloc_height ) ;
else if ( node - > background_gradient_type = = ST_GRADIENT_HORIZONTAL )
pattern = cairo_pattern_create_linear ( 0 , 0 , node - > alloc_width , 0 ) ;
else
{
gdouble cx , cy ;
cx = node - > alloc_width / 2. ;
cy = node - > alloc_height / 2. ;
pattern = cairo_pattern_create_radial ( cx , cy , 0 , cx , cy , MIN ( cx , cy ) ) ;
}
cairo_pattern_add_color_stop_rgba ( pattern , 0 ,
node - > background_color . red / 255. ,
node - > background_color . green / 255. ,
node - > background_color . blue / 255. ,
node - > background_color . alpha / 255. ) ;
cairo_pattern_add_color_stop_rgba ( pattern , 1 ,
node - > background_gradient_end . red / 255. ,
node - > background_gradient_end . green / 255. ,
node - > background_gradient_end . blue / 255. ,
node - > background_gradient_end . alpha / 255. ) ;
return pattern ;
}
/* 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
* cases . It currently only supports gradients , however .
*/
2010-02-08 18:40:25 +00:00
static CoglHandle
2010-12-12 20:41:41 +00:00
st_theme_node_render_background_with_border ( StThemeNode * node )
2010-02-08 18:40:25 +00:00
{
2011-01-14 19:03:40 +00:00
StBorderImage * border_image ;
2010-02-08 18:40:25 +00:00
CoglHandle texture ;
int radius [ 4 ] , i ;
cairo_t * cr ;
cairo_surface_t * surface ;
2010-12-12 20:41:41 +00:00
cairo_pattern_t * pattern = NULL ;
gboolean draw_solid_background ;
2010-02-08 18:40:25 +00:00
ClutterColor border_color ;
2010-11-23 14:10:57 +00:00
int border_width [ 4 ] ;
2010-02-08 18:40:25 +00:00
guint rowstride ;
guchar * data ;
2011-01-14 19:03:40 +00:00
border_image = st_theme_node_get_border_image ( node ) ;
2010-02-08 18:40:25 +00:00
rowstride = cairo_format_stride_for_width ( CAIRO_FORMAT_ARGB32 , node - > alloc_width ) ;
data = g_new0 ( guchar , node - > alloc_height * rowstride ) ;
surface = cairo_image_surface_create_for_data ( data ,
CAIRO_FORMAT_ARGB32 ,
node - > alloc_width ,
node - > alloc_height ,
rowstride ) ;
cr = cairo_create ( surface ) ;
2010-11-23 14:10:57 +00:00
/* TODO - support non-uniform border colors */
get_arbitrary_border_color ( node , & border_color ) ;
2010-02-08 18:40:25 +00:00
for ( i = 0 ; i < 4 ; i + + )
2010-11-23 14:10:57 +00:00
{
border_width [ i ] = st_theme_node_get_border_width ( node , i ) ;
radius [ i ] = st_theme_node_get_border_radius ( node , i ) ;
}
2010-02-08 18:40:25 +00:00
2010-12-12 20:41:41 +00:00
if ( node - > background_gradient_type ! = ST_GRADIENT_NONE )
{
pattern = create_cairo_pattern_of_background_gradient ( node ) ;
draw_solid_background = FALSE ;
}
2010-02-08 18:40:25 +00:00
else
{
2010-12-12 20:41:41 +00:00
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. " ) ;
draw_solid_background = TRUE ;
2010-02-08 18:40:25 +00:00
}
2010-12-09 09:35:40 +00:00
/* Create a path for the background's outline first */
if ( radius [ ST_CORNER_TOPLEFT ] > 0 )
cairo_arc ( cr ,
radius [ ST_CORNER_TOPLEFT ] ,
radius [ ST_CORNER_TOPLEFT ] ,
radius [ ST_CORNER_TOPLEFT ] , M_PI , 3 * M_PI / 2 ) ;
else
cairo_move_to ( cr , 0 , 0 ) ;
cairo_line_to ( cr , node - > alloc_width - radius [ ST_CORNER_TOPRIGHT ] , 0 ) ;
if ( radius [ ST_CORNER_TOPRIGHT ] > 0 )
cairo_arc ( cr ,
node - > alloc_width - radius [ ST_CORNER_TOPRIGHT ] ,
radius [ ST_CORNER_TOPRIGHT ] ,
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 ] ) ;
2010-11-23 14:10:57 +00:00
if ( radius [ ST_CORNER_BOTTOMRIGHT ] > 0 )
2010-12-09 09:35:40 +00:00
cairo_arc ( cr ,
node - > alloc_width - radius [ ST_CORNER_BOTTOMRIGHT ] ,
node - > alloc_height - radius [ ST_CORNER_BOTTOMRIGHT ] ,
radius [ ST_CORNER_BOTTOMRIGHT ] , 0 , M_PI / 2 ) ;
cairo_line_to ( cr , radius [ ST_CORNER_BOTTOMLEFT ] , node - > alloc_height ) ;
2010-11-23 14:10:57 +00:00
if ( radius [ ST_CORNER_BOTTOMLEFT ] > 0 )
2010-12-09 09:35:40 +00:00
cairo_arc ( cr ,
radius [ ST_CORNER_BOTTOMLEFT ] ,
node - > alloc_height - radius [ ST_CORNER_BOTTOMLEFT ] ,
radius [ ST_CORNER_BOTTOMLEFT ] , M_PI / 2 , M_PI ) ;
cairo_close_path ( cr ) ;
2010-12-12 20:41:41 +00:00
/* If we have a solid border, we fill the outline shape with the border
* color and create the inline shape for the background ;
2010-12-09 09:35:40 +00:00
* otherwise the outline shape is filled with the background
2010-12-12 20:41:41 +00:00
* directly
2010-12-09 09:35:40 +00:00
*/
2011-01-14 19:03:40 +00:00
if ( border_image = = NULL & &
( border_width [ ST_SIDE_TOP ] > 0 | |
border_width [ ST_SIDE_RIGHT ] > 0 | |
border_width [ ST_SIDE_BOTTOM ] > 0 | |
border_width [ ST_SIDE_LEFT ] > 0 ) )
2010-02-08 18:40:25 +00:00
{
2010-12-09 09:35:40 +00:00
cairo_set_source_rgba ( cr ,
border_color . red / 255. ,
border_color . green / 255. ,
border_color . blue / 255. ,
border_color . alpha / 255. ) ;
cairo_fill ( cr ) ;
2010-11-23 14:10:57 +00:00
if ( radius [ ST_CORNER_TOPLEFT ] > MAX ( border_width [ ST_SIDE_TOP ] ,
border_width [ ST_SIDE_LEFT ] ) )
elliptical_arc ( cr ,
radius [ ST_CORNER_TOPLEFT ] ,
radius [ ST_CORNER_TOPLEFT ] ,
radius [ ST_CORNER_TOPLEFT ] - border_width [ ST_SIDE_LEFT ] ,
radius [ ST_CORNER_TOPLEFT ] - border_width [ ST_SIDE_TOP ] ,
M_PI , 3 * M_PI / 2 ) ;
2010-02-08 18:40:25 +00:00
else
2010-11-23 14:10:57 +00:00
cairo_move_to ( cr ,
border_width [ ST_SIDE_LEFT ] ,
border_width [ ST_SIDE_TOP ] ) ;
2010-12-09 09:35:40 +00:00
cairo_line_to ( cr ,
2010-11-23 14:10:57 +00:00
node - > alloc_width - MAX ( radius [ ST_CORNER_TOPRIGHT ] , border_width [ ST_SIDE_RIGHT ] ) ,
border_width [ ST_SIDE_TOP ] ) ;
if ( radius [ ST_CORNER_TOPRIGHT ] > MAX ( border_width [ ST_SIDE_TOP ] ,
border_width [ ST_SIDE_RIGHT ] ) )
elliptical_arc ( cr ,
node - > alloc_width - radius [ ST_CORNER_TOPRIGHT ] ,
radius [ ST_CORNER_TOPRIGHT ] ,
radius [ ST_CORNER_TOPRIGHT ] - border_width [ ST_SIDE_RIGHT ] ,
radius [ ST_CORNER_TOPRIGHT ] - border_width [ ST_SIDE_TOP ] ,
3 * M_PI / 2 , 2 * M_PI ) ;
else
cairo_line_to ( cr ,
node - > alloc_width - border_width [ ST_SIDE_RIGHT ] ,
border_width [ ST_SIDE_TOP ] ) ;
2010-12-09 09:35:40 +00:00
cairo_line_to ( cr ,
2010-11-23 14:10:57 +00:00
node - > alloc_width - border_width [ ST_SIDE_RIGHT ] ,
node - > alloc_height - MAX ( radius [ ST_CORNER_BOTTOMRIGHT ] , border_width [ ST_SIDE_BOTTOM ] ) ) ;
if ( radius [ ST_CORNER_BOTTOMRIGHT ] > MAX ( border_width [ ST_SIDE_BOTTOM ] ,
border_width [ ST_SIDE_RIGHT ] ) )
elliptical_arc ( cr ,
node - > alloc_width - radius [ ST_CORNER_BOTTOMRIGHT ] ,
node - > alloc_height - radius [ ST_CORNER_BOTTOMRIGHT ] ,
radius [ ST_CORNER_BOTTOMRIGHT ] - border_width [ ST_SIDE_RIGHT ] ,
radius [ ST_CORNER_BOTTOMRIGHT ] - border_width [ ST_SIDE_BOTTOM ] ,
0 , M_PI / 2 ) ;
else
cairo_line_to ( cr ,
node - > alloc_width - border_width [ ST_SIDE_RIGHT ] ,
node - > alloc_height - border_width [ ST_SIDE_BOTTOM ] ) ;
2010-12-09 09:35:40 +00:00
cairo_line_to ( cr ,
2010-11-23 14:10:57 +00:00
MAX ( radius [ ST_CORNER_BOTTOMLEFT ] , border_width [ ST_SIDE_LEFT ] ) ,
node - > alloc_height - border_width [ ST_SIDE_BOTTOM ] ) ;
if ( radius [ ST_CORNER_BOTTOMLEFT ] > MAX ( border_width [ ST_SIDE_BOTTOM ] ,
border_width [ ST_SIDE_LEFT ] ) )
elliptical_arc ( cr ,
radius [ ST_CORNER_BOTTOMLEFT ] ,
node - > alloc_height - radius [ ST_CORNER_BOTTOMLEFT ] ,
radius [ ST_CORNER_BOTTOMLEFT ] - border_width [ ST_SIDE_LEFT ] ,
radius [ ST_CORNER_BOTTOMLEFT ] - border_width [ ST_SIDE_BOTTOM ] ,
M_PI / 2 , M_PI ) ;
else
cairo_line_to ( cr ,
border_width [ ST_SIDE_LEFT ] ,
node - > alloc_height - border_width [ ST_SIDE_BOTTOM ] ) ;
2010-02-08 18:40:25 +00:00
cairo_close_path ( cr ) ;
}
2010-12-12 20:41:41 +00:00
if ( draw_solid_background )
{
cairo_set_source_rgba ( cr ,
node - > background_color . red / 255. ,
node - > background_color . green / 255. ,
node - > background_color . blue / 255. ,
node - > background_color . alpha / 255. ) ;
cairo_fill_preserve ( cr ) ;
}
2010-02-08 18:40:25 +00:00
2010-12-12 20:41:41 +00:00
if ( pattern ! = NULL )
{
cairo_set_source ( cr , pattern ) ;
cairo_fill ( cr ) ;
cairo_pattern_destroy ( pattern ) ;
}
2010-02-08 18:40:25 +00:00
texture = cogl_texture_new_from_data ( node - > alloc_width , node - > alloc_height ,
COGL_TEXTURE_NONE ,
# if G_BYTE_ORDER == G_LITTLE_ENDIAN
2010-03-24 22:25:56 +00:00
COGL_PIXEL_FORMAT_BGRA_8888_PRE ,
2010-02-08 18:40:25 +00:00
# elif G_BYTE_ORDER == G_BIG_ENDIAN
COGL_PIXEL_FORMAT_ARGB_8888_PRE ,
# else
COGL_PIXEL_FORMAT_ANY ,
# error unknown endianness type
# endif
COGL_PIXEL_FORMAT_ANY ,
rowstride ,
data ) ;
cairo_destroy ( cr ) ;
cairo_surface_destroy ( surface ) ;
g_free ( data ) ;
return texture ;
}
void
_st_theme_node_free_drawing_state ( StThemeNode * node )
{
2010-10-01 01:58:59 +00:00
int corner_id ;
2010-02-08 18:40:25 +00:00
if ( node - > background_texture ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > background_texture ) ;
2010-10-28 20:51:13 +00:00
if ( node - > background_material ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > background_material ) ;
2010-03-11 23:14:12 +00:00
if ( node - > background_shadow_material ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > background_shadow_material ) ;
2011-01-12 22:07:19 +00:00
if ( node - > border_slices_texture ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > border_slices_texture ) ;
if ( node - > border_slices_material ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > border_slices_material ) ;
if ( node - > prerendered_texture ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > prerendered_texture ) ;
if ( node - > prerendered_material ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > prerendered_material ) ;
2011-01-13 23:27:24 +00:00
if ( node - > box_shadow_material ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > box_shadow_material ) ;
2010-02-08 18:40:25 +00:00
2010-10-01 01:58:59 +00:00
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
2010-10-27 16:41:20 +00:00
if ( node - > corner_material [ corner_id ] ! = COGL_INVALID_HANDLE )
cogl_handle_unref ( node - > corner_material [ corner_id ] ) ;
2010-10-01 01:58:59 +00:00
2010-02-08 18:40:25 +00:00
_st_theme_node_init_drawing_state ( node ) ;
}
void
_st_theme_node_init_drawing_state ( StThemeNode * node )
{
2010-10-01 01:58:59 +00:00
int corner_id ;
2010-02-08 18:40:25 +00:00
node - > background_texture = COGL_INVALID_HANDLE ;
2010-10-28 20:51:13 +00:00
node - > background_material = COGL_INVALID_HANDLE ;
2010-03-11 23:14:12 +00:00
node - > background_shadow_material = COGL_INVALID_HANDLE ;
2011-01-13 23:27:24 +00:00
node - > box_shadow_material = COGL_INVALID_HANDLE ;
2011-01-12 22:07:19 +00:00
node - > border_slices_texture = COGL_INVALID_HANDLE ;
node - > border_slices_material = COGL_INVALID_HANDLE ;
node - > prerendered_texture = COGL_INVALID_HANDLE ;
node - > prerendered_material = COGL_INVALID_HANDLE ;
2010-10-01 01:58:59 +00:00
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
2010-10-27 16:41:20 +00:00
node - > corner_material [ corner_id ] = COGL_INVALID_HANDLE ;
2010-02-08 18:40:25 +00:00
}
2010-03-11 23:14:12 +00:00
static void st_theme_node_paint_borders ( StThemeNode * node ,
const ClutterActorBox * box ,
guint8 paint_opacity ) ;
2010-02-08 18:40:25 +00:00
static void
st_theme_node_render_resources ( StThemeNode * node ,
float width ,
float height )
{
StTextureCache * texture_cache ;
StBorderImage * border_image ;
2011-01-13 23:27:24 +00:00
StShadow * box_shadow_spec ;
StShadow * background_image_shadow_spec ;
2010-02-08 18:40:25 +00:00
const char * background_image ;
texture_cache = st_texture_cache_get_default ( ) ;
/* FIXME - need to separate this into things that need to be recomputed on
* geometry change versus things that can be cached regardless , such as
* a background image .
*/
_st_theme_node_free_drawing_state ( node ) ;
node - > alloc_width = width ;
node - > alloc_height = height ;
_st_theme_node_ensure_background ( node ) ;
_st_theme_node_ensure_geometry ( node ) ;
2011-01-13 23:27:24 +00:00
box_shadow_spec = st_theme_node_get_box_shadow ( node ) ;
2010-03-11 23:14:12 +00:00
2010-02-08 18:40:25 +00:00
/* Load referenced images from disk and draw anything we need with cairo now */
border_image = st_theme_node_get_border_image ( node ) ;
if ( border_image )
{
const char * filename ;
filename = st_border_image_get_filename ( border_image ) ;
2011-01-12 22:07:19 +00:00
node - > border_slices_texture = st_texture_cache_load_file_to_cogl_texture ( texture_cache , filename ) ;
2010-02-08 18:40:25 +00:00
}
2011-01-14 19:03:40 +00:00
if ( node - > background_gradient_type ! = ST_GRADIENT_NONE )
2010-12-12 20:41:41 +00:00
node - > prerendered_texture = st_theme_node_render_background_with_border ( node ) ;
2010-02-08 18:40:25 +00:00
2011-01-12 22:07:19 +00:00
if ( node - > border_slices_texture )
node - > border_slices_material = _st_create_texture_material ( node - > border_slices_texture ) ;
2010-10-27 16:41:20 +00:00
else
2011-01-12 22:07:19 +00:00
node - > border_slices_material = COGL_INVALID_HANDLE ;
if ( node - > prerendered_texture )
node - > prerendered_material = _st_create_texture_material ( node - > prerendered_texture ) ;
else
node - > prerendered_material = COGL_INVALID_HANDLE ;
2010-10-27 16:41:20 +00:00
2011-01-13 23:27:24 +00:00
if ( box_shadow_spec )
2010-03-11 23:14:12 +00:00
{
2011-01-12 22:07:19 +00:00
if ( node - > border_slices_texture ! = COGL_INVALID_HANDLE )
node - > box_shadow_material = _st_create_shadow_material ( box_shadow_spec ,
node - > border_slices_texture ) ;
else if ( node - > prerendered_texture ! = COGL_INVALID_HANDLE )
2011-01-13 23:27:24 +00:00
node - > box_shadow_material = _st_create_shadow_material ( box_shadow_spec ,
2011-01-12 22:07:19 +00:00
node - > prerendered_texture ) ;
2010-03-11 23:14:12 +00:00
else if ( node - > background_color . alpha > 0 | |
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 ;
buffer = cogl_texture_new_with_size ( width ,
height ,
COGL_TEXTURE_NO_SLICING ,
COGL_PIXEL_FORMAT_ANY ) ;
offscreen = cogl_offscreen_new_to_texture ( buffer ) ;
if ( offscreen ! = COGL_INVALID_HANDLE )
{
ClutterActorBox box = { 0 , 0 , width , height } ;
cogl_push_framebuffer ( offscreen ) ;
cogl_ortho ( 0 , width , height , 0 , 0 , 1.0 ) ;
st_theme_node_paint_borders ( node , & box , 0xFF ) ;
cogl_pop_framebuffer ( ) ;
cogl_handle_unref ( offscreen ) ;
2011-01-13 23:27:24 +00:00
node - > box_shadow_material = _st_create_shadow_material ( box_shadow_spec ,
buffer ) ;
2010-03-11 23:14:12 +00:00
}
cogl_handle_unref ( buffer ) ;
}
}
2010-02-08 18:40:25 +00:00
background_image = st_theme_node_get_background_image ( node ) ;
2011-01-13 23:27:24 +00:00
background_image_shadow_spec = st_theme_node_get_background_image_shadow ( node ) ;
2010-02-08 18:40:25 +00:00
if ( background_image ! = NULL )
{
2011-01-13 22:46:59 +00:00
CoglHandle texture ;
texture = st_texture_cache_load_file_to_cogl_texture ( texture_cache , background_image ) ;
/* If no background position is specified, then we will automatically scale
* the background to fit within the node allocation . But , if a background
* position is specified , we won ' t scale the background , and it could
* potentially leak out of bounds . To prevent that , we subtexture from the
* in bounds area when necessary .
*/
if ( node - > background_position_set & &
( cogl_texture_get_width ( texture ) > width | |
cogl_texture_get_height ( texture ) > height ) )
{
CoglHandle subtexture ;
subtexture = cogl_texture_new_from_sub_texture ( texture ,
0 , 0 ,
width - node - > background_position_x ,
height - node - > background_position_y ) ;
cogl_handle_unref ( texture ) ;
node - > background_texture = subtexture ;
}
else
{
node - > background_texture = texture ;
}
2010-02-08 18:40:25 +00:00
2010-10-28 20:51:13 +00:00
node - > background_material = _st_create_texture_material ( node - > background_texture ) ;
2010-02-08 18:40:25 +00:00
2011-01-13 23:27:24 +00:00
if ( background_image_shadow_spec )
2010-02-08 18:40:25 +00:00
{
2011-01-13 23:27:24 +00:00
node - > background_shadow_material = _st_create_shadow_material ( background_image_shadow_spec ,
2010-07-24 15:41:25 +00:00
node - > background_texture ) ;
2010-02-08 18:40:25 +00:00
}
}
2010-10-27 16:41:20 +00:00
node - > corner_material [ ST_CORNER_TOPLEFT ] =
2010-10-01 01:58:59 +00:00
st_theme_node_lookup_corner ( node , ST_CORNER_TOPLEFT ) ;
2010-10-27 16:41:20 +00:00
node - > corner_material [ ST_CORNER_TOPRIGHT ] =
2010-10-01 01:58:59 +00:00
st_theme_node_lookup_corner ( node , ST_CORNER_TOPRIGHT ) ;
2010-10-27 16:41:20 +00:00
node - > corner_material [ ST_CORNER_BOTTOMRIGHT ] =
2010-10-01 01:58:59 +00:00
st_theme_node_lookup_corner ( node , ST_CORNER_BOTTOMRIGHT ) ;
2010-10-27 16:41:20 +00:00
node - > corner_material [ ST_CORNER_BOTTOMLEFT ] =
2010-10-01 01:58:59 +00:00
st_theme_node_lookup_corner ( node , ST_CORNER_BOTTOMLEFT ) ;
2010-02-08 18:40:25 +00:00
}
static void
2010-10-27 16:41:20 +00:00
paint_material_with_opacity ( CoglHandle material ,
ClutterActorBox * box ,
guint8 paint_opacity )
2010-02-08 18:40:25 +00:00
{
cogl_material_set_color4ub ( material ,
paint_opacity , paint_opacity , paint_opacity , paint_opacity ) ;
cogl_set_source ( material ) ;
cogl_rectangle ( box - > x1 , box - > y1 , box - > x2 , box - > y2 ) ;
}
static void
st_theme_node_paint_borders ( StThemeNode * node ,
const ClutterActorBox * box ,
guint8 paint_opacity )
{
float width , height ;
2010-11-23 14:10:57 +00:00
int border_width [ 4 ] ;
2010-10-01 01:58:59 +00:00
int max_border_radius = 0 ;
int max_width_radius [ 4 ] ;
2010-11-23 14:10:57 +00:00
int corner_id , side_id ;
2010-02-08 18:40:25 +00:00
ClutterColor border_color ;
2010-11-13 13:11:03 +00:00
guint8 alpha ;
2010-02-08 18:40:25 +00:00
width = box - > x2 - box - > x1 ;
height = box - > y2 - box - > y1 ;
2010-11-23 14:10:57 +00:00
/* TODO - support non-uniform border colors */
get_arbitrary_border_color ( node , & border_color ) ;
for ( side_id = 0 ; side_id < 4 ; side_id + + )
border_width [ side_id ] = st_theme_node_get_border_width ( node , side_id ) ;
2010-10-01 01:58:59 +00:00
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
{
2010-11-23 14:10:57 +00:00
guint border_width_1 , border_width_2 ;
st_theme_node_get_corner_border_widths ( node , corner_id ,
& border_width_1 , & border_width_2 ) ;
2010-10-01 01:58:59 +00:00
if ( node - > border_radius [ corner_id ] > max_border_radius )
max_border_radius = node - > border_radius [ corner_id ] ;
2010-11-23 14:10:57 +00:00
max_width_radius [ corner_id ] = MAX ( MAX ( border_width_1 , border_width_2 ) ,
2010-10-01 01:58:59 +00:00
node - > border_radius [ corner_id ] ) ;
}
2010-02-08 18:40:25 +00:00
/* borders */
2010-11-23 14:10:57 +00:00
if ( border_width [ ST_SIDE_TOP ] > 0 | |
border_width [ ST_SIDE_RIGHT ] > 0 | |
border_width [ ST_SIDE_BOTTOM ] > 0 | |
border_width [ ST_SIDE_LEFT ] > 0 )
2010-02-08 18:40:25 +00:00
{
ClutterColor effective_border ;
2010-10-01 01:58:59 +00:00
gboolean skip_corner_1 , skip_corner_2 ;
float x1 , y1 , x2 , y2 ;
2010-02-08 18:40:25 +00:00
over ( & border_color , & node - > background_color , & effective_border ) ;
2010-11-13 13:11:03 +00:00
alpha = paint_opacity * effective_border . alpha / 255 ;
2010-02-08 18:40:25 +00:00
2010-11-13 13:11:03 +00:00
if ( alpha > 0 )
{
cogl_set_source_color4ub ( effective_border . red ,
effective_border . green ,
effective_border . blue ,
alpha ) ;
/* NORTH */
skip_corner_1 = node - > border_radius [ ST_CORNER_TOPLEFT ] > 0 ;
skip_corner_2 = node - > border_radius [ ST_CORNER_TOPRIGHT ] > 0 ;
x1 = skip_corner_1 ? max_width_radius [ ST_CORNER_TOPLEFT ] : 0 ;
y1 = 0 ;
x2 = skip_corner_2 ? width - max_width_radius [ ST_CORNER_TOPRIGHT ] : width ;
2010-11-23 14:10:57 +00:00
y2 = border_width [ ST_SIDE_TOP ] ;
2010-11-13 13:11:03 +00:00
cogl_rectangle ( x1 , y1 , x2 , y2 ) ;
/* EAST */
skip_corner_1 = node - > border_radius [ ST_CORNER_TOPRIGHT ] > 0 ;
skip_corner_2 = node - > border_radius [ ST_CORNER_BOTTOMRIGHT ] > 0 ;
2010-11-23 14:10:57 +00:00
x1 = width - border_width [ ST_SIDE_RIGHT ] ;
y1 = skip_corner_1 ? max_width_radius [ ST_CORNER_TOPRIGHT ]
: border_width [ ST_SIDE_TOP ] ;
2010-11-13 13:11:03 +00:00
x2 = width ;
y2 = skip_corner_2 ? height - max_width_radius [ ST_CORNER_BOTTOMRIGHT ]
2010-11-23 14:10:57 +00:00
: height - border_width [ ST_SIDE_BOTTOM ] ;
2010-11-13 13:11:03 +00:00
cogl_rectangle ( x1 , y1 , x2 , y2 ) ;
/* SOUTH */
skip_corner_1 = node - > border_radius [ ST_CORNER_BOTTOMLEFT ] > 0 ;
skip_corner_2 = node - > border_radius [ ST_CORNER_BOTTOMRIGHT ] > 0 ;
x1 = skip_corner_1 ? max_width_radius [ ST_CORNER_BOTTOMLEFT ] : 0 ;
2010-11-23 14:10:57 +00:00
y1 = height - border_width [ ST_SIDE_BOTTOM ] ;
2010-11-13 13:11:03 +00:00
x2 = skip_corner_2 ? width - max_width_radius [ ST_CORNER_BOTTOMRIGHT ]
: width ;
y2 = height ;
cogl_rectangle ( x1 , y1 , x2 , y2 ) ;
/* WEST */
skip_corner_1 = node - > border_radius [ ST_CORNER_TOPLEFT ] > 0 ;
skip_corner_2 = node - > border_radius [ ST_CORNER_BOTTOMLEFT ] > 0 ;
x1 = 0 ;
2010-11-23 14:10:57 +00:00
y1 = skip_corner_1 ? max_width_radius [ ST_CORNER_TOPLEFT ]
: border_width [ ST_SIDE_TOP ] ;
x2 = border_width [ ST_SIDE_LEFT ] ;
2010-11-13 13:11:03 +00:00
y2 = skip_corner_2 ? height - max_width_radius [ ST_CORNER_BOTTOMLEFT ]
2010-11-23 14:10:57 +00:00
: height - border_width [ ST_SIDE_BOTTOM ] ;
2010-11-13 13:11:03 +00:00
cogl_rectangle ( x1 , y1 , x2 , y2 ) ;
}
2010-02-08 18:40:25 +00:00
}
/* corners */
2010-11-13 13:11:03 +00:00
if ( max_border_radius > 0 & & paint_opacity > 0 )
2010-02-08 18:40:25 +00:00
{
2010-10-01 01:58:59 +00:00
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
{
2010-10-27 16:41:20 +00:00
if ( node - > corner_material [ corner_id ] = = COGL_INVALID_HANDLE )
2010-10-01 01:58:59 +00:00
continue ;
2010-10-27 16:41:20 +00:00
cogl_material_set_color4ub ( node - > corner_material [ corner_id ] ,
paint_opacity , paint_opacity ,
paint_opacity , paint_opacity ) ;
cogl_set_source ( node - > corner_material [ corner_id ] ) ;
2010-02-08 18:40:25 +00:00
2010-10-01 01:58:59 +00:00
switch ( corner_id )
{
case ST_CORNER_TOPLEFT :
cogl_rectangle_with_texture_coords ( 0 , 0 ,
max_width_radius [ ST_CORNER_TOPLEFT ] , max_width_radius [ ST_CORNER_TOPLEFT ] ,
0 , 0 , 0.5 , 0.5 ) ;
break ;
case ST_CORNER_TOPRIGHT :
cogl_rectangle_with_texture_coords ( width - max_width_radius [ ST_CORNER_TOPRIGHT ] , 0 ,
width , max_width_radius [ ST_CORNER_TOPRIGHT ] ,
0.5 , 0 , 1 , 0.5 ) ;
break ;
case ST_CORNER_BOTTOMRIGHT :
cogl_rectangle_with_texture_coords ( width - max_width_radius [ ST_CORNER_BOTTOMRIGHT ] , height - max_width_radius [ ST_CORNER_BOTTOMRIGHT ] ,
width , height ,
0.5 , 0.5 , 1 , 1 ) ;
break ;
case ST_CORNER_BOTTOMLEFT :
cogl_rectangle_with_texture_coords ( 0 , height - max_width_radius [ ST_CORNER_BOTTOMLEFT ] ,
max_width_radius [ ST_CORNER_BOTTOMLEFT ] , height ,
0 , 0.5 , 0.5 , 1 ) ;
break ;
}
}
2010-02-08 18:40:25 +00:00
}
/* background color */
2010-11-13 13:11:03 +00:00
alpha = paint_opacity * node - > background_color . alpha / 255 ;
if ( alpha > 0 )
2010-10-01 01:58:59 +00:00
{
2010-11-13 13:11:03 +00:00
cogl_set_source_color4ub ( node - > background_color . red ,
node - > background_color . green ,
node - > background_color . blue ,
alpha ) ;
/* We add padding to each corner, so that all corners end up as if they
* had a border - radius of max_border_radius , which allows us to treat
* corners as uniform further on .
*/
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
{
float verts [ 8 ] ;
int n_rects ;
2010-10-01 01:58:59 +00:00
2010-11-13 13:11:03 +00:00
/* corner texture does not need padding */
if ( max_border_radius = = node - > border_radius [ corner_id ] )
continue ;
2010-10-01 01:58:59 +00:00
2010-11-13 13:11:03 +00:00
n_rects = node - > border_radius [ corner_id ] = = 0 ? 1 : 2 ;
2010-10-01 01:58:59 +00:00
2010-11-13 13:11:03 +00:00
switch ( corner_id )
{
case ST_CORNER_TOPLEFT :
2010-11-23 14:10:57 +00:00
verts [ 0 ] = border_width [ ST_SIDE_LEFT ] ;
verts [ 1 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_TOP ] ) ;
2010-11-13 13:11:03 +00:00
verts [ 2 ] = max_border_radius ;
verts [ 3 ] = max_border_radius ;
if ( n_rects = = 2 )
{
2010-11-23 14:10:57 +00:00
verts [ 4 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_LEFT ] ) ;
verts [ 5 ] = border_width [ ST_SIDE_TOP ] ;
2010-11-13 13:11:03 +00:00
verts [ 6 ] = max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 7 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_TOP ] ) ;
2010-11-13 13:11:03 +00:00
}
break ;
case ST_CORNER_TOPRIGHT :
verts [ 0 ] = width - max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 1 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_TOP ] ) ;
verts [ 2 ] = width - border_width [ ST_SIDE_RIGHT ] ;
2010-11-13 13:11:03 +00:00
verts [ 3 ] = max_border_radius ;
if ( n_rects = = 2 )
{
verts [ 4 ] = width - max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 5 ] = border_width [ ST_SIDE_TOP ] ;
verts [ 6 ] = width - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_RIGHT ] ) ;
verts [ 7 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_TOP ] ) ;
2010-11-13 13:11:03 +00:00
}
break ;
case ST_CORNER_BOTTOMRIGHT :
verts [ 0 ] = width - max_border_radius ;
verts [ 1 ] = height - max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 2 ] = width - border_width [ ST_SIDE_RIGHT ] ;
verts [ 3 ] = height - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_BOTTOM ] ) ;
2010-11-13 13:11:03 +00:00
if ( n_rects = = 2 )
{
verts [ 4 ] = width - max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 5 ] = height - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_BOTTOM ] ) ;
verts [ 6 ] = width - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_RIGHT ] ) ;
verts [ 7 ] = height - border_width [ ST_SIDE_BOTTOM ] ;
2010-11-13 13:11:03 +00:00
}
break ;
case ST_CORNER_BOTTOMLEFT :
2010-11-23 14:10:57 +00:00
verts [ 0 ] = border_width [ ST_SIDE_LEFT ] ;
2010-11-13 13:11:03 +00:00
verts [ 1 ] = height - max_border_radius ;
verts [ 2 ] = max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 3 ] = height - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_BOTTOM ] ) ;
2010-11-13 13:11:03 +00:00
if ( n_rects = = 2 )
{
2010-11-23 14:10:57 +00:00
verts [ 4 ] = MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_LEFT ] ) ;
verts [ 5 ] = height - MAX ( node - > border_radius [ corner_id ] ,
border_width [ ST_SIDE_BOTTOM ] ) ;
2010-11-13 13:11:03 +00:00
verts [ 6 ] = max_border_radius ;
2010-11-23 14:10:57 +00:00
verts [ 7 ] = height - border_width [ ST_SIDE_BOTTOM ] ;
2010-11-13 13:11:03 +00:00
}
break ;
}
cogl_rectangles ( verts , n_rects ) ;
}
2010-11-23 14:10:57 +00:00
/* Once we've drawn the borders and corners, if the corners are bigger
* then the border width , the remaining area is shaped like
*
* # # # # # # # #
* # # # # # # # # # #
* # # # # # # # # # #
* # # # # # # # #
*
* We draw it in at most 3 pieces - first the top and bottom if
* necessary , then the main rectangle
*/
if ( max_border_radius > border_width [ ST_SIDE_TOP ] )
cogl_rectangle ( MAX ( max_border_radius , border_width [ ST_SIDE_LEFT ] ) ,
border_width [ ST_SIDE_TOP ] ,
width - MAX ( max_border_radius , border_width [ ST_SIDE_RIGHT ] ) ,
max_border_radius ) ;
if ( max_border_radius > border_width [ ST_SIDE_BOTTOM ] )
cogl_rectangle ( MAX ( max_border_radius , border_width [ ST_SIDE_LEFT ] ) ,
height - max_border_radius ,
width - MAX ( max_border_radius , border_width [ ST_SIDE_RIGHT ] ) ,
height - border_width [ ST_SIDE_BOTTOM ] ) ;
cogl_rectangle ( border_width [ ST_SIDE_LEFT ] ,
MAX ( border_width [ ST_SIDE_TOP ] , max_border_radius ) ,
width - border_width [ ST_SIDE_RIGHT ] ,
height - MAX ( border_width [ ST_SIDE_BOTTOM ] , max_border_radius ) ) ;
2010-02-08 18:40:25 +00:00
}
}
static void
st_theme_node_paint_sliced_border_image ( StThemeNode * node ,
const ClutterActorBox * box ,
guint8 paint_opacity )
{
gfloat ex , ey ;
gfloat tx1 , ty1 , tx2 , ty2 ;
gint border_left , border_right , border_top , border_bottom ;
float img_width , img_height ;
StBorderImage * border_image ;
CoglHandle material ;
border_image = st_theme_node_get_border_image ( node ) ;
g_assert ( border_image ! = NULL ) ;
st_border_image_get_borders ( border_image ,
& border_left , & border_right , & border_top , & border_bottom ) ;
2011-01-12 22:07:19 +00:00
img_width = cogl_texture_get_width ( node - > border_slices_texture ) ;
img_height = cogl_texture_get_height ( node - > border_slices_texture ) ;
2010-02-08 18:40:25 +00:00
tx1 = border_left / img_width ;
tx2 = ( img_width - border_right ) / img_width ;
ty1 = border_top / img_height ;
ty2 = ( img_height - border_bottom ) / img_height ;
ex = node - > alloc_width - border_right ;
if ( ex < 0 )
ex = border_right ; /* FIXME ? */
ey = node - > alloc_height - border_bottom ;
if ( ey < 0 )
ey = border_bottom ; /* FIXME ? */
2011-01-12 22:07:19 +00:00
material = node - > border_slices_material ;
2010-02-08 18:40:25 +00:00
cogl_material_set_color4ub ( material ,
paint_opacity , paint_opacity , paint_opacity , paint_opacity ) ;
cogl_set_source ( material ) ;
{
GLfloat rectangles [ ] =
{
/* top left corner */
0 , 0 , border_left , border_top ,
0.0 , 0.0 ,
tx1 , ty1 ,
/* top middle */
border_left , 0 , ex , border_top ,
tx1 , 0.0 ,
tx2 , ty1 ,
/* top right */
ex , 0 , node - > alloc_width , border_top ,
tx2 , 0.0 ,
1.0 , ty1 ,
/* mid left */
0 , border_top , border_left , ey ,
0.0 , ty1 ,
tx1 , ty2 ,
/* center */
border_left , border_top , ex , ey ,
tx1 , ty1 ,
tx2 , ty2 ,
/* mid right */
ex , border_top , node - > alloc_width , ey ,
tx2 , ty1 ,
1.0 , ty2 ,
/* bottom left */
0 , ey , border_left , node - > alloc_height ,
0.0 , ty2 ,
tx1 , 1.0 ,
/* bottom center */
border_left , ey , ex , node - > alloc_height ,
tx1 , ty2 ,
tx2 , 1.0 ,
/* bottom right */
ex , ey , node - > alloc_width , node - > alloc_height ,
tx2 , ty2 ,
1.0 , 1.0
} ;
cogl_rectangles_with_texture_coords ( rectangles , 9 ) ;
}
}
2010-05-26 20:50:24 +00:00
static void
st_theme_node_paint_outline ( StThemeNode * node ,
const ClutterActorBox * box ,
guint8 paint_opacity )
{
float width , height ;
int outline_width ;
ClutterColor outline_color , effective_outline ;
width = box - > x2 - box - > x1 ;
height = box - > y2 - box - > y1 ;
outline_width = st_theme_node_get_outline_width ( node ) ;
if ( outline_width = = 0 )
return ;
st_theme_node_get_outline_color ( node , & outline_color ) ;
over ( & outline_color , & node - > background_color , & effective_outline ) ;
cogl_set_source_color4ub ( effective_outline . red ,
effective_outline . green ,
effective_outline . blue ,
paint_opacity * effective_outline . alpha / 255 ) ;
/* The outline is drawn just outside the border, which means just
* outside the allocation box . This means that in some situations
* involving clip_to_allocation or the screen edges , you won ' t be
* able to see the outline . In practice , it works well enough .
*/
/* NORTH */
cogl_rectangle ( - outline_width , - outline_width ,
width + outline_width , 0 ) ;
/* EAST */
cogl_rectangle ( width , 0 ,
width + outline_width , height ) ;
/* SOUTH */
cogl_rectangle ( - outline_width , height ,
width + outline_width , height + outline_width ) ;
/* WEST */
cogl_rectangle ( - outline_width , 0 ,
0 , height ) ;
}
2010-02-08 18:40:25 +00:00
void
st_theme_node_paint ( StThemeNode * node ,
const ClutterActorBox * box ,
guint8 paint_opacity )
{
float width , height ;
ClutterActorBox allocation ;
/* Some things take an ActorBox, some things just width/height */
width = box - > x2 - box - > x1 ;
height = box - > y2 - box - > y1 ;
allocation . x1 = allocation . y1 = 0 ;
allocation . x2 = width ;
allocation . y2 = height ;
if ( node - > alloc_width ! = width | | node - > alloc_height ! = height )
st_theme_node_render_resources ( node , width , height ) ;
/* Rough notes about the relationship of borders and backgrounds in CSS3;
* see http : //www.w3.org/TR/css3-background/ for more accurate details.
*
* - Things are drawn in 4 layers , from the bottom :
* Background color
* Background image
* Border color or border image
* Content
* - The background color , gradient and image extend to and are clipped by
* the edge of the border area , so will be rounded if the border is
* rounded . ( CSS3 background - clip property modifies this )
* - The border image replaces what would normally be drawn by the border
* - The border image is not clipped by a rounded border - radius
* - The border radius rounds the background even if the border is
* zero width or a border image is being used .
*
* Deviations from the above as implemented here :
* - The combination of border image and a non - zero border radius is
* not supported ; the background color will be drawn with square
* corners .
2011-01-14 19:03:40 +00:00
* - The background image is drawn above the border color , not below it .
2010-02-08 18:40:25 +00:00
* - We don ' t clip the background image to the ( rounded ) border area .
*/
2011-01-13 23:27:24 +00:00
if ( node - > box_shadow_material )
_st_paint_shadow_with_opacity ( node - > box_shadow ,
node - > box_shadow_material ,
2010-07-24 15:41:25 +00:00
& allocation ,
paint_opacity ) ;
2010-03-11 23:14:12 +00:00
2011-01-14 19:03:40 +00:00
if ( node - > prerendered_material ! = COGL_INVALID_HANDLE | |
node - > border_slices_material ! = COGL_INVALID_HANDLE )
{
if ( node - > prerendered_material ! = COGL_INVALID_HANDLE )
paint_material_with_opacity ( node - > prerendered_material , & allocation , paint_opacity ) ;
if ( node - > border_slices_material ! = COGL_INVALID_HANDLE )
st_theme_node_paint_sliced_border_image ( node , & allocation , paint_opacity ) ;
}
2010-02-08 18:40:25 +00:00
else
2011-01-14 19:03:40 +00:00
{
st_theme_node_paint_borders ( node , box , paint_opacity ) ;
}
2010-02-08 18:40:25 +00:00
2010-05-26 20:50:24 +00:00
st_theme_node_paint_outline ( node , box , paint_opacity ) ;
2010-02-08 18:40:25 +00:00
if ( node - > background_texture ! = COGL_INVALID_HANDLE )
{
ClutterActorBox background_box ;
get_background_position ( node , & allocation , & background_box ) ;
2011-01-22 02:52:18 +00:00
gboolean has_visible_outline ;
/* If the background doesn't have a border or opaque background,
* then we let its background image shadows leak out , but other
* wise we clip it .
*/
has_visible_outline = st_theme_node_has_visible_outline ( node ) ;
if ( has_visible_outline )
cogl_clip_push_rectangle ( allocation . x1 , allocation . y1 , allocation . x2 , allocation . y2 ) ;
2010-02-08 18:40:25 +00:00
/* CSS based drop shadows
*
* Drop shadows in ST are modelled after the CSS3 box - shadow property ;
* see http : //www.css3.info/preview/box-shadow/ for a detailed description.
*
* While the syntax of the property is mostly identical - we do not support
* multiple shadows and allow for a more liberal placement of the color
* parameter - its interpretation defers significantly in that the shadow ' s
* shape is not determined by the bounding box , but by the CSS background
2011-01-22 02:52:18 +00:00
* image . The drop shadows are allowed to escape the nodes allocation if
* there is nothing ( like a border , or the edge of the background color )
* to logically confine it .
2010-02-08 18:40:25 +00:00
*/
2010-03-11 23:14:12 +00:00
if ( node - > background_shadow_material ! = COGL_INVALID_HANDLE )
2011-01-13 23:27:24 +00:00
_st_paint_shadow_with_opacity ( node - > background_image_shadow ,
2010-07-24 15:41:25 +00:00
node - > background_shadow_material ,
& background_box ,
paint_opacity ) ;
2010-02-08 18:40:25 +00:00
2010-10-28 20:51:13 +00:00
paint_material_with_opacity ( node - > background_material , & background_box , paint_opacity ) ;
2011-01-22 02:52:18 +00:00
if ( has_visible_outline )
cogl_clip_pop ( ) ;
2010-02-08 18:40:25 +00:00
}
}
2010-08-26 18:10:46 +00:00
/**
* st_theme_node_copy_cached_paint_state :
* @ node : a # StThemeNode
* @ other : a different # StThemeNode
*
* Copy cached painting state from @ other to @ node . This function can be used to
* optimize redrawing cached background images when the style on an element changess
* in a way that doesn ' t affect background drawing . This function must only be called
* if st_theme_node_paint_equal ( node , other ) returns % TRUE .
*/
void
st_theme_node_copy_cached_paint_state ( StThemeNode * node ,
StThemeNode * other )
{
2010-10-01 01:58:59 +00:00
int corner_id ;
2010-08-26 18:10:46 +00:00
g_return_if_fail ( ST_IS_THEME_NODE ( node ) ) ;
g_return_if_fail ( ST_IS_THEME_NODE ( other ) ) ;
/* Check omitted for speed: */
/* g_return_if_fail (st_theme_node_paint_equal (node, other)); */
_st_theme_node_free_drawing_state ( node ) ;
node - > alloc_width = other - > alloc_width ;
node - > alloc_height = other - > alloc_height ;
if ( other - > background_shadow_material )
node - > background_shadow_material = cogl_handle_ref ( other - > background_shadow_material ) ;
2011-01-13 23:27:24 +00:00
if ( other - > box_shadow_material )
node - > box_shadow_material = cogl_handle_ref ( other - > box_shadow_material ) ;
2010-08-26 18:10:46 +00:00
if ( other - > background_texture )
node - > background_texture = cogl_handle_ref ( other - > background_texture ) ;
2010-10-28 20:51:13 +00:00
if ( other - > background_material )
node - > background_material = cogl_handle_ref ( other - > background_material ) ;
2011-01-12 22:07:19 +00:00
if ( other - > border_slices_texture )
node - > border_slices_texture = cogl_handle_ref ( other - > border_slices_texture ) ;
if ( other - > border_slices_material )
node - > border_slices_material = cogl_handle_ref ( other - > border_slices_material ) ;
if ( other - > prerendered_texture )
node - > prerendered_texture = cogl_handle_ref ( other - > prerendered_texture ) ;
if ( other - > prerendered_material )
node - > prerendered_material = cogl_handle_ref ( other - > prerendered_material ) ;
2010-10-01 01:58:59 +00:00
for ( corner_id = 0 ; corner_id < 4 ; corner_id + + )
2010-10-27 16:41:20 +00:00
if ( other - > corner_material [ corner_id ] )
node - > corner_material [ corner_id ] = cogl_handle_ref ( other - > corner_material [ corner_id ] ) ;
2010-08-26 18:10:46 +00:00
}