mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 21:51:08 +00:00
gtkcairoblur: Replace our exponential blur with the box blur from mutter
https://bugzilla.gnome.org/show_bug.cgi?id=734053
This commit is contained in:
parent
1148c96dc4
commit
f5d8f75c0f
@ -1,170 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Canonical Ltd
|
||||
* Copyright (C) 2014 Red Hat
|
||||
*
|
||||
* 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 program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU 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.
|
||||
* This program 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
|
||||
* 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>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
* Owen Taylor <otaylor@redhat.com>
|
||||
*/
|
||||
|
||||
#include "gtkcairoblurprivate.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.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.
|
||||
/* This applies a single box blur pass to a horizontal range of pixels;
|
||||
* since the box blur has the same weight for all pixels, we can
|
||||
* implement an efficient sliding window algorithm where we add
|
||||
* in pixels coming into the window from the right and remove
|
||||
* them when they leave the windw to the left.
|
||||
*
|
||||
* d is the filter width; for even d shift indicates how the blurred
|
||||
* result is aligned with the original - does ' x ' go to ' yy' (shift=1)
|
||||
* or 'yy ' (shift=-1)
|
||||
*/
|
||||
static void
|
||||
_expblur (guchar* pixels,
|
||||
gint width,
|
||||
gint height,
|
||||
gint rowstride,
|
||||
double radius,
|
||||
gint aprec,
|
||||
gint zprec)
|
||||
blur_xspan (guchar *row,
|
||||
guchar *tmp_buffer,
|
||||
int row_width,
|
||||
int d,
|
||||
int shift)
|
||||
{
|
||||
gint alpha;
|
||||
int row, col;
|
||||
int offset;
|
||||
int sum = 0;
|
||||
int i;
|
||||
|
||||
/* 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))));
|
||||
if (d % 2 == 1)
|
||||
offset = d / 2;
|
||||
else
|
||||
offset = (d - shift) / 2;
|
||||
|
||||
for (row = 0; row < height; row++)
|
||||
_blurrow (pixels,
|
||||
width,
|
||||
height,
|
||||
rowstride,
|
||||
row,
|
||||
alpha,
|
||||
aprec,
|
||||
zprec);
|
||||
/* All the conditionals in here look slow, but the branches will
|
||||
* be well predicted and there are enough different possibilities
|
||||
* that trying to write this as a series of unconditional loops
|
||||
* is hard and not an obvious win. The main slow down here seems
|
||||
* to be the integer division for pixel; one possible optimization
|
||||
* would be to accumulate into two 16-bit integer buffers and
|
||||
* only divide down after all three passes. (SSE parallel implementation
|
||||
* of the divide step is possible.)
|
||||
*/
|
||||
for (i = -d + offset; i < row_width + offset; i++)
|
||||
{
|
||||
if (i >= 0 && i < row_width)
|
||||
sum += row[i];
|
||||
|
||||
for(col = 0; col < width; col++)
|
||||
_blurcol (pixels,
|
||||
width,
|
||||
height,
|
||||
rowstride,
|
||||
col,
|
||||
alpha,
|
||||
aprec,
|
||||
zprec);
|
||||
if (i >= offset)
|
||||
{
|
||||
if (i >= d)
|
||||
sum -= row[i - d];
|
||||
|
||||
tmp_buffer[i - offset] = (sum + d / 2) / d;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy (row, tmp_buffer, row_width);
|
||||
}
|
||||
|
||||
static void
|
||||
blur_rows (guchar *dst_buffer,
|
||||
guchar *tmp_buffer,
|
||||
int buffer_width,
|
||||
int buffer_height,
|
||||
int d)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < buffer_height; i++)
|
||||
{
|
||||
guchar *row = dst_buffer + i * buffer_width;
|
||||
|
||||
/* We want to produce a symmetric blur that spreads a pixel
|
||||
* equally far to the left and right. If d is odd that happens
|
||||
* naturally, but for d even, we approximate by using a blur
|
||||
* on either side and then a centered blur of size d + 1.
|
||||
* (techique also from the SVG specification)
|
||||
*/
|
||||
if (d % 2 == 1)
|
||||
{
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d, 1);
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d, -1);
|
||||
blur_xspan (row, tmp_buffer, buffer_width, d + 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Swaps width and height. Either swaps in-place and returns the original
|
||||
* buffer or allocates a new buffer, frees the original buffer and returns
|
||||
* the new buffer.
|
||||
*/
|
||||
static void
|
||||
flip_buffer (guchar *dst_buffer,
|
||||
guchar *src_buffer,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
/* Working in blocks increases cache efficiency, compared to reading
|
||||
* or writing an entire column at once */
|
||||
#define BLOCK_SIZE 16
|
||||
|
||||
int i0, j0;
|
||||
|
||||
for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
|
||||
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
|
||||
{
|
||||
int max_j = MIN(j0 + BLOCK_SIZE, height);
|
||||
int max_i = MIN(i0 + BLOCK_SIZE, width);
|
||||
int i, j;
|
||||
|
||||
for (i = i0; i < max_i; i++)
|
||||
for (j = j0; j < max_j; j++)
|
||||
dst_buffer[i * height + j] = src_buffer[j * width + i];
|
||||
}
|
||||
#undef BLOCK_SIZE
|
||||
}
|
||||
|
||||
static void
|
||||
_boxblur (guchar *buffer,
|
||||
int width,
|
||||
int height,
|
||||
int radius)
|
||||
{
|
||||
guchar *flipped_buffer;
|
||||
|
||||
flipped_buffer = g_malloc (width * height);
|
||||
|
||||
/* Step 1: swap rows and columns */
|
||||
flip_buffer (flipped_buffer, buffer, width, height);
|
||||
|
||||
/* Step 2: blur rows (really columns) */
|
||||
blur_rows (flipped_buffer, buffer, height, width, radius);
|
||||
|
||||
/* Step 3: swap rows and columns */
|
||||
flip_buffer (buffer, flipped_buffer, height, width);
|
||||
|
||||
/* Step 4: blur rows */
|
||||
blur_rows (buffer, flipped_buffer, width, height, radius);
|
||||
|
||||
g_free (flipped_buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* _gtk_cairo_blur_surface:
|
||||
@ -175,9 +176,10 @@ _expblur (guchar* pixels,
|
||||
*/
|
||||
void
|
||||
_gtk_cairo_blur_surface (cairo_surface_t* surface,
|
||||
double radius)
|
||||
double radius_d)
|
||||
{
|
||||
cairo_format_t format;
|
||||
int radius = radius_d;
|
||||
|
||||
g_return_if_fail (surface != NULL);
|
||||
g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
|
||||
@ -191,13 +193,10 @@ _gtk_cairo_blur_surface (cairo_surface_t* surface,
|
||||
/* 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),
|
||||
_boxblur (cairo_image_surface_get_data (surface),
|
||||
cairo_image_surface_get_stride (surface),
|
||||
radius,
|
||||
16,
|
||||
7);
|
||||
cairo_image_surface_get_height (surface),
|
||||
radius);
|
||||
|
||||
/* Inform cairo we altered the surfaces contents. */
|
||||
cairo_surface_mark_dirty (surface);
|
||||
|
Loading…
Reference in New Issue
Block a user