gnome-shell-3.6.3.1/src/st/st-theme-node-drawing.c

No issues found

   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 /*
   3  * st-theme-node-drawing.c: Code to draw themed elements
   4  *
   5  * Copyright 2009, 2010 Red Hat, Inc.
   6  * Copyright 2009, 2010 Florian Mç«Żllner
   7  * Copyright 2010 Intel Corporation.
   8  * Copyright 2011 Quentin "Sardem FF7" Glidic
   9  *
  10  * Contains code derived from:
  11  *   rectangle.c: Rounded rectangle.
  12  *     Copyright 2008 litl, LLC.
  13  *   st-texture-frame.h: Expandible texture actor
  14  *     Copyright 2007 OpenedHand
  15  *     Copyright 2009 Intel Corporation.
  16  *
  17  * This program is free software; you can redistribute it and/or modify it
  18  * under the terms and conditions of the GNU Lesser General Public License,
  19  * version 2.1, as published by the Free Software Foundation.
  20  *
  21  * This program is distributed in the hope it will be useful, but WITHOUT ANY
  22  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  23  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
  24  * more details.
  25  *
  26  * You should have received a copy of the GNU Lesser General Public License
  27  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  28  */
  29 
  30 #include <stdlib.h>
  31 #include <math.h>
  32 
  33 #include "st-shadow.h"
  34 #include "st-private.h"
  35 #include "st-theme-private.h"
  36 #include "st-theme-context.h"
  37 #include "st-texture-cache.h"
  38 #include "st-theme-node-private.h"
  39 
  40 /****
  41  * Rounded corners
  42  ****/
  43 
  44 typedef struct {
  45   ClutterColor   color;
  46   ClutterColor   border_color_1;
  47   ClutterColor   border_color_2;
  48   guint          radius;
  49   guint          border_width_1;
  50   guint          border_width_2;
  51 } StCornerSpec;
  52 
  53 static void
  54 elliptical_arc (cairo_t *cr,
  55                 double   x_center,
  56                 double   y_center,
  57                 double   x_radius,
  58                 double   y_radius,
  59                 double   angle1,
  60                 double   angle2)
  61 {
  62   cairo_save (cr);
  63   cairo_translate (cr, x_center, y_center);
  64   cairo_scale (cr, x_radius, y_radius);
  65   cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
  66   cairo_restore (cr);
  67 }
  68 
  69 static CoglHandle
  70 create_corner_material (StCornerSpec *corner)
  71 {
  72   CoglHandle texture;
  73   cairo_t *cr;
  74   cairo_surface_t *surface;
  75   guint rowstride;
  76   guint8 *data;
  77   guint size;
  78   guint max_border_width;
  79 
  80   max_border_width = MAX(corner->border_width_2, corner->border_width_1);
  81   size = 2 * MAX(max_border_width, corner->radius);
  82   rowstride = size * 4;
  83   data = g_new0 (guint8, size * rowstride);
  84 
  85   surface = cairo_image_surface_create_for_data (data,
  86                                                  CAIRO_FORMAT_ARGB32,
  87                                                  size, size,
  88                                                  rowstride);
  89   cr = cairo_create (surface);
  90   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  91   cairo_scale (cr, size, size);
  92 
  93   if (max_border_width <= corner->radius)
  94     {
  95       double x_radius, y_radius;
  96 
  97       if (max_border_width != 0)
  98         {
  99           cairo_set_source_rgba (cr,
 100                                  corner->border_color_1.red / 255.,
 101                                  corner->border_color_1.green / 255.,
 102                                  corner->border_color_1.blue / 255.,
 103                                  corner->border_color_1.alpha / 255.);
 104 
 105           cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
 106           cairo_fill (cr);
 107         }
 108 
 109       cairo_set_source_rgba (cr,
 110                              corner->color.red / 255.,
 111                              corner->color.green / 255.,
 112                              corner->color.blue / 255.,
 113                              corner->color.alpha / 255.);
 114 
 115       x_radius = 0.5 * (1.0 - (double) corner->border_width_2 / corner->radius);
 116       y_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
 117 
 118       /* TOPRIGHT */
 119       elliptical_arc (cr,
 120                       0.5, 0.5,
 121                       x_radius, y_radius,
 122                       3 * M_PI / 2, 2 * M_PI);
 123 
 124       /* BOTTOMRIGHT */
 125       elliptical_arc (cr,
 126                       0.5, 0.5,
 127                       x_radius, y_radius,
 128                       0, M_PI / 2);
 129 
 130       /* TOPLEFT */
 131       elliptical_arc (cr,
 132                       0.5, 0.5,
 133                       x_radius, y_radius,
 134                       M_PI, 3 * M_PI / 2);
 135 
 136       /* BOTTOMLEFT */
 137       elliptical_arc (cr,
 138                       0.5, 0.5,
 139                       x_radius, y_radius,
 140                       M_PI / 2, M_PI);
 141 
 142       cairo_fill (cr);
 143     }
 144   else
 145     {
 146       double radius;
 147 
 148       radius = (gdouble)corner->radius / max_border_width;
 149 
 150       cairo_set_source_rgba (cr,
 151                              corner->border_color_1.red / 255.,
 152                              corner->border_color_1.green / 255.,
 153                              corner->border_color_1.blue / 255.,
 154                              corner->border_color_1.alpha / 255.);
 155 
 156       cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
 157       cairo_line_to (cr, 1.0 - radius, 0.0);
 158       cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI);
 159       cairo_line_to (cr, 1.0, 1.0 - radius);
 160       cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
 161       cairo_line_to (cr, radius, 1.0);
 162       cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
 163       cairo_fill (cr);
 164     }
 165   cairo_destroy (cr);
 166 
 167   cairo_surface_destroy (surface);
 168 
 169   texture = cogl_texture_new_from_data (size, size,
 170                                         COGL_TEXTURE_NONE,
 171                                         CLUTTER_CAIRO_FORMAT_ARGB32,
 172                                         COGL_PIXEL_FORMAT_ANY,
 173                                         rowstride,
 174                                         data);
 175   g_free (data);
 176   g_assert (texture != COGL_INVALID_HANDLE);
 177 
 178   return texture;
 179 }
 180 
 181 static char *
 182 corner_to_string (StCornerSpec *corner)
 183 {
 184   return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u",
 185                           corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha,
 186                           corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha,
 187                           corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha,
 188                           corner->radius,
 189                           corner->border_width_1,
 190                           corner->border_width_2);
 191 }
 192 
 193 static CoglHandle
 194 load_corner (StTextureCache  *cache,
 195              const char      *key,
 196              void            *datap,
 197              GError         **error)
 198 {
 199   return create_corner_material ((StCornerSpec *) datap);
 200 }
 201 
 202 /* To match the CSS specification, we want the border to look like it was
 203  * drawn over the background. But actually drawing the border over the
 204  * background will produce slightly bad antialiasing at the edges, so
 205  * compute the effective border color instead.
 206  */
 207 #define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
 208 #define MULT(c,a) NORM(c*a)
 209 
 210 static void
 211 premultiply (ClutterColor *color)
 212 {
 213   guint t;
 214   color->red = MULT (color->red, color->alpha);
 215   color->green = MULT (color->green, color->alpha);
 216   color->blue = MULT (color->blue, color->alpha);
 217 }
 218 
 219 static void
 220 unpremultiply (ClutterColor *color)
 221 {
 222   if (color->alpha != 0)
 223     {
 224       color->red = (color->red * 255 + 127) / color->alpha;
 225       color->green = (color->green * 255 + 127) / color->alpha;
 226       color->blue = (color->blue * 255 + 127) / color->alpha;
 227     }
 228 }
 229 
 230 static void
 231 over (const ClutterColor *source,
 232       const ClutterColor *destination,
 233       ClutterColor       *result)
 234 {
 235   guint t;
 236   ClutterColor src = *source;
 237   ClutterColor dst = *destination;
 238 
 239   premultiply (&src);
 240   premultiply (&dst);
 241 
 242   result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
 243   result->red   = src.red +   NORM ((255 - src.alpha) * dst.red);
 244   result->green = src.green + NORM ((255 - src.alpha) * dst.green);
 245   result->blue  = src.blue +  NORM ((255 - src.alpha) * dst.blue);
 246 
 247   unpremultiply (result);
 248 }
 249 
 250 /*
 251  * st_theme_node_reduce_border_radius:
 252  * @node: a #StThemeNode
 253  * @corners: (array length=4) (out): reduced corners
 254  *
 255  * Implements the corner overlap algorithm mentioned at
 256  * http://www.w3.org/TR/css3-background/#corner-overlap
 257  */
 258 static void
 259 st_theme_node_reduce_border_radius (StThemeNode  *node,
 260                                     guint        *corners)
 261 {
 262   gfloat scale;
 263   guint sum;
 264 
 265   scale = 1.0;
 266 
 267   /* top */
 268   sum = node->border_radius[ST_CORNER_TOPLEFT]
 269     + node->border_radius[ST_CORNER_TOPRIGHT];
 270 
 271   if (sum > 0)
 272     scale = MIN (node->alloc_width / sum, scale);
 273 
 274   /* right */
 275   sum = node->border_radius[ST_CORNER_TOPRIGHT]
 276     + node->border_radius[ST_CORNER_BOTTOMRIGHT];
 277 
 278   if (sum > 0)
 279     scale = MIN (node->alloc_height / sum, scale);
 280 
 281   /* bottom */
 282   sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
 283     + node->border_radius[ST_CORNER_BOTTOMRIGHT];
 284 
 285   if (sum > 0)
 286     scale = MIN (node->alloc_width / sum, scale);
 287 
 288   /* left */
 289   sum = node->border_radius[ST_CORNER_BOTTOMLEFT]
 290     + node->border_radius[ST_CORNER_TOPLEFT];
 291 
 292   if (sum > 0)
 293     scale = MIN (node->alloc_height / sum, scale);
 294 
 295   corners[ST_CORNER_TOPLEFT]     = node->border_radius[ST_CORNER_TOPLEFT]     * scale;
 296   corners[ST_CORNER_TOPRIGHT]    = node->border_radius[ST_CORNER_TOPRIGHT]    * scale;
 297   corners[ST_CORNER_BOTTOMLEFT]  = node->border_radius[ST_CORNER_BOTTOMLEFT]  * scale;
 298   corners[ST_CORNER_BOTTOMRIGHT] = node->border_radius[ST_CORNER_BOTTOMRIGHT] * scale;
 299 }
 300 
 301 static void
 302 st_theme_node_get_corner_border_widths (StThemeNode *node,
 303                                         StCorner     corner_id,
 304                                         guint       *border_width_1,
 305                                         guint       *border_width_2)
 306 {
 307   switch (corner_id)
 308     {
 309       case ST_CORNER_TOPLEFT:
 310         if (border_width_1)
 311             *border_width_1 = node->border_width[ST_SIDE_TOP];
 312         if (border_width_2)
 313             *border_width_2 = node->border_width[ST_SIDE_LEFT];
 314         break;
 315       case ST_CORNER_TOPRIGHT:
 316         if (border_width_1)
 317             *border_width_1 = node->border_width[ST_SIDE_TOP];
 318         if (border_width_2)
 319             *border_width_2 = node->border_width[ST_SIDE_RIGHT];
 320         break;
 321       case ST_CORNER_BOTTOMRIGHT:
 322         if (border_width_1)
 323             *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
 324         if (border_width_2)
 325             *border_width_2 = node->border_width[ST_SIDE_RIGHT];
 326         break;
 327       case ST_CORNER_BOTTOMLEFT:
 328         if (border_width_1)
 329             *border_width_1 = node->border_width[ST_SIDE_BOTTOM];
 330         if (border_width_2)
 331             *border_width_2 = node->border_width[ST_SIDE_LEFT];
 332         break;
 333     }
 334 }
 335 
 336 static CoglHandle
 337 st_theme_node_lookup_corner (StThemeNode    *node,
 338                              StCorner        corner_id)
 339 {
 340   CoglHandle texture, material;
 341   char *key;
 342   StTextureCache *cache;
 343   StCornerSpec corner;
 344   guint radius[4];
 345 
 346   cache = st_texture_cache_get_default ();
 347 
 348   st_theme_node_reduce_border_radius (node, radius);
 349 
 350   if (radius[corner_id] == 0)
 351     return COGL_INVALID_HANDLE;
 352 
 353   corner.radius = radius[corner_id];
 354   corner.color = node->background_color;
 355   st_theme_node_get_corner_border_widths (node, corner_id,
 356                                           &corner.border_width_1,
 357                                           &corner.border_width_2);
 358 
 359   switch (corner_id)
 360     {
 361       case ST_CORNER_TOPLEFT:
 362         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
 363         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
 364         break;
 365       case ST_CORNER_TOPRIGHT:
 366         over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
 367         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
 368         break;
 369       case ST_CORNER_BOTTOMRIGHT:
 370         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
 371         over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
 372         break;
 373       case ST_CORNER_BOTTOMLEFT:
 374         over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
 375         over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
 376         break;
 377     }
 378 
 379   if (corner.color.alpha == 0 &&
 380       corner.border_color_1.alpha == 0 &&
 381       corner.border_color_2.alpha == 0)
 382     return COGL_INVALID_HANDLE;
 383 
 384   key = corner_to_string (&corner);
 385   texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_NONE, load_corner, &corner, NULL);
 386   material = _st_create_texture_material (texture);
 387   cogl_handle_unref (texture);
 388 
 389   g_free (key);
 390 
 391   return material;
 392 }
 393 
 394 static void
 395 get_background_scale (StThemeNode *node,
 396                       gdouble      painting_area_width,
 397                       gdouble      painting_area_height,
 398                       gdouble      background_image_width,
 399                       gdouble      background_image_height,
 400                       gdouble     *scale_w,
 401                       gdouble     *scale_h)
 402 {
 403   *scale_w = -1.0;
 404   *scale_h = -1.0;
 405 
 406   switch (node->background_size)
 407     {
 408       case ST_BACKGROUND_SIZE_AUTO:
 409         *scale_w = 1.0;
 410         break;
 411       case ST_BACKGROUND_SIZE_CONTAIN:
 412         *scale_w = MIN (painting_area_width / background_image_width,
 413                         painting_area_height / background_image_height);
 414         break;
 415       case ST_BACKGROUND_SIZE_COVER:
 416         *scale_w = MAX (painting_area_width / background_image_width,
 417                         painting_area_height / background_image_height);
 418         break;
 419       case ST_BACKGROUND_SIZE_FIXED:
 420         if (node->background_size_w > -1)
 421           {
 422             *scale_w = node->background_size_w / background_image_width;
 423             if (node->background_size_h > -1)
 424               *scale_h = node->background_size_h / background_image_height;
 425           }
 426         else if (node->background_size_h > -1)
 427           *scale_w = node->background_size_h / background_image_height;
 428         break;
 429     }
 430   if (*scale_h < 0.0)
 431     *scale_h = *scale_w;
 432 }
 433 
 434 static void
 435 get_background_coordinates (StThemeNode *node,
 436                             gdouble      painting_area_width,
 437                             gdouble      painting_area_height,
 438                             gdouble      background_image_width,
 439                             gdouble      background_image_height,
 440                             gdouble     *x,
 441                             gdouble     *y)
 442 {
 443   /* honor the specified position if any */
 444   if (node->background_position_set)
 445     {
 446       *x = node->background_position_x;
 447       *y = node->background_position_y;
 448     }
 449   else
 450     {
 451       /* center the background on the widget */
 452       *x = (painting_area_width / 2.0) - (background_image_width / 2.0);
 453       *y = (painting_area_height / 2.0) - (background_image_height / 2.0);
 454     }
 455 }
 456 
 457 static void
 458 get_background_position (StThemeNode             *self,
 459                          const ClutterActorBox   *allocation,
 460                          ClutterActorBox         *result,
 461                          ClutterActorBox         *texture_coords)
 462 {
 463   gdouble painting_area_width, painting_area_height;
 464   gdouble background_image_width, background_image_height;
 465   gdouble x1, y1;
 466   gdouble scale_w, scale_h;
 467 
 468   /* get the background image size */
 469   background_image_width = cogl_texture_get_width (self->background_texture);
 470   background_image_height = cogl_texture_get_height (self->background_texture);
 471 
 472   /* get the painting area size */
 473   painting_area_width = allocation->x2 - allocation->x1;
 474   painting_area_height = allocation->y2 - allocation->y1;
 475 
 476   /* scale if requested */
 477   get_background_scale (self,
 478                         painting_area_width, painting_area_height,
 479                         background_image_width, background_image_height,
 480                         &scale_w, &scale_h);
 481   background_image_width *= scale_w;
 482   background_image_height *= scale_h;
 483 
 484   /* get coordinates */
 485   get_background_coordinates (self,
 486                               painting_area_width, painting_area_height,
 487                               background_image_width, background_image_height,
 488                               &x1, &y1);
 489 
 490   if (self->background_repeat)
 491     {
 492       gdouble width = allocation->x2 - allocation->x1 + x1;
 493       gdouble height = allocation->y2 - allocation->y1 + y1;
 494 
 495       *result = *allocation;
 496 
 497       /* reference image is at x1, y1 */
 498       texture_coords->x1 = x1 / background_image_width;
 499       texture_coords->y1 = y1 / background_image_height;
 500       texture_coords->x2 = width / background_image_width;
 501       texture_coords->y2 = height / background_image_height;
 502     }
 503   else
 504     {
 505       result->x1 = x1;
 506       result->y1 = y1;
 507       result->x2 = x1 + background_image_width;
 508       result->y2 = y1 + background_image_height;
 509 
 510       texture_coords->x1 = texture_coords->y1 = 0;
 511       texture_coords->x2 = texture_coords->y2 = 1;
 512     }
 513 }
 514 
 515 /* Use of this function marks code which doesn't support
 516  * non-uniform colors.
 517  */
 518 static void
 519 get_arbitrary_border_color (StThemeNode   *node,
 520                             ClutterColor  *color)
 521 {
 522   if (color)
 523     st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
 524 }
 525 
 526 static gboolean
 527 st_theme_node_has_visible_outline (StThemeNode *node)
 528 {
 529   if (node->background_color.alpha > 0)
 530     return TRUE;
 531 
 532   if (node->background_gradient_end.alpha > 0)
 533     return TRUE;
 534 
 535   if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
 536       node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
 537       node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
 538       node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
 539     return TRUE;
 540 
 541   if (node->border_width[ST_SIDE_TOP] > 0 ||
 542       node->border_width[ST_SIDE_LEFT] > 0 ||
 543       node->border_width[ST_SIDE_RIGHT] > 0 ||
 544       node->border_width[ST_SIDE_BOTTOM] > 0)
 545     return TRUE;
 546 
 547   return FALSE;
 548 }
 549 
 550 static cairo_pattern_t *
 551 create_cairo_pattern_of_background_gradient (StThemeNode *node)
 552 {
 553   cairo_pattern_t *pattern;
 554 
 555   g_return_val_if_fail (node->background_gradient_type != ST_GRADIENT_NONE,
 556                         NULL);
 557 
 558   if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
 559     pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
 560   else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL)
 561     pattern = cairo_pattern_create_linear (0, 0, node->alloc_width, 0);
 562   else
 563     {
 564       gdouble cx, cy;
 565 
 566       cx = node->alloc_width / 2.;
 567       cy = node->alloc_height / 2.;
 568       pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
 569     }
 570 
 571   cairo_pattern_add_color_stop_rgba (pattern, 0,
 572                                      node->background_color.red / 255.,
 573                                      node->background_color.green / 255.,
 574                                      node->background_color.blue / 255.,
 575                                      node->background_color.alpha / 255.);
 576   cairo_pattern_add_color_stop_rgba (pattern, 1,
 577                                      node->background_gradient_end.red / 255.,
 578                                      node->background_gradient_end.green / 255.,
 579                                      node->background_gradient_end.blue / 255.,
 580                                      node->background_gradient_end.alpha / 255.);
 581   return pattern;
 582 }
 583 
 584 static cairo_pattern_t *
 585 create_cairo_pattern_of_background_image (StThemeNode *node,
 586                                           gboolean    *needs_background_fill)
 587 {
 588   cairo_surface_t *surface;
 589   cairo_pattern_t *pattern;
 590   cairo_content_t  content;
 591   cairo_matrix_t   matrix;
 592   const char *file;
 593 
 594   StTextureCache *texture_cache;
 595 
 596   gdouble background_image_width, background_image_height;
 597   gdouble x, y;
 598   gdouble scale_w, scale_h;
 599 
 600   file = st_theme_node_get_background_image (node);
 601 
 602   texture_cache = st_texture_cache_get_default ();
 603 
 604   surface = st_texture_cache_load_file_to_cairo_surface (texture_cache, file);
 605 
 606   if (surface == NULL)
 607     return NULL;
 608 
 609   g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
 610 
 611   content = cairo_surface_get_content (surface);
 612   pattern = cairo_pattern_create_for_surface (surface);
 613 
 614   background_image_width = cairo_image_surface_get_width (surface);
 615   background_image_height = cairo_image_surface_get_height (surface);
 616 
 617   *needs_background_fill = TRUE;
 618 
 619   cairo_matrix_init_identity (&matrix);
 620 
 621   get_background_scale (node,
 622                         node->alloc_width, node->alloc_height,
 623                         background_image_width, background_image_height,
 624                         &scale_w, &scale_h);
 625   if ((scale_w != 1) || (scale_h != 1))
 626     cairo_matrix_scale (&matrix, 1.0/scale_w, 1.0/scale_h);
 627   background_image_width *= scale_w;
 628   background_image_height *= scale_h;
 629 
 630   get_background_coordinates (node,
 631                               node->alloc_width, node->alloc_height,
 632                               background_image_width, background_image_height,
 633                               &x, &y);
 634   cairo_matrix_translate (&matrix, -x, -y);
 635 
 636   if (node->background_repeat)
 637     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
 638 
 639   /* If it's opaque, fills up the entire allocated
 640    * area, then don't bother doing a background fill first
 641    */
 642   if (content != CAIRO_CONTENT_COLOR_ALPHA)
 643     {
 644       if (node->background_repeat ||
 645           (x >= 0 &&
 646            y >= 0 &&
 647            background_image_width - x >= node->alloc_width &&
 648            background_image_height -y >= node->alloc_height))
 649         *needs_background_fill = FALSE;
 650     }
 651 
 652   cairo_pattern_set_matrix (pattern, &matrix);
 653 
 654   return pattern;
 655 }
 656 
 657 /* fill_exterior = TRUE means that pattern is a surface pattern and
 658  * we should extend the pattern with a solid fill from its edges.
 659  * This is a bit of a hack; the alternative would be to make the
 660  * surface of the surface pattern 1 pixel bigger and use CAIRO_EXTEND_PAD.
 661  */
 662 static void
 663 paint_shadow_pattern_to_cairo_context (StShadow *shadow_spec,
 664                                        cairo_pattern_t *pattern,
 665                                        gboolean         fill_exterior,
 666                                        cairo_t         *cr,
 667                                        cairo_path_t    *interior_path,
 668                                        cairo_path_t    *outline_path)
 669 {
 670   /* If there are borders, clip the shadow to the interior
 671    * of the borders; if there is a visible outline, clip the shadow to
 672    * that outline
 673    */
 674   cairo_path_t *path = (interior_path != NULL) ? interior_path : outline_path;
 675   double x1, x2, y1, y2;
 676 
 677   /* fill_exterior only makes sense if we're clipping the shadow - filling
 678    * to the edges of the surface would be silly */
 679   g_assert (!(fill_exterior && path == NULL));
 680 
 681   cairo_save (cr);
 682   if (path != NULL)
 683     {
 684       cairo_append_path (cr, path);
 685 
 686       /* There's no way to invert a path in cairo, so we need bounds for
 687        * the area we are drawing in order to create the "exterior" region.
 688        * Pixel align to hit fast paths.
 689        */
 690       if (fill_exterior)
 691         {
 692           cairo_path_extents (cr, &x1, &y1, &x2, &y2);
 693           x1 = floor (x1);
 694           y1 = floor (y1);
 695           x2 = ceil (x2);
 696           y2 = ceil (y2);
 697         }
 698 
 699       cairo_clip (cr);
 700     }
 701 
 702   cairo_set_source_rgba (cr,
 703                          shadow_spec->color.red / 255.0,
 704                          shadow_spec->color.green / 255.0,
 705                          shadow_spec->color.blue / 255.0,
 706                          shadow_spec->color.alpha / 255.0);
 707   if (fill_exterior)
 708     {
 709       cairo_surface_t *surface;
 710       int width, height;
 711       cairo_matrix_t matrix;
 712 
 713       cairo_save (cr);
 714 
 715       /* Start with a rectangle enclosing the bounds of the clipped
 716        * region */
 717       cairo_rectangle (cr, x1, y1, x2 - x1, y2 - y1);
 718 
 719       /* Then subtract out the bounds of the surface in the surface
 720        * pattern; we transform the context by the inverse of the
 721        * pattern matrix to get to surface coordinates */
 722       cairo_pattern_get_surface (pattern, &surface);
 723       width = cairo_image_surface_get_width  (surface);
 724       height = cairo_image_surface_get_height (surface);
 725 
 726       cairo_pattern_get_matrix (pattern, &matrix);
 727       cairo_matrix_invert (&matrix);
 728       cairo_transform (cr, &matrix);
 729 
 730       cairo_rectangle (cr, 0, height, width, - height);
 731       cairo_fill (cr);
 732 
 733       cairo_restore (cr);
 734     }
 735 
 736   cairo_mask (cr, pattern);
 737   cairo_restore (cr);
 738 }
 739 
 740 static void
 741 paint_background_image_shadow_to_cairo_context (StThemeNode     *node,
 742                                                 StShadow        *shadow_spec,
 743                                                 cairo_pattern_t *pattern,
 744                                                 cairo_t         *cr,
 745                                                 cairo_path_t    *interior_path,
 746                                                 cairo_path_t    *outline_path,
 747                                                 int              x,
 748                                                 int              y,
 749                                                 int              width,
 750                                                 int              height)
 751 {
 752   cairo_pattern_t *shadow_pattern;
 753 
 754   g_assert (shadow_spec != NULL);
 755   g_assert (pattern != NULL);
 756 
 757   if (outline_path != NULL)
 758     {
 759       cairo_surface_t *clipped_surface;
 760       cairo_pattern_t *clipped_pattern;
 761       cairo_t *temp_cr;
 762 
 763       /* Prerender the pattern to a temporary surface,
 764        * so it's properly clipped before we create a shadow from it
 765        */
 766       clipped_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
 767       temp_cr = cairo_create (clipped_surface);
 768 
 769       cairo_set_operator (temp_cr, CAIRO_OPERATOR_CLEAR);
 770       cairo_paint (temp_cr);
 771       cairo_set_operator (temp_cr, CAIRO_OPERATOR_SOURCE);
 772 
 773       if (interior_path != NULL)
 774         {
 775           cairo_append_path (temp_cr, interior_path);
 776           cairo_clip (temp_cr);
 777         }
 778 
 779       cairo_append_path (temp_cr, outline_path);
 780       cairo_translate (temp_cr, x, y);
 781       cairo_set_source (temp_cr, pattern);
 782       cairo_clip (temp_cr);
 783       cairo_paint (temp_cr);
 784       cairo_destroy (temp_cr);
 785 
 786       clipped_pattern = cairo_pattern_create_for_surface (clipped_surface);
 787       cairo_surface_destroy (clipped_surface);
 788 
 789       shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
 790                                                         clipped_pattern);
 791       cairo_pattern_destroy (clipped_pattern);
 792     }
 793   else
 794     {
 795       shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec,
 796                                                         pattern);
 797     }
 798 
 799   paint_shadow_pattern_to_cairo_context (shadow_spec,
 800                                          shadow_pattern, FALSE,
 801                                          cr,
 802                                          interior_path,
 803                                          outline_path);
 804   cairo_pattern_destroy (shadow_pattern);
 805 }
 806 
 807 /* gets the extents of a cairo_path_t; slightly inefficient, but much simpler than
 808  * computing from the raw path data */
 809 static void
 810 path_extents (cairo_path_t *path,
 811               double       *x1,
 812               double       *y1,
 813               double       *x2,
 814               double       *y2)
 815 
 816 {
 817   cairo_surface_t *dummy = cairo_image_surface_create (CAIRO_FORMAT_A8, 1, 1);
 818   cairo_t *cr = cairo_create (dummy);
 819 
 820   cairo_append_path (cr, path);
 821   cairo_path_extents (cr, x1, y1, x2, y2);
 822 
 823   cairo_destroy (cr);
 824   cairo_surface_destroy (dummy);
 825 }
 826 
 827 static void
 828 paint_inset_box_shadow_to_cairo_context (StThemeNode     *node,
 829                                          StShadow        *shadow_spec,
 830                                          cairo_t         *cr,
 831                                          cairo_path_t    *shadow_outline)
 832 {
 833   cairo_surface_t *shadow_surface;
 834   cairo_pattern_t *shadow_pattern;
 835   double extents_x1, extents_y1, extents_x2, extents_y2;
 836   double shrunk_extents_x1, shrunk_extents_y1, shrunk_extents_x2, shrunk_extents_y2;
 837   gboolean fill_exterior;
 838 
 839   g_assert (shadow_spec != NULL);
 840   g_assert (shadow_outline != NULL);
 841 
 842   /* Create the pattern used to create the inset shadow; as the shadow
 843    * should be drawn as if everything outside the outline was opaque,
 844    * we use a temporary surface to draw the background as a solid shape,
 845    * which is inverted when creating the shadow pattern.
 846    */
 847 
 848   /* First we need to find the size of the temporary surface
 849    */
 850   path_extents (shadow_outline,
 851                 &extents_x1, &extents_y1, &extents_x2, &extents_y2);
 852 
 853   /* Shrink the extents by the spread, and offset */
 854   shrunk_extents_x1 = extents_x1 + shadow_spec->xoffset + shadow_spec->spread;
 855   shrunk_extents_y1 = extents_y1 + shadow_spec->yoffset + shadow_spec->spread;
 856   shrunk_extents_x2 = extents_x2 + shadow_spec->xoffset - shadow_spec->spread;
 857   shrunk_extents_y2 = extents_y2 + shadow_spec->yoffset - shadow_spec->spread;
 858 
 859   if (shrunk_extents_x1 >= shrunk_extents_x2 || shrunk_extents_y1 >= shrunk_extents_x2)
 860     {
 861       /* Shadow occupies entire area within border */
 862       shadow_pattern = cairo_pattern_create_rgb (0., 0., 0.);
 863       fill_exterior = FALSE;
 864     }
 865   else
 866     {
 867       /* Bounds of temporary surface */
 868       int surface_x = floor (shrunk_extents_x1);
 869       int surface_y = floor (shrunk_extents_y1);
 870       int surface_width = ceil (shrunk_extents_x2) - surface_x;
 871       int surface_height = ceil (shrunk_extents_y2) - surface_y;
 872 
 873       /* Center of the original path */
 874       double x_center = (extents_x1 + extents_x2) / 2;
 875       double y_center = (extents_y1 + extents_y2) / 2;
 876 
 877       cairo_pattern_t *pattern;
 878       cairo_t *temp_cr;
 879       cairo_matrix_t matrix;
 880 
 881       shadow_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, surface_width, surface_height);
 882       temp_cr = cairo_create (shadow_surface);
 883 
 884       /* Match the coordinates in the temporary context to the parent context */
 885       cairo_translate (temp_cr, - surface_x, - surface_y);
 886 
 887       /* Shadow offset */
 888       cairo_translate (temp_cr, shadow_spec->xoffset, shadow_spec->yoffset);
 889 
 890       /* Scale the path around the center to match the shrunk bounds */
 891       cairo_translate (temp_cr, x_center, y_center);
 892       cairo_scale (temp_cr,
 893                    (shrunk_extents_x2 - shrunk_extents_x1) / (extents_x2 - extents_x1),
 894                    (shrunk_extents_y2 - shrunk_extents_y1) / (extents_y2 - extents_y1));
 895       cairo_translate (temp_cr, - x_center, - y_center);
 896 
 897       cairo_append_path (temp_cr, shadow_outline);
 898       cairo_fill (temp_cr);
 899       cairo_destroy (temp_cr);
 900 
 901       pattern = cairo_pattern_create_for_surface (shadow_surface);
 902       cairo_surface_destroy (shadow_surface);
 903 
 904       /* The pattern needs to be offset back to coordinates in the parent context */
 905       cairo_matrix_init_translate (&matrix, - surface_x, - surface_y);
 906       cairo_pattern_set_matrix (pattern, &matrix);
 907 
 908       shadow_pattern = _st_create_shadow_cairo_pattern (shadow_spec, pattern);
 909       fill_exterior = TRUE;
 910 
 911       cairo_pattern_destroy (pattern);
 912     }
 913 
 914   paint_shadow_pattern_to_cairo_context (shadow_spec,
 915                                          shadow_pattern, fill_exterior,
 916                                          cr,
 917                                          shadow_outline,
 918                                          NULL);
 919 
 920   cairo_pattern_destroy (shadow_pattern);
 921 }
 922 
 923 /* In order for borders to be smoothly blended with non-solid backgrounds,
 924  * we need to use cairo.  This function is a slow fallback path for those
 925  * cases (gradients, background images, etc).
 926  */
 927 static CoglHandle
 928 st_theme_node_prerender_background (StThemeNode *node)
 929 {
 930   StBorderImage *border_image;
 931   CoglHandle texture;
 932   guint radius[4];
 933   int i;
 934   cairo_t *cr;
 935   cairo_surface_t *surface;
 936   StShadow *shadow_spec;
 937   StShadow *box_shadow_spec;
 938   cairo_pattern_t *pattern = NULL;
 939   cairo_path_t *outline_path = NULL;
 940   gboolean draw_solid_background = TRUE;
 941   gboolean background_is_translucent;
 942   gboolean interior_dirty;
 943   gboolean draw_background_image_shadow = FALSE;
 944   gboolean has_visible_outline;
 945   ClutterColor border_color;
 946   int border_width[4];
 947   guint rowstride;
 948   guchar *data;
 949   ClutterActorBox actor_box;
 950   ClutterActorBox paint_box;
 951   cairo_path_t *interior_path = NULL;
 952 
 953   border_image = st_theme_node_get_border_image (node);
 954 
 955   shadow_spec = st_theme_node_get_background_image_shadow (node);
 956   box_shadow_spec = st_theme_node_get_box_shadow (node);
 957 
 958   actor_box.x1 = 0;
 959   actor_box.x2 = node->alloc_width;
 960   actor_box.y1 = 0;
 961   actor_box.y2 = node->alloc_height;
 962 
 963   /* If there's a background image shadow, we
 964    * may need to create an image bigger than the nodes
 965    * allocation
 966    */
 967   st_theme_node_get_background_paint_box (node, &actor_box, &paint_box);
 968 
 969   /* translate the boxes so the paint box is at 0,0
 970   */
 971   actor_box.x1 += - paint_box.x1;
 972   actor_box.x2 += - paint_box.x1;
 973   actor_box.y1 += - paint_box.y1;
 974   actor_box.y2 += - paint_box.y1;
 975 
 976   paint_box.x2 += - paint_box.x1;
 977   paint_box.x1 += - paint_box.x1;
 978   paint_box.y2 += - paint_box.y1;
 979   paint_box.y1 += - paint_box.y1;
 980 
 981   rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32,
 982                                              paint_box.x2 - paint_box.x1);
 983   data = g_new0 (guchar, (paint_box.y2 - paint_box.y1) * rowstride);
 984 
 985   /* We zero initialize the destination memory, so it's fully transparent
 986    * by default.
 987    */
 988   interior_dirty = FALSE;
 989 
 990   surface = cairo_image_surface_create_for_data (data,
 991                                                  CAIRO_FORMAT_ARGB32,
 992                                                  paint_box.x2 - paint_box.x1,
 993                                                  paint_box.y2 - paint_box.y1,
 994                                                  rowstride);
 995   cr = cairo_create (surface);
 996 
 997   /* TODO - support non-uniform border colors */
 998   get_arbitrary_border_color (node, &border_color);
 999 
1000   st_theme_node_reduce_border_radius (node, radius);
1001 
1002   for (i = 0; i < 4; i++)
1003     border_width[i] = st_theme_node_get_border_width (node, i);
1004 
1005   /* Note we don't support translucent background images on top
1006    * of gradients. It's strictly either/or.
1007    */
1008   if (node->background_gradient_type != ST_GRADIENT_NONE)
1009     {
1010       pattern = create_cairo_pattern_of_background_gradient (node);
1011       draw_solid_background = FALSE;
1012 
1013       /* If the gradient has any translucent areas, we need to
1014        * erase the interior region before drawing, so that we show
1015        * what's actually under the gradient and not whatever is
1016        * left over from filling the border, etc.
1017        */
1018       if (node->background_color.alpha < 255 ||
1019           node->background_gradient_end.alpha < 255)
1020         background_is_translucent = TRUE;
1021       else
1022         background_is_translucent = FALSE;
1023     }
1024   else
1025     {
1026       const char *background_image;
1027 
1028       background_image = st_theme_node_get_background_image (node);
1029 
1030       if (background_image != NULL)
1031         {
1032           pattern = create_cairo_pattern_of_background_image (node,
1033                                                               &draw_solid_background);
1034           if (shadow_spec && pattern != NULL)
1035             draw_background_image_shadow = TRUE;
1036         }
1037 
1038       /* We never need to clear the interior region before drawing the
1039        * background image, because it either always fills the entire area
1040        * opaquely, or we draw the solid background behind it.
1041        */
1042       background_is_translucent = FALSE;
1043     }
1044 
1045   if (pattern == NULL)
1046     draw_solid_background = TRUE;
1047 
1048   /* drawing the solid background implicitly clears the interior
1049    * region, so if we're going to draw a solid background before drawing
1050    * the background pattern, then we don't need to bother also clearing the
1051    * background region.
1052    */
1053   if (draw_solid_background)
1054     background_is_translucent = FALSE;
1055 
1056   has_visible_outline = st_theme_node_has_visible_outline (node);
1057 
1058   /* Create a path for the background's outline first */
1059   if (radius[ST_CORNER_TOPLEFT] > 0)
1060     cairo_arc (cr,
1061                actor_box.x1 + radius[ST_CORNER_TOPLEFT],
1062                actor_box.y1 + radius[ST_CORNER_TOPLEFT],
1063                radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
1064   else
1065     cairo_move_to (cr, actor_box.x1, actor_box.y1);
1066   cairo_line_to (cr, actor_box.x2 - radius[ST_CORNER_TOPRIGHT], actor_box.x1);
1067   if (radius[ST_CORNER_TOPRIGHT] > 0)
1068     cairo_arc (cr,
1069                actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
1070                actor_box.x1 + radius[ST_CORNER_TOPRIGHT],
1071                radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
1072   cairo_line_to (cr, actor_box.x2, actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT]);
1073   if (radius[ST_CORNER_BOTTOMRIGHT] > 0)
1074     cairo_arc (cr,
1075                actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
1076                actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
1077                radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
1078   cairo_line_to (cr, actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT], actor_box.y2);
1079   if (radius[ST_CORNER_BOTTOMLEFT] > 0)
1080     cairo_arc (cr,
1081                actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
1082                actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
1083                radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
1084   cairo_close_path (cr);
1085 
1086   outline_path = cairo_copy_path (cr);
1087 
1088   /* If we have a solid border, we fill the outline shape with the border
1089    * color and create the inline shape for the background;
1090    * otherwise the outline shape is filled with the background
1091    * directly
1092    */
1093   if (border_image == NULL &&
1094       (border_width[ST_SIDE_TOP] > 0 ||
1095        border_width[ST_SIDE_RIGHT] > 0 ||
1096        border_width[ST_SIDE_BOTTOM] > 0 ||
1097        border_width[ST_SIDE_LEFT] > 0))
1098     {
1099       cairo_set_source_rgba (cr,
1100                              border_color.red / 255.,
1101                              border_color.green / 255.,
1102                              border_color.blue / 255.,
1103                              border_color.alpha / 255.);
1104       cairo_fill (cr);
1105 
1106       /* We were sloppy when filling in the border, and now the interior
1107        * is filled with the border color, too.
1108        */
1109       interior_dirty = TRUE;
1110 
1111       if (radius[ST_CORNER_TOPLEFT] > MAX(border_width[ST_SIDE_TOP],
1112                                           border_width[ST_SIDE_LEFT]))
1113         elliptical_arc (cr,
1114                         actor_box.x1 + radius[ST_CORNER_TOPLEFT],
1115                         actor_box.y1 + radius[ST_CORNER_TOPLEFT],
1116                         radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_LEFT],
1117                         radius[ST_CORNER_TOPLEFT] - border_width[ST_SIDE_TOP],
1118                         M_PI, 3 * M_PI / 2);
1119       else
1120         cairo_move_to (cr,
1121                        actor_box.x1 + border_width[ST_SIDE_LEFT],
1122                        actor_box.y1 + border_width[ST_SIDE_TOP]);
1123 
1124       cairo_line_to (cr,
1125                      actor_box.x2 - MAX(radius[ST_CORNER_TOPRIGHT], border_width[ST_SIDE_RIGHT]),
1126                      actor_box.y1 + border_width[ST_SIDE_TOP]);
1127 
1128       if (radius[ST_CORNER_TOPRIGHT] > MAX(border_width[ST_SIDE_TOP],
1129                                            border_width[ST_SIDE_RIGHT]))
1130         elliptical_arc (cr,
1131                         actor_box.x2 - radius[ST_CORNER_TOPRIGHT],
1132                         actor_box.y1 + radius[ST_CORNER_TOPRIGHT],
1133                         radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_RIGHT],
1134                         radius[ST_CORNER_TOPRIGHT] - border_width[ST_SIDE_TOP],
1135                         3 * M_PI / 2, 2 * M_PI);
1136       else
1137         cairo_line_to (cr,
1138                        actor_box.x2 - border_width[ST_SIDE_RIGHT],
1139                        actor_box.y1 + border_width[ST_SIDE_TOP]);
1140 
1141       cairo_line_to (cr,
1142                      actor_box.x2 - border_width[ST_SIDE_RIGHT],
1143                      actor_box.y2 - MAX(radius[ST_CORNER_BOTTOMRIGHT], border_width[ST_SIDE_BOTTOM]));
1144 
1145       if (radius[ST_CORNER_BOTTOMRIGHT] > MAX(border_width[ST_SIDE_BOTTOM],
1146                                               border_width[ST_SIDE_RIGHT]))
1147         elliptical_arc (cr,
1148                         actor_box.x2 - radius[ST_CORNER_BOTTOMRIGHT],
1149                         actor_box.y2 - radius[ST_CORNER_BOTTOMRIGHT],
1150                         radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_RIGHT],
1151                         radius[ST_CORNER_BOTTOMRIGHT] - border_width[ST_SIDE_BOTTOM],
1152                         0, M_PI / 2);
1153       else
1154         cairo_line_to (cr,
1155                        actor_box.x2 - border_width[ST_SIDE_RIGHT],
1156                        actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
1157 
1158       cairo_line_to (cr,
1159                      MAX(radius[ST_CORNER_BOTTOMLEFT], border_width[ST_SIDE_LEFT]),
1160                      actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
1161 
1162       if (radius[ST_CORNER_BOTTOMLEFT] > MAX(border_width[ST_SIDE_BOTTOM],
1163                                              border_width[ST_SIDE_LEFT]))
1164         elliptical_arc (cr,
1165                         actor_box.x1 + radius[ST_CORNER_BOTTOMLEFT],
1166                         actor_box.y2 - radius[ST_CORNER_BOTTOMLEFT],
1167                         radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_LEFT],
1168                         radius[ST_CORNER_BOTTOMLEFT] - border_width[ST_SIDE_BOTTOM],
1169                         M_PI / 2, M_PI);
1170       else
1171         cairo_line_to (cr,
1172                        actor_box.x1 + border_width[ST_SIDE_LEFT],
1173                        actor_box.y2 - border_width[ST_SIDE_BOTTOM]);
1174 
1175       cairo_close_path (cr);
1176 
1177       interior_path = cairo_copy_path (cr);
1178 
1179       /* clip drawing to the region inside of the borders
1180        */
1181       cairo_clip (cr);
1182 
1183       /* But fill the pattern as if it started at the edge of outline,
1184        * behind the borders.  This is similar to
1185        * background-clip: border-box; semantics.
1186        */
1187       cairo_append_path (cr, outline_path);
1188     }
1189 
1190   if (interior_dirty && background_is_translucent)
1191     {
1192       cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1193       cairo_fill_preserve (cr);
1194       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1195     }
1196 
1197   if (draw_solid_background)
1198     {
1199       cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1200 
1201       cairo_set_source_rgba (cr,
1202                              node->background_color.red / 255.,
1203                              node->background_color.green / 255.,
1204                              node->background_color.blue / 255.,
1205                              node->background_color.alpha / 255.);
1206       cairo_fill_preserve (cr);
1207       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1208     }
1209 
1210   if (draw_background_image_shadow)
1211     {
1212       paint_background_image_shadow_to_cairo_context (node,
1213                                                       shadow_spec,
1214                                                       pattern,
1215                                                       cr,
1216                                                       interior_path,
1217                                                       has_visible_outline?  outline_path : NULL,
1218                                                       actor_box.x1,
1219                                                       actor_box.y1,
1220                                                       paint_box.x2 - paint_box.x1,
1221                                                       paint_box.y2 - paint_box.y1);
1222       cairo_append_path (cr, outline_path);
1223     }
1224 
1225   cairo_translate (cr, actor_box.x1, actor_box.y1);
1226 
1227   if (pattern != NULL)
1228     {
1229       cairo_set_source (cr, pattern);
1230       cairo_fill (cr);
1231       cairo_pattern_destroy (pattern);
1232     }
1233 
1234   if (box_shadow_spec && box_shadow_spec->inset)
1235     {
1236       paint_inset_box_shadow_to_cairo_context (node,
1237                                                box_shadow_spec,
1238                                                cr,
1239                                                interior_path ? interior_path
1240                                                              : outline_path);
1241     }
1242 
1243   if (outline_path != NULL)
1244     cairo_path_destroy (outline_path);
1245 
1246   if (interior_path != NULL)
1247     cairo_path_destroy (interior_path);
1248 
1249   texture = cogl_texture_new_from_data (paint_box.x2 - paint_box.x1,
1250                                         paint_box.y2 - paint_box.y1,
1251                                         COGL_TEXTURE_NONE,
1252                                         CLUTTER_CAIRO_FORMAT_ARGB32,
1253                                         COGL_PIXEL_FORMAT_ANY,
1254                                         rowstride,
1255                                         data);
1256 
1257   cairo_destroy (cr);
1258   cairo_surface_destroy (surface);
1259   g_free (data);
1260 
1261   return texture;
1262 }
1263 
1264 void
1265 _st_theme_node_free_drawing_state (StThemeNode  *node)
1266 {
1267   int corner_id;
1268 
1269   if (node->background_texture != COGL_INVALID_HANDLE)
1270     cogl_handle_unref (node->background_texture);
1271   if (node->background_material != COGL_INVALID_HANDLE)
1272     cogl_handle_unref (node->background_material);
1273   if (node->background_shadow_material != COGL_INVALID_HANDLE)
1274     cogl_handle_unref (node->background_shadow_material);
1275   if (node->border_slices_texture != COGL_INVALID_HANDLE)
1276     cogl_handle_unref (node->border_slices_texture);
1277   if (node->border_slices_material != COGL_INVALID_HANDLE)
1278     cogl_handle_unref (node->border_slices_material);
1279   if (node->prerendered_texture != COGL_INVALID_HANDLE)
1280     cogl_handle_unref (node->prerendered_texture);
1281   if (node->prerendered_material != COGL_INVALID_HANDLE)
1282     cogl_handle_unref (node->prerendered_material);
1283   if (node->box_shadow_material != COGL_INVALID_HANDLE)
1284     cogl_handle_unref (node->box_shadow_material);
1285 
1286   for (corner_id = 0; corner_id < 4; corner_id++)
1287     if (node->corner_material[corner_id] != COGL_INVALID_HANDLE)
1288       cogl_handle_unref (node->corner_material[corner_id]);
1289 
1290   _st_theme_node_init_drawing_state (node);
1291 }
1292 
1293 void
1294 _st_theme_node_init_drawing_state (StThemeNode *node)
1295 {
1296   int corner_id;
1297 
1298   node->background_texture = COGL_INVALID_HANDLE;
1299   node->background_material = COGL_INVALID_HANDLE;
1300   node->background_shadow_material = COGL_INVALID_HANDLE;
1301   node->box_shadow_material = COGL_INVALID_HANDLE;
1302   node->border_slices_texture = COGL_INVALID_HANDLE;
1303   node->border_slices_material = COGL_INVALID_HANDLE;
1304   node->prerendered_texture = COGL_INVALID_HANDLE;
1305   node->prerendered_material = COGL_INVALID_HANDLE;
1306 
1307   for (corner_id = 0; corner_id < 4; corner_id++)
1308     node->corner_material[corner_id] = COGL_INVALID_HANDLE;
1309 }
1310 
1311 static void st_theme_node_paint_borders (StThemeNode           *node,
1312                                          const ClutterActorBox *box,
1313                                          guint8                 paint_opacity);
1314 
1315 static void
1316 st_theme_node_render_resources (StThemeNode   *node,
1317                                 float          width,
1318                                 float          height)
1319 {
1320   StTextureCache *texture_cache;
1321   StBorderImage *border_image;
1322   gboolean has_border;
1323   gboolean has_border_radius;
1324   gboolean has_inset_box_shadow;
1325   gboolean has_large_corners;
1326   StShadow *box_shadow_spec;
1327   StShadow *background_image_shadow_spec;
1328   const char *background_image;
1329 
1330   g_return_if_fail (width > 0 && height > 0);
1331 
1332   texture_cache = st_texture_cache_get_default ();
1333 
1334   /* FIXME - need to separate this into things that need to be recomputed on
1335    * geometry change versus things that can be cached regardless, such as
1336    * a background image.
1337    */
1338   _st_theme_node_free_drawing_state (node);
1339 
1340   node->alloc_width = width;
1341   node->alloc_height = height;
1342 
1343   _st_theme_node_ensure_background (node);
1344   _st_theme_node_ensure_geometry (node);
1345 
1346   box_shadow_spec = st_theme_node_get_box_shadow (node);
1347   has_inset_box_shadow = box_shadow_spec && box_shadow_spec->inset;
1348 
1349   if (node->border_width[ST_SIDE_TOP] > 0 ||
1350       node->border_width[ST_SIDE_LEFT] > 0 ||
1351       node->border_width[ST_SIDE_RIGHT] > 0 ||
1352       node->border_width[ST_SIDE_BOTTOM] > 0)
1353     has_border = TRUE;
1354   else
1355     has_border = FALSE;
1356 
1357   if (node->border_radius[ST_CORNER_TOPLEFT] > 0 ||
1358       node->border_radius[ST_CORNER_TOPRIGHT] > 0 ||
1359       node->border_radius[ST_CORNER_BOTTOMLEFT] > 0 ||
1360       node->border_radius[ST_CORNER_BOTTOMRIGHT] > 0)
1361     has_border_radius = TRUE;
1362   else
1363     has_border_radius = FALSE;
1364 
1365   /* The cogl code pads each corner to the maximum border radius,
1366    * which results in overlapping corner areas if the radius
1367    * exceeds the actor's halfsize, causing rendering errors.
1368    * Fall back to cairo in these cases. */
1369   has_large_corners = FALSE;
1370 
1371   if (has_border_radius) {
1372     guint border_radius[4];
1373     int corner;
1374 
1375     st_theme_node_reduce_border_radius (node, border_radius);
1376 
1377     for (corner = 0; corner < 4; corner ++) {
1378       if (border_radius[corner] * 2 > height ||
1379           border_radius[corner] * 2 > width) {
1380         has_large_corners = TRUE;
1381         break;
1382       }
1383     }
1384   }
1385 
1386   /* Load referenced images from disk and draw anything we need with cairo now */
1387   background_image = st_theme_node_get_background_image (node);
1388   border_image = st_theme_node_get_border_image (node);
1389 
1390   if (border_image)
1391     {
1392       const char *filename;
1393 
1394       filename = st_border_image_get_filename (border_image);
1395 
1396       node->border_slices_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
1397     }
1398 
1399   if (node->border_slices_texture)
1400     node->border_slices_material = _st_create_texture_material (node->border_slices_texture);
1401   else
1402     node->border_slices_material = COGL_INVALID_HANDLE;
1403 
1404   /* Use cairo to prerender the node if there is a gradient, or
1405    * background image with borders and/or rounded corners,
1406    * or large corners, since we can't do those things
1407    * easily with cogl.
1408    *
1409    * FIXME: if we could figure out ahead of time that a
1410    * background image won't overlap with the node borders,
1411    * then we could use cogl for that case.
1412    */
1413   if ((node->background_gradient_type != ST_GRADIENT_NONE)
1414       || (has_inset_box_shadow && (has_border || node->background_color.alpha > 0))
1415       || (background_image && (has_border || has_border_radius))
1416       || has_large_corners)
1417     node->prerendered_texture = st_theme_node_prerender_background (node);
1418 
1419   if (node->prerendered_texture)
1420     node->prerendered_material = _st_create_texture_material (node->prerendered_texture);
1421   else
1422     node->prerendered_material = COGL_INVALID_HANDLE;
1423 
1424   if (box_shadow_spec && !has_inset_box_shadow)
1425     {
1426       if (node->border_slices_texture != COGL_INVALID_HANDLE)
1427         node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
1428                                                                 node->border_slices_texture);
1429       else if (node->prerendered_texture != COGL_INVALID_HANDLE)
1430         node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
1431                                                                 node->prerendered_texture);
1432       else if (node->background_color.alpha > 0 || has_border)
1433         {
1434           CoglHandle buffer, offscreen;
1435           int texture_width = ceil (width);
1436           int texture_height = ceil (height);
1437 
1438           buffer = cogl_texture_new_with_size (texture_width,
1439                                                texture_height,
1440                                                COGL_TEXTURE_NO_SLICING,
1441                                                COGL_PIXEL_FORMAT_ANY);
1442           offscreen = cogl_offscreen_new_to_texture (buffer);
1443 
1444           if (offscreen != COGL_INVALID_HANDLE)
1445             {
1446               ClutterActorBox box = { 0, 0, width, height };
1447               CoglColor clear_color;
1448 
1449               cogl_push_framebuffer (offscreen);
1450               cogl_ortho (0, width, height, 0, 0, 1.0);
1451 
1452               cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
1453               cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
1454 
1455               st_theme_node_paint_borders (node, &box, 0xFF);
1456               cogl_pop_framebuffer ();
1457               cogl_handle_unref (offscreen);
1458 
1459               node->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
1460                                                                       buffer);
1461             }
1462           cogl_handle_unref (buffer);
1463         }
1464     }
1465 
1466   background_image_shadow_spec = st_theme_node_get_background_image_shadow (node);
1467   if (background_image != NULL && !has_border && !has_border_radius)
1468     {
1469       node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image);
1470       node->background_material = _st_create_texture_material (node->background_texture);
1471 
1472       if (node->background_repeat)
1473         cogl_material_set_layer_wrap_mode (node->background_material, 0, COGL_MATERIAL_WRAP_MODE_REPEAT);
1474 
1475       if (background_image_shadow_spec)
1476         {
1477           node->background_shadow_material = _st_create_shadow_material (background_image_shadow_spec,
1478                                                                          node->background_texture);
1479         }
1480     }
1481 
1482   node->corner_material[ST_CORNER_TOPLEFT] =
1483     st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT);
1484   node->corner_material[ST_CORNER_TOPRIGHT] =
1485     st_theme_node_lookup_corner (node, ST_CORNER_TOPRIGHT);
1486   node->corner_material[ST_CORNER_BOTTOMRIGHT] =
1487     st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMRIGHT);
1488   node->corner_material[ST_CORNER_BOTTOMLEFT] =
1489     st_theme_node_lookup_corner (node, ST_CORNER_BOTTOMLEFT);
1490 }
1491 
1492 static void
1493 paint_material_with_opacity (CoglHandle       material,
1494                              ClutterActorBox *box,
1495                              ClutterActorBox *coords,
1496                              guint8           paint_opacity)
1497 {
1498   cogl_material_set_color4ub (material,
1499                               paint_opacity, paint_opacity, paint_opacity, paint_opacity);
1500 
1501   cogl_set_source (material);
1502 
1503   if (coords)
1504     cogl_rectangle_with_texture_coords (box->x1, box->y1, box->x2, box->y2,
1505                                         coords->x1, coords->y1, coords->x2, coords->y2);
1506   else
1507     cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
1508 }
1509 
1510 static void
1511 st_theme_node_paint_borders (StThemeNode           *node,
1512                              const ClutterActorBox *box,
1513                              guint8                 paint_opacity)
1514 {
1515   float width, height;
1516   int border_width[4];
1517   guint border_radius[4];
1518   int max_border_radius = 0;
1519   int max_width_radius[4];
1520   int corner_id, side_id;
1521   ClutterColor border_color;
1522   guint8 alpha;
1523 
1524   width = box->x2 - box->x1;
1525   height = box->y2 - box->y1;
1526 
1527   /* TODO - support non-uniform border colors */
1528   get_arbitrary_border_color (node, &border_color);
1529 
1530   for (side_id = 0; side_id < 4; side_id++)
1531     border_width[side_id] = st_theme_node_get_border_width(node, side_id);
1532 
1533   st_theme_node_reduce_border_radius (node, border_radius);
1534 
1535   for (corner_id = 0; corner_id < 4; corner_id++)
1536     {
1537       guint border_width_1, border_width_2;
1538 
1539       st_theme_node_get_corner_border_widths (node, corner_id,
1540                                               &border_width_1, &border_width_2);
1541 
1542       if (border_radius[corner_id] > max_border_radius)
1543         max_border_radius = border_radius[corner_id];
1544       max_width_radius[corner_id] = MAX(MAX(border_width_1, border_width_2),
1545                                         border_radius[corner_id]);
1546     }
1547 
1548   /* borders */
1549   if (border_width[ST_SIDE_TOP] > 0 ||
1550       border_width[ST_SIDE_RIGHT] > 0 ||
1551       border_width[ST_SIDE_BOTTOM] > 0 ||
1552       border_width[ST_SIDE_LEFT] > 0)
1553     {
1554       ClutterColor effective_border;
1555       gboolean skip_corner_1, skip_corner_2;
1556       float x1, y1, x2, y2;
1557 
1558       over (&border_color, &node->background_color, &effective_border);
1559       alpha = paint_opacity * effective_border.alpha / 255;
1560 
1561       if (alpha > 0)
1562         {
1563           cogl_set_source_color4ub (effective_border.red,
1564                                     effective_border.green,
1565                                     effective_border.blue,
1566                                     alpha);
1567 
1568           /* NORTH */
1569           skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
1570           skip_corner_2 = border_radius[ST_CORNER_TOPRIGHT] > 0;
1571 
1572           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT] : 0;
1573           y1 = 0;
1574           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_TOPRIGHT] : width;
1575           y2 = border_width[ST_SIDE_TOP];
1576           cogl_rectangle (x1, y1, x2, y2);
1577 
1578           /* EAST */
1579           skip_corner_1 = border_radius[ST_CORNER_TOPRIGHT] > 0;
1580           skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
1581 
1582           x1 = width - border_width[ST_SIDE_RIGHT];
1583           y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPRIGHT]
1584                              : border_width[ST_SIDE_TOP];
1585           x2 = width;
1586           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMRIGHT]
1587                              : height - border_width[ST_SIDE_BOTTOM];
1588           cogl_rectangle (x1, y1, x2, y2);
1589 
1590           /* SOUTH */
1591           skip_corner_1 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
1592           skip_corner_2 = border_radius[ST_CORNER_BOTTOMRIGHT] > 0;
1593 
1594           x1 = skip_corner_1 ? max_width_radius[ST_CORNER_BOTTOMLEFT] : 0;
1595           y1 = height - border_width[ST_SIDE_BOTTOM];
1596           x2 = skip_corner_2 ? width - max_width_radius[ST_CORNER_BOTTOMRIGHT]
1597                              : width;
1598           y2 = height;
1599           cogl_rectangle (x1, y1, x2, y2);
1600 
1601           /* WEST */
1602           skip_corner_1 = border_radius[ST_CORNER_TOPLEFT] > 0;
1603           skip_corner_2 = border_radius[ST_CORNER_BOTTOMLEFT] > 0;
1604 
1605           x1 = 0;
1606           y1 = skip_corner_1 ? max_width_radius[ST_CORNER_TOPLEFT]
1607                              : border_width[ST_SIDE_TOP];
1608           x2 = border_width[ST_SIDE_LEFT];
1609           y2 = skip_corner_2 ? height - max_width_radius[ST_CORNER_BOTTOMLEFT]
1610                              : height - border_width[ST_SIDE_BOTTOM];
1611           cogl_rectangle (x1, y1, x2, y2);
1612         }
1613     }
1614 
1615   /* corners */
1616   if (max_border_radius > 0 && paint_opacity > 0)
1617     {
1618       for (corner_id = 0; corner_id < 4; corner_id++)
1619         {
1620           if (node->corner_material[corner_id] == COGL_INVALID_HANDLE)
1621             continue;
1622 
1623           cogl_material_set_color4ub (node->corner_material[corner_id],
1624                                       paint_opacity, paint_opacity,
1625                                       paint_opacity, paint_opacity);
1626           cogl_set_source (node->corner_material[corner_id]);
1627 
1628           switch (corner_id)
1629             {
1630               case ST_CORNER_TOPLEFT:
1631                 cogl_rectangle_with_texture_coords (0, 0,
1632                                                     max_width_radius[ST_CORNER_TOPLEFT], max_width_radius[ST_CORNER_TOPLEFT],
1633                                                     0, 0, 0.5, 0.5);
1634                 break;
1635               case ST_CORNER_TOPRIGHT:
1636                 cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_TOPRIGHT], 0,
1637                                                     width, max_width_radius[ST_CORNER_TOPRIGHT],
1638                                                     0.5, 0, 1, 0.5);
1639                 break;
1640               case ST_CORNER_BOTTOMRIGHT:
1641                 cogl_rectangle_with_texture_coords (width - max_width_radius[ST_CORNER_BOTTOMRIGHT], height - max_width_radius[ST_CORNER_BOTTOMRIGHT],
1642                                                     width, height,
1643                                                     0.5, 0.5, 1, 1);
1644                 break;
1645               case ST_CORNER_BOTTOMLEFT:
1646                 cogl_rectangle_with_texture_coords (0, height - max_width_radius[ST_CORNER_BOTTOMLEFT],
1647                                                     max_width_radius[ST_CORNER_BOTTOMLEFT], height,
1648                                                     0, 0.5, 0.5, 1);
1649                 break;
1650             }
1651         }
1652     }
1653 
1654   /* background color */
1655   alpha = paint_opacity * node->background_color.alpha / 255;
1656   if (alpha > 0)
1657     {
1658       cogl_set_source_color4ub (node->background_color.red,
1659                                 node->background_color.green,
1660                                 node->background_color.blue,
1661                                 alpha);
1662 
1663       /* We add padding to each corner, so that all corners end up as if they
1664        * had a border-radius of max_border_radius, which allows us to treat
1665        * corners as uniform further on.
1666        */
1667       for (corner_id = 0; corner_id < 4; corner_id++)
1668         {
1669           float verts[8];
1670           int n_rects;
1671 
1672           /* corner texture does not need padding */
1673           if (max_border_radius == border_radius[corner_id])
1674             continue;
1675 
1676           n_rects = border_radius[corner_id] == 0 ? 1 : 2;
1677 
1678           switch (corner_id)
1679             {
1680               case ST_CORNER_TOPLEFT:
1681                 verts[0] = border_width[ST_SIDE_LEFT];
1682                 verts[1] = MAX(border_radius[corner_id],
1683                                border_width[ST_SIDE_TOP]);
1684                 verts[2] = max_border_radius;
1685                 verts[3] = max_border_radius;
1686                 if (n_rects == 2)
1687                   {
1688                     verts[4] = MAX(border_radius[corner_id],
1689                                    border_width[ST_SIDE_LEFT]);
1690                     verts[5] = border_width[ST_SIDE_TOP];
1691                     verts[6] = max_border_radius;
1692                     verts[7] = MAX(border_radius[corner_id],
1693                                    border_width[ST_SIDE_TOP]);
1694                   }
1695                 break;
1696               case ST_CORNER_TOPRIGHT:
1697                 verts[0] = width - max_border_radius;
1698                 verts[1] = MAX(border_radius[corner_id],
1699                                border_width[ST_SIDE_TOP]);
1700                 verts[2] = width - border_width[ST_SIDE_RIGHT];
1701                 verts[3] = max_border_radius;
1702                 if (n_rects == 2)
1703                   {
1704                     verts[4] = width - max_border_radius;
1705                     verts[5] = border_width[ST_SIDE_TOP];
1706                     verts[6] = width - MAX(border_radius[corner_id],
1707                                            border_width[ST_SIDE_RIGHT]);
1708                     verts[7] = MAX(border_radius[corner_id],
1709                                    border_width[ST_SIDE_TOP]);
1710                   }
1711                 break;
1712               case ST_CORNER_BOTTOMRIGHT:
1713                 verts[0] = width - max_border_radius;
1714                 verts[1] = height - max_border_radius;
1715                 verts[2] = width - border_width[ST_SIDE_RIGHT];
1716                 verts[3] = height - MAX(border_radius[corner_id],
1717                                         border_width[ST_SIDE_BOTTOM]);
1718                 if (n_rects == 2)
1719                   {
1720                     verts[4] = width - max_border_radius;
1721                     verts[5] = height - MAX(border_radius[corner_id],
1722                                             border_width[ST_SIDE_BOTTOM]);
1723                     verts[6] = width - MAX(border_radius[corner_id],
1724                                            border_width[ST_SIDE_RIGHT]);
1725                     verts[7] = height - border_width[ST_SIDE_BOTTOM];
1726                   }
1727                 break;
1728               case ST_CORNER_BOTTOMLEFT:
1729                 verts[0] = border_width[ST_SIDE_LEFT];
1730                 verts[1] = height - max_border_radius;
1731                 verts[2] = max_border_radius;
1732                 verts[3] = height - MAX(border_radius[corner_id],
1733                                         border_width[ST_SIDE_BOTTOM]);
1734                 if (n_rects == 2)
1735                   {
1736                     verts[4] = MAX(border_radius[corner_id],
1737                                    border_width[ST_SIDE_LEFT]);
1738                     verts[5] = height - MAX(border_radius[corner_id],
1739                                             border_width[ST_SIDE_BOTTOM]);
1740                     verts[6] = max_border_radius;
1741                     verts[7] = height - border_width[ST_SIDE_BOTTOM];
1742                   }
1743                 break;
1744             }
1745           cogl_rectangles (verts, n_rects);
1746         }
1747 
1748       /* Once we've drawn the borders and corners, if the corners are bigger
1749        * then the border width, the remaining area is shaped like
1750        *
1751        *  ########
1752        * ##########
1753        * ##########
1754        *  ########
1755        *
1756        * We draw it in at most 3 pieces - first the top and bottom if
1757        * necessary, then the main rectangle
1758        */
1759       if (max_border_radius > border_width[ST_SIDE_TOP])
1760         cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
1761                         border_width[ST_SIDE_TOP],
1762                         width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
1763                         max_border_radius);
1764       if (max_border_radius > border_width[ST_SIDE_BOTTOM])
1765         cogl_rectangle (MAX(max_border_radius, border_width[ST_SIDE_LEFT]),
1766                         height - max_border_radius,
1767                         width - MAX(max_border_radius, border_width[ST_SIDE_RIGHT]),
1768                         height - border_width[ST_SIDE_BOTTOM]);
1769 
1770       cogl_rectangle (border_width[ST_SIDE_LEFT],
1771                       MAX(border_width[ST_SIDE_TOP], max_border_radius),
1772                       width - border_width[ST_SIDE_RIGHT],
1773                       height - MAX(border_width[ST_SIDE_BOTTOM], max_border_radius));
1774     }
1775 }
1776 
1777 static void
1778 st_theme_node_paint_sliced_border_image (StThemeNode           *node,
1779                                          const ClutterActorBox *box,
1780                                          guint8                 paint_opacity)
1781 {
1782   gfloat ex, ey;
1783   gfloat tx1, ty1, tx2, ty2;
1784   gint border_left, border_right, border_top, border_bottom;
1785   float img_width, img_height;
1786   StBorderImage *border_image;
1787   CoglHandle material;
1788 
1789   border_image = st_theme_node_get_border_image (node);
1790   g_assert (border_image != NULL);
1791 
1792   st_border_image_get_borders (border_image,
1793                                &border_left, &border_right, &border_top, &border_bottom);
1794 
1795   img_width = cogl_texture_get_width (node->border_slices_texture);
1796   img_height = cogl_texture_get_height (node->border_slices_texture);
1797 
1798   tx1 = border_left / img_width;
1799   tx2 = (img_width - border_right) / img_width;
1800   ty1 = border_top / img_height;
1801   ty2 = (img_height - border_bottom) / img_height;
1802 
1803   ex = node->alloc_width - border_right;
1804   if (ex < 0)
1805     ex = border_right;           /* FIXME ? */
1806 
1807   ey = node->alloc_height - border_bottom;
1808   if (ey < 0)
1809     ey = border_bottom;          /* FIXME ? */
1810 
1811   material = node->border_slices_material;
1812   cogl_material_set_color4ub (material,
1813                               paint_opacity, paint_opacity, paint_opacity, paint_opacity);
1814 
1815   cogl_set_source (material);
1816 
1817   {
1818     float rectangles[] =
1819     {
1820       /* top left corner */
1821       0, 0, border_left, border_top,
1822       0.0, 0.0,
1823       tx1, ty1,
1824 
1825       /* top middle */
1826       border_left, 0, ex, border_top,
1827       tx1, 0.0,
1828       tx2, ty1,
1829 
1830       /* top right */
1831       ex, 0, node->alloc_width, border_top,
1832       tx2, 0.0,
1833       1.0, ty1,
1834 
1835       /* mid left */
1836       0, border_top, border_left, ey,
1837       0.0, ty1,
1838       tx1, ty2,
1839 
1840       /* center */
1841       border_left, border_top, ex, ey,
1842       tx1, ty1,
1843       tx2, ty2,
1844 
1845       /* mid right */
1846       ex, border_top, node->alloc_width, ey,
1847       tx2, ty1,
1848       1.0, ty2,
1849 
1850       /* bottom left */
1851       0, ey, border_left, node->alloc_height,
1852       0.0, ty2,
1853       tx1, 1.0,
1854 
1855       /* bottom center */
1856       border_left, ey, ex, node->alloc_height,
1857       tx1, ty2,
1858       tx2, 1.0,
1859 
1860       /* bottom right */
1861       ex, ey, node->alloc_width, node->alloc_height,
1862       tx2, ty2,
1863       1.0, 1.0
1864     };
1865 
1866     cogl_rectangles_with_texture_coords (rectangles, 9);
1867   }
1868 }
1869 
1870 static void
1871 st_theme_node_paint_outline (StThemeNode           *node,
1872                              const ClutterActorBox *box,
1873                              guint8                 paint_opacity)
1874 
1875 {
1876   float width, height;
1877   int outline_width;
1878   ClutterColor outline_color, effective_outline;
1879 
1880   width = box->x2 - box->x1;
1881   height = box->y2 - box->y1;
1882 
1883   outline_width = st_theme_node_get_outline_width (node);
1884   if (outline_width == 0)
1885     return;
1886 
1887   st_theme_node_get_outline_color (node, &outline_color);
1888   over (&outline_color, &node->background_color, &effective_outline);
1889 
1890   cogl_set_source_color4ub (effective_outline.red,
1891                             effective_outline.green,
1892                             effective_outline.blue,
1893                             paint_opacity * effective_outline.alpha / 255);
1894 
1895   /* The outline is drawn just outside the border, which means just
1896    * outside the allocation box. This means that in some situations
1897    * involving clip_to_allocation or the screen edges, you won't be
1898    * able to see the outline. In practice, it works well enough.
1899    */
1900 
1901   /* NORTH */
1902   cogl_rectangle (-outline_width, -outline_width,
1903                   width + outline_width, 0);
1904 
1905   /* EAST */
1906   cogl_rectangle (width, 0,
1907                   width + outline_width, height);
1908 
1909   /* SOUTH */
1910   cogl_rectangle (-outline_width, height,
1911                   width + outline_width, height + outline_width);
1912 
1913   /* WEST */
1914   cogl_rectangle (-outline_width, 0,
1915                   0, height);
1916 }
1917 
1918 void
1919 st_theme_node_paint (StThemeNode           *node,
1920                      const ClutterActorBox *box,
1921                      guint8                 paint_opacity)
1922 {
1923   float width, height;
1924   ClutterActorBox allocation;
1925 
1926   /* Some things take an ActorBox, some things just width/height */
1927   width = box->x2 - box->x1;
1928   height = box->y2 - box->y1;
1929   allocation.x1 = allocation.y1 = 0;
1930   allocation.x2 = width;
1931   allocation.y2 = height;
1932 
1933   if (width <= 0 || height <= 0)
1934     return;
1935 
1936   if (node->alloc_width != width || node->alloc_height != height)
1937     st_theme_node_render_resources (node, width, height);
1938 
1939   /* Rough notes about the relationship of borders and backgrounds in CSS3;
1940    * see http://www.w3.org/TR/css3-background/ for more accurate details.
1941    *
1942    * - Things are drawn in 4 layers, from the bottom:
1943    *     Background color
1944    *     Background image
1945    *     Border color or border image
1946    *     Content
1947    * - The background color, gradient and image extend to and are clipped by
1948    *   the edge of the border area, so will be rounded if the border is
1949    *   rounded. (CSS3 background-clip property modifies this)
1950    * - The border image replaces what would normally be drawn by the border
1951    * - The border image is not clipped by a rounded border-radius
1952    * - The border radius rounds the background even if the border is
1953    *   zero width or a border image is being used.
1954    *
1955    * Deviations from the above as implemented here:
1956    *  - The combination of border image and a non-zero border radius is
1957    *    not supported; the background color will be drawn with square
1958    *    corners.
1959    *  - The background image is drawn above the border color, not below it.
1960    *  - We clip the background image to the inside edges of the border
1961    *    instead of the outside edges of the border (but position the image
1962    *    such that it's aligned to the outside edges)
1963    */
1964 
1965   if (node->box_shadow_material)
1966     _st_paint_shadow_with_opacity (node->box_shadow,
1967                                    node->box_shadow_material,
1968                                    &allocation,
1969                                    paint_opacity);
1970 
1971   if (node->prerendered_material != COGL_INVALID_HANDLE ||
1972       node->border_slices_material != COGL_INVALID_HANDLE)
1973     {
1974       if (node->prerendered_material != COGL_INVALID_HANDLE)
1975         {
1976           ClutterActorBox paint_box;
1977 
1978           st_theme_node_get_background_paint_box (node,
1979                                                   &allocation,
1980                                                   &paint_box);
1981 
1982           paint_material_with_opacity (node->prerendered_material,
1983                                        &paint_box,
1984                                        NULL,
1985                                        paint_opacity);
1986         }
1987 
1988       if (node->border_slices_material != COGL_INVALID_HANDLE)
1989         st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity);
1990     }
1991   else
1992     {
1993       st_theme_node_paint_borders (node, box, paint_opacity);
1994     }
1995 
1996   st_theme_node_paint_outline (node, box, paint_opacity);
1997 
1998   if (node->background_texture != COGL_INVALID_HANDLE)
1999     {
2000       ClutterActorBox background_box;
2001       ClutterActorBox texture_coords;
2002       gboolean has_visible_outline;
2003 
2004       /* If the node doesn't have an opaque or repeating background or
2005        * a border then we let its background image shadows leak out,
2006        * but otherwise we clip it.
2007        */
2008       has_visible_outline = st_theme_node_has_visible_outline (node);
2009 
2010       get_background_position (node, &allocation, &background_box, &texture_coords);
2011 
2012       if (has_visible_outline || node->background_repeat)
2013         cogl_clip_push_rectangle (allocation.x1, allocation.y1, allocation.x2, allocation.y2);
2014 
2015       /* CSS based drop shadows
2016        *
2017        * Drop shadows in ST are modelled after the CSS3 box-shadow property;
2018        * see http://www.css3.info/preview/box-shadow/ for a detailed description.
2019        *
2020        * While the syntax of the property is mostly identical - we do not support
2021        * multiple shadows and allow for a more liberal placement of the color
2022        * parameter - its interpretation defers significantly in that the shadow's
2023        * shape is not determined by the bounding box, but by the CSS background
2024        * image. The drop shadows are allowed to escape the nodes allocation if
2025        * there is nothing (like a border, or the edge of the background color)
2026        * to logically confine it.
2027        */
2028       if (node->background_shadow_material != COGL_INVALID_HANDLE)
2029         _st_paint_shadow_with_opacity (node->background_image_shadow,
2030                                        node->background_shadow_material,
2031                                        &background_box,
2032                                        paint_opacity);
2033 
2034       paint_material_with_opacity (node->background_material,
2035                                    &background_box,
2036                                    &texture_coords,
2037                                    paint_opacity);
2038 
2039       if (has_visible_outline || node->background_repeat)
2040         cogl_clip_pop ();
2041     }
2042 }
2043 
2044 /**
2045  * st_theme_node_copy_cached_paint_state:
2046  * @node: a #StThemeNode
2047  * @other: a different #StThemeNode
2048  *
2049  * Copy cached painting state from @other to @node. This function can be used to
2050  * optimize redrawing cached background images when the style on an element changess
2051  * in a way that doesn't affect background drawing. This function must only be called
2052  * if st_theme_node_paint_equal (node, other) returns %TRUE.
2053  */
2054 void
2055 st_theme_node_copy_cached_paint_state (StThemeNode *node,
2056                                        StThemeNode *other)
2057 {
2058   int corner_id;
2059 
2060   g_return_if_fail (ST_IS_THEME_NODE (node));
2061   g_return_if_fail (ST_IS_THEME_NODE (other));
2062 
2063   /* Check omitted for speed: */
2064   /* g_return_if_fail (st_theme_node_paint_equal (node, other)); */
2065 
2066   _st_theme_node_free_drawing_state (node);
2067 
2068   node->alloc_width = other->alloc_width;
2069   node->alloc_height = other->alloc_height;
2070 
2071   if (other->background_shadow_material)
2072     node->background_shadow_material = cogl_handle_ref (other->background_shadow_material);
2073   if (other->box_shadow_material)
2074     node->box_shadow_material = cogl_handle_ref (other->box_shadow_material);
2075   if (other->background_texture)
2076     node->background_texture = cogl_handle_ref (other->background_texture);
2077   if (other->background_material)
2078     node->background_material = cogl_handle_ref (other->background_material);
2079   if (other->border_slices_texture)
2080     node->border_slices_texture = cogl_handle_ref (other->border_slices_texture);
2081   if (other->border_slices_material)
2082     node->border_slices_material = cogl_handle_ref (other->border_slices_material);
2083   if (other->prerendered_texture)
2084     node->prerendered_texture = cogl_handle_ref (other->prerendered_texture);
2085   if (other->prerendered_material)
2086     node->prerendered_material = cogl_handle_ref (other->prerendered_material);
2087   for (corner_id = 0; corner_id < 4; corner_id++)
2088     if (other->corner_material[corner_id])
2089       node->corner_material[corner_id] = cogl_handle_ref (other->corner_material[corner_id]);
2090 }
2091 
2092 void
2093 st_theme_node_invalidate_paint_state (StThemeNode *node)
2094 {
2095   node->alloc_width = 0;
2096   node->alloc_height = 0;
2097 }