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