~ubuntu-branches/ubuntu/oneiric/mutter/oneiric-201109070806

« back to all changes in this revision

Viewing changes to src/compositor/meta-shadow-factory.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2010-11-30 10:56:20 UTC
  • Revision ID: james.westby@ubuntu.com-20101130105620-5pg8qjx4fn4nt00b
Tags: 2.91.3-0ubuntu1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
/*
 
3
 * MetaShadowFactory:
 
4
 *
 
5
 * Create and cache shadow textures for abritrary window shapes
 
6
 *
 
7
 * Copyright 2010 Red Hat, Inc.
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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
 
22
 * 02111-1307, USA.
 
23
 */
 
24
#include <config.h>
 
25
#include <math.h>
 
26
#include <string.h>
 
27
 
 
28
#include "cogl-utils.h"
 
29
#include "meta-shadow-factory-private.h"
 
30
#include "region-utils.h"
 
31
 
 
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:
 
35
 *
 
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
 
40
 *   size.
 
41
 *
 
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
 
44
 *   columns.
 
45
 *
 
46
 * - For better cache efficiency, we blur rows, transpose the image
 
47
 *   in blocks, blur rows again, and then transpose back.
 
48
 *
 
49
 * - We approximate the 1D gaussian blur as 3 successive box filters.
 
50
 */
 
51
 
 
52
typedef struct _MetaShadowCacheKey  MetaShadowCacheKey;
 
53
typedef struct _MetaShadowClassInfo MetaShadowClassInfo;
 
54
 
 
55
struct _MetaShadowCacheKey
 
56
{
 
57
  MetaWindowShape *shape;
 
58
  int radius;
 
59
  int top_fade;
 
60
};
 
61
 
 
62
struct _MetaShadow
 
63
{
 
64
  int ref_count;
 
65
 
 
66
  MetaShadowFactory *factory;
 
67
  MetaShadowCacheKey key;
 
68
  CoglHandle texture;
 
69
  CoglHandle material;
 
70
 
 
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
 
73
   * shape */
 
74
  int outer_border_top;
 
75
  int inner_border_top;
 
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;
 
82
 
 
83
  guint scale_width : 1;
 
84
  guint scale_height : 1;
 
85
};
 
86
 
 
87
struct _MetaShadowClassInfo
 
88
{
 
89
  const char *name; /* const so we can reuse for static definitions */
 
90
  MetaShadowParams focused;
 
91
  MetaShadowParams unfocused;
 
92
};
 
93
 
 
94
struct _MetaShadowFactory
 
95
{
 
96
  GObject parent_instance;
 
97
 
 
98
  /* MetaShadowCacheKey => MetaShadow; the shadows are not referenced
 
99
   * by the factory, they are simply removed from the table when freed */
 
100
  GHashTable *shadows;
 
101
 
 
102
  /* class name => MetaShadowClassInfo */
 
103
  GHashTable *shadow_classes;
 
104
};
 
105
 
 
106
struct _MetaShadowFactoryClass
 
107
{
 
108
  GObjectClass parent_class;
 
109
};
 
110
 
 
111
enum
 
112
{
 
113
  CHANGED,
 
114
 
 
115
  LAST_SIGNAL
 
116
};
 
117
 
 
118
static guint signals[LAST_SIGNAL] = { 0 };
 
119
 
 
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 } },
 
129
 
 
130
  { "popup-menu",    { 6, -1, 0, 4, 255 }, { 6, -1, 0, 4, 255 } },
 
131
 
 
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 } }
 
134
};
 
135
 
 
136
G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT);
 
137
 
 
138
static guint
 
139
meta_shadow_cache_key_hash (gconstpointer val)
 
140
{
 
141
  const MetaShadowCacheKey *key = val;
 
142
 
 
143
  return 59 * key->radius + 67 * key->top_fade + 73 * meta_window_shape_hash (key->shape);
 
144
}
 
145
 
 
146
static gboolean
 
147
meta_shadow_cache_key_equal (gconstpointer a,
 
148
                             gconstpointer b)
 
149
{
 
150
  const MetaShadowCacheKey *key_a = a;
 
151
  const MetaShadowCacheKey *key_b = b;
 
152
 
 
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));
 
155
}
 
156
 
 
157
MetaShadow *
 
158
meta_shadow_ref (MetaShadow *shadow)
 
159
{
 
160
  shadow->ref_count++;
 
161
 
 
162
  return shadow;
 
163
}
 
164
 
 
165
void
 
166
meta_shadow_unref (MetaShadow *shadow)
 
167
{
 
168
  shadow->ref_count--;
 
169
  if (shadow->ref_count == 0)
 
170
    {
 
171
      if (shadow->factory)
 
172
        {
 
173
          g_hash_table_remove (shadow->factory->shadows,
 
174
                               &shadow->key);
 
175
        }
 
176
 
 
177
      meta_window_shape_unref (shadow->key.shape);
 
178
      cogl_handle_unref (shadow->texture);
 
179
      cogl_handle_unref (shadow->material);
 
180
 
 
181
      g_slice_free (MetaShadow, shadow);
 
182
    }
 
183
}
 
184
 
 
185
/**
 
186
 * meta_shadow_paint:
 
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.
 
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
 
201
meta_shadow_paint (MetaShadow     *shadow,
 
202
                   int             window_x,
 
203
                   int             window_y,
 
204
                   int             window_width,
 
205
                   int             window_height,
 
206
                   guint8          opacity,
 
207
                   cairo_region_t *clip)
 
208
{
 
209
  float texture_width = cogl_texture_get_width (shadow->texture);
 
210
  float texture_height = cogl_texture_get_height (shadow->texture);
 
211
  int i, j;
 
212
  float src_x[4];
 
213
  float src_y[4];
 
214
  int dest_x[4];
 
215
  int dest_y[4];
 
216
  int n_x, n_y;
 
217
 
 
218
  cogl_material_set_color4ub (shadow->material,
 
219
                              opacity, opacity, opacity, opacity);
 
220
 
 
221
  cogl_set_source (shadow->material);
 
222
 
 
223
  if (shadow->scale_width)
 
224
    {
 
225
      n_x = 3;
 
226
 
 
227
      src_x[0] = 0.0;
 
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;
 
230
      src_x[3] = 1.0;
 
231
 
 
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;
 
236
    }
 
237
  else
 
238
    {
 
239
      n_x = 1;
 
240
 
 
241
      src_x[0] = 0.0;
 
242
      src_x[1] = 1.0;
 
243
 
 
244
      dest_x[0] = window_x - shadow->outer_border_left;
 
245
      dest_x[1] = window_x + window_width + shadow->outer_border_right;
 
246
    }
 
247
 
 
248
  if (shadow->scale_height)
 
249
    {
 
250
      n_y = 3;
 
251
 
 
252
      src_y[0] = 0.0;
 
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;
 
255
      src_y[3] = 1.0;
 
256
 
 
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;
 
261
    }
 
262
  else
 
263
    {
 
264
      n_y = 1;
 
265
 
 
266
      src_y[0] = 0.0;
 
267
      src_y[1] = 1.0;
 
268
 
 
269
      dest_y[0] = window_y - shadow->outer_border_top;
 
270
      dest_y[1] = window_y + window_height + shadow->outer_border_bottom;
 
271
    }
 
272
 
 
273
  for (j = 0; j < n_y; j++)
 
274
    {
 
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];
 
278
 
 
279
      for (i = 0; i < n_x; i++)
 
280
        {
 
281
          cairo_region_overlap_t overlap;
 
282
 
 
283
          dest_rect.x = dest_x[i];
 
284
          dest_rect.width = dest_x[i + 1] - dest_x[i];
 
285
 
 
286
          if (clip)
 
287
            overlap = cairo_region_contains_rectangle (clip, &dest_rect);
 
288
          else
 
289
            overlap = CAIRO_REGION_OVERLAP_PART;
 
290
 
 
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],
 
294
                                                src_x[i], src_y[j],
 
295
                                                src_x[i + 1], src_y[j + 1]);
 
296
        }
 
297
    }
 
298
}
 
299
 
 
300
/**
 
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
 
307
 *
 
308
 * Computes the bounds of the pixels that will be affected by
 
309
 * meta_shadow_paints()
 
310
 */
 
311
void
 
312
meta_shadow_get_bounds  (MetaShadow            *shadow,
 
313
                         int                    window_x,
 
314
                         int                    window_y,
 
315
                         int                    window_width,
 
316
                         int                    window_height,
 
317
                         cairo_rectangle_int_t *bounds)
 
318
{
 
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;
 
323
}
 
324
 
 
325
static void
 
326
meta_shadow_class_info_free (MetaShadowClassInfo *class_info)
 
327
{
 
328
  g_free ((char *)class_info->name);
 
329
  g_slice_free (MetaShadowClassInfo, class_info);
 
330
}
 
331
 
 
332
static void
 
333
meta_shadow_factory_init (MetaShadowFactory *factory)
 
334
{
 
335
  guint i;
 
336
 
 
337
  factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash,
 
338
                                       meta_shadow_cache_key_equal);
 
339
 
 
340
  factory->shadow_classes = g_hash_table_new_full (g_str_hash,
 
341
                                                   g_str_equal,
 
342
                                                   NULL,
 
343
                                                   (GDestroyNotify)meta_shadow_class_info_free);
 
344
 
 
345
  for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++)
 
346
    {
 
347
      MetaShadowClassInfo *class_info = g_slice_new (MetaShadowClassInfo);
 
348
 
 
349
      *class_info = default_shadow_classes[i];
 
350
      class_info->name = g_strdup (class_info->name);
 
351
 
 
352
      g_hash_table_insert (factory->shadow_classes,
 
353
                           (char *)class_info->name, class_info);
 
354
    }
 
355
}
 
356
 
 
357
static void
 
358
meta_shadow_factory_finalize (GObject *object)
 
359
{
 
360
  MetaShadowFactory *factory = META_SHADOW_FACTORY (object);
 
361
  GHashTableIter iter;
 
362
  gpointer key, value;
 
363
 
 
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))
 
368
    {
 
369
      MetaShadow *shadow = key;
 
370
      shadow->factory = NULL;
 
371
    }
 
372
 
 
373
  g_hash_table_destroy (factory->shadows);
 
374
  g_hash_table_destroy (factory->shadow_classes);
 
375
 
 
376
  G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object);
 
377
}
 
378
 
 
379
static void
 
380
meta_shadow_factory_class_init (MetaShadowFactoryClass *klass)
 
381
{
 
382
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
383
 
 
384
  object_class->finalize = meta_shadow_factory_finalize;
 
385
 
 
386
  signals[CHANGED] =
 
387
    g_signal_new ("changed",
 
388
                  G_TYPE_FROM_CLASS (object_class),
 
389
                  G_SIGNAL_RUN_LAST,
 
390
                  0,
 
391
                  NULL, NULL,
 
392
                  g_cclosure_marshal_VOID__VOID,
 
393
                  G_TYPE_NONE, 0);
 
394
}
 
395
 
 
396
MetaShadowFactory *
 
397
meta_shadow_factory_new (void)
 
398
{
 
399
  return g_object_new (META_TYPE_SHADOW_FACTORY, NULL);
 
400
}
 
401
 
 
402
/**
 
403
 * meta_shadow_factory_get_default:
 
404
 *
 
405
 * Return value: (transfer none): the global singleton shadow factory
 
406
 */
 
407
MetaShadowFactory *
 
408
meta_shadow_factory_get_default (void)
 
409
{
 
410
  static MetaShadowFactory *factory;
 
411
 
 
412
  if (factory == NULL)
 
413
    factory = meta_shadow_factory_new ();
 
414
 
 
415
  return factory;
 
416
}
 
417
 
 
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:
 
423
 *
 
424
 * http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
 
425
 *
 
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.)
 
430
 */
 
431
static int
 
432
get_box_filter_size (int radius)
 
433
{
 
434
  return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI)));
 
435
}
 
436
 
 
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
 
441
 */
 
442
static int
 
443
get_shadow_spread (int radius)
 
444
{
 
445
  int d = get_box_filter_size (radius);
 
446
 
 
447
  if (d % 2 == 1)
 
448
    return 3 * (d / 2);
 
449
  else
 
450
    return 3 * (d / 2) - 1;
 
451
}
 
452
 
 
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.
 
458
 *
 
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)
 
462
 */
 
463
static void
 
464
blur_xspan (guchar *row,
 
465
            guchar *tmp_buffer,
 
466
            int     row_width,
 
467
            int     x0,
 
468
            int     x1,
 
469
            int     d,
 
470
            int     shift)
 
471
{
 
472
  int offset;
 
473
  int sum = 0;
 
474
  int i;
 
475
 
 
476
  if (d % 2 == 1)
 
477
    offset = d / 2;
 
478
  else
 
479
    offset = (d - shift) / 2;
 
480
 
 
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.)
 
489
   */
 
490
  for (i = x0 - d + offset; i < x1 + offset; i++)
 
491
    {
 
492
      if (i >= 0 && i < row_width)
 
493
        sum += row[i];
 
494
 
 
495
      if (i >= x0 + offset)
 
496
        {
 
497
          if (i >= d)
 
498
            sum -= row[i - d];
 
499
 
 
500
          tmp_buffer[i - offset] = (sum + d / 2) / d;
 
501
        }
 
502
    }
 
503
 
 
504
  memcpy(row + x0, tmp_buffer + x0, x1 - x0);
 
505
}
 
506
 
 
507
static void
 
508
blur_rows (cairo_region_t   *convolve_region,
 
509
           int               x_offset,
 
510
           int               y_offset,
 
511
           guchar           *buffer,
 
512
           int               buffer_width,
 
513
           int               buffer_height,
 
514
           int               d)
 
515
{
 
516
  int i, j;
 
517
  int n_rectangles;
 
518
  guchar *tmp_buffer;
 
519
 
 
520
  tmp_buffer = g_malloc (buffer_width);
 
521
 
 
522
  n_rectangles = cairo_region_num_rectangles (convolve_region);
 
523
  for (i = 0; i < n_rectangles; i++)
 
524
    {
 
525
      cairo_rectangle_int_t rect;
 
526
 
 
527
      cairo_region_get_rectangle (convolve_region, i, &rect);
 
528
 
 
529
      for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
 
530
        {
 
531
          guchar *row = buffer + j * buffer_width;
 
532
          int x0 = x_offset + rect.x;
 
533
          int x1 = x0 + rect.width;
 
534
 
 
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)
 
540
           */
 
541
          if (d % 2 == 1)
 
542
            {
 
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);
 
546
            }
 
547
          else
 
548
            {
 
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);
 
552
            }
 
553
        }
 
554
    }
 
555
 
 
556
  g_free (tmp_buffer);
 
557
}
 
558
 
 
559
static void
 
560
fade_bytes (guchar *bytes,
 
561
            int     width,
 
562
            int     distance,
 
563
            int     total)
 
564
{
 
565
  guint32 multiplier = (distance * 0x10000 + 0x8000) / total;
 
566
  int i;
 
567
 
 
568
  for (i = 0; i < width; i++)
 
569
    bytes[i] = (bytes[i] * multiplier) >> 16;
 
570
}
 
571
 
 
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
 
574
 * the new buffer.
 
575
 */
 
576
static guchar *
 
577
flip_buffer (guchar *buffer,
 
578
             int     width,
 
579
             int     height)
 
580
{
 
581
  /* Working in blocks increases cache efficiency, compared to reading
 
582
   * or writing an entire column at once */
 
583
#define BLOCK_SIZE 16
 
584
 
 
585
  if (width == height)
 
586
    {
 
587
      int i0, j0;
 
588
 
 
589
      for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
 
590
        for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE)
 
591
          {
 
592
            int max_j = MIN(j0 + BLOCK_SIZE, height);
 
593
            int max_i = MIN(i0 + BLOCK_SIZE, width);
 
594
            int i, j;
 
595
 
 
596
            if (i0 == j0)
 
597
              {
 
598
                for (j = j0; j < max_j; j++)
 
599
                  for (i = i0; i < j; i++)
 
600
                    {
 
601
                      guchar tmp = buffer[j * width + i];
 
602
                      buffer[j * width + i] = buffer[i * width + j];
 
603
                      buffer[i * width + j] = tmp;
 
604
                    }
 
605
              }
 
606
            else
 
607
              {
 
608
                for (j = j0; j < max_j; j++)
 
609
                  for (i = i0; i < max_i; i++)
 
610
                    {
 
611
                      guchar tmp = buffer[j * width + i];
 
612
                      buffer[j * width + i] = buffer[i * width + j];
 
613
                      buffer[i * width + j] = tmp;
 
614
                    }
 
615
              }
 
616
          }
 
617
 
 
618
      return buffer;
 
619
    }
 
620
  else
 
621
    {
 
622
      guchar *new_buffer = g_malloc (height * width);
 
623
      int i0, j0;
 
624
 
 
625
      for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
 
626
        for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
 
627
          {
 
628
            int max_j = MIN(j0 + BLOCK_SIZE, height);
 
629
            int max_i = MIN(i0 + BLOCK_SIZE, width);
 
630
            int i, j;
 
631
 
 
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];
 
635
          }
 
636
 
 
637
      g_free (buffer);
 
638
 
 
639
      return new_buffer;
 
640
    }
 
641
#undef BLOCK_SIZE
 
642
}
 
643
 
 
644
static void
 
645
make_shadow (MetaShadow     *shadow,
 
646
             cairo_region_t *region)
 
647
{
 
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;
 
653
  guchar *buffer;
 
654
  int buffer_width;
 
655
  int buffer_height;
 
656
  int x_offset;
 
657
  int y_offset;
 
658
  int n_rectangles, j, k;
 
659
 
 
660
  cairo_region_get_extents (region, &extents);
 
661
 
 
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.
 
669
   */
 
670
 
 
671
  buffer_width = extents.width + 2 * spread;
 
672
  buffer_height = extents.height + 2 * spread;
 
673
 
 
674
  /* Round up so we have aligned rows/columns */
 
675
  buffer_width = (buffer_width + 3) & ~3;
 
676
  buffer_height = (buffer_height + 3) & ~3;
 
677
 
 
678
  /* Square buffer allows in-place swaps, which are roughly 70% faster, but we
 
679
   * don't want to over-allocate too much memory.
 
680
   */
 
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;
 
685
 
 
686
  buffer = g_malloc0 (buffer_width * buffer_height);
 
687
 
 
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.
 
691
   */
 
692
  row_convolve_region = meta_make_border_region (region, spread, spread, FALSE);
 
693
  column_convolve_region = meta_make_border_region (region, 0, spread, TRUE);
 
694
 
 
695
  /* Offsets between coordinates of the regions and coordinates in the buffer */
 
696
  x_offset = spread;
 
697
  y_offset = spread;
 
698
 
 
699
  /* Step 1: unblurred image */
 
700
  n_rectangles = cairo_region_num_rectangles (region);
 
701
  for (k = 0; k < n_rectangles; k++)
 
702
    {
 
703
      cairo_rectangle_int_t rect;
 
704
 
 
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);
 
708
    }
 
709
 
 
710
  /* Step 2: swap rows and columns */
 
711
  buffer = flip_buffer (buffer, buffer_width, buffer_height);
 
712
 
 
713
  /* Step 3: blur rows (really columns) */
 
714
  blur_rows (column_convolve_region, y_offset, x_offset,
 
715
             buffer, buffer_height, buffer_width,
 
716
             d);
 
717
 
 
718
  /* Step 4: swap rows and columns */
 
719
  buffer = flip_buffer (buffer, buffer_height, buffer_width);
 
720
 
 
721
  /* Step 5: blur rows */
 
722
  blur_rows (row_convolve_region, x_offset, y_offset,
 
723
             buffer, buffer_width, buffer_height,
 
724
             d);
 
725
 
 
726
  /* Step 6: fade out the top, if applicable */
 
727
  if (shadow->key.top_fade >= 0)
 
728
    {
 
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);
 
731
    }
 
732
 
 
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.
 
736
   */
 
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,
 
739
                                                COGL_TEXTURE_NONE,
 
740
                                                COGL_PIXEL_FORMAT_A_8,
 
741
                                                COGL_PIXEL_FORMAT_ANY,
 
742
                                                buffer_width,
 
743
                                                (buffer +
 
744
                                                 (y_offset - shadow->outer_border_top) * buffer_width +
 
745
                                                 (x_offset - shadow->outer_border_left)));
 
746
 
 
747
  cairo_region_destroy (row_convolve_region);
 
748
  cairo_region_destroy (column_convolve_region);
 
749
  g_free (buffer);
 
750
 
 
751
  shadow->material = meta_create_texture_material (shadow->texture);
 
752
}
 
753
 
 
754
static MetaShadowParams *
 
755
get_shadow_params (MetaShadowFactory *factory,
 
756
                   const char        *class_name,
 
757
                   gboolean           focused,
 
758
                   gboolean           create)
 
759
{
 
760
  MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes,
 
761
                                                         class_name);
 
762
  if (class_info == NULL)
 
763
    {
 
764
      if (create)
 
765
        {
 
766
          class_info = g_slice_new0 (MetaShadowClassInfo);
 
767
          *class_info = default_shadow_classes[0];
 
768
          class_info->name = g_strdup (class_info->name);
 
769
 
 
770
          g_hash_table_insert (factory->shadow_classes,
 
771
                               (char *)class_info->name, class_info);
 
772
        }
 
773
      else
 
774
        {
 
775
          class_info = &default_shadow_classes[0];
 
776
        }
 
777
    }
 
778
 
 
779
  if (focused)
 
780
    return &class_info->focused;
 
781
  else
 
782
    return &class_info->unfocused;
 
783
}
 
784
 
 
785
/**
 
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
 
793
 *
 
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.
 
799
 *
 
800
 * Return value: (transfer full): a newly referenced #MetaShadow; unref with
 
801
 *  meta_shadow_unref()
 
802
 */
 
803
MetaShadow *
 
804
meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
 
805
                                MetaWindowShape   *shape,
 
806
                                int                width,
 
807
                                int                height,
 
808
                                const char        *class_name,
 
809
                                gboolean           focused)
 
810
{
 
811
  MetaShadowParams *params;
 
812
  MetaShadowCacheKey key;
 
813
  MetaShadow *shadow;
 
814
  cairo_region_t *region;
 
815
  int spread;
 
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;
 
820
  gboolean cacheable;
 
821
  int center_width, center_height;
 
822
 
 
823
  g_return_val_if_fail (META_IS_SHADOW_FACTORY (factory), NULL);
 
824
  g_return_val_if_fail (shape != NULL, NULL);
 
825
 
 
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
 
829
   * shadow image.
 
830
   *                         *********          ***********
 
831
   *  /----------\         *###########*      *#############*
 
832
   *  |          |   =>   **#*********#** => **#***********#**
 
833
   *  |          |        **#**     **#**    **#**       **#**
 
834
   *  |          |        **#*********#**    **#***********#**
 
835
   *  \----------/         *###########*      *#############*
 
836
   *                         **********         ************
 
837
   *   Original                Blur            Stretched Blur
 
838
   *
 
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
 
844
   * a bit tricker.)
 
845
   *
 
846
   * In the case where we are fading a the top, that also has to fit
 
847
   * within the top unscaled border.
 
848
   */
 
849
 
 
850
  params = get_shadow_params (factory, class_name, focused, FALSE);
 
851
 
 
852
  spread = get_shadow_spread (params->radius);
 
853
  meta_window_shape_get_borders (shape,
 
854
                                 &shape_border_top,
 
855
                                 &shape_border_right,
 
856
                                 &shape_border_bottom,
 
857
                                 &shape_border_left);
 
858
 
 
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;
 
867
 
 
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;
 
871
 
 
872
  if (cacheable)
 
873
    {
 
874
      key.shape = shape;
 
875
      key.radius = params->radius;
 
876
      key.top_fade = params->top_fade;
 
877
 
 
878
      shadow = g_hash_table_lookup (factory->shadows, &key);
 
879
      if (shadow)
 
880
        return meta_shadow_ref (shadow);
 
881
    }
 
882
 
 
883
  shadow = g_slice_new0 (MetaShadow);
 
884
 
 
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;
 
890
 
 
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;
 
899
 
 
900
  shadow->scale_width = scale_width;
 
901
  if (scale_width)
 
902
    center_width = inner_border_left + inner_border_right - (shape_border_left + shape_border_right);
 
903
  else
 
904
    center_width = width - (shape_border_left + shape_border_right);
 
905
 
 
906
  shadow->scale_height = scale_height;
 
907
  if (scale_height)
 
908
    center_height = inner_border_top + inner_border_bottom - (shape_border_top + shape_border_bottom);
 
909
  else
 
910
    center_height = height - (shape_border_top + shape_border_bottom);
 
911
 
 
912
  g_assert (center_width >= 0 && center_height >= 0);
 
913
 
 
914
  region = meta_window_shape_to_region (shape, center_width, center_height);
 
915
  make_shadow (shadow, region);
 
916
 
 
917
  cairo_region_destroy (region);
 
918
 
 
919
  if (cacheable)
 
920
    g_hash_table_insert (factory->shadows, &shadow->key, shadow);
 
921
 
 
922
  return shadow;
 
923
}
 
924
 
 
925
/**
 
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
 
932
 *  and dropdown-menu.
 
933
 * @focused: whether the shadow is for a focused window
 
934
 * @params: new parameter values
 
935
 *
 
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
 
940
 * assigned to it.)
 
941
 */
 
942
void
 
943
meta_shadow_factory_set_params (MetaShadowFactory *factory,
 
944
                                const char        *class_name,
 
945
                                gboolean           focused,
 
946
                                MetaShadowParams  *params)
 
947
{
 
948
  MetaShadowParams *stored_params;
 
949
 
 
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);
 
954
 
 
955
  stored_params = get_shadow_params (factory, class_name, focused, TRUE);
 
956
 
 
957
  *stored_params = *params;
 
958
 
 
959
  g_signal_emit (factory, signals[CHANGED], 0);
 
960
}
 
961
 
 
962
/**
 
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
 
968
 *
 
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.
 
973
 */
 
974
void
 
975
meta_shadow_factory_get_params (MetaShadowFactory *factory,
 
976
                                const char        *class_name,
 
977
                                gboolean           focused,
 
978
                                MetaShadowParams  *params)
 
979
{
 
980
  MetaShadowParams *stored_params;
 
981
 
 
982
  g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
 
983
  g_return_if_fail (class_name != NULL);
 
984
 
 
985
  stored_params = get_shadow_params (factory, class_name, focused, FALSE);
 
986
 
 
987
  if (params)
 
988
    *params = *stored_params;
 
989
}