forked from AuroraMiddleware/gtk
45bdec84f5
This is considerably faster to draw and paint.
231 lines
5.8 KiB
C
231 lines
5.8 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 *zA,
|
|
gint alpha,
|
|
gint aprec,
|
|
gint zprec)
|
|
{
|
|
guchar A;
|
|
|
|
A = *pixel;
|
|
*zA += (alpha * ((A << zprec) - *zA)) >> aprec;
|
|
*pixel = *zA >> zprec;
|
|
}
|
|
|
|
static inline void
|
|
_blurrow (guchar* pixels,
|
|
gint width,
|
|
gint height,
|
|
gint rowstride,
|
|
gint line,
|
|
gint alpha,
|
|
gint aprec,
|
|
gint zprec)
|
|
{
|
|
gint zA;
|
|
gint index;
|
|
guchar* scanline;
|
|
|
|
scanline = &pixels[line * rowstride];
|
|
|
|
zA = *scanline << zprec;
|
|
|
|
for (index = 0; index < width; index ++)
|
|
_blurinner (&scanline[index],
|
|
&zA,
|
|
alpha,
|
|
aprec,
|
|
zprec);
|
|
|
|
for (index = width - 2; index >= 0; index--)
|
|
_blurinner (&scanline[index],
|
|
&zA,
|
|
alpha,
|
|
aprec,
|
|
zprec);
|
|
}
|
|
|
|
static inline void
|
|
_blurcol (guchar* pixels,
|
|
gint width,
|
|
gint height,
|
|
gint rowstride,
|
|
gint x,
|
|
gint alpha,
|
|
gint aprec,
|
|
gint zprec)
|
|
{
|
|
gint zA;
|
|
gint index;
|
|
guchar* ptr;
|
|
|
|
ptr = pixels;
|
|
|
|
ptr += x;
|
|
|
|
zA = *ptr << zprec;
|
|
|
|
for (index = 0; index < height; index++)
|
|
_blurinner (&ptr[index * rowstride],
|
|
&zA,
|
|
alpha,
|
|
aprec,
|
|
zprec);
|
|
|
|
for (index = height - 2; index >= 0; index--)
|
|
_blurinner (&ptr[index * rowstride],
|
|
&zA,
|
|
alpha,
|
|
aprec,
|
|
zprec);
|
|
}
|
|
|
|
/*
|
|
* _expblur:
|
|
* @pixels: image data
|
|
* @width: image width
|
|
* @height: image height
|
|
* @rowstride: image rowstride
|
|
* @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,
|
|
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,
|
|
row,
|
|
alpha,
|
|
aprec,
|
|
zprec);
|
|
|
|
for(col = 0; col < width; col++)
|
|
_blurcol (pixels,
|
|
width,
|
|
height,
|
|
rowstride,
|
|
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_A8);
|
|
|
|
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),
|
|
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);
|
|
}
|