Commit 5751bb11 authored by Jehan's avatar Jehan
Browse files

Bug 781621 - PDB shapeburst gradient is slower than the blend tool.

PDB function gimp_edit_blend() was based on "gimp:shapeburst" operation
whereas the rest of GIMP (in particular, the Blend tool) used
"gegl:distance-transform" which is much faster.
Setting the operation to "manhattan" metric ensures that it still
renders the same way as in 2.8 while being a lot faster.

There was still a problem regarding as how it renders differently from
the Blend tool, but it turns out that the Blend tool is the one
rendering differently from how it used to in 2.8. We should discuss
adding the "metric" property in the tool options.
parent 6f127f9d
......@@ -200,16 +200,16 @@ gimp_drawable_blend_shapeburst_distmap (GimpDrawable *drawable,
}
}
if (legacy_shapeburst)
shapeburst = gegl_node_new_child (NULL,
"operation", "gimp:shapeburst",
"normalize", TRUE,
NULL);
else
shapeburst = gegl_node_new_child (NULL,
"operation", "gegl:distance-transform",
"normalize", TRUE,
NULL);
shapeburst = gegl_node_new_child (NULL,
"operation", "gegl:distance-transform",
"normalize", TRUE,
/* Legacy blend used "manhattan"
* metric to compute distance, vs.
* "euclidean". API needs to stay
* compatible.
*/
"metric", legacy_shapeburst ? 1 : 0,
NULL);
if (progress)
gimp_gegl_progress_connect (shapeburst, progress,
......
......@@ -70,8 +70,6 @@ libappoperations_a_sources = \
gimpoperationsemiflatten.h \
gimpoperationsetalpha.c \
gimpoperationsetalpha.h \
gimpoperationshapeburst.c \
gimpoperationshapeburst.h \
gimpoperationshrink.c \
gimpoperationshrink.h \
gimpoperationthresholdalpha.c \
......
......@@ -45,7 +45,6 @@
#include "gimpoperationscalarmultiply.h"
#include "gimpoperationsemiflatten.h"
#include "gimpoperationsetalpha.h"
#include "gimpoperationshapeburst.h"
#include "gimpoperationshrink.h"
#include "gimpoperationthresholdalpha.h"
......@@ -139,7 +138,6 @@ gimp_operations_init (Gimp *gimp)
g_type_class_ref (GIMP_TYPE_OPERATION_SCALAR_MULTIPLY);
g_type_class_ref (GIMP_TYPE_OPERATION_SEMI_FLATTEN);
g_type_class_ref (GIMP_TYPE_OPERATION_SET_ALPHA);
g_type_class_ref (GIMP_TYPE_OPERATION_SHAPEBURST);
g_type_class_ref (GIMP_TYPE_OPERATION_SHRINK);
g_type_class_ref (GIMP_TYPE_OPERATION_THRESHOLD_ALPHA);
......
/* 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_NORMALIZE,
};
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;
/* This operation is currently broken when multi-threaded.
* Cf. bug 781621. FIXME.
*/
operation_class->threaded = FALSE;
filter_class->process = gimp_operation_shapeburst_process;
g_object_class_install_property (object_class, PROP_NORMALIZE,
g_param_spec_boolean ("normalize",
"Normalize",
"Normalize",
FALSE,
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_NORMALIZE:
g_value_set_boolean (value, self->normalize);
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_NORMALIZE:
self->normalize = g_value_get_boolean (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 float"));
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 float");
const Babl *output_format = babl_format ("Y float");
gfloat max_dist = 0.0;
gfloat *distbuf;
gint x, y;
distbuf = g_new0 (gfloat, (roi->width + 1) * 2);
for (y = 0; y < roi->height; y++)
{
gfloat *distbuf_cur;
gfloat *distbuf_prev;
gfloat src = 0.0;
/* toggling distance buffers for the current and previous row.
* with one spare zero element on the left side */
if (y % 2)
{
distbuf_prev = distbuf + 1;
distbuf_cur = distbuf + 1 + roi->width + 1;
}
else
{
distbuf_prev = distbuf + 1 + roi->width + 1;
distbuf_cur = distbuf + 1;
}
/* clear the current rows distbuffer */
memset (distbuf_cur, 0, sizeof (gfloat) * roi->width);
for (x = 0; x < roi->width; x++)
{
gfloat dist_nw = MIN (distbuf_cur[x-1], distbuf_prev[x]);
gfloat dist_se = MIN ((roi->width - x - 1), (roi->height - y - 1));
gfloat dist = MIN (dist_se, dist_nw);
gfloat frac = 1.0;
gint k;
#define EPSILON 0.0001
/* This loop used to start at "k = (dist) ? (dist - 1) : 0"
* and this comment suggested it might have to be changed to
* "k = 0", but "k = 0" increases processing time significantly.
*
* When porting this to float, i noticed that starting at
* "dist - 2" gets rid of a lot of 8-bit artifacts, while starting
* at "dist - 3" or smaller would introduce different artifacts.
*
* Note that I didn't really understand the entire algorithm,
* I just "blindly" ported it to float :) --mitch
*/
/* the idea here is to check the south-eastern "thick" diagonal
* along the already established accumulated minimum distance.
*
* it is easy to understand why it is sufficient to check
* the triangle to this diagonal (k=0), but in fact we can
* omit that, since this check has already been incorporated
* in the accumulated minimum distance of the previous pixels.
*
* Not sure however if this is implemented properly.
* -- simon
*/
for (k = MAX (dist - 2, 0); k <= dist; k++)
{
gint x1 = x;
gint y1 = y + k;
while (y1 >= y)
{
/* FIXME: this should be much faster, it converts to
* 32 bit rgba intermediately, bah...
*/
gegl_buffer_sample (input, x1 + roi->x, y1 + roi->y,
NULL, &src, input_format,
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
if (src < EPSILON)
{
dist = k;
break;
}
frac = MIN (frac, src);
x1++;
y1--;
}
}
if (src > EPSILON)
{
/* If dist_se != dist_nw use the previous frac
* if it is less than the one found
*/
if (dist_se != dist)
{
gfloat prev_frac = dist_nw - dist;
if (ABS (prev_frac - 1.0) < EPSILON)
prev_frac = 0.0;
frac = MIN (frac, prev_frac);
}
dist += 1.0;
}
distbuf_cur[x] = dist + frac;
max_dist = MAX (max_dist, distbuf_cur[x]);
}
/* set the dist row */
gegl_buffer_set (output,
GEGL_RECTANGLE (roi->x, roi->y + y,
roi->width, 1),
0, output_format, distbuf_cur,
GEGL_AUTO_ROWSTRIDE);
gegl_operation_progress (operation, (gdouble) y / roi->height, "");
}
g_free (distbuf);
if (GIMP_OPERATION_SHAPEBURST (operation)->normalize && max_dist > 0.0)
{
GeglBufferIterator *iter;
iter = gegl_buffer_iterator_new (output, NULL, 0, NULL,
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (iter))
{
gint count = iter->length;
gfloat *data = iter->data[0];
while (count--)
*data++ /= max_dist;
}
}
gegl_operation_progress (operation, 1.0, "");
return TRUE;
}
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* gimpoperationshapeburst.h
* 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/>.
*/
#ifndef __GIMP_OPERATION_SHAPEBURST_H__
#define __GIMP_OPERATION_SHAPEBURST_H__
#include <gegl-plugin.h>
#define GIMP_TYPE_OPERATION_SHAPEBURST (gimp_operation_shapeburst_get_type ())
#define GIMP_OPERATION_SHAPEBURST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_SHAPEBURST, GimpOperationShapeburst))
#define GIMP_OPERATION_SHAPEBURST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_SHAPEBURST, GimpOperationShapeburstClass))
#define GIMP_IS_OPERATION_SHAPEBURST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_SHAPEBURST))
#define GIMP_IS_OPERATION_SHAPEBURST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_SHAPEBURST))
#define GIMP_OPERATION_SHAPEBURST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_SHAPEBURST, GimpOperationShapeburstClass))
typedef struct _GimpOperationShapeburst GimpOperationShapeburst;
typedef struct _GimpOperationShapeburstClass GimpOperationShapeburstClass;
struct _GimpOperationShapeburst
{
GeglOperationFilter parent_instance;
gboolean normalize;
};
struct _GimpOperationShapeburstClass
{
GeglOperationFilterClass parent_class;
};
GType gimp_operation_shapeburst_get_type (void) G_GNUC_CONST;
#endif /* __GIMP_OPERATION_SHAPEBURST_H__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment