diff --git a/app/gegl/gimp-gegl-apply-operation.c b/app/gegl/gimp-gegl-apply-operation.c index cb38043268..cf519bea39 100644 --- a/app/gegl/gimp-gegl-apply-operation.c +++ b/app/gegl/gimp-gegl-apply-operation.c @@ -426,6 +426,28 @@ gimp_gegl_apply_shrink (GeglBuffer *src_buffer, g_object_unref (node); } +void +gimp_gegl_apply_flood (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect) +{ + GeglNode *node; + + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); + g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); + g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + + node = gegl_node_new_child (NULL, + "operation", "gimp:flood", + NULL); + + gimp_gegl_apply_operation (src_buffer, progress, undo_desc, + node, dest_buffer, dest_rect); + g_object_unref (node); +} + void gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, GimpProgress *progress, diff --git a/app/gegl/gimp-gegl-apply-operation.h b/app/gegl/gimp-gegl-apply-operation.h index 14e554278a..ad1fd91f7f 100644 --- a/app/gegl/gimp-gegl-apply-operation.h +++ b/app/gegl/gimp-gegl-apply-operation.h @@ -95,6 +95,12 @@ void gimp_gegl_apply_shrink (GeglBuffer *src_buffer, gint radius_y, gboolean edge_lock); +void gimp_gegl_apply_flood (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect); + void gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, GimpProgress *progress, const gchar *undo_desc, diff --git a/app/operations/Makefile.am b/app/operations/Makefile.am index 4d169f8413..82606b7915 100644 --- a/app/operations/Makefile.am +++ b/app/operations/Makefile.am @@ -48,6 +48,8 @@ libappoperations_generic_a_sources = \ gimpoperationcagetransform.h \ gimpoperationequalize.c \ gimpoperationequalize.h \ + gimpoperationflood.c \ + gimpoperationflood.h \ gimpoperationgrow.c \ gimpoperationgrow.h \ gimpoperationhistogramsink.c \ diff --git a/app/operations/gimp-operations.c b/app/operations/gimp-operations.c index e3f322ece9..afeec94f64 100644 --- a/app/operations/gimp-operations.c +++ b/app/operations/gimp-operations.c @@ -35,6 +35,7 @@ #include "gimpoperationcagecoefcalc.h" #include "gimpoperationcagetransform.h" #include "gimpoperationequalize.h" +#include "gimpoperationflood.h" #include "gimpoperationgrow.h" #include "gimpoperationhistogramsink.h" #include "gimpoperationmaskcomponents.h" @@ -103,6 +104,7 @@ gimp_operations_init (void) g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_COEF_CALC); g_type_class_ref (GIMP_TYPE_OPERATION_CAGE_TRANSFORM); g_type_class_ref (GIMP_TYPE_OPERATION_EQUALIZE); + g_type_class_ref (GIMP_TYPE_OPERATION_FLOOD); g_type_class_ref (GIMP_TYPE_OPERATION_GROW); g_type_class_ref (GIMP_TYPE_OPERATION_HISTOGRAM_SINK); g_type_class_ref (GIMP_TYPE_OPERATION_MASK_COMPONENTS); diff --git a/app/operations/gimpoperationflood.c b/app/operations/gimpoperationflood.c new file mode 100644 index 0000000000..59d4b93a25 --- /dev/null +++ b/app/operations/gimpoperationflood.c @@ -0,0 +1,240 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationflood.c + * Copyright (C) 2016 Ell + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" + +#include "operations-types.h" + +#include "gimpoperationflood.h" + + +static void gimp_operation_flood_prepare (GeglOperation *operation); +static GeglRectangle + gimp_operation_flood_get_required_for_output (GeglOperation *self, + const gchar *input_pad, + const GeglRectangle *roi); +static GeglRectangle + gimp_operation_flood_get_cached_region (GeglOperation *self, + const GeglRectangle *roi); + +static gboolean gimp_operation_flood_process_segment (const gfloat *src, + gfloat *dest, + gchar *changed, + gint size); +static gboolean gimp_operation_flood_process (GeglOperation *operation, + GeglBuffer *input, + GeglBuffer *output, + const GeglRectangle *roi, + gint level); + + +G_DEFINE_TYPE (GimpOperationFlood, gimp_operation_flood, + GEGL_TYPE_OPERATION_FILTER) + +#define parent_class gimp_operation_flood_parent_class + + +static void +gimp_operation_flood_class_init (GimpOperationFloodClass *klass) +{ + GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass); + GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass); + + operation_class->want_in_place = FALSE; + operation_class->threaded = FALSE; + + gegl_operation_class_set_keys (operation_class, + "name", "gimp:flood", + "categories", "gimp", + "description", "GIMP Flood operation", + NULL); + + operation_class->prepare = gimp_operation_flood_prepare; + operation_class->get_required_for_output = gimp_operation_flood_get_required_for_output; + operation_class->get_cached_region = gimp_operation_flood_get_cached_region; + + filter_class->process = gimp_operation_flood_process; +} + +static void +gimp_operation_flood_init (GimpOperationFlood *self) +{ +} + +static void +gimp_operation_flood_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_flood_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_flood_get_cached_region (GeglOperation *self, + const GeglRectangle *roi) +{ + return *gegl_operation_source_get_bounding_box (self, "input"); +} + +static gboolean +gimp_operation_flood_process_segment (const gfloat *src, + gfloat *dest, + gchar *changed, + gint size) +{ + gint dir; + gboolean any_changed = FALSE; + + for (dir = 1; dir >= -1; dir -= 2) /* for dir in [1, -1]: ... */ + { + gint i; + gfloat level = 0.0; + + for (i = size; i; i--) + { + if (*src > level) { level = *src; } + if (*dest < level) { level = *dest; } + else if (level < *dest) { *dest = level; + *changed = any_changed = TRUE; } + + if (i > 1) + { + src += dir; + dest += dir; + changed += dir; + } + } + } + + return any_changed; +} + +static gboolean +gimp_operation_flood_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 *src_buffer, *dest_buffer; + gchar *horz_changed, *vert_changed; + gboolean any_changed; + gint forced; + gboolean horz; + GeglColor *color; + + g_return_val_if_fail (input != output, FALSE); + + src_buffer = g_new (gfloat, MAX (roi->width, roi->height)); + dest_buffer = g_new (gfloat, MAX (roi->width, roi->height)); + horz_changed = g_new (gchar, roi->width); + vert_changed = g_new (gchar, roi->height); + + color = gegl_color_new ("#fff"); + gegl_buffer_set_color (output, roi, color); + g_object_unref (color); + + forced = 2; /* Force at least one horizontal and one vertical iterations. */ + horz = TRUE; + do + { + gint count, size; + GeglRectangle rect = *roi; + gint *coord; + gchar *prev_changed, *curr_changed; + gint i; + + any_changed = FALSE; + + if (horz) + { + count = roi->height; + size = roi->width; + rect.height = 1; + coord = &rect.y; + prev_changed = vert_changed; + curr_changed = horz_changed; + } + else + { + count = roi->width; + size = roi->height; + rect.width = 1; + coord = &rect.x; + prev_changed = horz_changed; + curr_changed = vert_changed; + } + + for (i = 0; i < count; i++, (*coord)++) + { + gboolean this_changed; + + if (! (forced || prev_changed[i])) + continue; + prev_changed[i] = FALSE; + + gegl_buffer_get (input, &rect, 1.0, input_format, src_buffer, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + gegl_buffer_get (output, &rect, 1.0, output_format, dest_buffer, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + this_changed = gimp_operation_flood_process_segment (src_buffer, + dest_buffer, + curr_changed, + size); + + if (this_changed) + { + gegl_buffer_set (output, &rect, 0, output_format, dest_buffer, + GEGL_AUTO_ROWSTRIDE); + + any_changed = TRUE; + } + } + + horz = ! horz; + if (forced) + forced--; + } + while (forced || any_changed); + + g_free (src_buffer); + g_free (dest_buffer); + g_free (horz_changed); + g_free (vert_changed); + + return TRUE; +} diff --git a/app/operations/gimpoperationflood.h b/app/operations/gimpoperationflood.h new file mode 100644 index 0000000000..60787f274d --- /dev/null +++ b/app/operations/gimpoperationflood.h @@ -0,0 +1,53 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpoperationflood.h + * Copyright (C) 2016 Ell + * + * 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 . + */ + +#ifndef __GIMP_OPERATION_FLOOD_H__ +#define __GIMP_OPERATION_FLOOD_H__ + + +#include + + +#define GIMP_TYPE_OPERATION_FLOOD (gimp_operation_flood_get_type ()) +#define GIMP_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFlood)) +#define GIMP_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass)) +#define GIMP_IS_OPERATION_FLOOD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_OPERATION_FLOOD)) +#define GIMP_IS_OPERATION_FLOOD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_OPERATION_FLOOD)) +#define GIMP_OPERATION_FLOOD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_OPERATION_FLOOD, GimpOperationFloodClass)) + + +typedef struct _GimpOperationFlood GimpOperationFlood; +typedef struct _GimpOperationFloodClass GimpOperationFloodClass; + +struct _GimpOperationFlood +{ + GeglOperationFilter parent_instance; +}; + +struct _GimpOperationFloodClass +{ + GeglOperationFilterClass parent_class; +}; + + +GType gimp_operation_flood_get_type (void) G_GNUC_CONST; + + +#endif /* __GIMP_OPERATION_FLOOD_H__ */