/* screenshot-shadow.c - part of GNOME Screenshot
*
* Copyright (C) 2001-2006 Jonathan Blandford <jrb@alum.mit.edu>
* Copyright (C) 2013 Nils Dagsson Moskopp <nils@dieweltistgarnichtso.net>
*
* 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 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 General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*/
/* Shadow code from anders */
#include "config.h"
#include "screenshot-shadow.h"
#include <math.h>
#define BLUR_RADIUS 5
#define SHADOW_OFFSET (BLUR_RADIUS * 4 / 5)
#define SHADOW_OPACITY 0.5
#define OUTLINE_RADIUS 1
#define OUTLINE_OFFSET 0
#define OUTLINE_OPACITY 1.0
#define VINTAGE_SATURATION 0.8
#define VINTAGE_OVERLAY_COLOR 0xFFFBF2A3
#define VINTAGE_SOURCE_ALPHA 192
#define VINTAGE_OUTLINE_COLOR 0xFFEEEEEE
#define VINTAGE_OUTLINE_RADIUS 24
#define dist(x0, y0, x1, y1) sqrt(((x0) - (x1))*((x0) - (x1)) + ((y0) - (y1))*((y0) - (y1)))
typedef struct {
int size;
double *data;
} ConvFilter;
static double
gaussian (double x, double y, double r)
{
return ((1 / (2 * M_PI * r)) *
exp ((- (x * x + y * y)) / (2 * r * r)));
}
static ConvFilter *
create_blur_filter (int radius)
{
ConvFilter *filter;
int x, y;
double sum;
filter = g_new0 (ConvFilter, 1);
filter->size = radius * 2 + 1;
filter->data = g_new (double, filter->size * filter->size);
sum = 0.0;
for (y = 0 ; y < filter->size; y++)
{
for (x = 0 ; x < filter->size; x++)
{
sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1),
y - (filter->size >> 1),
radius);
}
}
for (y = 0; y < filter->size; y++)
{
for (x = 0; x < filter->size; x++)
{
filter->data[y * filter->size + x] /= sum;
}
}
return filter;
}
static ConvFilter *
create_outline_filter (int radius)
{
ConvFilter *filter;
double *iter;
filter = g_new0 (ConvFilter, 1);
filter->size = radius * 2 + 1;
filter->data = g_new (double, filter->size * filter->size);
for (iter = filter->data;
iter < filter->data + (filter->size * filter->size);
iter++)
{
*iter = 1.0;
}
return filter;
}
static GdkPixbuf *
create_effect (GdkPixbuf *src,
ConvFilter const *filter,
int radius,
int offset,
double opacity)
{
GdkPixbuf *dest;
int x, y, i, j;
int src_x, src_y;
int suma;
int dest_width, dest_height;
int src_width, src_height;
int src_rowstride, dest_rowstride;
gboolean src_has_alpha;
guchar *src_pixels, *dest_pixels;
src_has_alpha = gdk_pixbuf_get_has_alpha (src);
src_width = gdk_pixbuf_get_width (src);
src_height = gdk_pixbuf_get_height (src);
dest_width = src_width + 2 * radius + offset;
dest_height = src_height + 2 * radius + offset;
dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
TRUE,
gdk_pixbuf_get_bits_per_sample (src),
dest_width, dest_height);
gdk_pixbuf_fill (dest, 0);
src_pixels = gdk_pixbuf_get_pixels (src);
src_rowstride = gdk_pixbuf_get_rowstride (src);
dest_pixels = gdk_pixbuf_get_pixels (dest);
dest_rowstride = gdk_pixbuf_get_rowstride (dest);
for (y = 0; y < dest_height; y++)
{
for (x = 0; x < dest_width; x++)
{
suma = 0;
src_x = x - radius;
src_y = y - radius;
/* We don't need to compute effect here, since this pixel will be
* discarded when compositing */
if (src_x >= 0 && src_x < src_width &&
src_y >= 0 && src_y < src_height &&
(!src_has_alpha ||
src_pixels [src_y * src_rowstride + src_x * 4 + 3] == 0xFF))
continue;
for (i = 0; i < filter->size; i++)
{
for (j = 0; j < filter->size; j++)
{
src_y = -(radius + offset) + y - (filter->size >> 1) + i;
src_x = -(radius + offset) + x - (filter->size >> 1) + j;
if (src_y < 0 || src_y >= src_height ||
src_x < 0 || src_x >= src_width)
continue;
suma += ( src_has_alpha ?
src_pixels [src_y * src_rowstride + src_x * 4 + 3] :
0xFF ) * filter->data [i * filter->size + j];
}
}
dest_pixels [y * dest_rowstride + x * 4 + 3] = CLAMP (suma * opacity, 0x00, 0xFF);
}
}
return dest;
}
void
screenshot_add_shadow (GdkPixbuf **src)
{
GdkPixbuf *dest;
static ConvFilter *filter = NULL;
if (!filter)
filter = create_blur_filter (BLUR_RADIUS);
dest = create_effect (*src, filter,
BLUR_RADIUS,
SHADOW_OFFSET, SHADOW_OPACITY);
if (dest == NULL)
return;
gdk_pixbuf_composite (*src, dest,
BLUR_RADIUS, BLUR_RADIUS,
gdk_pixbuf_get_width (*src),
gdk_pixbuf_get_height (*src),
BLUR_RADIUS, BLUR_RADIUS, 1.0, 1.0,
GDK_INTERP_BILINEAR, 255);
g_object_unref (*src);
*src = dest;
}
void
screenshot_add_border (GdkPixbuf **src)
{
GdkPixbuf *dest;
static ConvFilter *filter = NULL;
if (!filter)
filter = create_outline_filter (OUTLINE_RADIUS);
dest = create_effect (*src, filter,
OUTLINE_RADIUS,
OUTLINE_OFFSET, OUTLINE_OPACITY);
if (dest == NULL)
return;
gdk_pixbuf_composite (*src, dest,
OUTLINE_RADIUS, OUTLINE_RADIUS,
gdk_pixbuf_get_width (*src),
gdk_pixbuf_get_height (*src),
OUTLINE_RADIUS, OUTLINE_RADIUS, 1.0, 1.0,
GDK_INTERP_BILINEAR, 255);
g_object_unref (*src);
*src = dest;
}
void
screenshot_add_vintage (GdkPixbuf **src)
{
GdkPixbuf *dest;
static ConvFilter *filter = NULL;
if (!filter)
filter = create_outline_filter (VINTAGE_OUTLINE_RADIUS);
dest = create_effect (*src, filter,
VINTAGE_OUTLINE_RADIUS,
OUTLINE_OFFSET, OUTLINE_OPACITY);
if (dest == NULL)
return;
gdk_pixbuf_fill(dest, VINTAGE_OUTLINE_COLOR);
gdk_pixbuf_composite (*src, dest,
VINTAGE_OUTLINE_RADIUS, VINTAGE_OUTLINE_RADIUS,
gdk_pixbuf_get_width (*src),
gdk_pixbuf_get_height (*src),
VINTAGE_OUTLINE_RADIUS, VINTAGE_OUTLINE_RADIUS, 1.0, 1.0,
GDK_INTERP_HYPER, 255);
g_object_unref (*src);
*src = dest;
gdk_pixbuf_saturate_and_pixelate(*src, *src,
VINTAGE_SATURATION, FALSE);
dest = gdk_pixbuf_composite_color_simple(*src,
gdk_pixbuf_get_width(*src),
gdk_pixbuf_get_height(*src),
GDK_INTERP_BILINEAR,
VINTAGE_SOURCE_ALPHA, 64,
VINTAGE_OVERLAY_COLOR, VINTAGE_OVERLAY_COLOR);
if (dest == NULL)
return;
g_object_unref (*src);
*src = dest;
}