280 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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    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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * _gtk_cairo_blur_compute_pixels:
 | |
|  * @radius: the radius to compute the pixels for
 | |
|  *
 | |
|  * Computes the number of pixels necessary to extend an image in one
 | |
|  * direction to hold the image with shadow.
 | |
|  *
 | |
|  * This is just the number of pixels added by the blur radius, shadow
 | |
|  * offset and spread are not included.
 | |
|  * 
 | |
|  * Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
 | |
|  * approximating a Gaussian using box blurs.  This yields quite a good
 | |
|  * approximation for a Gaussian.  Then we multiply this by 1.5 since our
 | |
|  * code wants the radius of the entire triple-box-blur kernel instead of
 | |
|  * the diameter of an individual box blur.  For more details, see:
 | |
|  * http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
 | |
|  * https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
 | |
|  */
 | |
| #define GAUSSIAN_SCALE_FACTOR ((3.0 * sqrt(2 * G_PI) / 4) * 1.5)
 | |
| 
 | |
| int
 | |
| _gtk_cairo_blur_compute_pixels (double radius)
 | |
| {
 | |
|   return floor (radius * GAUSSIAN_SCALE_FACTOR + 0.5);
 | |
| }
 | 
