1 /*
2  * Exponential blur, based on algorithm by Jani Huhtanen.
3  *
4  * Copyright 2009 Canonical Ltd.
5  *
6  * Authors:
7  *     Mirco "MacSlow" Mueller <mirco.mueller@canonical.com>
8  *     Jason Smith <jason.smith@canonical.com>
9  *
10  * This program is free software: you can redistribute it and/or modify it
11  * under the terms of either or both of the following licenses:
12  *
13  * 1) the GNU Lesser General Public License version 3, as published by the
14  * Free Software Foundation; and/or
15  * 2) the GNU Lesser General Public License version 2.1, as published by
16  * the Free Software Foundation.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranties of
20  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
21  * PURPOSE.  See the applicable version of the GNU Lesser General Public
22  * License for more details.
23  *
24  * You should have received a copy of both the GNU Lesser General Public
25  * License version 3 and version 2.1 along with this program.  If not, see
26  * <http://www.gnu.org/licenses/>
27  */
28 
29 // FIXME: not working yet, unfinished
30 
31 #include <math.h>
32 
33 #include "exponential-blur.h"
34 
35 static inline void
36 _blurinner (guchar* pixel,
37 	    gint   *zR,
38 	    gint   *zG,
39 	    gint   *zB,
40 	    gint   *zA,
41 	    gint    alpha,
42 	    gint    aprec,
43 	    gint    zprec);
44 
45 static inline void
46 _blurrow (guchar* pixels,
47 	  gint    width,
48 	  gint    height,
49 	  gint    channels,
50 	  gint    line,
51 	  gint    alpha,
52 	  gint    aprec,
53 	  gint    zprec);
54 
55 static inline void
56 _blurcol (guchar* pixels,
57 	  gint    width,
58 	  gint    height,
59 	  gint    channels,
60 	  gint    col,
61 	  gint    alpha,
62 	  gint    aprec,
63 	  gint    zprec);
64 
65 void
66 _expblur (guchar* pixels,
67 	  gint    width,
68 	  gint    height,
69 	  gint    channels,
70 	  gint    radius,
71 	  gint    aprec,
72 	  gint    zprec);
73 
74 void
surface_exponential_blur(cairo_surface_t * surface,guint radius)75 surface_exponential_blur (cairo_surface_t* surface,
76 			  guint            radius)
77 {
78 	guchar*        pixels;
79 	guint          width;
80 	guint          height;
81 	cairo_format_t format;
82 
83 	// sanity checks are done in raico-blur.c
84 
85 	// before we mess with the surface execute any pending drawing
86 	cairo_surface_flush (surface);
87 
88 	pixels = cairo_image_surface_get_data (surface);
89 	width  = cairo_image_surface_get_width (surface);
90 	height = cairo_image_surface_get_height (surface);
91 	format = cairo_image_surface_get_format (surface);
92 
93 	switch (format)
94 	{
95 		case CAIRO_FORMAT_ARGB32:
96 			_expblur (pixels, width, height, 4, radius, 16, 7);
97 		break;
98 
99 		case CAIRO_FORMAT_RGB24:
100 			_expblur (pixels, width, height, 3, radius, 16, 7);
101 		break;
102 
103 		case CAIRO_FORMAT_A8:
104 			_expblur (pixels, width, height, 1, radius, 16, 7);
105 		break;
106 
107 		default :
108 			// do nothing
109 		break;
110 	}
111 
112 	// inform cairo we altered the surfaces contents
113 	cairo_surface_mark_dirty (surface);
114 }
115 
116 //
117 // pixels   image-data
118 // width    image-width
119 // height   image-height
120 // channels image-channels
121 //
122 // in-place blur of image 'img' with kernel of approximate radius 'radius'
123 //
124 // blurs with two sided exponential impulse response
125 //
126 // aprec = precision of alpha parameter in fixed-point format 0.aprec
127 //
128 // zprec = precision of state parameters zR,zG,zB and zA in fp format 8.zprec
129 //
130 void
_expblur(guchar * pixels,gint width,gint height,gint channels,gint radius,gint aprec,gint zprec)131 _expblur (guchar* pixels,
132 	  gint    width,
133 	  gint    height,
134 	  gint    channels,
135 	  gint    radius,
136 	  gint    aprec,
137 	  gint    zprec)
138 {
139 	gint alpha;
140 	gint row = 0;
141 	gint col = 0;
142 
143 	if (radius < 1)
144 		return;
145 
146 	// calculate the alpha such that 90% of
147 	// the kernel is within the radius.
148 	// (Kernel extends to infinity)
149 	alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
150 
151 	for (; row < height; row++)
152 		_blurrow (pixels,
153 			  width,
154 			  height,
155 			  channels,
156 			  row,
157 			  alpha,
158 			  aprec,
159 			  zprec);
160 
161 	for(; col < width; col++)
162 		_blurcol (pixels,
163 			  width,
164 			  height,
165 			  channels,
166 			  col,
167 			  alpha,
168 			  aprec,
169 			  zprec);
170 
171 	return;
172 }
173 
174 static inline void
_blurinner(guchar * pixel,gint * zR,gint * zG,gint * zB,gint * zA,gint alpha,gint aprec,gint zprec)175 _blurinner (guchar* pixel,
176 	    gint   *zR,
177 	    gint   *zG,
178 	    gint   *zB,
179 	    gint   *zA,
180 	    gint    alpha,
181 	    gint    aprec,
182 	    gint    zprec)
183 {
184 	gint R;
185 	gint G;
186 	gint B;
187 	guchar A;
188 
189 	R = *pixel;
190 	G = *(pixel + 1);
191 	B = *(pixel + 2);
192 	A = *(pixel + 3);
193 
194 	*zR += (alpha * ((R << zprec) - *zR)) >> aprec;
195 	*zG += (alpha * ((G << zprec) - *zG)) >> aprec;
196 	*zB += (alpha * ((B << zprec) - *zB)) >> aprec;
197 	*zA += (alpha * ((A << zprec) - *zA)) >> aprec;
198 
199 	*pixel       = *zR >> zprec;
200 	*(pixel + 1) = *zG >> zprec;
201 	*(pixel + 2) = *zB >> zprec;
202 	*(pixel + 3) = *zA >> zprec;
203 }
204 
205 static inline void
_blurrow(guchar * pixels,gint width,gint height,gint channels,gint line,gint alpha,gint aprec,gint zprec)206 _blurrow (guchar* pixels,
207 	  gint    width,
208 	  gint    height,
209 	  gint    channels,
210 	  gint    line,
211 	  gint    alpha,
212 	  gint    aprec,
213 	  gint    zprec)
214 {
215 	gint    zR;
216 	gint    zG;
217 	gint    zB;
218 	gint    zA;
219 	gint    index;
220 	guchar* scanline;
221 
222 	scanline = &(pixels[line * width * channels]);
223 
224 	zR = *scanline << zprec;
225 	zG = *(scanline + 1) << zprec;
226 	zB = *(scanline + 2) << zprec;
227 	zA = *(scanline + 3) << zprec;
228 
229 	for (index = 0; index < width; index ++)
230 		_blurinner (&scanline[index * channels],
231 			    &zR,
232 			    &zG,
233 			    &zB,
234 			    &zA,
235 			    alpha,
236 			    aprec,
237 			    zprec);
238 
239 	for (index = width - 2; index >= 0; index--)
240 		_blurinner (&scanline[index * channels],
241 			    &zR,
242 			    &zG,
243 			    &zB,
244 			    &zA,
245 			    alpha,
246 			    aprec,
247 			    zprec);
248 }
249 
250 static inline void
_blurcol(guchar * pixels,gint width,gint height,gint channels,gint x,gint alpha,gint aprec,gint zprec)251 _blurcol (guchar* pixels,
252 	  gint    width,
253 	  gint    height,
254 	  gint    channels,
255 	  gint    x,
256 	  gint    alpha,
257 	  gint    aprec,
258 	  gint    zprec)
259 {
260 	gint zR;
261 	gint zG;
262 	gint zB;
263 	gint zA;
264 	gint index;
265 	guchar* ptr;
266 
267 	ptr = pixels;
268 
269 	ptr += x * channels;
270 
271 	zR = *((guchar*) ptr    ) << zprec;
272 	zG = *((guchar*) ptr + 1) << zprec;
273 	zB = *((guchar*) ptr + 2) << zprec;
274 	zA = *((guchar*) ptr + 3) << zprec;
275 
276 	for (index = width; index < (height - 1) * width; index += width)
277 		_blurinner ((guchar*) &ptr[index * channels],
278 			    &zR,
279 			    &zG,
280 			    &zB,
281 			    &zA,
282 			    alpha,
283 			    aprec,
284 			    zprec);
285 
286 	for (index = (height - 2) * width; index >= 0; index -= width)
287 		_blurinner ((guchar*) &ptr[index * channels],
288 			    &zR,
289 			    &zG,
290 			    &zB,
291 			    &zA,
292 			    alpha,
293 			    aprec,
294 			    zprec);
295 }
296 
297