1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
5
* Create and cache shadow textures for abritrary window shapes
7
* Copyright 2010 Red Hat, Inc.
9
* This program is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public License as
11
* published by the Free Software Foundation; either version 2 of the
12
* License, or (at your option) any later version.
14
* This program is distributed in the hope that it will be useful, but
15
* WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
* General Public License for more details.
19
* You should have received a copy of the GNU General Public License
20
* along with this program; if not, write to the Free Software
21
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28
#include "cogl-utils.h"
29
#include "meta-shadow-factory-private.h"
30
#include "region-utils.h"
32
/* This file implements blurring the shape of a window to produce a
33
* shadow texture. The details are discussed below; a quick summary
34
* of the optimizations we use:
36
* - If the window shape is along the lines of a rounded rectangle -
37
* a rectangular center portion with stuff at the corners - then
38
* the blur of this - the shadow - can also be represented as a
39
* 9-sliced texture and the same texture can be used for different
42
* - We use the fact that a Gaussian blur is separable to do a
43
* 2D blur as 1D blur of the rows followed by a 1D blur of the
46
* - For better cache efficiency, we blur rows, transpose the image
47
* in blocks, blur rows again, and then transpose back.
49
* - We approximate the 1D gaussian blur as 3 successive box filters.
52
typedef struct _MetaShadowCacheKey MetaShadowCacheKey;
53
typedef struct _MetaShadowClassInfo MetaShadowClassInfo;
55
struct _MetaShadowCacheKey
57
MetaWindowShape *shape;
66
MetaShadowFactory *factory;
67
MetaShadowCacheKey key;
71
/* The outer order is the distance the shadow extends outside the window
72
* shape; the inner border is the unscaled portion inside the window
76
int outer_border_right;
77
int inner_border_right;
78
int outer_border_bottom;
79
int inner_border_bottom;
80
int outer_border_left;
81
int inner_border_left;
83
guint scale_width : 1;
84
guint scale_height : 1;
87
struct _MetaShadowClassInfo
89
const char *name; /* const so we can reuse for static definitions */
90
MetaShadowParams focused;
91
MetaShadowParams unfocused;
94
struct _MetaShadowFactory
96
GObject parent_instance;
98
/* MetaShadowCacheKey => MetaShadow; the shadows are not referenced
99
* by the factory, they are simply removed from the table when freed */
102
/* class name => MetaShadowClassInfo */
103
GHashTable *shadow_classes;
106
struct _MetaShadowFactoryClass
108
GObjectClass parent_class;
118
static guint signals[LAST_SIGNAL] = { 0 };
120
/* The first element in this array also defines the default parameters
121
* for newly created classes */
122
MetaShadowClassInfo default_shadow_classes[] = {
123
{ "normal", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
124
{ "dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
125
{ "modal_dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
126
{ "utility", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
127
{ "border", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
128
{ "menu", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
130
{ "popup-menu", { 6, -1, 0, 4, 255 }, { 6, -1, 0, 4, 255 } },
132
{ "dropdown-menu", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } },
133
{ "attached", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } }
136
G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT);
139
meta_shadow_cache_key_hash (gconstpointer val)
141
const MetaShadowCacheKey *key = val;
143
return 59 * key->radius + 67 * key->top_fade + 73 * meta_window_shape_hash (key->shape);
147
meta_shadow_cache_key_equal (gconstpointer a,
150
const MetaShadowCacheKey *key_a = a;
151
const MetaShadowCacheKey *key_b = b;
153
return (key_a->radius == key_b->radius && key_a->top_fade == key_b->top_fade &&
154
meta_window_shape_equal (key_a->shape, key_b->shape));
158
meta_shadow_ref (MetaShadow *shadow)
166
meta_shadow_unref (MetaShadow *shadow)
169
if (shadow->ref_count == 0)
173
g_hash_table_remove (shadow->factory->shadows,
177
meta_window_shape_unref (shadow->key.shape);
178
cogl_handle_unref (shadow->texture);
179
cogl_handle_unref (shadow->material);
181
g_slice_free (MetaShadow, shadow);
187
* @window_x: x position of the region to paint a shadow for
188
* @window_y: y position of the region to paint a shadow for
189
* @window_width: actual width of the region to paint a shadow for
190
* @window_height: actual height of the region to paint a shadow for
191
* @clip: (allow-none): if non-%NULL specifies the visible portion
192
* of the shadow. Drawing won't be strictly clipped to this region
193
* but it will be used to optimize what is drawn.
195
* Paints the shadow at the given position, for the specified actual
196
* size of the region. (Since a #MetaShadow can be shared between
197
* different sizes with the same extracted #MetaWindowShape the
198
* size needs to be passed in here.)
201
meta_shadow_paint (MetaShadow *shadow,
207
cairo_region_t *clip)
209
float texture_width = cogl_texture_get_width (shadow->texture);
210
float texture_height = cogl_texture_get_height (shadow->texture);
218
cogl_material_set_color4ub (shadow->material,
219
opacity, opacity, opacity, opacity);
221
cogl_set_source (shadow->material);
223
if (shadow->scale_width)
228
src_x[1] = (shadow->inner_border_left + shadow->outer_border_left) / texture_width;
229
src_x[2] = (texture_width - (shadow->inner_border_right + shadow->outer_border_right)) / texture_width;
232
dest_x[0] = window_x - shadow->outer_border_left;
233
dest_x[1] = window_x + shadow->inner_border_left;
234
dest_x[2] = window_x + window_width - shadow->inner_border_right;
235
dest_x[3] = window_x + window_width + shadow->outer_border_right;
244
dest_x[0] = window_x - shadow->outer_border_left;
245
dest_x[1] = window_x + window_width + shadow->outer_border_right;
248
if (shadow->scale_height)
253
src_y[1] = (shadow->inner_border_top + shadow->outer_border_top) / texture_height;
254
src_y[2] = (texture_height - (shadow->inner_border_bottom + shadow->outer_border_bottom)) / texture_height;
257
dest_y[0] = window_y - shadow->outer_border_top;
258
dest_y[1] = window_y + shadow->inner_border_top;
259
dest_y[2] = window_y + window_height - shadow->inner_border_bottom;
260
dest_y[3] = window_y + window_height + shadow->outer_border_bottom;
269
dest_y[0] = window_y - shadow->outer_border_top;
270
dest_y[1] = window_y + window_height + shadow->outer_border_bottom;
273
for (j = 0; j < n_y; j++)
275
cairo_rectangle_int_t dest_rect;
276
dest_rect.y = dest_y[j];
277
dest_rect.height = dest_y[j + 1] - dest_y[j];
279
for (i = 0; i < n_x; i++)
281
cairo_region_overlap_t overlap;
283
dest_rect.x = dest_x[i];
284
dest_rect.width = dest_x[i + 1] - dest_x[i];
287
overlap = cairo_region_contains_rectangle (clip, &dest_rect);
289
overlap = CAIRO_REGION_OVERLAP_PART;
291
if (overlap != CAIRO_REGION_OVERLAP_OUT)
292
cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j],
293
dest_x[i + 1], dest_y[j + 1],
295
src_x[i + 1], src_y[j + 1]);
301
* meta_shadow_get_bounds:
302
* @shadow: a #MetaShadow
303
* @window_x: x position of the region to paint a shadow for
304
* @window_y: y position of the region to paint a shadow for
305
* @window_width: actual width of the region to paint a shadow for
306
* @window_height: actual height of the region to paint a shadow for
308
* Computes the bounds of the pixels that will be affected by
309
* meta_shadow_paints()
312
meta_shadow_get_bounds (MetaShadow *shadow,
317
cairo_rectangle_int_t *bounds)
319
bounds->x = window_x - shadow->outer_border_left;
320
bounds->y = window_x - shadow->outer_border_top;
321
bounds->width = window_width + shadow->outer_border_left + shadow->outer_border_right;
322
bounds->height = window_height + shadow->outer_border_top + shadow->outer_border_bottom;
326
meta_shadow_class_info_free (MetaShadowClassInfo *class_info)
328
g_free ((char *)class_info->name);
329
g_slice_free (MetaShadowClassInfo, class_info);
333
meta_shadow_factory_init (MetaShadowFactory *factory)
337
factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash,
338
meta_shadow_cache_key_equal);
340
factory->shadow_classes = g_hash_table_new_full (g_str_hash,
343
(GDestroyNotify)meta_shadow_class_info_free);
345
for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++)
347
MetaShadowClassInfo *class_info = g_slice_new (MetaShadowClassInfo);
349
*class_info = default_shadow_classes[i];
350
class_info->name = g_strdup (class_info->name);
352
g_hash_table_insert (factory->shadow_classes,
353
(char *)class_info->name, class_info);
358
meta_shadow_factory_finalize (GObject *object)
360
MetaShadowFactory *factory = META_SHADOW_FACTORY (object);
364
/* Detach from the shadows in the table so we won't try to
365
* remove them when they're freed. */
366
g_hash_table_iter_init (&iter, factory->shadows);
367
while (g_hash_table_iter_next (&iter, &key, &value))
369
MetaShadow *shadow = key;
370
shadow->factory = NULL;
373
g_hash_table_destroy (factory->shadows);
374
g_hash_table_destroy (factory->shadow_classes);
376
G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object);
380
meta_shadow_factory_class_init (MetaShadowFactoryClass *klass)
382
GObjectClass *object_class = G_OBJECT_CLASS (klass);
384
object_class->finalize = meta_shadow_factory_finalize;
387
g_signal_new ("changed",
388
G_TYPE_FROM_CLASS (object_class),
392
g_cclosure_marshal_VOID__VOID,
397
meta_shadow_factory_new (void)
399
return g_object_new (META_TYPE_SHADOW_FACTORY, NULL);
403
* meta_shadow_factory_get_default:
405
* Return value: (transfer none): the global singleton shadow factory
408
meta_shadow_factory_get_default (void)
410
static MetaShadowFactory *factory;
413
factory = meta_shadow_factory_new ();
418
/* We emulate a 1D Gaussian blur by using 3 consecutive box blurs;
419
* this produces a result that's within 3% of the original and can be
420
* implemented much faster for large filter sizes because of the
421
* efficiency of implementation of a box blur. Idea and formula
422
* for choosing the box blur size come from:
424
* http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
426
* The 2D blur is then done by blurring the rows, flipping the
427
* image and blurring the columns. (This is possible because the
428
* Gaussian kernel is separable - it's the product of a horizontal
429
* blur and a vertical blur.)
432
get_box_filter_size (int radius)
434
return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI)));
437
/* The "spread" of the filter is the number of pixels from an original
438
* pixel that it's blurred image extends. (A no-op blur that doesn't
439
* blur would have a spread of 0.) See comment in blur_rows() for why the
440
* odd and even cases are different
443
get_shadow_spread (int radius)
445
int d = get_box_filter_size (radius);
450
return 3 * (d / 2) - 1;
453
/* This applies a single box blur pass to a horizontal range of pixels;
454
* since the box blur has the same weight for all pixels, we can
455
* implement an efficient sliding window algorithm where we add
456
* in pixels coming into the window from the right and remove
457
* them when they leave the windw to the left.
459
* d is the filter width; for even d shift indicates how the blurred
460
* result is aligned with the original - does ' x ' go to ' yy' (shift=1)
461
* or 'yy ' (shift=-1)
464
blur_xspan (guchar *row,
479
offset = (d - shift) / 2;
481
/* All the conditionals in here look slow, but the branches will
482
* be well predicted and there are enough different possibilities
483
* that trying to write this as a series of unconditional loops
484
* is hard and not an obvious win. The main slow down here seems
485
* to be the integer division for pixel; one possible optimization
486
* would be to accumulate into two 16-bit integer buffers and
487
* only divide down after all three passes. (SSE parallel implementation
488
* of the divide step is possible.)
490
for (i = x0 - d + offset; i < x1 + offset; i++)
492
if (i >= 0 && i < row_width)
495
if (i >= x0 + offset)
500
tmp_buffer[i - offset] = (sum + d / 2) / d;
504
memcpy(row + x0, tmp_buffer + x0, x1 - x0);
508
blur_rows (cairo_region_t *convolve_region,
520
tmp_buffer = g_malloc (buffer_width);
522
n_rectangles = cairo_region_num_rectangles (convolve_region);
523
for (i = 0; i < n_rectangles; i++)
525
cairo_rectangle_int_t rect;
527
cairo_region_get_rectangle (convolve_region, i, &rect);
529
for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
531
guchar *row = buffer + j * buffer_width;
532
int x0 = x_offset + rect.x;
533
int x1 = x0 + rect.width;
535
/* We want to produce a symmetric blur that spreads a pixel
536
* equally far to the left and right. If d is odd that happens
537
* naturally, but for d even, we approximate by using a blur
538
* on either side and then a centered blur of size d + 1.
539
* (techique also from the SVG specification)
543
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
544
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
545
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
549
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 1);
550
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, -1);
551
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d + 1, 0);
560
fade_bytes (guchar *bytes,
565
guint32 multiplier = (distance * 0x10000 + 0x8000) / total;
568
for (i = 0; i < width; i++)
569
bytes[i] = (bytes[i] * multiplier) >> 16;
572
/* Swaps width and height. Either swaps in-place and returns the original
573
* buffer or allocates a new buffer, frees the original buffer and returns
577
flip_buffer (guchar *buffer,
581
/* Working in blocks increases cache efficiency, compared to reading
582
* or writing an entire column at once */
583
#define BLOCK_SIZE 16
589
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
590
for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE)
592
int max_j = MIN(j0 + BLOCK_SIZE, height);
593
int max_i = MIN(i0 + BLOCK_SIZE, width);
598
for (j = j0; j < max_j; j++)
599
for (i = i0; i < j; i++)
601
guchar tmp = buffer[j * width + i];
602
buffer[j * width + i] = buffer[i * width + j];
603
buffer[i * width + j] = tmp;
608
for (j = j0; j < max_j; j++)
609
for (i = i0; i < max_i; i++)
611
guchar tmp = buffer[j * width + i];
612
buffer[j * width + i] = buffer[i * width + j];
613
buffer[i * width + j] = tmp;
622
guchar *new_buffer = g_malloc (height * width);
625
for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
626
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
628
int max_j = MIN(j0 + BLOCK_SIZE, height);
629
int max_i = MIN(i0 + BLOCK_SIZE, width);
632
for (i = i0; i < max_i; i++)
633
for (j = j0; j < max_j; j++)
634
new_buffer[i * height + j] = buffer[j * width + i];
645
make_shadow (MetaShadow *shadow,
646
cairo_region_t *region)
648
int d = get_box_filter_size (shadow->key.radius);
649
int spread = get_shadow_spread (shadow->key.radius);
650
cairo_rectangle_int_t extents;
651
cairo_region_t *row_convolve_region;
652
cairo_region_t *column_convolve_region;
658
int n_rectangles, j, k;
660
cairo_region_get_extents (region, &extents);
662
/* In the case where top_fade >= 0 and the portion above the top
663
* edge of the shape will be cropped, it seems like we could create
664
* a smaller buffer and omit the top portion, but actually, in our
665
* multi-pass blur algorithm, the blur into the area above the window
666
* in the first pass will contribute back to the final pixel values
667
* for the top pixels, so we create a buffer as if we weren't cropping
668
* and only crop when creating the CoglTexture.
671
buffer_width = extents.width + 2 * spread;
672
buffer_height = extents.height + 2 * spread;
674
/* Round up so we have aligned rows/columns */
675
buffer_width = (buffer_width + 3) & ~3;
676
buffer_height = (buffer_height + 3) & ~3;
678
/* Square buffer allows in-place swaps, which are roughly 70% faster, but we
679
* don't want to over-allocate too much memory.
681
if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4)
682
buffer_height = buffer_width;
683
if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4)
684
buffer_width = buffer_height;
686
buffer = g_malloc0 (buffer_width * buffer_height);
688
/* Blurring with multiple box-blur passes is fast, but (especially for
689
* large shadow sizes) we can improve efficiency by restricting the blur
690
* to the region that actually needs to be blurred.
692
row_convolve_region = meta_make_border_region (region, spread, spread, FALSE);
693
column_convolve_region = meta_make_border_region (region, 0, spread, TRUE);
695
/* Offsets between coordinates of the regions and coordinates in the buffer */
699
/* Step 1: unblurred image */
700
n_rectangles = cairo_region_num_rectangles (region);
701
for (k = 0; k < n_rectangles; k++)
703
cairo_rectangle_int_t rect;
705
cairo_region_get_rectangle (region, k, &rect);
706
for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
707
memset (buffer + buffer_width * j + x_offset + rect.x, 255, rect.width);
710
/* Step 2: swap rows and columns */
711
buffer = flip_buffer (buffer, buffer_width, buffer_height);
713
/* Step 3: blur rows (really columns) */
714
blur_rows (column_convolve_region, y_offset, x_offset,
715
buffer, buffer_height, buffer_width,
718
/* Step 4: swap rows and columns */
719
buffer = flip_buffer (buffer, buffer_height, buffer_width);
721
/* Step 5: blur rows */
722
blur_rows (row_convolve_region, x_offset, y_offset,
723
buffer, buffer_width, buffer_height,
726
/* Step 6: fade out the top, if applicable */
727
if (shadow->key.top_fade >= 0)
729
for (j = y_offset; j < y_offset + MIN (shadow->key.top_fade, extents.height + shadow->outer_border_bottom); j++)
730
fade_bytes(buffer + j * buffer_width, buffer_width, j - y_offset, shadow->key.top_fade);
733
/* We offset the passed in pixels to crop off the extra area we allocated at the top
734
* in the case of top_fade >= 0. We also account for padding at the left for symmetry
735
* though that doesn't currently occur.
737
shadow->texture = cogl_texture_new_from_data (shadow->outer_border_left + extents.width + shadow->outer_border_right,
738
shadow->outer_border_top + extents.height + shadow->outer_border_bottom,
740
COGL_PIXEL_FORMAT_A_8,
741
COGL_PIXEL_FORMAT_ANY,
744
(y_offset - shadow->outer_border_top) * buffer_width +
745
(x_offset - shadow->outer_border_left)));
747
cairo_region_destroy (row_convolve_region);
748
cairo_region_destroy (column_convolve_region);
751
shadow->material = meta_create_texture_material (shadow->texture);
754
static MetaShadowParams *
755
get_shadow_params (MetaShadowFactory *factory,
756
const char *class_name,
760
MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes,
762
if (class_info == NULL)
766
class_info = g_slice_new0 (MetaShadowClassInfo);
767
*class_info = default_shadow_classes[0];
768
class_info->name = g_strdup (class_info->name);
770
g_hash_table_insert (factory->shadow_classes,
771
(char *)class_info->name, class_info);
775
class_info = &default_shadow_classes[0];
780
return &class_info->focused;
782
return &class_info->unfocused;
786
* meta_shadow_factory_get_shadow:
787
* @factory: a #MetaShadowFactory
788
* @shape: the size-invariant shape of the window's region
789
* @width: the actual width of the window's region
790
* @height: the actual height of the window's region
791
* @class_name: name of the class of window shadows
792
* @focused: whether the shadow is for a focused window
794
* Gets the appropriate shadow object for drawing shadows for the
795
* specified window shape. The region that we are shadowing is specified
796
* as a combination of a size-invariant extracted shape and the size.
797
* In some cases, the same shadow object can be shared between sizes;
798
* in other cases a different shadow object is used for each size.
800
* Return value: (transfer full): a newly referenced #MetaShadow; unref with
801
* meta_shadow_unref()
804
meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
805
MetaWindowShape *shape,
808
const char *class_name,
811
MetaShadowParams *params;
812
MetaShadowCacheKey key;
814
cairo_region_t *region;
816
int shape_border_top, shape_border_right, shape_border_bottom, shape_border_left;
817
int inner_border_top, inner_border_right, inner_border_bottom, inner_border_left;
818
int outer_border_top, outer_border_right, outer_border_bottom, outer_border_left;
819
gboolean scale_width, scale_height;
821
int center_width, center_height;
823
g_return_val_if_fail (META_IS_SHADOW_FACTORY (factory), NULL);
824
g_return_val_if_fail (shape != NULL, NULL);
826
/* Using a single shadow texture for different window sizes only works
827
* when there is a central scaled area that is greater than twice
828
* the spread of the gaussian blur we are applying to get to the
830
* ********* ***********
831
* /----------\ *###########* *#############*
832
* | | => **#*********#** => **#***********#**
833
* | | **#** **#** **#** **#**
834
* | | **#*********#** **#***********#**
835
* \----------/ *###########* *#############*
836
* ********** ************
837
* Original Blur Stretched Blur
839
* For smaller sizes, we create a separate shadow image for each size;
840
* since we assume that there will be little reuse, we don't try to
841
* cache such images but just recreate them. (Since the current cache
842
* policy is to only keep around referenced shadows, there wouldn't
843
* be any harm in caching them, it would just make the book-keeping
846
* In the case where we are fading a the top, that also has to fit
847
* within the top unscaled border.
850
params = get_shadow_params (factory, class_name, focused, FALSE);
852
spread = get_shadow_spread (params->radius);
853
meta_window_shape_get_borders (shape,
856
&shape_border_bottom,
859
inner_border_top = MAX (shape_border_top + spread, params->top_fade);
860
outer_border_top = params->top_fade >= 0 ? 0 : spread;
861
inner_border_right = shape_border_right + spread;
862
outer_border_right = spread;
863
inner_border_bottom = shape_border_bottom + spread;
864
outer_border_bottom = spread;
865
inner_border_left = shape_border_left + spread;
866
outer_border_left = spread;
868
scale_width = inner_border_left + inner_border_right <= width;
869
scale_height = inner_border_top + inner_border_bottom <= height;
870
cacheable = scale_width && scale_height;
875
key.radius = params->radius;
876
key.top_fade = params->top_fade;
878
shadow = g_hash_table_lookup (factory->shadows, &key);
880
return meta_shadow_ref (shadow);
883
shadow = g_slice_new0 (MetaShadow);
885
shadow->ref_count = 1;
886
shadow->factory = factory;
887
shadow->key.shape = meta_window_shape_ref (shape);
888
shadow->key.radius = params->radius;
889
shadow->key.top_fade = params->top_fade;
891
shadow->outer_border_top = outer_border_top;
892
shadow->inner_border_top = inner_border_top;
893
shadow->outer_border_right = outer_border_right;
894
shadow->inner_border_right = inner_border_right;
895
shadow->outer_border_bottom = outer_border_bottom;
896
shadow->inner_border_bottom = inner_border_bottom;
897
shadow->outer_border_left = outer_border_left;
898
shadow->inner_border_left = inner_border_left;
900
shadow->scale_width = scale_width;
902
center_width = inner_border_left + inner_border_right - (shape_border_left + shape_border_right);
904
center_width = width - (shape_border_left + shape_border_right);
906
shadow->scale_height = scale_height;
908
center_height = inner_border_top + inner_border_bottom - (shape_border_top + shape_border_bottom);
910
center_height = height - (shape_border_top + shape_border_bottom);
912
g_assert (center_width >= 0 && center_height >= 0);
914
region = meta_window_shape_to_region (shape, center_width, center_height);
915
make_shadow (shadow, region);
917
cairo_region_destroy (region);
920
g_hash_table_insert (factory->shadows, &shadow->key, shadow);
926
* meta_shadow_factory_set_params:
927
* @factory: a #MetaShadowFactory
928
* @class_name: name of the class of shadow to set the params for.
929
* the default shadow classes are the names of the different
930
* theme frame types (normal, dialog, modal_dialog, utility,
931
* border, menu, attached) and in addition, popup-menu
933
* @focused: whether the shadow is for a focused window
934
* @params: new parameter values
936
* Updates the shadow parameters for a particular class of shadows
937
* for either the focused or unfocused state. If the class name
938
* does not name an existing class, a new class will be created
939
* (the other focus state for that class will have default values
943
meta_shadow_factory_set_params (MetaShadowFactory *factory,
944
const char *class_name,
946
MetaShadowParams *params)
948
MetaShadowParams *stored_params;
950
g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
951
g_return_if_fail (class_name != NULL);
952
g_return_if_fail (params != NULL);
953
g_return_if_fail (params->radius >= 0);
955
stored_params = get_shadow_params (factory, class_name, focused, TRUE);
957
*stored_params = *params;
959
g_signal_emit (factory, signals[CHANGED], 0);
963
* meta_shadow_factory_get_params:
964
* @factory: a #MetaShadowFactory
965
* @class_name: name of the class of shadow to get the params for
966
* @focused: whether the shadow is for a focused window
967
* @params: (out caller-allocates): location to store the current parameter values
969
* Gets the shadow parameters for a particular class of shadows
970
* for either the focused or unfocused state. If the class name
971
* does not name an existing class, default values will be returned
972
* without printing an error.
975
meta_shadow_factory_get_params (MetaShadowFactory *factory,
976
const char *class_name,
978
MetaShadowParams *params)
980
MetaShadowParams *stored_params;
982
g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
983
g_return_if_fail (class_name != NULL);
985
stored_params = get_shadow_params (factory, class_name, focused, FALSE);
988
*params = *stored_params;