summaryrefslogtreecommitdiff
path: root/gtk/gtkcairoblur.c
diff options
context:
space:
mode:
authorAndrea Cimitan <andrea.cimitan@canonical.com>2012-04-13 11:59:26 +0200
committerBenjamin Otte <otte@redhat.com>2012-09-20 02:45:41 +0200
commit43673dafdca7985d0bd896e14a57ae43acb5035c (patch)
tree223379a6d5be1815993c1c11944665450f3a0696 /gtk/gtkcairoblur.c
parent75e746f9ef0914dcdf96254ff44b0abf39e4f15a (diff)
downloadgtk+-43673dafdca7985d0bd896e14a57ae43acb5035c.tar.gz
Add code for blurring (original code from Unico, copyright fine for Gtk+)
Diffstat (limited to 'gtk/gtkcairoblur.c')
-rw-r--r--gtk/gtkcairoblur.c267
1 files changed, 267 insertions, 0 deletions
diff --git a/gtk/gtkcairoblur.c b/gtk/gtkcairoblur.c
new file mode 100644
index 0000000000..c85e2e2ee6
--- /dev/null
+++ b/gtk/gtkcairoblur.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2012 Canonical Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Authored by Andrea Cimitan <andrea.cimitan@canonical.com>
+ * Original code from Mirco Mueller <mirco.mueller@canonical.com>
+ *
+ */
+
+#include "gtkcairoblurprivate.h"
+
+#include <math.h>
+
+/*
+ * Notes:
+ * based on exponential-blur algorithm by Jani Huhtanen
+ */
+static inline void
+_blurinner (guchar* pixel,
+ gint *zR,
+ gint *zG,
+ gint *zB,
+ gint *zA,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint R;
+ gint G;
+ gint B;
+ guchar A;
+
+ R = *pixel;
+ G = *(pixel + 1);
+ B = *(pixel + 2);
+ A = *(pixel + 3);
+
+ *zR += (alpha * ((R << zprec) - *zR)) >> aprec;
+ *zG += (alpha * ((G << zprec) - *zG)) >> aprec;
+ *zB += (alpha * ((B << zprec) - *zB)) >> aprec;
+ *zA += (alpha * ((A << zprec) - *zA)) >> aprec;
+
+ *pixel = *zR >> zprec;
+ *(pixel + 1) = *zG >> zprec;
+ *(pixel + 2) = *zB >> zprec;
+ *(pixel + 3) = *zA >> zprec;
+}
+
+static inline void
+_blurrow (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint line,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint zR;
+ gint zG;
+ gint zB;
+ gint zA;
+ gint index;
+ guchar* scanline;
+
+ scanline = &(pixels[line * width * channels]);
+
+ zR = *scanline << zprec;
+ zG = *(scanline + 1) << zprec;
+ zB = *(scanline + 2) << zprec;
+ zA = *(scanline + 3) << zprec;
+
+ for (index = 0; index < width; index ++)
+ _blurinner (&scanline[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+
+ for (index = width - 2; index >= 0; index--)
+ _blurinner (&scanline[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+}
+
+static inline void
+_blurcol (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint x,
+ gint alpha,
+ gint aprec,
+ gint zprec)
+{
+ gint zR;
+ gint zG;
+ gint zB;
+ gint zA;
+ gint index;
+ guchar* ptr;
+
+ ptr = pixels;
+
+ ptr += x * channels;
+
+ zR = *((guchar*) ptr ) << zprec;
+ zG = *((guchar*) ptr + 1) << zprec;
+ zB = *((guchar*) ptr + 2) << zprec;
+ zA = *((guchar*) ptr + 3) << zprec;
+
+ for (index = width; index < (height - 1) * width; index += width)
+ _blurinner ((guchar*) &ptr[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+
+ for (index = (height - 2) * width; index >= 0; index -= width)
+ _blurinner ((guchar*) &ptr[index * channels],
+ &zR,
+ &zG,
+ &zB,
+ &zA,
+ alpha,
+ aprec,
+ zprec);
+}
+
+/*
+ * _expblur:
+ * @pixels: image data
+ * @width: image width
+ * @height: image height
+ * @channels: image channels
+ * @radius: kernel radius
+ * @aprec: precision of alpha parameter in fixed-point format 0.aprec
+ * @zprec: precision of state parameters zR,zG,zB and zA in fp format 8.zprec
+ *
+ * Performs an in-place blur of image data 'pixels'
+ * with kernel of approximate radius 'radius'.
+ *
+ * Blurs with two sided exponential impulse response.
+ *
+ */
+static void
+_expblur (guchar* pixels,
+ gint width,
+ gint height,
+ gint channels,
+ gint radius,
+ gint aprec,
+ gint zprec)
+{
+ gint alpha;
+ gint row = 0;
+ gint col = 0;
+
+ if (radius < 1)
+ return;
+
+ /* Calculate the alpha such that 90% of
+ * the kernel is within the radius.
+ * (Kernel extends to infinity) */
+ alpha = (gint) ((1 << aprec) * (1.0f - expf (-2.3f / (radius + 1.f))));
+
+ for (; row < height; row++)
+ _blurrow (pixels,
+ width,
+ height,
+ channels,
+ row,
+ alpha,
+ aprec,
+ zprec);
+
+ for(; col < width; col++)
+ _blurcol (pixels,
+ width,
+ height,
+ channels,
+ col,
+ alpha,
+ aprec,
+ zprec);
+}
+
+
+/*
+ * _gtk_cairo_blur_surface:
+ * @surface: a cairo image surface.
+ * @radius: the blur radius.
+ *
+ * Blurs the cairo image surface at the given radius.
+ *
+ */
+void
+_gtk_cairo_blur_surface (cairo_surface_t* surface,
+ guint radius)
+{
+ cairo_format_t format;
+ guchar* pixels;
+ guint width;
+ guint height;
+
+ g_return_if_fail (surface != NULL);
+ g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+ format = cairo_image_surface_get_format (surface);
+ g_return_if_fail (format == CAIRO_FORMAT_A8 ||
+ format == CAIRO_FORMAT_RGB24 ||
+ format == CAIRO_FORMAT_ARGB32);
+
+ if (radius == 0)
+ return;
+
+ /* Before we mess with the surface execute any pending drawing. */
+ cairo_surface_flush (surface);
+
+ pixels = cairo_image_surface_get_data (surface);
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+ format = cairo_image_surface_get_format (surface);
+
+ switch (format)
+ {
+ case CAIRO_FORMAT_ARGB32:
+ _expblur (pixels, width, height, 4, radius, 16, 7);
+ break;
+ case CAIRO_FORMAT_RGB24:
+ _expblur (pixels, width, height, 3, radius, 16, 7);
+ break;
+ case CAIRO_FORMAT_A8:
+ _expblur (pixels, width, height, 1, radius, 16, 7);
+ break;
+ default:
+ break;
+ }
+
+ /* Inform cairo we altered the surfaces contents. */
+ cairo_surface_mark_dirty (surface);
+}