1 /*
2 * Copyright © 2008 Kristian Høgsberg
3 * Copyright © 2009 Chris Wilson
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24 #include <math.h>
25 #include <stdint.h>
26
27 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
28
29 /* Performs a simple 2D Gaussian blur of radius @radius on surface @surface. */
30 static void
blur_image_surface(cairo_surface_t * surface,int radius)31 blur_image_surface (cairo_surface_t *surface, int radius)
32 {
33 cairo_surface_t *tmp;
34 int width, height;
35 int src_stride, dst_stride;
36 int x, y, z, w;
37 uint8_t *src, *dst;
38 uint32_t *s, *d, a, p;
39 int i, j, k;
40 uint8_t kernel[17];
41 const int size = ARRAY_LENGTH (kernel);
42 const int half = size / 2;
43
44 if (cairo_surface_status (surface))
45 return;
46
47 width = cairo_image_surface_get_width (surface);
48 height = cairo_image_surface_get_height (surface);
49
50 switch (cairo_image_surface_get_format (surface)) {
51 case CAIRO_FORMAT_A1:
52 default:
53 /* Don't even think about it! */
54 return;
55
56 case CAIRO_FORMAT_A8:
57 /* Handle a8 surfaces by effectively unrolling the loops by a
58 * factor of 4 - this is safe since we know that stride has to be a
59 * multiple of uint32_t. */
60 width /= 4;
61 break;
62
63 case CAIRO_FORMAT_RGB24:
64 case CAIRO_FORMAT_ARGB32:
65 break;
66 }
67
68 tmp = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
69 if (cairo_surface_status (tmp))
70 return;
71
72 src = cairo_image_surface_get_data (surface);
73 src_stride = cairo_image_surface_get_stride (surface);
74
75 dst = cairo_image_surface_get_data (tmp);
76 dst_stride = cairo_image_surface_get_stride (tmp);
77
78 a = 0;
79 for (i = 0; i < size; i++) {
80 double f = i - half;
81 a += kernel[i] = exp (- f * f / 30.0) * 80;
82 }
83
84 /* Horizontally blur from surface -> tmp */
85 for (i = 0; i < height; i++) {
86 s = (uint32_t *) (src + i * src_stride);
87 d = (uint32_t *) (dst + i * dst_stride);
88 for (j = 0; j < width; j++) {
89 if (radius < j && j < width - radius) {
90 d[j] = s[j];
91 continue;
92 }
93
94 x = y = z = w = 0;
95 for (k = 0; k < size; k++) {
96 if (j - half + k < 0 || j - half + k >= width)
97 continue;
98
99 p = s[j - half + k];
100
101 x += ((p >> 24) & 0xff) * kernel[k];
102 y += ((p >> 16) & 0xff) * kernel[k];
103 z += ((p >> 8) & 0xff) * kernel[k];
104 w += ((p >> 0) & 0xff) * kernel[k];
105 }
106 d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
107 }
108 }
109
110 /* Then vertically blur from tmp -> surface */
111 for (i = 0; i < height; i++) {
112 s = (uint32_t *) (dst + i * dst_stride);
113 d = (uint32_t *) (src + i * src_stride);
114 for (j = 0; j < width; j++) {
115 if (radius <= i && i < height - radius) {
116 d[j] = s[j];
117 continue;
118 }
119
120 x = y = z = w = 0;
121 for (k = 0; k < size; k++) {
122 if (i - half + k < 0 || i - half + k >= height)
123 continue;
124
125 s = (uint32_t *) (dst + (i - half + k) * dst_stride);
126 p = s[j];
127
128 x += ((p >> 24) & 0xff) * kernel[k];
129 y += ((p >> 16) & 0xff) * kernel[k];
130 z += ((p >> 8) & 0xff) * kernel[k];
131 w += ((p >> 0) & 0xff) * kernel[k];
132 }
133 d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
134 }
135 }
136
137 cairo_surface_destroy (tmp);
138 cairo_surface_mark_dirty (surface);
139 }
140