]> Pileus Git - ~andy/gtk/blob - gtk/gtkcairoblur.c
8186fc362fc26f8b0f8871dcfce4901c975964e3
[~andy/gtk] / gtk / gtkcairoblur.c
1 /*
2  * Copyright (C) 2012 Canonical Ltd
3  *
4  * This  library is free  software; you can  redistribute it and/or
5  * modify it  under  the terms  of the  GNU Lesser  General  Public
6  * License  as published  by the Free  Software  Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed  in the hope that it will be useful,
10  * but  WITHOUT ANY WARRANTY; without even  the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License  along  with  this library;  if not,  write to  the Free
16  * Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
17  * MA 02110-1301, USA.
18  *
19  * Authored by Andrea Cimitan <andrea.cimitan@canonical.com>
20  * Original code from Mirco Mueller <mirco.mueller@canonical.com>
21  *
22  */
23
24 #include "gtkcairoblurprivate.h"
25
26 #include <math.h>
27
28 /*
29  * Notes:
30  *   based on exponential-blur algorithm by Jani Huhtanen
31  */
32 static inline void
33 _blurinner (guchar* pixel,
34             gint   *zR,
35             gint   *zG,
36             gint   *zB,
37             gint   *zA,
38             gint    alpha,
39             gint    aprec,
40             gint    zprec)
41 {
42   gint R;
43   gint G;
44   gint B;
45   guchar A;
46
47   R = *pixel;
48   G = *(pixel + 1);
49   B = *(pixel + 2);
50   A = *(pixel + 3);
51
52   *zR += (alpha * ((R << zprec) - *zR)) >> aprec;
53   *zG += (alpha * ((G << zprec) - *zG)) >> aprec;
54   *zB += (alpha * ((B << zprec) - *zB)) >> aprec;
55   *zA += (alpha * ((A << zprec) - *zA)) >> aprec;
56
57   *pixel       = *zR >> zprec;
58   *(pixel + 1) = *zG >> zprec;
59   *(pixel + 2) = *zB >> zprec;
60   *(pixel + 3) = *zA >> zprec;
61
62
63 static inline void
64 _blurrow (guchar* pixels,
65           gint    width,
66           gint    height,
67           gint    channels,
68           gint    line,
69           gint    alpha,
70           gint    aprec,
71           gint    zprec)
72 {
73   gint    zR;
74   gint    zG;
75   gint    zB;
76   gint    zA;
77   gint    index;
78   guchar* scanline;
79
80   scanline = &(pixels[line * width * channels]);
81
82   zR = *scanline << zprec;
83   zG = *(scanline + 1) << zprec;
84   zB = *(scanline + 2) << zprec;
85   zA = *(scanline + 3) << zprec;
86
87   for (index = 0; index < width; index ++)
88     _blurinner (&scanline[index * channels],
89                 &zR,
90                 &zG,
91                 &zB,
92                 &zA,
93                 alpha,
94                 aprec,
95                 zprec);
96
97   for (index = width - 2; index >= 0; index--)
98     _blurinner (&scanline[index * channels],
99                 &zR,
100                 &zG,
101                 &zB,
102                 &zA,
103                 alpha,
104                 aprec,
105                 zprec);
106 }
107
108 static inline void
109 _blurcol (guchar* pixels,
110           gint    width,
111           gint    height,
112           gint    channels,
113           gint    x,
114           gint    alpha,
115           gint    aprec,
116           gint    zprec)
117 {
118   gint zR;
119   gint zG;
120   gint zB;
121   gint zA;
122   gint index;
123   guchar* ptr;
124
125   ptr = pixels;
126   
127   ptr += x * channels;
128
129   zR = *((guchar*) ptr    ) << zprec;
130   zG = *((guchar*) ptr + 1) << zprec;
131   zB = *((guchar*) ptr + 2) << zprec;
132   zA = *((guchar*) ptr + 3) << zprec;
133
134   for (index = width; index < (height - 1) * width; index += width)
135     _blurinner ((guchar*) &ptr[index * channels],
136                 &zR,
137                 &zG,
138                 &zB,
139                 &zA,
140                 alpha,
141                 aprec,
142                 zprec);
143
144   for (index = (height - 2) * width; index >= 0; index -= width)
145     _blurinner ((guchar*) &ptr[index * channels],
146                 &zR,
147                 &zG,
148                 &zB,
149                 &zA,
150                 alpha,
151                 aprec,
152                 zprec);
153 }
154
155 /*
156  * _expblur:
157  * @pixels: image data
158  * @width: image width
159  * @height: image height
160  * @channels: image channels
161  * @radius: kernel radius
162  * @aprec: precision of alpha parameter in fixed-point format 0.aprec
163  * @zprec: precision of state parameters zR,zG,zB and zA in fp format 8.zprec
164  *
165  * Performs an in-place blur of image data 'pixels'
166  * with kernel of approximate radius 'radius'.
167  *
168  * Blurs with two sided exponential impulse response.
169  *
170  */
171 static void
172 _expblur (guchar* pixels,
173           gint    width,
174           gint    height,
175           gint    channels,
176           double  radius,
177           gint    aprec,
178           gint    zprec)
179 {
180   gint alpha;
181   int row, col;
182
183   /* Calculate the alpha such that 90% of 
184    * the kernel is within the radius.
185    * (Kernel extends to infinity) */
186   alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
187
188   for (row = 0; row < height; row++)
189     _blurrow (pixels,
190               width,
191               height,
192               channels,
193               row,
194               alpha,
195               aprec,
196               zprec);
197
198   for(col = 0; col < width; col++)
199     _blurcol (pixels,
200               width,
201               height,
202               channels,
203               col,
204               alpha,
205               aprec,
206               zprec);
207 }
208
209
210 /*
211  * _gtk_cairo_blur_surface:
212  * @surface: a cairo image surface.
213  * @radius: the blur radius.
214  *
215  * Blurs the cairo image surface at the given radius.
216  */
217 void
218 _gtk_cairo_blur_surface (cairo_surface_t* surface,
219                          double           radius)
220 {
221   cairo_format_t format;
222
223   g_return_if_fail (surface != NULL);
224   g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
225
226   format = cairo_image_surface_get_format (surface);
227   g_return_if_fail (format == CAIRO_FORMAT_RGB24 ||
228                     format == CAIRO_FORMAT_ARGB32);
229
230   if (radius == 0)
231     return;
232
233   /* Before we mess with the surface execute any pending drawing. */
234   cairo_surface_flush (surface);
235
236   _expblur (cairo_image_surface_get_data (surface),
237             cairo_image_surface_get_width (surface),
238             cairo_image_surface_get_height (surface),
239             4,
240             radius,
241             16,
242             7);
243
244   /* Inform cairo we altered the surfaces contents. */
245   cairo_surface_mark_dirty (surface);
246 }