gimp/app/operations/gimpoperationshapeburst.c

321 lines
11 KiB
C

/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpoperationshapeburst.c
* Copyright (C) 2012 Michael Natterer <mitch@gimp.org>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <cairo.h>
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "libgimpcolor/gimpcolor.h"
#include "libgimpmath/gimpmath.h"
#include "operations-types.h"
#include "gimpoperationshapeburst.h"
enum
{
PROP_0,
PROP_MAX_ITERATIONS,
PROP_PROGRESS
};
static void gimp_operation_shapeburst_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_operation_shapeburst_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static GeglRectangle
gimp_operation_shapeburst_get_required_for_output (GeglOperation *self,
const gchar *input_pad,
const GeglRectangle *roi);
static GeglRectangle
gimp_operation_shapeburst_get_cached_region (GeglOperation *self,
const GeglRectangle *roi);
static void gimp_operation_shapeburst_prepare (GeglOperation *operation);
static gboolean gimp_operation_shapeburst_process (GeglOperation *operation,
GeglBuffer *input,
GeglBuffer *output,
const GeglRectangle *roi,
gint level);
G_DEFINE_TYPE (GimpOperationShapeburst, gimp_operation_shapeburst,
GEGL_TYPE_OPERATION_FILTER)
#define parent_class gimp_operation_shapeburst_parent_class
static void
gimp_operation_shapeburst_class_init (GimpOperationShapeburstClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
object_class->set_property = gimp_operation_shapeburst_set_property;
object_class->get_property = gimp_operation_shapeburst_get_property;
gegl_operation_class_set_keys (operation_class,
"name", "gimp:shapeburst",
"categories", "gimp",
"description", "GIMP Shapeburst operation",
NULL);
operation_class->prepare = gimp_operation_shapeburst_prepare;
operation_class->get_required_for_output = gimp_operation_shapeburst_get_required_for_output;
operation_class->get_cached_region = gimp_operation_shapeburst_get_cached_region;
filter_class->process = gimp_operation_shapeburst_process;
g_object_class_install_property (object_class, PROP_MAX_ITERATIONS,
g_param_spec_double ("max-iterations",
"Max Iterations",
"Max Iterations",
0.0, G_MAXFLOAT, 0.0,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_PROGRESS,
g_param_spec_double ("progress",
"Progress",
"Progress indicator, and a bad hack",
0.0, 1.0, 0.0,
G_PARAM_READWRITE));
}
static void
gimp_operation_shapeburst_init (GimpOperationShapeburst *self)
{
}
static void
gimp_operation_shapeburst_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpOperationShapeburst *self = GIMP_OPERATION_SHAPEBURST (object);
switch (property_id)
{
case PROP_MAX_ITERATIONS:
g_value_set_double (value, self->max_iterations);
break;
case PROP_PROGRESS:
g_value_set_double (value, self->progress);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_operation_shapeburst_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpOperationShapeburst *self = GIMP_OPERATION_SHAPEBURST (object);
switch (property_id)
{
case PROP_MAX_ITERATIONS:
self->max_iterations = g_value_get_double (value);
break;
case PROP_PROGRESS:
self->progress = g_value_get_double (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_operation_shapeburst_prepare (GeglOperation *operation)
{
gegl_operation_set_format (operation, "input", babl_format ("Y u8"));
gegl_operation_set_format (operation, "output", babl_format ("Y float"));
}
static GeglRectangle
gimp_operation_shapeburst_get_required_for_output (GeglOperation *self,
const gchar *input_pad,
const GeglRectangle *roi)
{
return *gegl_operation_source_get_bounding_box (self, "input");
}
static GeglRectangle
gimp_operation_shapeburst_get_cached_region (GeglOperation *self,
const GeglRectangle *roi)
{
return *gegl_operation_source_get_bounding_box (self, "input");
}
static gboolean
gimp_operation_shapeburst_process (GeglOperation *operation,
GeglBuffer *input,
GeglBuffer *output,
const GeglRectangle *roi,
gint level)
{
const Babl *input_format = babl_format ("Y u8");
const Babl *output_format = babl_format ("Y float");
gfloat max_iterations = 0.0;
gfloat *distp_cur;
gfloat *distp_prev;
gfloat *memory;
gint length;
gint i;
length = roi->width + 1;
memory = g_new (gfloat, length * 2);
distp_prev = memory;
for (i = 0; i < length; i++)
distp_prev[i] = 0.0;
distp_prev += 1;
distp_cur = distp_prev + length;
for (i = 0; i < roi->height; i++)
{
gfloat *tmp;
gint src = 0;
gint j;
/* set the current dist row to 0's */
memset (distp_cur - 1, 0, sizeof (gfloat) * (length - 1));
for (j = 0; j < roi->width; j++)
{
gfloat float_tmp;
gfloat min_prev = MIN (distp_cur[j-1], distp_prev[j]);
gint min_left = MIN ((roi->width - j - 1), (roi->height - i - 1));
gint min = (gint) MIN (min_left, min_prev);
gint fraction = 255;
gint k;
/* This might need to be changed to 0
instead of k = (min) ? (min - 1) : 0 */
for (k = (min) ? (min - 1) : 0; k <= min; k++)
{
gint x = j;
gint y = i + k;
gint end = y - k;
while (y >= end)
{
guchar src_uchar;
#if 1
/* FIXME: this should be much faster, it converts
* to 32 bit rgba intermediately, bah...
*/
gegl_buffer_sample (input, x, y, NULL, &src_uchar,
input_format,
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
#else
gegl_buffer_get (input, GEGL_RECTANGLE (x, y, 1, 1), 1.0,
input_format, &src_uchar,
GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
#endif
src = src_uchar;
if (src == 0)
{
min = k;
y = -1;
break;
}
if (src < fraction)
fraction = src;
x++;
y--;
}
}
if (src != 0)
{
/* If min_left != min_prev use the previous fraction
* if it is less than the one found
*/
if (min_left != min)
{
gint prev_frac = (int) (255 * (min_prev - min));
if (prev_frac == 255)
prev_frac = 0;
fraction = MIN (fraction, prev_frac);
}
min++;
}
float_tmp = distp_cur[j] = min + fraction / 256.0;
if (float_tmp > max_iterations)
max_iterations = float_tmp;
}
/* set the dist row */
gegl_buffer_set (output,
GEGL_RECTANGLE (roi->x, roi->y + i,
roi->width, 1),
1.0, output_format, distp_cur,
GEGL_AUTO_ROWSTRIDE);
/* swap pointers around */
tmp = distp_prev;
distp_prev = distp_cur;
distp_cur = tmp;
g_object_set (operation,
"progress", (gdouble) i / roi->height,
NULL);
}
g_free (memory);
g_object_set (operation,
"max-iterations", (gdouble) max_iterations,
NULL);
return TRUE;
}