mirror of https://github.com/GNOME/gimp.git
321 lines
11 KiB
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;
|
|
}
|