Code

Add auto gap filling for Paint Bucket and implement "safety valve" to prevent runaway...
authorjohncoswell <johncoswell@users.sourceforge.net>
Mon, 28 May 2007 18:02:50 +0000 (18:02 +0000)
committerjohncoswell <johncoswell@users.sourceforge.net>
Mon, 28 May 2007 18:02:50 +0000 (18:02 +0000)
src/flood-context.cpp
src/flood-context.h
src/widgets/toolbox.cpp

index 3b7cc8d741d01c39e7c0f1efae6cf39237fb5740..b5b6359b47d4a886548348f11dfd322a672f12ed 100644 (file)
@@ -253,6 +253,17 @@ GList * flood_channels_dropdown_items_list() {
     return glist;
 }
 
+GList * flood_autogap_dropdown_items_list() {
+    GList *glist = NULL;
+
+    glist = g_list_append (glist, _("None"));
+    glist = g_list_append (glist, _("Small"));
+    glist = g_list_append (glist, _("Medium"));
+    glist = g_list_append (glist, _("Large"));
+
+    return glist;
+}
+
 static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned char *dtc, int threshold, PaintBucketChannels method) {
     int diff = 0;
     float hsl_check[3], hsl_orig[3];
@@ -296,12 +307,12 @@ static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned c
     return false;
 }
 
-static bool try_add_to_queue(std::deque<NR::Point> *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig, unsigned char *dtc, int x, int y, int width, int threshold, PaintBucketChannels method, bool fill_switch) {
+static bool try_add_to_queue(std::deque<NR::Point> *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig, unsigned char *dtc, int x, int y, int width, int threshold, PaintBucketChannels method, bool fill_switch, unsigned int max_queue_size) {
     unsigned char *t = get_pixel(px, x, y, width);
     if (compare_pixels(t, orig, dtc, threshold, method)) {
         unsigned char *trace_t = get_pixel(trace_px, x, y, width);
         if (trace_t[3] != 255 && trace_t[0] != 255) {
-            if (fill_switch) {
+            if (fill_switch && (fill_queue->size() < max_queue_size)) {
                 fill_queue->push_back(NR::Point(x, y));
                 trace_t[0] = 255;
             }
@@ -432,7 +443,9 @@ struct bitmap_coords_info {
     int y;
     int y_limit;
     int width;
+    int height;
     int threshold;
+    int radius;
     PaintBucketChannels method;
     unsigned char *dtc;
     bool top_fill;
@@ -447,13 +460,49 @@ enum ScanlineCheckResult {
     SCANLINE_CHECK_BOUNDARY
 };
 
+static void paint_pixel(guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci) {
+    unsigned char *t, *trace_t;
+  
+    for (int r = 0; r <= bci.radius; r++) {
+        if (r == 0) {
+            t = get_pixel(px, bci.x, bci.y, bci.width);
+            if (compare_pixels(t, orig_color, bci.dtc, bci.threshold, bci.method)) {
+                for (int i = 0; i < 4; i++) { t[i] = 255 - t[i]; }
+                trace_t = get_pixel(trace_px, bci.x, bci.y, bci.width);
+                trace_t[3] = 255; 
+            }
+        } else {
+            for (int y = -bci.radius; y <= bci.radius; y++) {
+                for (int x = -bci.radius; x <= bci.radius; x++) {
+                    int tx = bci.x + x;
+                    int ty = bci.y + y;
+                    
+                    if (   (tx >= 0) 
+                        && (tx < bci.width)
+                        && (ty >= 0)
+                        && (ty < bci.height)) {
+                        t = get_pixel(px, tx, ty, bci.width);
+                        if (compare_pixels(t, orig_color, bci.dtc, bci.threshold, bci.method)) {
+                            for (int i = 0; i < 4; i++) { t[i] = 255 - t[i]; }
+                            trace_t = get_pixel(trace_px, tx, ty, bci.width);
+                            trace_t[3] = 255; 
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
 static ScanlineCheckResult perform_bitmap_scanline_check(std::deque<NR::Point> *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci) {
     bool aborted = false;
     bool reached_screen_boundary = false;
     bool ok;
   
     bool keep_tracing;
-    unsigned char *t, *trace_t;
+    unsigned char *t;
+  
+    unsigned int max_queue_size = bci.width * bci.height;
   
     do {
         ok = false;
@@ -466,19 +515,17 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque<NR::Point> *
         if (keep_tracing) {
             t = get_pixel(px, bci.x, bci.y, bci.width);
             if (compare_pixels(t, orig_color, bci.dtc, bci.threshold, bci.method)) {
-                for (int i = 0; i < 4; i++) { t[i] = 255 - t[i]; }
-                trace_t = get_pixel(trace_px, bci.x, bci.y, bci.width);
-                trace_t[3] = 255; 
+                paint_pixel(px, trace_px, orig_color, bci);
                 if (bci.y > 0) { 
-                    bci.top_fill = try_add_to_queue(fill_queue, px, trace_px, orig_color, bci.dtc, bci.x, bci.y - 1, bci.width, bci.threshold, bci.method, bci.top_fill);
+                    bci.top_fill = try_add_to_queue(fill_queue, px, trace_px, orig_color, bci.dtc, bci.x, bci.y - (1 + bci.radius), bci.width, bci.threshold, bci.method, bci.top_fill, max_queue_size);
                 }
                 if (bci.y < bci.y_limit) { 
-                    bci.bottom_fill = try_add_to_queue(fill_queue, px, trace_px, orig_color, bci.dtc, bci.x, bci.y + 1, bci.width, bci.threshold, bci.method, bci.bottom_fill);
+                    bci.bottom_fill = try_add_to_queue(fill_queue, px, trace_px, orig_color, bci.dtc, bci.x, bci.y + (1 + bci.radius), bci.width, bci.threshold, bci.method, bci.bottom_fill, max_queue_size);
                 }
                 if (bci.is_left) {
-                    bci.x--;
+                    bci.x -= (1 + bci.radius);
                 } else {
-                    bci.x++;
+                    bci.x += (1 + bci.radius);
                 }
                 ok = true;
             }
@@ -638,11 +685,15 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even
     
     bci.y_limit = y_limit;
     bci.width = width;
+    bci.height = height;
     bci.threshold = threshold;
     bci.method = method;
     bci.bbox = *bbox;
     bci.screen = screen;
     bci.dtc = dtc;
+    bci.radius = prefs_get_int_attribute_limited("tools.paintbucket", "autogap", 0, 0, 3);
+
+    unsigned int max_queue_size = width * height;
 
     while (!color_queue.empty() && !aborted) {
         NR::Point color_point = color_queue.front();
@@ -676,7 +727,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even
                 bool bottom_fill = true;
                 
                 if (y > 0) { 
-                    top_fill = try_add_to_queue(&fill_queue, px, trace_px, orig_color, dtc, x, y - 1, width, threshold, method, top_fill);
+                    top_fill = try_add_to_queue(&fill_queue, px, trace_px, orig_color, dtc, x, y - (1 + bci.radius), width, threshold, method, top_fill, max_queue_size);
                 } else {
                     if (bbox->min()[NR::Y] > screen.min()[NR::Y]) {
                         aborted = true; break;
@@ -685,7 +736,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even
                     }
                 }
                 if (y < y_limit) { 
-                    bottom_fill = try_add_to_queue(&fill_queue, px, trace_px, orig_color, dtc, x, y + 1, width, threshold, method, bottom_fill);
+                    bottom_fill = try_add_to_queue(&fill_queue, px, trace_px, orig_color, dtc, x, y + (1 + bci.radius), width, threshold, method, bottom_fill, max_queue_size);
                   } else {
                       if (bbox->max()[NR::Y] < screen.max()[NR::Y]) {
                           aborted = true; break;
@@ -714,7 +765,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even
                 }
                 
                 bci.is_left = false;
-                bci.x = x + 1;
+                bci.x = x + (1 + bci.radius);
                 bci.y = y;
                 bci.top_fill = top_fill;
                 bci.bottom_fill = bottom_fill;
index 10de77896360ee64c25a9e97a7eef92c34fe493b..a0d9e7d44fbbc4d0c10776abc05ba90fbc2e80b2 100644 (file)
@@ -50,6 +50,7 @@ struct SPFloodContextClass {
 GtkType sp_flood_context_get_type (void);
 
 GList* flood_channels_dropdown_items_list (void);
+GList* flood_autogap_dropdown_items_list (void);
 void flood_channels_set_channels( gint channels );
 
 enum PaintBucketChannels {
index 9698d7c0c1d6bb8621286b5e72432816b47eb142..9f8e675c2ab5a24502bb210cd5c0eb4aa47b466b 100644 (file)
@@ -326,6 +326,8 @@ static gchar const * ui_descr =
         "    <toolitem action='OffsetAction' />"
         "    <toolitem action='PaintbucketUnitsAction' />"
         "    <separator />"
+        "    <toolitem action='AutoGapAction' />"
+        "    <separator />"
         "    <toolitem action='PaintbucketResetAction' />"
         "  </toolbar>"
 
@@ -4316,6 +4318,11 @@ static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject *tbl)
     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
 }
 
+static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject *tbl)
+{
+    prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
+}
+
 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
 {
     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
@@ -4346,6 +4353,8 @@ static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
     
     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
+    EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
+    ege_select_one_action_set_active( autogap_action, 0 );
 }
 
 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
@@ -4411,6 +4420,28 @@ static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* main
         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
     }
     
+    /* Auto Gap */
+    {
+        GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
+
+        GList* items = 0;
+        gint count = 0;
+        for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
+        {
+            GtkTreeIter iter;
+            gtk_list_store_append( model, &iter );
+            gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
+            count++;
+        }
+        g_list_free( items );
+        items = 0;
+        EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Fill gaps:"), _(""), NULL, GTK_TREE_MODEL(model) );
+        ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
+        g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
+        gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
+        g_object_set_data( holder, "autogap_action", act2 );
+    }
+
     /* Reset */
     {
         GtkAction* act = gtk_action_new( "PaintbucketResetAction",