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