1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * Copyright 2010 Red Hat, Inc.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * SECTION:meta-shadow-factory
21 * @title: MetaShadowFactory
22 * @short_description: Create and cache shadow textures for arbitrary window shapes
23 */
24
25 #include "config.h"
26
27 #include <math.h>
28 #include <string.h>
29
30 #include "compositor/cogl-utils.h"
31 #include "compositor/region-utils.h"
32 #include "meta/meta-shadow-factory.h"
33 #include "meta/util.h"
34
35 /* This file implements blurring the shape of a window to produce a
36 * shadow texture. The details are discussed below; a quick summary
37 * of the optimizations we use:
38 *
39 * - If the window shape is along the lines of a rounded rectangle -
40 * a rectangular center portion with stuff at the corners - then
41 * the blur of this - the shadow - can also be represented as a
42 * 9-sliced texture and the same texture can be used for different
43 * size.
44 *
45 * - We use the fact that a Gaussian blur is separable to do a
46 * 2D blur as 1D blur of the rows followed by a 1D blur of the
47 * columns.
48 *
49 * - For better cache efficiency, we blur rows, transpose the image
50 * in blocks, blur rows again, and then transpose back.
51 *
52 * - We approximate the 1D gaussian blur as 3 successive box filters.
53 */
54
55 typedef struct _MetaShadowCacheKey MetaShadowCacheKey;
56 typedef struct _MetaShadowClassInfo MetaShadowClassInfo;
57
58 struct _MetaShadowCacheKey
59 {
60 MetaWindowShape *shape;
61 int radius;
62 int top_fade;
63 };
64
65 struct _MetaShadow
66 {
67 int ref_count;
68
69 MetaShadowFactory *factory;
70 MetaShadowCacheKey key;
71 CoglTexture *texture;
72 CoglPipeline *pipeline;
73
74 /* The outer order is the distance the shadow extends outside the window
75 * shape; the inner border is the unscaled portion inside the window
76 * shape */
77 int outer_border_top;
78 int inner_border_top;
79 int outer_border_right;
80 int inner_border_right;
81 int outer_border_bottom;
82 int inner_border_bottom;
83 int outer_border_left;
84 int inner_border_left;
85
86 guint scale_width : 1;
87 guint scale_height : 1;
88 };
89
90 struct _MetaShadowClassInfo
91 {
92 const char *name; /* const so we can reuse for static definitions */
93 MetaShadowParams focused;
94 MetaShadowParams unfocused;
95 };
96
97 struct _MetaShadowFactory
98 {
99 GObject parent_instance;
100
101 /* MetaShadowCacheKey => MetaShadow; the shadows are not referenced
102 * by the factory, they are simply removed from the table when freed */
103 GHashTable *shadows;
104
105 /* class name => MetaShadowClassInfo */
106 GHashTable *shadow_classes;
107 };
108
109 enum
110 {
111 CHANGED,
112
113 LAST_SIGNAL
114 };
115
116 static guint signals[LAST_SIGNAL] = { 0 };
117
118 /* The first element in this array also defines the default parameters
119 * for newly created classes */
120 MetaShadowClassInfo default_shadow_classes[] = {
121 { "normal", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
122 { "dialog", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
123 { "modal_dialog", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
124 { "utility", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
125 { "border", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
126 { "menu", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } },
127
128 { "popup-menu", { 1, -1, 0, 0, 128 }, { 1, -1, 0, 0, 128 } },
129 { "dropdown-menu", { 1, -1, 0, 0, 128 }, { 1, -1, 0, 0, 128 } },
130
131 { "attached", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }
132 };
133
134 G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT);
135
136 static guint
meta_shadow_cache_key_hash(gconstpointer val)137 meta_shadow_cache_key_hash (gconstpointer val)
138 {
139 const MetaShadowCacheKey *key = val;
140
141 return 59 * key->radius + 67 * key->top_fade + 73 * meta_window_shape_hash (key->shape);
142 }
143
144 static gboolean
meta_shadow_cache_key_equal(gconstpointer a,gconstpointer b)145 meta_shadow_cache_key_equal (gconstpointer a,
146 gconstpointer b)
147 {
148 const MetaShadowCacheKey *key_a = a;
149 const MetaShadowCacheKey *key_b = b;
150
151 return (key_a->radius == key_b->radius && key_a->top_fade == key_b->top_fade &&
152 meta_window_shape_equal (key_a->shape, key_b->shape));
153 }
154
155 MetaShadow *
meta_shadow_ref(MetaShadow * shadow)156 meta_shadow_ref (MetaShadow *shadow)
157 {
158 shadow->ref_count++;
159
160 return shadow;
161 }
162
163 void
meta_shadow_unref(MetaShadow * shadow)164 meta_shadow_unref (MetaShadow *shadow)
165 {
166 shadow->ref_count--;
167 if (shadow->ref_count == 0)
168 {
169 if (shadow->factory)
170 {
171 g_hash_table_remove (shadow->factory->shadows,
172 &shadow->key);
173 }
174
175 meta_window_shape_unref (shadow->key.shape);
176 cogl_object_unref (shadow->texture);
177 cogl_object_unref (shadow->pipeline);
178
179 g_free (shadow);
180 }
181 }
182
183 /**
184 * meta_shadow_paint:
185 * @window_x: x position of the region to paint a shadow for
186 * @window_y: y position of the region to paint a shadow for
187 * @window_width: actual width of the region to paint a shadow for
188 * @window_height: actual height of the region to paint a shadow for
189 * @clip: (nullable): if non-%NULL specifies the visible portion
190 * of the shadow.
191 * @clip_strictly: if %TRUE, drawing will be clipped strictly
192 * to @clip, otherwise, it will be only used to optimize
193 * drawing.
194 *
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.)
199 */
200 void
meta_shadow_paint(MetaShadow * shadow,CoglFramebuffer * framebuffer,int window_x,int window_y,int window_width,int window_height,guint8 opacity,cairo_region_t * clip,gboolean clip_strictly)201 meta_shadow_paint (MetaShadow *shadow,
202 CoglFramebuffer *framebuffer,
203 int window_x,
204 int window_y,
205 int window_width,
206 int window_height,
207 guint8 opacity,
208 cairo_region_t *clip,
209 gboolean clip_strictly)
210 {
211 float texture_width = cogl_texture_get_width (shadow->texture);
212 float texture_height = cogl_texture_get_height (shadow->texture);
213 int i, j;
214 float src_x[4];
215 float src_y[4];
216 int dest_x[4];
217 int dest_y[4];
218 int n_x, n_y;
219
220 if (clip && cairo_region_is_empty (clip))
221 return;
222
223 cogl_pipeline_set_color4ub (shadow->pipeline,
224 opacity, opacity, opacity, opacity);
225
226 if (shadow->scale_width)
227 {
228 n_x = 3;
229
230 src_x[0] = 0.0;
231 src_x[1] = (shadow->inner_border_left + shadow->outer_border_left) / texture_width;
232 src_x[2] = (texture_width - (shadow->inner_border_right + shadow->outer_border_right)) / texture_width;
233 src_x[3] = 1.0;
234
235 dest_x[0] = window_x - shadow->outer_border_left;
236 dest_x[1] = window_x + shadow->inner_border_left;
237 dest_x[2] = window_x + window_width - shadow->inner_border_right;
238 dest_x[3] = window_x + window_width + shadow->outer_border_right;
239 }
240 else
241 {
242 n_x = 1;
243
244 src_x[0] = 0.0;
245 src_x[1] = 1.0;
246
247 dest_x[0] = window_x - shadow->outer_border_left;
248 dest_x[1] = window_x + window_width + shadow->outer_border_right;
249 }
250
251 if (shadow->scale_height)
252 {
253 n_y = 3;
254
255 src_y[0] = 0.0;
256 src_y[1] = (shadow->inner_border_top + shadow->outer_border_top) / texture_height;
257 src_y[2] = (texture_height - (shadow->inner_border_bottom + shadow->outer_border_bottom)) / texture_height;
258 src_y[3] = 1.0;
259
260 dest_y[0] = window_y - shadow->outer_border_top;
261 dest_y[1] = window_y + shadow->inner_border_top;
262 dest_y[2] = window_y + window_height - shadow->inner_border_bottom;
263 dest_y[3] = window_y + window_height + shadow->outer_border_bottom;
264 }
265 else
266 {
267 n_y = 1;
268
269 src_y[0] = 0.0;
270 src_y[1] = 1.0;
271
272 dest_y[0] = window_y - shadow->outer_border_top;
273 dest_y[1] = window_y + window_height + shadow->outer_border_bottom;
274 }
275
276 for (j = 0; j < n_y; j++)
277 {
278 cairo_rectangle_int_t dest_rect;
279 dest_rect.y = dest_y[j];
280 dest_rect.height = dest_y[j + 1] - dest_y[j];
281
282 if (dest_rect.height == 0)
283 continue;
284
285 for (i = 0; i < n_x; i++)
286 {
287 cairo_region_overlap_t overlap;
288
289 dest_rect.x = dest_x[i];
290 dest_rect.width = dest_x[i + 1] - dest_x[i];
291
292 if (dest_rect.width == 0)
293 continue;
294
295 if (clip)
296 overlap = cairo_region_contains_rectangle (clip, &dest_rect);
297 else
298 overlap = CAIRO_REGION_OVERLAP_IN;
299
300 if (overlap == CAIRO_REGION_OVERLAP_OUT)
301 continue;
302
303 /* There's quite a bit of overhead from allocating a new
304 * region in order to find an exact intersection and
305 * generating more geometry - we make the assumption that
306 * unless we have to clip strictly it will be cheaper to
307 * just draw the entire rectangle.
308 */
309 if (overlap == CAIRO_REGION_OVERLAP_IN ||
310 (overlap == CAIRO_REGION_OVERLAP_PART && !clip_strictly))
311 {
312 cogl_framebuffer_draw_textured_rectangle (framebuffer,
313 shadow->pipeline,
314 dest_x[i], dest_y[j],
315 dest_x[i + 1], dest_y[j + 1],
316 src_x[i], src_y[j],
317 src_x[i + 1], src_y[j + 1]);
318 }
319 else if (overlap == CAIRO_REGION_OVERLAP_PART)
320 {
321 cairo_region_t *intersection;
322 int n_rectangles, k;
323
324 intersection = cairo_region_create_rectangle (&dest_rect);
325 cairo_region_intersect (intersection, clip);
326
327 n_rectangles = cairo_region_num_rectangles (intersection);
328 for (k = 0; k < n_rectangles; k++)
329 {
330 cairo_rectangle_int_t rect;
331 float src_x1, src_x2, src_y1, src_y2;
332
333 cairo_region_get_rectangle (intersection, k, &rect);
334
335 /* Separately linear interpolate X and Y coordinates in the source
336 * based on the destination X and Y coordinates */
337
338 src_x1 = (src_x[i] * (dest_rect.x + dest_rect.width - rect.x) +
339 src_x[i + 1] * (rect.x - dest_rect.x)) / dest_rect.width;
340 src_x2 = (src_x[i] * (dest_rect.x + dest_rect.width - (rect.x + rect.width)) +
341 src_x[i + 1] * (rect.x + rect.width - dest_rect.x)) / dest_rect.width;
342
343 src_y1 = (src_y[j] * (dest_rect.y + dest_rect.height - rect.y) +
344 src_y[j + 1] * (rect.y - dest_rect.y)) / dest_rect.height;
345 src_y2 = (src_y[j] * (dest_rect.y + dest_rect.height - (rect.y + rect.height)) +
346 src_y[j + 1] * (rect.y + rect.height - dest_rect.y)) / dest_rect.height;
347
348 cogl_framebuffer_draw_textured_rectangle (framebuffer,
349 shadow->pipeline,
350 rect.x, rect.y,
351 rect.x + rect.width, rect.y + rect.height,
352 src_x1, src_y1, src_x2, src_y2);
353 }
354
355 cairo_region_destroy (intersection);
356 }
357 }
358 }
359 }
360
361 /**
362 * meta_shadow_get_bounds:
363 * @shadow: a #MetaShadow
364 * @window_x: x position of the region to paint a shadow for
365 * @window_y: y position of the region to paint a shadow for
366 * @window_width: actual width of the region to paint a shadow for
367 * @window_height: actual height of the region to paint a shadow for
368 *
369 * Computes the bounds of the pixels that will be affected by
370 * meta_shadow_paint()
371 */
372 void
meta_shadow_get_bounds(MetaShadow * shadow,int window_x,int window_y,int window_width,int window_height,cairo_rectangle_int_t * bounds)373 meta_shadow_get_bounds (MetaShadow *shadow,
374 int window_x,
375 int window_y,
376 int window_width,
377 int window_height,
378 cairo_rectangle_int_t *bounds)
379 {
380 bounds->x = window_x - shadow->outer_border_left;
381 bounds->y = window_y - shadow->outer_border_top;
382 bounds->width = window_width + shadow->outer_border_left + shadow->outer_border_right;
383 bounds->height = window_height + shadow->outer_border_top + shadow->outer_border_bottom;
384 }
385
386 static void
meta_shadow_class_info_free(MetaShadowClassInfo * class_info)387 meta_shadow_class_info_free (MetaShadowClassInfo *class_info)
388 {
389 g_free ((char *)class_info->name);
390 g_free (class_info);
391 }
392
393 static void
meta_shadow_factory_init(MetaShadowFactory * factory)394 meta_shadow_factory_init (MetaShadowFactory *factory)
395 {
396 guint i;
397
398 factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash,
399 meta_shadow_cache_key_equal);
400
401 factory->shadow_classes = g_hash_table_new_full (g_str_hash,
402 g_str_equal,
403 NULL,
404 (GDestroyNotify)meta_shadow_class_info_free);
405
406 for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++)
407 {
408 MetaShadowClassInfo *class_info = g_new0 (MetaShadowClassInfo, 1);
409
410 *class_info = default_shadow_classes[i];
411 class_info->name = g_strdup (class_info->name);
412
413 g_hash_table_insert (factory->shadow_classes,
414 (char *)class_info->name, class_info);
415 }
416 }
417
418 static void
meta_shadow_factory_finalize(GObject * object)419 meta_shadow_factory_finalize (GObject *object)
420 {
421 MetaShadowFactory *factory = META_SHADOW_FACTORY (object);
422 GHashTableIter iter;
423 gpointer key, value;
424
425 /* Detach from the shadows in the table so we won't try to
426 * remove them when they're freed. */
427 g_hash_table_iter_init (&iter, factory->shadows);
428 while (g_hash_table_iter_next (&iter, &key, &value))
429 {
430 MetaShadow *shadow = key;
431 shadow->factory = NULL;
432 }
433
434 g_hash_table_destroy (factory->shadows);
435 g_hash_table_destroy (factory->shadow_classes);
436
437 G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object);
438 }
439
440 static void
meta_shadow_factory_class_init(MetaShadowFactoryClass * klass)441 meta_shadow_factory_class_init (MetaShadowFactoryClass *klass)
442 {
443 GObjectClass *object_class = G_OBJECT_CLASS (klass);
444
445 object_class->finalize = meta_shadow_factory_finalize;
446
447 signals[CHANGED] =
448 g_signal_new ("changed",
449 G_TYPE_FROM_CLASS (object_class),
450 G_SIGNAL_RUN_LAST,
451 0,
452 NULL, NULL, NULL,
453 G_TYPE_NONE, 0);
454 }
455
456 MetaShadowFactory *
meta_shadow_factory_new(void)457 meta_shadow_factory_new (void)
458 {
459 return g_object_new (META_TYPE_SHADOW_FACTORY, NULL);
460 }
461
462 /**
463 * meta_shadow_factory_get_default:
464 *
465 * Return value: (transfer none): the global singleton shadow factory
466 */
467 MetaShadowFactory *
meta_shadow_factory_get_default(void)468 meta_shadow_factory_get_default (void)
469 {
470 static MetaShadowFactory *factory;
471
472 if (factory == NULL)
473 factory = meta_shadow_factory_new ();
474
475 return factory;
476 }
477
478 /* We emulate a 1D Gaussian blur by using 3 consecutive box blurs;
479 * this produces a result that's within 3% of the original and can be
480 * implemented much faster for large filter sizes because of the
481 * efficiency of implementation of a box blur. Idea and formula
482 * for choosing the box blur size come from:
483 *
484 * http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
485 *
486 * The 2D blur is then done by blurring the rows, flipping the
487 * image and blurring the columns. (This is possible because the
488 * Gaussian kernel is separable - it's the product of a horizontal
489 * blur and a vertical blur.)
490 */
491 static int
get_box_filter_size(int radius)492 get_box_filter_size (int radius)
493 {
494 return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI)));
495 }
496
497 /* The "spread" of the filter is the number of pixels from an original
498 * pixel that it's blurred image extends. (A no-op blur that doesn't
499 * blur would have a spread of 0.) See comment in blur_rows() for why the
500 * odd and even cases are different
501 */
502 static int
get_shadow_spread(int radius)503 get_shadow_spread (int radius)
504 {
505 int d;
506
507 if (radius == 0)
508 return 0;
509
510 d = get_box_filter_size (radius);
511
512 if (d % 2 == 1)
513 return 3 * (d / 2);
514 else
515 return 3 * (d / 2) - 1;
516 }
517
518 /* This applies a single box blur pass to a horizontal range of pixels;
519 * since the box blur has the same weight for all pixels, we can
520 * implement an efficient sliding window algorithm where we add
521 * in pixels coming into the window from the right and remove
522 * them when they leave the windw to the left.
523 *
524 * d is the filter width; for even d shift indicates how the blurred
525 * result is aligned with the original - does ' x ' go to ' yy' (shift=1)
526 * or 'yy ' (shift=-1)
527 */
528 static void
blur_xspan(guchar * row,guchar * tmp_buffer,int row_width,int x0,int x1,int d,int shift)529 blur_xspan (guchar *row,
530 guchar *tmp_buffer,
531 int row_width,
532 int x0,
533 int x1,
534 int d,
535 int shift)
536 {
537 int offset;
538 int sum = 0;
539 int i;
540
541 if (d % 2 == 1)
542 offset = d / 2;
543 else
544 offset = (d - shift) / 2;
545
546 /* All the conditionals in here look slow, but the branches will
547 * be well predicted and there are enough different possibilities
548 * that trying to write this as a series of unconditional loops
549 * is hard and not an obvious win. The main slow down here seems
550 * to be the integer division per pixel; one possible optimization
551 * would be to accumulate into two 16-bit integer buffers and
552 * only divide down after all three passes. (SSE parallel implementation
553 * of the divide step is possible.)
554 */
555 for (i = x0 - d + offset; i < x1 + offset; i++)
556 {
557 if (i >= 0 && i < row_width)
558 sum += row[i];
559
560 if (i >= x0 + offset)
561 {
562 if (i >= d)
563 sum -= row[i - d];
564
565 tmp_buffer[i - offset] = (sum + d / 2) / d;
566 }
567 }
568
569 memcpy (row + x0, tmp_buffer + x0, x1 - x0);
570 }
571
572 static void
blur_rows(cairo_region_t * convolve_region,int x_offset,int y_offset,guchar * buffer,int buffer_width,int buffer_height,int d)573 blur_rows (cairo_region_t *convolve_region,
574 int x_offset,
575 int y_offset,
576 guchar *buffer,
577 int buffer_width,
578 int buffer_height,
579 int d)
580 {
581 int i, j;
582 int n_rectangles;
583 guchar *tmp_buffer;
584
585 tmp_buffer = g_malloc (buffer_width);
586
587 n_rectangles = cairo_region_num_rectangles (convolve_region);
588 for (i = 0; i < n_rectangles; i++)
589 {
590 cairo_rectangle_int_t rect;
591
592 cairo_region_get_rectangle (convolve_region, i, &rect);
593
594 for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
595 {
596 guchar *row = buffer + j * buffer_width;
597 int x0 = x_offset + rect.x;
598 int x1 = x0 + rect.width;
599
600 /* We want to produce a symmetric blur that spreads a pixel
601 * equally far to the left and right. If d is odd that happens
602 * naturally, but for d even, we approximate by using a blur
603 * on either side and then a centered blur of size d + 1.
604 * (technique also from the SVG specification)
605 */
606 if (d % 2 == 1)
607 {
608 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
609 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
610 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
611 }
612 else
613 {
614 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 1);
615 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, -1);
616 blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d + 1, 0);
617 }
618 }
619 }
620
621 g_free (tmp_buffer);
622 }
623
624 static void
fade_bytes(guchar * bytes,int width,int distance,int total)625 fade_bytes (guchar *bytes,
626 int width,
627 int distance,
628 int total)
629 {
630 guint32 multiplier = (distance * 0x10000 + 0x8000) / total;
631 int i;
632
633 for (i = 0; i < width; i++)
634 bytes[i] = (bytes[i] * multiplier) >> 16;
635 }
636
637 /* Swaps width and height. Either swaps in-place and returns the original
638 * buffer or allocates a new buffer, frees the original buffer and returns
639 * the new buffer.
640 */
641 static guchar *
flip_buffer(guchar * buffer,int width,int height)642 flip_buffer (guchar *buffer,
643 int width,
644 int height)
645 {
646 /* Working in blocks increases cache efficiency, compared to reading
647 * or writing an entire column at once */
648 #define BLOCK_SIZE 16
649
650 if (width == height)
651 {
652 int i0, j0;
653
654 for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
655 for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE)
656 {
657 int max_j = MIN(j0 + BLOCK_SIZE, height);
658 int max_i = MIN(i0 + BLOCK_SIZE, width);
659 int i, j;
660
661 if (i0 == j0)
662 {
663 for (j = j0; j < max_j; j++)
664 for (i = i0; i < j; i++)
665 {
666 guchar tmp = buffer[j * width + i];
667 buffer[j * width + i] = buffer[i * width + j];
668 buffer[i * width + j] = tmp;
669 }
670 }
671 else
672 {
673 for (j = j0; j < max_j; j++)
674 for (i = i0; i < max_i; i++)
675 {
676 guchar tmp = buffer[j * width + i];
677 buffer[j * width + i] = buffer[i * width + j];
678 buffer[i * width + j] = tmp;
679 }
680 }
681 }
682
683 return buffer;
684 }
685 else
686 {
687 guchar *new_buffer = g_malloc (height * width);
688 int i0, j0;
689
690 for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
691 for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
692 {
693 int max_j = MIN(j0 + BLOCK_SIZE, height);
694 int max_i = MIN(i0 + BLOCK_SIZE, width);
695 int i, j;
696
697 for (i = i0; i < max_i; i++)
698 for (j = j0; j < max_j; j++)
699 new_buffer[i * height + j] = buffer[j * width + i];
700 }
701
702 g_free (buffer);
703
704 return new_buffer;
705 }
706 #undef BLOCK_SIZE
707 }
708
709 static void
make_shadow(MetaShadow * shadow,cairo_region_t * region)710 make_shadow (MetaShadow *shadow,
711 cairo_region_t *region)
712 {
713 ClutterBackend *backend = clutter_get_default_backend ();
714 CoglContext *ctx = clutter_backend_get_cogl_context (backend);
715 GError *error = NULL;
716 int d = get_box_filter_size (shadow->key.radius);
717 int spread = get_shadow_spread (shadow->key.radius);
718 cairo_rectangle_int_t extents;
719 cairo_region_t *row_convolve_region;
720 cairo_region_t *column_convolve_region;
721 guchar *buffer;
722 int buffer_width;
723 int buffer_height;
724 int x_offset;
725 int y_offset;
726 int n_rectangles, j, k;
727
728 cairo_region_get_extents (region, &extents);
729
730 /* In the case where top_fade >= 0 and the portion above the top
731 * edge of the shape will be cropped, it seems like we could create
732 * a smaller buffer and omit the top portion, but actually, in our
733 * multi-pass blur algorithm, the blur into the area above the window
734 * in the first pass will contribute back to the final pixel values
735 * for the top pixels, so we create a buffer as if we weren't cropping
736 * and only crop when creating the CoglTexture.
737 */
738
739 buffer_width = extents.width + 2 * spread;
740 buffer_height = extents.height + 2 * spread;
741
742 /* Round up so we have aligned rows/columns */
743 buffer_width = (buffer_width + 3) & ~3;
744 buffer_height = (buffer_height + 3) & ~3;
745
746 /* Square buffer allows in-place swaps, which are roughly 70% faster, but we
747 * don't want to over-allocate too much memory.
748 */
749 if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4)
750 buffer_height = buffer_width;
751 if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4)
752 buffer_width = buffer_height;
753
754 buffer = g_malloc0 (buffer_width * buffer_height);
755
756 /* Blurring with multiple box-blur passes is fast, but (especially for
757 * large shadow sizes) we can improve efficiency by restricting the blur
758 * to the region that actually needs to be blurred.
759 */
760 row_convolve_region = meta_make_border_region (region, spread, spread, FALSE);
761 column_convolve_region = meta_make_border_region (region, 0, spread, TRUE);
762
763 /* Offsets between coordinates of the regions and coordinates in the buffer */
764 x_offset = spread;
765 y_offset = spread;
766
767 /* Step 1: unblurred image */
768 n_rectangles = cairo_region_num_rectangles (region);
769 for (k = 0; k < n_rectangles; k++)
770 {
771 cairo_rectangle_int_t rect;
772
773 cairo_region_get_rectangle (region, k, &rect);
774 for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
775 memset (buffer + buffer_width * j + x_offset + rect.x, 255, rect.width);
776 }
777
778 /* Step 2: swap rows and columns */
779 buffer = flip_buffer (buffer, buffer_width, buffer_height);
780
781 /* Step 3: blur rows (really columns) */
782 blur_rows (column_convolve_region, y_offset, x_offset,
783 buffer, buffer_height, buffer_width,
784 d);
785
786 /* Step 4: swap rows and columns */
787 buffer = flip_buffer (buffer, buffer_height, buffer_width);
788
789 /* Step 5: blur rows */
790 blur_rows (row_convolve_region, x_offset, y_offset,
791 buffer, buffer_width, buffer_height,
792 d);
793
794 /* Step 6: fade out the top, if applicable */
795 if (shadow->key.top_fade >= 0)
796 {
797 for (j = y_offset; j < y_offset + MIN (shadow->key.top_fade, extents.height + shadow->outer_border_bottom); j++)
798 fade_bytes(buffer + j * buffer_width, buffer_width, j - y_offset, shadow->key.top_fade);
799 }
800
801 /* We offset the passed in pixels to crop off the extra area we allocated at the top
802 * in the case of top_fade >= 0. We also account for padding at the left for symmetry
803 * though that doesn't currently occur.
804 */
805 shadow->texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx,
806 shadow->outer_border_left + extents.width + shadow->outer_border_right,
807 shadow->outer_border_top + extents.height + shadow->outer_border_bottom,
808 COGL_PIXEL_FORMAT_A_8,
809 buffer_width,
810 (buffer +
811 (y_offset - shadow->outer_border_top) * buffer_width +
812 (x_offset - shadow->outer_border_left)),
813 &error));
814
815 if (error)
816 {
817 meta_warning ("Failed to allocate shadow texture: %s", error->message);
818 g_error_free (error);
819 }
820
821 cairo_region_destroy (row_convolve_region);
822 cairo_region_destroy (column_convolve_region);
823 g_free (buffer);
824
825 shadow->pipeline = meta_create_texture_pipeline (shadow->texture);
826 }
827
828 static MetaShadowParams *
get_shadow_params(MetaShadowFactory * factory,const char * class_name,gboolean focused,gboolean create)829 get_shadow_params (MetaShadowFactory *factory,
830 const char *class_name,
831 gboolean focused,
832 gboolean create)
833 {
834 MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes,
835 class_name);
836 if (class_info == NULL)
837 {
838 if (create)
839 {
840 class_info = g_new0 (MetaShadowClassInfo, 1);
841 *class_info = default_shadow_classes[0];
842 class_info->name = g_strdup (class_info->name);
843
844 g_hash_table_insert (factory->shadow_classes,
845 (char *)class_info->name, class_info);
846 }
847 else
848 {
849 class_info = &default_shadow_classes[0];
850 }
851 }
852
853 if (focused)
854 return &class_info->focused;
855 else
856 return &class_info->unfocused;
857 }
858
859 /**
860 * meta_shadow_factory_get_shadow:
861 * @factory: a #MetaShadowFactory
862 * @shape: the size-invariant shape of the window's region
863 * @width: the actual width of the window's region
864 * @height: the actual height of the window's region
865 * @class_name: name of the class of window shadows
866 * @focused: whether the shadow is for a focused window
867 *
868 * Gets the appropriate shadow object for drawing shadows for the
869 * specified window shape. The region that we are shadowing is specified
870 * as a combination of a size-invariant extracted shape and the size.
871 * In some cases, the same shadow object can be shared between sizes;
872 * in other cases a different shadow object is used for each size.
873 *
874 * Return value: (transfer full): a newly referenced #MetaShadow; unref with
875 * meta_shadow_unref()
876 */
877 MetaShadow *
meta_shadow_factory_get_shadow(MetaShadowFactory * factory,MetaWindowShape * shape,int width,int height,const char * class_name,gboolean focused)878 meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
879 MetaWindowShape *shape,
880 int width,
881 int height,
882 const char *class_name,
883 gboolean focused)
884 {
885 MetaShadowParams *params;
886 MetaShadowCacheKey key;
887 MetaShadow *shadow;
888 cairo_region_t *region;
889 int spread;
890 int shape_border_top, shape_border_right, shape_border_bottom, shape_border_left;
891 int inner_border_top, inner_border_right, inner_border_bottom, inner_border_left;
892 int outer_border_top, outer_border_right, outer_border_bottom, outer_border_left;
893 gboolean scale_width, scale_height;
894 gboolean cacheable;
895 int center_width, center_height;
896
897 g_return_val_if_fail (META_IS_SHADOW_FACTORY (factory), NULL);
898 g_return_val_if_fail (shape != NULL, NULL);
899
900 /* Using a single shadow texture for different window sizes only works
901 * when there is a central scaled area that is greater than twice
902 * the spread of the gaussian blur we are applying to get to the
903 * shadow image.
904 * ********* ***********
905 * /----------\ *###########* *#############*
906 * | | => **#*********#** => **#***********#**
907 * | | **#** **#** **#** **#**
908 * | | **#*********#** **#***********#**
909 * \----------/ *###########* *#############*
910 * ********** ************
911 * Original Blur Stretched Blur
912 *
913 * For smaller sizes, we create a separate shadow image for each size;
914 * since we assume that there will be little reuse, we don't try to
915 * cache such images but just recreate them. (Since the current cache
916 * policy is to only keep around referenced shadows, there wouldn't
917 * be any harm in caching them, it would just make the book-keeping
918 * a bit tricker.)
919 *
920 * In the case where we are fading a the top, that also has to fit
921 * within the top unscaled border.
922 */
923
924 params = get_shadow_params (factory, class_name, focused, FALSE);
925
926 spread = get_shadow_spread (params->radius);
927 meta_window_shape_get_borders (shape,
928 &shape_border_top,
929 &shape_border_right,
930 &shape_border_bottom,
931 &shape_border_left);
932
933 inner_border_top = MAX (shape_border_top + spread, params->top_fade);
934 outer_border_top = params->top_fade >= 0 ? 0 : spread;
935 inner_border_right = shape_border_right + spread;
936 outer_border_right = spread;
937 inner_border_bottom = shape_border_bottom + spread;
938 outer_border_bottom = spread;
939 inner_border_left = shape_border_left + spread;
940 outer_border_left = spread;
941
942 scale_width = inner_border_left + inner_border_right <= width;
943 scale_height = inner_border_top + inner_border_bottom <= height;
944 cacheable = scale_width && scale_height;
945
946 if (cacheable)
947 {
948 key.shape = shape;
949 key.radius = params->radius;
950 key.top_fade = params->top_fade;
951
952 shadow = g_hash_table_lookup (factory->shadows, &key);
953 if (shadow)
954 return meta_shadow_ref (shadow);
955 }
956
957 shadow = g_new0 (MetaShadow, 1);
958
959 shadow->ref_count = 1;
960 shadow->factory = factory;
961 shadow->key.shape = meta_window_shape_ref (shape);
962 shadow->key.radius = params->radius;
963 shadow->key.top_fade = params->top_fade;
964
965 shadow->outer_border_top = outer_border_top;
966 shadow->inner_border_top = inner_border_top;
967 shadow->outer_border_right = outer_border_right;
968 shadow->inner_border_right = inner_border_right;
969 shadow->outer_border_bottom = outer_border_bottom;
970 shadow->inner_border_bottom = inner_border_bottom;
971 shadow->outer_border_left = outer_border_left;
972 shadow->inner_border_left = inner_border_left;
973
974 shadow->scale_width = scale_width;
975 if (scale_width)
976 center_width = inner_border_left + inner_border_right - (shape_border_left + shape_border_right);
977 else
978 center_width = width - (shape_border_left + shape_border_right);
979
980 shadow->scale_height = scale_height;
981 if (scale_height)
982 center_height = inner_border_top + inner_border_bottom - (shape_border_top + shape_border_bottom);
983 else
984 center_height = height - (shape_border_top + shape_border_bottom);
985
986 g_assert (center_width >= 0 && center_height >= 0);
987
988 region = meta_window_shape_to_region (shape, center_width, center_height);
989 make_shadow (shadow, region);
990
991 cairo_region_destroy (region);
992
993 if (cacheable)
994 g_hash_table_insert (factory->shadows, &shadow->key, shadow);
995
996 return shadow;
997 }
998
999 /**
1000 * meta_shadow_factory_set_params:
1001 * @factory: a #MetaShadowFactory
1002 * @class_name: name of the class of shadow to set the params for.
1003 * the default shadow classes are the names of the different
1004 * theme frame types (normal, dialog, modal_dialog, utility,
1005 * border, menu, attached) and in addition, popup-menu
1006 * and dropdown-menu.
1007 * @focused: whether the shadow is for a focused window
1008 * @params: new parameter values
1009 *
1010 * Updates the shadow parameters for a particular class of shadows
1011 * for either the focused or unfocused state. If the class name
1012 * does not name an existing class, a new class will be created
1013 * (the other focus state for that class will have default values
1014 * assigned to it.)
1015 */
1016 void
meta_shadow_factory_set_params(MetaShadowFactory * factory,const char * class_name,gboolean focused,MetaShadowParams * params)1017 meta_shadow_factory_set_params (MetaShadowFactory *factory,
1018 const char *class_name,
1019 gboolean focused,
1020 MetaShadowParams *params)
1021 {
1022 MetaShadowParams *stored_params;
1023
1024 g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
1025 g_return_if_fail (class_name != NULL);
1026 g_return_if_fail (params != NULL);
1027 g_return_if_fail (params->radius >= 0);
1028
1029 stored_params = get_shadow_params (factory, class_name, focused, TRUE);
1030
1031 *stored_params = *params;
1032
1033 g_signal_emit (factory, signals[CHANGED], 0);
1034 }
1035
1036 /**
1037 * meta_shadow_factory_get_params:
1038 * @factory: a #MetaShadowFactory
1039 * @class_name: name of the class of shadow to get the params for
1040 * @focused: whether the shadow is for a focused window
1041 * @params: (out caller-allocates): location to store the current parameter values
1042 *
1043 * Gets the shadow parameters for a particular class of shadows
1044 * for either the focused or unfocused state. If the class name
1045 * does not name an existing class, default values will be returned
1046 * without printing an error.
1047 */
1048 void
meta_shadow_factory_get_params(MetaShadowFactory * factory,const char * class_name,gboolean focused,MetaShadowParams * params)1049 meta_shadow_factory_get_params (MetaShadowFactory *factory,
1050 const char *class_name,
1051 gboolean focused,
1052 MetaShadowParams *params)
1053 {
1054 MetaShadowParams *stored_params;
1055
1056 g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
1057 g_return_if_fail (class_name != NULL);
1058
1059 stored_params = get_shadow_params (factory, class_name, focused, FALSE);
1060
1061 if (params)
1062 *params = *stored_params;
1063 }
1064
1065 G_DEFINE_BOXED_TYPE (MetaShadow, meta_shadow,
1066 meta_shadow_ref, meta_shadow_unref)
1067