/* * 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 * Original code from Mirco Mueller * */ #include "gtkcairoblurprivate.h" #include /* * 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 rowstride, 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 * rowstride]; 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 rowstride, 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 = 0; index < height; index++) _blurinner (&ptr[index * rowstride], &zR, &zG, &zB, &zA, alpha, aprec, zprec); for (index = height - 2; index >= 0; index--) _blurinner (&ptr[index * rowstride], &zR, &zG, &zB, &zA, alpha, aprec, zprec); } /* * _expblur: * @pixels: image data * @width: image width * @height: image height * @rowstride: image rowstride * @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 rowstride, gint channels, double radius, gint aprec, gint zprec) { gint alpha; int row, col; /* 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 = 0; row < height; row++) _blurrow (pixels, width, height, rowstride, channels, row, alpha, aprec, zprec); for(col = 0; col < width; col++) _blurcol (pixels, width, height, rowstride, 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, double radius) { cairo_format_t format; 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_RGB24 || format == CAIRO_FORMAT_ARGB32); if (radius == 0) return; /* Before we mess with the surface execute any pending drawing. */ cairo_surface_flush (surface); _expblur (cairo_image_surface_get_data (surface), cairo_image_surface_get_width (surface), cairo_image_surface_get_height (surface), cairo_image_surface_get_stride (surface), 4, radius, 16, 7); /* Inform cairo we altered the surfaces contents. */ cairo_surface_mark_dirty (surface); }