Code

Add flood fill tool
authorjohncoswell <johncoswell@users.sourceforge.net>
Sun, 25 Feb 2007 06:04:46 +0000 (06:04 +0000)
committerjohncoswell <johncoswell@users.sourceforge.net>
Sun, 25 Feb 2007 06:04:46 +0000 (06:04 +0000)
12 files changed:
share/icons/icons.svg
src/Makefile_insert
src/flood-context.cpp [new file with mode: 0644]
src/flood-context.h [new file with mode: 0644]
src/preferences-skeleton.h
src/tools-switch.cpp
src/tools-switch.h
src/ui/dialog/inkscape-preferences.cpp
src/ui/dialog/inkscape-preferences.h
src/verbs.cpp
src/verbs.h
src/widgets/toolbox.cpp

index 7193bac7cb8d83da25ff84b53b3c5113e19dea71..262d2ae03593f148a5bfd0701e0ecbeb222114ad 100644 (file)
@@ -8,15 +8,27 @@
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   inkscape:version="0.44+devel"
+   inkscape:version="0.45+devel"
    sodipodi:docname="icons.svg"
    height="1000.0000pt"
    width="1000.0000pt"
    sodipodi:version="0.32"
    id="svg1"
-   sodipodi:docbase="/home/rejon/Documents/projects/inkscape/inkscape/share/icons">
+   sodipodi:docbase="/home/john/Desktop/inkscape-optimized/inkscape-flood-fill-git/share/icons"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
   <defs
      id="defs3">
+    <linearGradient
+       id="linearGradient10090">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1.0000000;"
+         offset="0.0000000"
+         id="stop10092" />
+      <stop
+         style="stop-color:#5eba69;stop-opacity:1;"
+         offset="1"
+         id="stop10094" />
+    </linearGradient>
     <linearGradient
        inkscape:collect="always"
        id="linearGradient4721">
        y1="207.75523"
        x2="173.03127"
        y2="217.25519" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10090"
+       id="linearGradient9497"
+       gradientUnits="userSpaceOnUse"
+       x1="-21.386486"
+       y1="95.282486"
+       x2="4.0606599"
+       y2="106.86136"
+       gradientTransform="matrix(0.7152345,0,0,0.8651215,96.208489,-131.43211)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5663"
+       id="linearGradient9499"
+       gradientUnits="userSpaceOnUse"
+       x1="475.70343"
+       y1="404.15198"
+       x2="456.58426"
+       y2="394.94302"
+       gradientTransform="matrix(-0.3576173,0.6194113,-0.7492172,-0.4325608,454.81851,-14.482809)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5734"
+       id="linearGradient10098"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.973548,-22.5,-56.939068)"
+       x1="15"
+       y1="177.44151"
+       x2="13.848969"
+       y2="175.94685" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5704"
+       id="linearGradient10100"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.973548,-22.5,-56.939068)"
+       x1="14.019764"
+       y1="174.94193"
+       x2="15.847412"
+       y2="178.02231" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5740"
+       id="linearGradient10134"
+       gradientUnits="userSpaceOnUse"
+       x1="-21.386486"
+       y1="95.282486"
+       x2="4.0606599"
+       y2="106.86136" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5663"
+       id="linearGradient10136"
+       gradientUnits="userSpaceOnUse"
+       x1="475.70343"
+       y1="404.15198"
+       x2="456.58426"
+       y2="394.94302" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient10090"
+       id="linearGradient10138"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7152345,0,0,0.8651215,96.208489,-131.43211)"
+       x1="-21.386486"
+       y1="95.282486"
+       x2="4.0606599"
+       y2="106.86136" />
   </defs>
   <sodipodi:namedview
      inkscape:guide-bbox="true"
      pagecolor="#e8e8e4"
      snaptoguides="true"
      showguides="true"
-     inkscape:window-y="30"
-     inkscape:window-x="426"
-     inkscape:window-height="929"
-     inkscape:window-width="852"
-     inkscape:cy="986.74787"
-     inkscape:cx="77.398376"
-     inkscape:zoom="12.354839"
+     inkscape:window-y="27"
+     inkscape:window-x="0"
+     inkscape:window-height="968"
+     inkscape:window-width="1272"
+     inkscape:cy="805.50154"
+     inkscape:cx="492.28281"
+     inkscape:zoom="4.3680952"
      gridtolerance="1.0000000px"
      snaptogrid="false"
      showgrid="true"
@@ -14864,4 +14944,29 @@ http://www.inkscape.org/</dc:description>
        d="M 22.5,122.5 L 22.499999,118.5"
        id="path5831" />
   </g>
+  <g
+     id="draw_flood"
+     transform="matrix(0.8333394,0,0,0.8333394,467.46488,369.87223)">
+    <rect
+       y="-53.571178"
+       x="81.903801"
+       height="20.762917"
+       width="17.165628"
+       id="rect9485"
+       style="opacity:1;color:#000000;fill:none;fill-opacity:0.0656934;fill-rule:evenodd;stroke:none;stroke-width:0.4532662;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+       transform="matrix(-0.5,0.8660254,-0.8660254,-0.5,0,0)" />
+    <path
+       style="fill:url(#linearGradient10098);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient10100);stroke-width:1.19999146px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
+       d="M -12,112.35248 C -14,113.35248 -12,112.40538 -12,114.35248 C -12,116.29957 -6,115.35248 -3,115.35248 C 0,115.35248 3.2360874,116.09271 3,115.35248 C 2.9500431,115.19585 4.9281712,112.71758 4,111.35248 C 3.4871797,110.59825 0.75155763,114.04656 0,113.35248 C -1.0553787,112.37782 -1.1712281,109.32891 -2,108.85248 C -2,108.85248 -3.5,111.35248 -3.5,111.35248 C -4.860028,110.9137 -3.2911587,112.93223 -4,113.35248 C -4.5080637,113.6537 -10,111.35248 -12,112.35248 z"
+       id="path10096"
+       sodipodi:nodetypes="czzsssssss" />
+    <rect
+       y="-52.706055"
+       x="82.619034"
+       height="19.032673"
+       width="15.735159"
+       id="rect9487"
+       style="opacity:1;color:#000000;fill:url(#linearGradient10138);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.19999135;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:0;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:inline"
+       transform="matrix(-0.5,0.8660254,-0.8660254,-0.5,0,0)" />
+  </g>
 </svg>
index 29ea862686f973c9ebb6dd3b68981ec021690fc8..04cebc8667878255f183a9fe92c5ef347c5b225a 100644 (file)
@@ -71,6 +71,7 @@ libinkpre_a_SOURCES = \
        extract-uri.cpp extract-uri.h   \
        file.cpp file.h \
        filter-chemistry.cpp filter-chemistry.h \
+       flood-context.cpp flood-context.h       \
        fontsize-expansion.cpp fontsize-expansion.h     \
        forward.h       \
        geom.cpp geom.h \
diff --git a/src/flood-context.cpp b/src/flood-context.cpp
new file mode 100644 (file)
index 0000000..22add8e
--- /dev/null
@@ -0,0 +1,552 @@
+#define __SP_FLOOD_CONTEXT_C__
+
+/*
+ * Flood fill drawing context
+ *
+ * Author:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   bulia byak <buliabyak@users.sf.net>
+ *   John Bintz <jcoswell@coswellproductions.org>
+ *
+ * Copyright (C) 2006      Johan Engelen <johan@shouraizou.nl>
+ * Copyright (C) 2000-2005 authors
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "config.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <queue>
+
+#include "macros.h"
+#include "display/sp-canvas.h"
+#include "document.h"
+#include "sp-namedview.h"
+#include "sp-object.h"
+#include "sp-rect.h"
+#include "selection.h"
+#include "selection-chemistry.h"
+#include "desktop-handles.h"
+#include "snap.h"
+#include "desktop.h"
+#include "desktop-style.h"
+#include "message-context.h"
+#include "pixmaps/cursor-rect.xpm"
+#include "flood-context.h"
+#include "sp-metrics.h"
+#include <glibmm/i18n.h>
+#include "object-edit.h"
+#include "xml/repr.h"
+#include "xml/node-event-vector.h"
+#include "prefs-utils.h"
+#include "context-fns.h"
+
+#include "display/nr-arena-item.h"
+#include "display/nr-arena.h"
+#include "display/nr-arena-image.h"
+#include "display/canvas-arena.h"
+#include "helper/png-write.h"
+#include "libnr/nr-pixops.h"
+#include "libnr/nr-matrix-rotate-ops.h"
+#include "libnr/nr-matrix-translate-ops.h"
+#include "libnr/nr-rotate-fns.h"
+#include "libnr/nr-scale-ops.h"
+#include "libnr/nr-scale-translate-ops.h"
+#include "libnr/nr-translate-matrix-ops.h"
+#include "libnr/nr-translate-scale-ops.h"
+#include "libnr/nr-matrix-ops.h"
+#include "sp-item.h"
+#include "sp-root.h"
+#include "sp-defs.h"
+#include "splivarot.h"
+#include "livarot/Path.h"
+#include "livarot/Shape.h"
+#include "libnr/n-art-bpath.h"
+#include "svg/svg.h"
+
+#include "trace/trace.h"
+#include "trace/potrace/inkscape-potrace.h"
+
+static void sp_flood_context_class_init(SPFloodContextClass *klass);
+static void sp_flood_context_init(SPFloodContext *flood_context);
+static void sp_flood_context_dispose(GObject *object);
+
+static void sp_flood_context_setup(SPEventContext *ec);
+
+static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event);
+static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
+
+static void sp_flood_finish(SPFloodContext *rc);
+
+static SPEventContextClass *parent_class;
+
+
+struct SPEBP {
+    int width, height, sheight;
+    guchar r, g, b, a;
+    NRArenaItem *root; // the root arena item to show; it is assumed that all unneeded items are hidden
+    guchar *px;
+    unsigned (*status)(float, void *);
+    void *data;
+};
+
+GtkType sp_flood_context_get_type()
+{
+    static GType type = 0;
+    if (!type) {
+        GTypeInfo info = {
+            sizeof(SPFloodContextClass),
+            NULL, NULL,
+            (GClassInitFunc) sp_flood_context_class_init,
+            NULL, NULL,
+            sizeof(SPFloodContext),
+            4,
+            (GInstanceInitFunc) sp_flood_context_init,
+            NULL,    /* value_table */
+        };
+        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPFloodContext", &info, (GTypeFlags) 0);
+    }
+    return type;
+}
+
+static void sp_flood_context_class_init(SPFloodContextClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
+
+    parent_class = (SPEventContextClass *) g_type_class_peek_parent(klass);
+
+    object_class->dispose = sp_flood_context_dispose;
+
+    event_context_class->setup = sp_flood_context_setup;
+    event_context_class->root_handler  = sp_flood_context_root_handler;
+    event_context_class->item_handler  = sp_flood_context_item_handler;
+}
+
+static void sp_flood_context_init(SPFloodContext *flood_context)
+{
+    SPEventContext *event_context = SP_EVENT_CONTEXT(flood_context);
+
+    event_context->cursor_shape = cursor_rect_xpm;
+    event_context->hot_x = 4;
+    event_context->hot_y = 4;
+    event_context->xp = 0;
+    event_context->yp = 0;
+    event_context->tolerance = 0;
+    event_context->within_tolerance = false;
+    event_context->item_to_select = NULL;
+
+    event_context->shape_repr = NULL;
+    event_context->shape_knot_holder = NULL;
+
+    flood_context->item = NULL;
+
+    new (&flood_context->sel_changed_connection) sigc::connection();
+}
+
+static void sp_flood_context_dispose(GObject *object)
+{
+    SPFloodContext *rc = SP_FLOOD_CONTEXT(object);
+    SPEventContext *ec = SP_EVENT_CONTEXT(object);
+
+    rc->sel_changed_connection.disconnect();
+    rc->sel_changed_connection.~connection();
+
+    /* fixme: This is necessary because we do not grab */
+    if (rc->item) {
+        sp_flood_finish(rc);
+    }
+
+    if (ec->shape_repr) { // remove old listener
+        sp_repr_remove_listener_by_data(ec->shape_repr, ec);
+        Inkscape::GC::release(ec->shape_repr);
+        ec->shape_repr = 0;
+    }
+
+    if (rc->_message_context) {
+        delete rc->_message_context;
+    }
+
+    G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+static Inkscape::XML::NodeEventVector ec_shape_repr_events = {
+    NULL, /* child_added */
+    NULL, /* child_removed */
+    ec_shape_event_attr_changed,
+    NULL, /* content_changed */
+    NULL  /* order_changed */
+};
+
+/**
+\brief  Callback that processes the "changed" signal on the selection;
+destroys old and creates new knotholder
+*/
+void sp_flood_context_selection_changed(Inkscape::Selection *selection, gpointer data)
+{
+    SPFloodContext *rc = SP_FLOOD_CONTEXT(data);
+    SPEventContext *ec = SP_EVENT_CONTEXT(rc);
+
+    if (ec->shape_repr) { // remove old listener
+        sp_repr_remove_listener_by_data(ec->shape_repr, ec);
+        Inkscape::GC::release(ec->shape_repr);
+        ec->shape_repr = 0;
+    }
+
+    SPItem *item = selection->singleItem();
+    if (item) {
+        Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item);
+        if (shape_repr) {
+            ec->shape_repr = shape_repr;
+            Inkscape::GC::anchor(shape_repr);
+            sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
+        }
+    }
+}
+
+static void sp_flood_context_setup(SPEventContext *ec)
+{
+    SPFloodContext *rc = SP_FLOOD_CONTEXT(ec);
+
+    if (((SPEventContextClass *) parent_class)->setup) {
+        ((SPEventContextClass *) parent_class)->setup(ec);
+    }
+
+    SPItem *item = sp_desktop_selection(ec->desktop)->singleItem();
+    if (item) {
+        Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item);
+        if (shape_repr) {
+            ec->shape_repr = shape_repr;
+            Inkscape::GC::anchor(shape_repr);
+            sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec);
+        }
+    }
+
+    rc->sel_changed_connection.disconnect();
+    rc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(
+        sigc::bind(sigc::ptr_fun(&sp_flood_context_selection_changed), (gpointer)rc)
+    );
+
+    rc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
+}
+
+/**
+Hide all items which are not listed in list, recursively, skipping groups and defs
+*/
+static void
+hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
+{
+    if (SP_IS_ITEM(o)
+        && !SP_IS_DEFS(o)
+        && !SP_IS_ROOT(o)
+        && !SP_IS_GROUP(o)
+        && !g_slist_find(list, o))
+    {
+        sp_item_invoke_hide(SP_ITEM(o), dkey);
+    }
+
+     // recurse
+    if (!g_slist_find(list, o)) {
+        for (SPObject *child = sp_object_first_child(o) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+            hide_other_items_recursively(child, list, dkey);
+        }
+    }
+}
+
+inline unsigned char * get_pixel(guchar *px, int x, int y, int width) {
+  return px + (x + y * width) * 4;
+}
+
+static void try_add_to_queue(std::queue<NR::Point> *fill_queue, guchar *px, int x, int y, int width) {
+  unsigned char *t = get_pixel(px, x, y, width);
+  if (t[3] == 0) {
+    fill_queue->push(NR::Point(x, y));
+  }
+}
+
+static void do_trace(GdkPixbuf *px, SPDesktop *desktop, NR::Matrix transform) {
+    SPDocument *document = sp_desktop_document(desktop);
+    
+    Inkscape::Trace::Potrace::PotraceTracingEngine pte;
+        
+    pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS);
+    pte.setInvert(false);
+
+    Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(px, true);
+    
+    std::vector<Inkscape::Trace::TracingEngineResult> results = pte.trace(pixbuf);
+    
+    Inkscape::XML::Node *layer_repr = SP_GROUP(desktop->currentLayer())->repr;
+    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
+
+    long totalNodeCount = 0L;
+
+    for (unsigned int i=0 ; i<results.size() ; i++) {
+        Inkscape::Trace::TracingEngineResult result = results[i];
+        totalNodeCount += result.getNodeCount();
+
+        Inkscape::XML::Node *pathRepr = xml_doc->createElement("svg:path");
+        /* Set style */
+        sp_desktop_apply_style_tool (desktop, pathRepr, "tools.flood", false);
+
+        NArtBpath *bpath = sp_svg_read_path(result.getPathData().c_str());
+        Path *path = bpath_to_Path(bpath);
+        g_free(bpath);
+        
+        Shape *path_shape = new Shape();
+        
+        path->ConvertWithBackData(0.03);
+        path->Fill(path_shape, 0);
+        delete path;
+        
+        Shape *expanded_path_shape = new Shape();
+        
+        expanded_path_shape->ConvertToShape(path_shape, fill_nonZero);
+        path_shape->MakeOffset(expanded_path_shape, 1.5, join_round, 4);
+        expanded_path_shape->ConvertToShape(path_shape, fill_positive);
+
+        Path *expanded_path = new Path();
+        
+        expanded_path->Reset();
+        expanded_path_shape->ConvertToForme(expanded_path);
+        expanded_path->ConvertEvenLines(1.0);
+        expanded_path->Simplify(1.0);
+        
+        delete path_shape;
+        delete expanded_path_shape;
+        
+        gchar *str = expanded_path->svg_dump_path();
+        delete expanded_path;
+        pathRepr->setAttribute("d", str);
+        g_free(str);
+        
+        layer_repr->addChild(pathRepr, NULL);
+
+        SPObject *reprobj = document->getObjectByRepr(pathRepr);
+        if (reprobj) {
+            sp_item_write_transform(SP_ITEM(reprobj), pathRepr, transform, NULL);
+            Inkscape::Selection *selection = sp_desktop_selection(desktop);
+            selection->set(reprobj);
+        }
+        
+        Inkscape::GC::release(pathRepr);
+    }
+}
+
+static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *event) {
+    SPDesktop *desktop = event_context->desktop;
+    SPDocument *document = sp_desktop_document(desktop);
+
+    /* Create new arena */
+    NRArena *arena = NRArena::create();
+    unsigned dkey = sp_item_display_key_new(1);
+
+    sp_document_ensure_up_to_date (document);
+    
+    SPItem *document_root = SP_ITEM(SP_DOCUMENT_ROOT(document));
+    NR::Rect bbox = document_root->invokeBbox(NR::identity());
+
+    if (bbox.isEmpty()) { return; }
+
+    int width = (int)ceil(bbox.extent(NR::X));
+    int height = (int)ceil(bbox.extent(NR::Y));
+
+    NR::Point origin(bbox.min()[NR::X], bbox.min()[NR::Y]);
+    
+    NR::scale scale(width / (bbox.extent(NR::X)), height / (bbox.extent(NR::Y)));
+    NR::Matrix affine = scale * NR::translate(-origin * scale);
+    
+    /* Create ArenaItems and set transform */
+    NRArenaItem *root = sp_item_invoke_show(SP_ITEM(sp_document_root(document)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
+    nr_arena_item_set_transform(root, affine);
+
+    NRGC gc(NULL);
+    nr_matrix_set_identity(&gc.transform);
+    
+    NRRectL final_bbox;
+    final_bbox.x0 = 0;
+    final_bbox.y0 = 0;//row;
+    final_bbox.x1 = width;
+    final_bbox.y1 = height;//row + num_rows;
+    
+    nr_arena_item_invoke_update(root, &final_bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
+
+//     /* Set up pixblocks */
+    guchar *px = g_new(guchar, 4 * width * height);
+    memset(px, 0x00, 4 * width * height);
+
+    guchar *trace_px = g_new(guchar, 4 * width * height);
+    memset(trace_px, 0x00, 4 * width * height);
+// 
+//     /* Render */
+    NRPixBlock B;
+    nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N,
+                              final_bbox.x0, final_bbox.y0, final_bbox.x1, final_bbox.y1,
+                              px, 4 * width, FALSE, FALSE );
+    
+    nr_arena_item_invoke_render( root, &final_bbox, &B, NR_ARENA_ITEM_RENDER_NO_CACHE );
+    nr_pixblock_release(&B);
+// 
+    double zoom_scale = desktop->current_zoom();
+
+    NR::Point pw = NR::Point(event->button.x / zoom_scale, sp_document_height(document) + (event->button.y / zoom_scale)) * affine;
+    
+    pw[NR::X] = (int)MIN(width - 1, MAX(0, pw[NR::X]));
+    pw[NR::Y] = (int)MIN(height - 1, MAX(0, pw[NR::Y]));
+
+    std::queue<NR::Point> fill_queue;
+    fill_queue.push(pw);
+    
+    while (!fill_queue.empty()) {
+      NR::Point cp = fill_queue.front();
+      fill_queue.pop();
+      unsigned char *s = get_pixel(px, (int)cp[NR::X], (int)cp[NR::Y], width);
+      
+      // nothing at this point
+      if (s[3] == 0) {
+        int left = (int)cp[NR::X];
+        int right = (int)cp[NR::X] + 1;
+        int x = (int)cp[NR::X];
+        int y = (int)cp[NR::Y];
+        
+        if (y > 0) { try_add_to_queue(&fill_queue, px, x, y - 1, width); }
+        if (y < (height - 1)) { try_add_to_queue(&fill_queue, px, x, y + 1, width); }
+        
+        unsigned char *t, *trace_t;
+        bool ok = false;
+        
+        do {
+          ok = false;
+          // go left
+          if (left >= 0) {
+            t = get_pixel(px, left, y, width);
+            if (t[3] == 0) {
+              t[0] = 255; t[3] = 255;
+              trace_t = get_pixel(trace_px, left, y, width);
+              trace_t[0] = 255; trace_t[3] = 255;
+              if (y > 0) { try_add_to_queue(&fill_queue, px, left, y - 1, width); }
+              if (y < (height - 1)) { try_add_to_queue(&fill_queue, px, left, y + 1, width); }
+              left--; ok = true;
+            }
+          }
+        } while (ok);
+      
+        do {
+          ok = false;
+          // go left
+          if (right < width) {
+            t = get_pixel(px, right, y, width);
+            if (t[3] == 0) {
+              t[0] = 255; t[3] = 255; 
+              trace_t = get_pixel(trace_px, right, y, width);
+              trace_t[0] = 255; trace_t[3] = 255; 
+              if (y > 0) { try_add_to_queue(&fill_queue, px, right, y - 1, width); }
+              if (y < (height - 1)) { try_add_to_queue(&fill_queue, px, right, y + 1, width); }
+              right++; ok = true;
+            }
+          }
+        } while (ok);
+      }
+    }
+    
+    g_free(px);
+    
+    GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(trace_px,
+                                      GDK_COLORSPACE_RGB,
+                                      TRUE,
+                                      8, width, height, width * 4,
+                                      (GdkPixbufDestroyNotify)g_free,
+                                      NULL);
+
+    NR::Matrix inverted_affine = NR::Matrix(affine).inverse();
+    
+    do_trace(pixbuf, desktop, inverted_affine);
+
+    /* Free Arena and ArenaItem */
+    nr_arena_item_unref(root);
+    nr_object_unref((NRObject *) arena);
+    
+    sp_document_done(document, SP_VERB_CONTEXT_FLOOD, _("Flood fill"));
+}
+
+static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
+{
+    gint ret = FALSE;
+
+    switch (event->type) {
+    case GDK_BUTTON_PRESS:
+        if ( event->button.button == 1 ) {
+            sp_flood_do_flood_fill(event_context, event);
+            ret = TRUE;
+        }
+        break;
+        // motion and release are always on root (why?)
+    default:
+        break;
+    }
+
+    if (((SPEventContextClass *) parent_class)->item_handler) {
+        ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
+    }
+
+    return ret;
+}
+
+static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event)
+{
+    gint ret = FALSE;
+    switch (event->type) {
+    case GDK_BUTTON_PRESS:
+        if ( event->button.button == 1 ) {
+            sp_flood_do_flood_fill(event_context, event);
+
+            ret = TRUE;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (!ret) {
+        if (((SPEventContextClass *) parent_class)->root_handler) {
+            ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
+        }
+    }
+
+    return ret;
+}
+
+
+static void sp_flood_finish(SPFloodContext *rc)
+{
+    rc->_message_context->clear();
+
+    if ( rc->item != NULL ) {
+        SPDesktop * desktop;
+
+        desktop = SP_EVENT_CONTEXT_DESKTOP(rc);
+
+        SP_OBJECT(rc->item)->updateRepr();
+
+        sp_canvas_end_forced_full_redraws(desktop->canvas);
+
+        sp_desktop_selection(desktop)->set(rc->item);
+        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
+                         _("Create floodangle"));
+
+        rc->item = NULL;
+    }
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/flood-context.h b/src/flood-context.h
new file mode 100644 (file)
index 0000000..46b952b
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __SP_FLOOD_CONTEXT_H__
+#define __SP_FLOOD_CONTEXT_H__
+
+/*
+ * Flood fill drawing context
+ *
+ * Authors:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   John Bintz <jcoswell@coswellproductions.org>
+ *
+ * Released under GNU GPL
+ */
+
+#include <sigc++/sigc++.h>
+#include "event-context.h"
+struct SPKnotHolder;
+
+#define SP_TYPE_FLOOD_CONTEXT            (sp_flood_context_get_type ())
+#define SP_FLOOD_CONTEXT(obj)            (GTK_CHECK_CAST ((obj), SP_TYPE_FLOOD_CONTEXT, SPFloodContext))
+#define SP_FLOOD_CONTEXT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_FLOOD_CONTEXT, SPFloodContextClass))
+#define SP_IS_FLOOD_CONTEXT(obj)         (GTK_CHECK_TYPE ((obj), SP_TYPE_FLOOD_CONTEXT))
+#define SP_IS_FLOOD_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_FLOOD_CONTEXT))
+
+class SPFloodContext;
+class SPFloodContextClass;
+
+struct SPFloodContext : public SPEventContext {
+       SPItem *item;
+
+       sigc::connection sel_changed_connection;
+
+       Inkscape::MessageContext *_message_context;
+};
+
+struct SPFloodContextClass {
+       SPEventContextClass parent_class;
+};
+
+/* Standard Gtk function */
+
+GtkType sp_flood_context_get_type (void);
+
+#endif
index 0660549ff537ec7725289cb710510ebfb01376f5..bd10f1134030dbbb9be1425231063447c38635fb 100644 (file)
@@ -72,6 +72,7 @@ static char const preferences_skeleton[] =
 "    <eventcontext id=\"dropper\" selcue=\"1\" gradientdrag=\"1\" pick=\"1\" setalpha=\"1\"/>\n"
 "    <eventcontext id=\"select\" selcue=\"1\" gradientdrag=\"0\"/>\n"
 "    <eventcontext id=\"connector\" style=\"fill:none;fill-rule:evenodd;stroke:black;stroke-opacity:1;stroke-linejoin:miter;stroke-width:1px;stroke-linecap:butt;\" selcue=\"1\"/>\n"
+"    <eventcontext id=\"flood\" style=\"fill:#808080;\" usecurrent=\"1\"/>\n"
 "  </group>\n"
 "  <group id=\"palette\">\n"
 "    <group id=\"dashes\">\n"
index 017aed979e26d66bbdc0437e6121f13fcb45e716..c5b5bd5a4e7d334148ff66543e6c57d9088bd760 100644 (file)
@@ -43,6 +43,7 @@
 #include "zoom-context.h"
 #include "dropper-context.h"
 #include "connector-context.h"
+#include "flood-context.h"
 #include "sp-offset.h"
 #include "message-context.h"
 
@@ -64,6 +65,7 @@ static char const *const tool_names[] = {
     "tools.zoom",
     "tools.dropper",
     "tools.connector",
+    "tools.flood",
     NULL
 };
 
@@ -83,6 +85,7 @@ static char const *const tool_ids[] = {
     "zoom",
     "dropper",
     "connector",
+    "flood",
     NULL
 };
 
@@ -206,6 +209,12 @@ tools_switch(SPDesktop *dt, int num)
             inkscape_eventcontext_set(sp_desktop_event_context(dt));
             dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Click and drag</b> between shapes to create a connector."));
             break;
+        case TOOLS_FLOOD:
+            dt->set_event_context(SP_TYPE_FLOOD_CONTEXT, tool_names[num]);
+            dt->activate_guides(false);
+            inkscape_eventcontext_set(sp_desktop_event_context(dt));
+            dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> to paint an unfilled area."));
+            break;
     }
 }
 
index 128e6a46b297d1ba7f59fba714cb0a4fd1b79c08..93aa9b3a965208ca0f9c7a7447bb8d58cdfa00ad 100644 (file)
@@ -29,7 +29,8 @@ enum {
   TOOLS_GRADIENT,
   TOOLS_ZOOM,
   TOOLS_DROPPER,
-  TOOLS_CONNECTOR
+  TOOLS_CONNECTOR,
+  TOOLS_FLOOD 
 };
 
 int tools_isactive(SPDesktop *dt, unsigned num);
index 34cdee00bf40df11678ff115e14f1bb8bceb1469..c9e1542017074245108008dae9dbb6e4b1a9e33c 100644 (file)
@@ -397,6 +397,9 @@ void InkscapePreferences::initPageTools()
     this->AddPage(_page_dropper, _("Dropper"), iter_tools, PREFS_PAGE_TOOLS_DROPPER);
     this->AddSelcueCheckbox(_page_dropper, "tools.dropper", true);
     this->AddGradientCheckbox(_page_dropper, "tools.dropper", true);
+    //Flood
+    this->AddPage(_page_flood, _("Flood Fill"), iter_tools, PREFS_PAGE_TOOLS_FLOOD);
+    this->AddNewObjectsStyle(_page_flood, "tools.flood");
 }
 
 void InkscapePreferences::initPageWindows()
index 74a3604c466101c63718d89d151344202ca2fb70..96aea6e61d84fa3750e633cfc79df9291eb55709 100644 (file)
@@ -52,6 +52,7 @@ enum {
     PREFS_PAGE_TOOLS_GRADIENT,
     PREFS_PAGE_TOOLS_CONNECTOR,
     PREFS_PAGE_TOOLS_DROPPER,
+    PREFS_PAGE_TOOLS_FLOOD,
     PREFS_PAGE_WINDOWS,
     PREFS_PAGE_CLONES,
     PREFS_PAGE_FILTERS,
@@ -98,7 +99,7 @@ protected:
                _page_clones, _page_transforms, _page_filters, _page_select, _page_misc;
     DialogPage _page_selector, _page_node, _page_zoom, _page_shapes, _page_pencil, _page_pen,
                _page_calligraphy, _page_text, _page_gradient, _page_connector, _page_dropper;
-    DialogPage _page_rectangle, _page_ellipse, _page_star, _page_spiral;
+    DialogPage _page_rectangle, _page_ellipse, _page_star, _page_spiral, _page_flood;
 
     PrefSpinButton _mouse_sens, _mouse_thres;
 
index a68e093566895c3e78fdb54edb269b30b2738bc5..2dec64a2f5b498fc7065bf13fa9c2fbd4df29706 100644 (file)
@@ -1335,7 +1335,7 @@ ContextVerb::perform(SPAction *action, void *data, void *pdata)
     /** \todo !!! hopefully this can go away soon and actions can look after
      * themselves
      */
-    for (vidx = SP_VERB_CONTEXT_SELECT; vidx <= SP_VERB_CONTEXT_DROPPER_PREFS; vidx++)
+    for (vidx = SP_VERB_CONTEXT_SELECT; vidx <= SP_VERB_CONTEXT_FLOOD_PREFS; vidx++)
     {
         SPAction *tool_action= get((sp_verb_t)vidx)->get_action(dt);
         if (tool_action) {
@@ -1386,6 +1386,9 @@ ContextVerb::perform(SPAction *action, void *data, void *pdata)
         case SP_VERB_CONTEXT_CONNECTOR:
             tools_switch_current (TOOLS_CONNECTOR);
             break;
+        case SP_VERB_CONTEXT_FLOOD:
+            tools_switch_current(TOOLS_FLOOD);
+            break;
 
         case SP_VERB_CONTEXT_SELECT_PREFS:
             prefs_set_int_attribute("dialogs.preferences", "page", PREFS_PAGE_TOOLS_SELECTOR);
@@ -1443,6 +1446,10 @@ ContextVerb::perform(SPAction *action, void *data, void *pdata)
             prefs_set_int_attribute ("dialogs.preferences", "page", PREFS_PAGE_TOOLS_CONNECTOR);
             dt->_dlg_mgr->showDialog("InkscapePreferences");
             break;
+        case SP_VERB_CONTEXT_FLOOD_PREFS:
+            prefs_set_int_attribute ("dialogs.preferences", "page", PREFS_PAGE_TOOLS_FLOOD);
+            dt->_dlg_mgr->showDialog("InkscapePreferences");
+            break;
 
         default:
             break;
@@ -2226,6 +2233,8 @@ Verb *Verb::_base_verbs[] = {
                     N_("Pick averaged colors from image"), "draw_dropper"),
     new ContextVerb(SP_VERB_CONTEXT_CONNECTOR, "ToolConnector", N_("Connector"),
                     N_("Create connectors"), "draw_connector"),
+    new ContextVerb(SP_VERB_CONTEXT_FLOOD, "ToolFlood", N_("Flood Fill"),
+                    N_("Fill open areas"), "draw_flood"),
 
     /* Tool prefs */
     new ContextVerb(SP_VERB_CONTEXT_SELECT_PREFS, "SelectPrefs", N_("Selector Preferences"),
@@ -2256,6 +2265,8 @@ Verb *Verb::_base_verbs[] = {
                     N_("Open Preferences for the Dropper tool"), NULL),
     new ContextVerb(SP_VERB_CONTEXT_CONNECTOR_PREFS, "ConnectorPrefs", N_("Connector Preferences"),
                     N_("Open Preferences for the Connector tool"), NULL),
+    new ContextVerb(SP_VERB_CONTEXT_FLOOD_PREFS, "FloodPrefs", N_("Flood Fill Preferences"),
+                    N_("Open Preferences for the Flood Fill tool"), NULL),
 
     /* Zoom/View */
     new ZoomVerb(SP_VERB_ZOOM_IN, "ZoomIn", N_("Zoom In"), N_("Zoom in"), "zoom_in"),
index 7567deefa61d43e4b4d4d454c07a73a5e768417f..c1cbb6fc7079033da94bc85e306e2624521297f4 100644 (file)
@@ -147,6 +147,7 @@ enum {
     SP_VERB_CONTEXT_ZOOM,
     SP_VERB_CONTEXT_DROPPER,
     SP_VERB_CONTEXT_CONNECTOR,
+    SP_VERB_CONTEXT_FLOOD,
     /* Tool preferences */
     SP_VERB_CONTEXT_SELECT_PREFS,
     SP_VERB_CONTEXT_NODE_PREFS,
@@ -162,6 +163,7 @@ enum {
     SP_VERB_CONTEXT_ZOOM_PREFS,
     SP_VERB_CONTEXT_DROPPER_PREFS,
     SP_VERB_CONTEXT_CONNECTOR_PREFS,
+    SP_VERB_CONTEXT_FLOOD_PREFS,
     /* Zooming and desktop settings */
     SP_VERB_ZOOM_IN,
     SP_VERB_ZOOM_OUT,
index 8d2f5c00afa950ffb34be2878c2001dd47008133..603047d6e969098182e075f4159f973c937be9e0 100644 (file)
@@ -101,6 +101,7 @@ static GtkWidget *sp_calligraphy_toolbox_new(SPDesktop *desktop);
 static GtkWidget *sp_dropper_toolbox_new(SPDesktop *desktop);
 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
 static GtkWidget *sp_connector_toolbox_new(SPDesktop *desktop);
+static GtkWidget *sp_flood_toolbox_new(SPDesktop *desktop);
 
 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
 
@@ -125,6 +126,7 @@ static struct {
     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
+    { "SPFloodContext",    "flood_tool",     SP_VERB_CONTEXT_FLOOD, SP_VERB_CONTEXT_FLOOD_PREFS },
     { NULL, NULL, 0, 0 }
 };
 
@@ -147,6 +149,7 @@ static struct {
     { "SPDropperContext", "dropper_toolbox", sp_dropper_toolbox_new },
     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new },
     { "SPConnectorContext", "connector_toolbox", sp_connector_toolbox_new },
+    { "SPFloodContext",  "flood_toolbox",  sp_flood_toolbox_new },
     { NULL, NULL, NULL }
 };
 
@@ -4429,6 +4432,22 @@ sp_connector_toolbox_new(SPDesktop *desktop)
 
 } // end of sp_connector_toolbox_new()
 
+static GtkWidget *
+sp_flood_toolbox_new(SPDesktop *desktop)
+{
+    GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
+    
+    Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch(NULL, _("Style of new rectangles"));
+    swatch->setDesktop (desktop);
+    swatch->setClickVerb (SP_VERB_CONTEXT_RECT_PREFS);
+    swatch->setWatchedTool ("tools.shapes.rect", true);
+    GtkWidget *swatch_ = GTK_WIDGET(swatch->gobj());
+    gtk_box_pack_end(GTK_BOX(tbl), swatch_, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(tbl);
+    sp_set_font_size_smaller (tbl);
+    return tbl;
+} // end of sp_flood_toolbox_new()
 
 /*
   Local Variables: