~danieljabailey/inkscape/arc_node_editor

« back to all changes in this revision

Viewing changes to src/flood-context.cpp

  • Committer: scislac
  • Date: 2009-08-12 07:57:52 UTC
  • Revision ID: scislac@users.sourceforge.net-20090812075752-3zt99jgeqr3bm16j
much better quality multi-size windows icon, thanks ChrisMorgan

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * @file
3
 
 * Bucket fill drawing context, works by bitmap filling an area on a rendered version
 
1
#define __SP_FLOOD_CONTEXT_C__
 
2
 
 
3
/** @file
 
4
 * @brief Bucket fill drawing context, works by bitmap filling an area on a rendered version
4
5
 * of the current display and then tracing the result using potrace.
5
6
 */
6
7
/* Author:
7
8
 *   Lauris Kaplinski <lauris@kaplinski.com>
8
9
 *   bulia byak <buliabyak@users.sf.net>
9
10
 *   John Bintz <jcoswell@coswellproductions.org>
10
 
 *   Jon A. Cruz <jon@joncruz.org>
11
 
 *   Abhishek Sharma
12
11
 *
13
12
 * Copyright (C) 2006      Johan Engelen <johan@shouraizou.nl>
14
13
 * Copyright (C) 2000-2005 authors
21
20
#include "config.h"
22
21
#endif
23
22
 
24
 
#include "trace/potrace/inkscape-potrace.h"
25
 
#include <2geom/pathvector.h>
26
23
#include <gdk/gdkkeysyms.h>
27
24
#include <queue>
28
25
#include <deque>
29
 
#include <glibmm/i18n.h>
30
26
 
31
 
#include "color.h"
32
 
#include "context-fns.h"
 
27
#include "macros.h"
 
28
#include "display/sp-canvas.h"
 
29
#include "document.h"
 
30
#include "sp-namedview.h"
 
31
#include "sp-object.h"
 
32
#include "sp-rect.h"
 
33
#include "selection.h"
 
34
#include "desktop-handles.h"
33
35
#include "desktop.h"
34
 
 
35
36
#include "desktop-style.h"
36
 
#include "display/cairo-utils.h"
37
 
#include "display/drawing-context.h"
38
 
#include "display/drawing-image.h"
39
 
#include "display/drawing-item.h"
40
 
#include "display/drawing.h"
41
 
#include "display/sp-canvas.h"
42
 
#include "document.h"
43
 
#include "document-undo.h"
44
 
#include "ui/tools/flood-tool.h"
45
 
#include "livarot/Path.h"
46
 
#include "livarot/Shape.h"
47
 
#include "macros.h"
 
37
#include "message-stack.h"
48
38
#include "message-context.h"
49
 
#include "message-stack.h"
 
39
#include "pixmaps/cursor-paintbucket.xpm"
 
40
#include "flood-context.h"
 
41
#include "sp-metrics.h"
 
42
#include <glibmm/i18n.h>
 
43
#include "object-edit.h"
 
44
#include "xml/repr.h"
 
45
#include "xml/node-event-vector.h"
50
46
#include "preferences.h"
 
47
#include "context-fns.h"
51
48
#include "rubberband.h"
52
 
#include "selection.h"
53
 
#include "ui/shape-editor.h"
 
49
#include "shape-editor.h"
 
50
 
 
51
#include "display/nr-arena-item.h"
 
52
#include "display/nr-arena.h"
 
53
#include "display/nr-arena-image.h"
 
54
#include "display/canvas-arena.h"
 
55
#include "libnr/nr-pixops.h"
 
56
#include "libnr/nr-matrix-translate-ops.h"
 
57
#include "libnr/nr-scale-ops.h"
 
58
#include "libnr/nr-scale-translate-ops.h"
 
59
#include "libnr/nr-translate-matrix-ops.h"
 
60
#include "libnr/nr-translate-scale-ops.h"
 
61
#include "libnr/nr-matrix-ops.h"
 
62
#include <2geom/pathvector.h>
 
63
#include "sp-item.h"
 
64
#include "sp-root.h"
54
65
#include "sp-defs.h"
55
 
#include "sp-item.h"
 
66
#include "sp-path.h"
56
67
#include "splivarot.h"
57
 
#include "sp-namedview.h"
58
 
#include "sp-object.h"
59
 
#include "sp-path.h"
60
 
#include "sp-rect.h"
61
 
#include "sp-root.h"
 
68
#include "livarot/Path.h"
 
69
#include "livarot/Shape.h"
62
70
#include "svg/svg.h"
 
71
#include "color.h"
 
72
 
 
73
#include "trace/trace.h"
63
74
#include "trace/imagemap.h"
64
 
#include "trace/trace.h"
65
 
#include "xml/node-event-vector.h"
66
 
#include "xml/repr.h"
67
 
#include "verbs.h"
68
 
 
69
 
#include "pixmaps/cursor-paintbucket.xpm"
70
 
 
71
 
using Inkscape::DocumentUndo;
72
 
 
73
 
using Inkscape::Display::ExtractARGB32;
74
 
using Inkscape::Display::ExtractRGB32;
75
 
using Inkscape::Display::AssembleARGB32;
76
 
 
77
 
namespace Inkscape {
78
 
namespace UI {
79
 
namespace Tools {
80
 
 
81
 
const std::string& FloodTool::getPrefsPath() {
82
 
        return FloodTool::prefsPath;
83
 
}
84
 
 
85
 
const std::string FloodTool::prefsPath = "/tools/paintbucket";
86
 
 
87
 
// TODO: Replace by C++11 initialization
88
 
// Must match PaintBucketChannels enum
89
 
Glib::ustring ch_init[8] = {
90
 
    _("Visible Colors"),
91
 
    _("Red"),
92
 
    _("Green"),
93
 
    _("Blue"),
94
 
    _("Hue"),
95
 
    _("Saturation"),
96
 
    _("Lightness"),
97
 
    _("Alpha"),
98
 
};
99
 
const std::vector<Glib::ustring> FloodTool::channel_list( ch_init, ch_init+8 );
100
 
 
101
 
Glib::ustring gap_init[4] = {
102
 
    C_("Flood autogap", "None"),
103
 
    C_("Flood autogap", "Small"),
104
 
    C_("Flood autogap", "Medium"),
105
 
    C_("Flood autogap", "Large")
106
 
};
107
 
const std::vector<Glib::ustring> FloodTool::gap_list( gap_init, gap_init+4 );
108
 
 
109
 
FloodTool::FloodTool()
110
 
    : ToolBase(cursor_paintbucket_xpm, 11, 30)
111
 
    , item(NULL)
112
 
{
113
 
    // TODO: Why does the flood tool use a hardcoded tolerance instead of a pref?
114
 
    this->tolerance = 4;
115
 
}
116
 
 
117
 
FloodTool::~FloodTool() {
118
 
    this->sel_changed_connection.disconnect();
119
 
 
120
 
    delete this->shape_editor;
121
 
    this->shape_editor = NULL;
 
75
#include "trace/potrace/inkscape-potrace.h"
 
76
 
 
77
static void sp_flood_context_class_init(SPFloodContextClass *klass);
 
78
static void sp_flood_context_init(SPFloodContext *flood_context);
 
79
static void sp_flood_context_dispose(GObject *object);
 
80
 
 
81
static void sp_flood_context_setup(SPEventContext *ec);
 
82
 
 
83
static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event);
 
84
static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
 
85
 
 
86
static void sp_flood_finish(SPFloodContext *rc);
 
87
 
 
88
static SPEventContextClass *parent_class;
 
89
 
 
90
 
 
91
GtkType sp_flood_context_get_type()
 
92
{
 
93
    static GType type = 0;
 
94
    if (!type) {
 
95
        GTypeInfo info = {
 
96
            sizeof(SPFloodContextClass),
 
97
            NULL, NULL,
 
98
            (GClassInitFunc) sp_flood_context_class_init,
 
99
            NULL, NULL,
 
100
            sizeof(SPFloodContext),
 
101
            4,
 
102
            (GInstanceInitFunc) sp_flood_context_init,
 
103
            NULL,    /* value_table */
 
104
        };
 
105
        type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPFloodContext", &info, (GTypeFlags) 0);
 
106
    }
 
107
    return type;
 
108
}
 
109
 
 
110
static void sp_flood_context_class_init(SPFloodContextClass *klass)
 
111
{
 
112
    GObjectClass *object_class = (GObjectClass *) klass;
 
113
    SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
 
114
 
 
115
    parent_class = (SPEventContextClass *) g_type_class_peek_parent(klass);
 
116
 
 
117
    object_class->dispose = sp_flood_context_dispose;
 
118
 
 
119
    event_context_class->setup = sp_flood_context_setup;
 
120
    event_context_class->root_handler  = sp_flood_context_root_handler;
 
121
    event_context_class->item_handler  = sp_flood_context_item_handler;
 
122
}
 
123
 
 
124
static void sp_flood_context_init(SPFloodContext *flood_context)
 
125
{
 
126
    SPEventContext *event_context = SP_EVENT_CONTEXT(flood_context);
 
127
 
 
128
    event_context->cursor_shape = cursor_paintbucket_xpm;
 
129
    event_context->hot_x = 11;
 
130
    event_context->hot_y = 30;
 
131
    event_context->xp = 0;
 
132
    event_context->yp = 0;
 
133
    event_context->tolerance = 4;
 
134
    event_context->within_tolerance = false;
 
135
    event_context->item_to_select = NULL;
 
136
 
 
137
    flood_context->item = NULL;
 
138
 
 
139
    new (&flood_context->sel_changed_connection) sigc::connection();
 
140
}
 
141
 
 
142
static void sp_flood_context_dispose(GObject *object)
 
143
{
 
144
    SPFloodContext *rc = SP_FLOOD_CONTEXT(object);
 
145
    SPEventContext *ec = SP_EVENT_CONTEXT(object);
 
146
 
 
147
    rc->sel_changed_connection.disconnect();
 
148
    rc->sel_changed_connection.~connection();
 
149
 
 
150
    delete ec->shape_editor;
 
151
    ec->shape_editor = NULL;
122
152
 
123
153
    /* fixme: This is necessary because we do not grab */
124
 
    if (this->item) {
125
 
        this->finishItem();
126
 
    }
 
154
    if (rc->item) {
 
155
        sp_flood_finish(rc);
 
156
    }
 
157
 
 
158
    if (rc->_message_context) {
 
159
        delete rc->_message_context;
 
160
    }
 
161
 
 
162
    G_OBJECT_CLASS(parent_class)->dispose(object);
127
163
}
128
164
 
129
165
/**
130
 
 * Callback that processes the "changed" signal on the selection;
131
 
 * destroys old and creates new knotholder.
132
 
 */
133
 
void FloodTool::selection_changed(Inkscape::Selection* selection) {
134
 
    this->shape_editor->unset_item();
135
 
    this->shape_editor->set_item(selection->singleItem());
 
166
\brief  Callback that processes the "changed" signal on the selection;
 
167
destroys old and creates new knotholder
 
168
*/
 
169
void sp_flood_context_selection_changed(Inkscape::Selection *selection, gpointer data)
 
170
{
 
171
    SPFloodContext *rc = SP_FLOOD_CONTEXT(data);
 
172
    SPEventContext *ec = SP_EVENT_CONTEXT(rc);
 
173
 
 
174
    ec->shape_editor->unset_item(SH_KNOTHOLDER);
 
175
    SPItem *item = selection->singleItem(); 
 
176
    ec->shape_editor->set_item(item, SH_KNOTHOLDER);
136
177
}
137
178
 
138
 
void FloodTool::setup() {
139
 
    ToolBase::setup();
140
 
 
141
 
    this->shape_editor = new ShapeEditor(this->desktop);
142
 
 
143
 
    SPItem *item = this->desktop->getSelection()->singleItem();
 
179
static void sp_flood_context_setup(SPEventContext *ec)
 
180
{
 
181
    SPFloodContext *rc = SP_FLOOD_CONTEXT(ec);
 
182
 
 
183
    if (((SPEventContextClass *) parent_class)->setup) {
 
184
        ((SPEventContextClass *) parent_class)->setup(ec);
 
185
    }
 
186
 
 
187
    ec->shape_editor = new ShapeEditor(ec->desktop);
 
188
 
 
189
    SPItem *item = sp_desktop_selection(ec->desktop)->singleItem();
144
190
    if (item) {
145
 
        this->shape_editor->set_item(item);
 
191
        ec->shape_editor->set_item(item, SH_KNOTHOLDER);
146
192
    }
147
193
 
148
 
    this->sel_changed_connection.disconnect();
149
 
    this->sel_changed_connection = this->desktop->getSelection()->connectChanged(
150
 
        sigc::mem_fun(this, &FloodTool::selection_changed)
 
194
    rc->sel_changed_connection.disconnect();
 
195
    rc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(
 
196
        sigc::bind(sigc::ptr_fun(&sp_flood_context_selection_changed), (gpointer)rc)
151
197
    );
152
198
 
 
199
    rc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
 
200
 
153
201
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
154
 
 
155
202
    if (prefs->getBool("/tools/paintbucket/selcue")) {
156
 
        this->enableSelectionCue();
 
203
        rc->enableSelectionCue();
157
204
    }
158
205
}
159
206
 
160
 
 
161
 
// Changes from 0.48 -> 0.49 (Cairo)
162
 
// 0.49: Ignores alpha in background
163
 
// 0.48: RGBA, 0.49 ARGB
164
 
// 0.49: premultiplied alpha
165
 
inline static guint32 compose_onto(guint32 px, guint32 bg)
 
207
/**
 
208
 * \brief Merge a pixel with the background color.
 
209
 * \param orig The pixel to merge with the background.
 
210
 * \param bg The background color.
 
211
 * \param base The pixel to merge the original and background into.
 
212
 */
 
213
inline static void
 
214
merge_pixel_with_background (unsigned char *orig, unsigned char *bg,
 
215
           unsigned char *base)
166
216
{
167
 
    guint ap = 0, rp = 0, gp = 0, bp = 0;
168
 
    guint rb = 0, gb = 0, bb = 0;
169
 
    ExtractARGB32(px, ap, rp, gp, bp);
170
 
    ExtractRGB32(bg, rb, gb, bb);
171
 
 
172
 
    // guint ao = 255*255 - (255-ap)*(255-bp);  ao = (ao + 127) / 255;
173
 
    // guint ao = (255-ap)*ab + 255*ap;             ao = (ao + 127) / 255;
174
 
    guint ao = 255; // Cairo version doesn't allow background to have alpha != 1.
175
 
    guint ro = (255-ap)*rb + 255*rp;             ro = (ro + 127) / 255;
176
 
    guint go = (255-ap)*gb + 255*gp;             go = (go + 127) / 255;
177
 
    guint bo = (255-ap)*bb + 255*bp;             bo = (bo + 127) / 255;
178
 
 
179
 
    guint pxout = AssembleARGB32(ao, ro, go, bo);
180
 
    return pxout;
 
217
    int precalc_bg_alpha = (255 * (255 - bg[3])) / 255;
 
218
    
 
219
    for (int i = 0; i < 3; i++) {
 
220
        base[i] = precalc_bg_alpha + (bg[i] * bg[3]) / 255;
 
221
        base[i] = (base[i] * (255 - orig[3])) / 255 + (orig[i] * orig[3]) / 255;
 
222
    }
181
223
}
182
224
 
183
225
/**
184
 
 * Get the pointer to a pixel in a pixel buffer.
185
 
 * @param px The pixel buffer.
186
 
 * @param x The X coordinate.
187
 
 * @param y The Y coordinate.
188
 
 * @param stride The rowstride of the pixel buffer.
 
226
 * \brief Get the pointer to a pixel in a pixel buffer.
 
227
 * \param px The pixel buffer.
 
228
 * \param x The X coordinate.
 
229
 * \param y The Y coordinate.
 
230
 * \param width The width of the pixel buffer.
189
231
 */
190
 
inline guint32 get_pixel(guchar *px, int x, int y, int stride) {
191
 
    return *reinterpret_cast<guint32*>(px + y * stride + x * 4);
 
232
inline unsigned char * get_pixel(guchar *px, int x, int y, int width) {
 
233
    return px + (x + y * width) * 4;
192
234
}
193
235
 
194
236
inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width) {
196
238
}
197
239
 
198
240
/**
199
 
 * Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
200
 
 * @param check The pixel in the pixel buffer to check.
201
 
 * @param orig The original selected pixel to use as the fill target color.
202
 
 * @param merged_orig_pixel The original pixel merged with the background.
203
 
 * @param dtc The desktop background color.
204
 
 * @param threshold The fill threshold.
205
 
 * @param method The fill method to use as defined in PaintBucketChannels.
206
 
 */
207
 
static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method)
208
 
{
 
241
 * \brief Generate the list of trace channel selection entries.
 
242
 */
 
243
GList * flood_channels_dropdown_items_list() {
 
244
    GList *glist = NULL;
 
245
 
 
246
    glist = g_list_append (glist, _("Visible Colors"));
 
247
    glist = g_list_append (glist, _("Red"));
 
248
    glist = g_list_append (glist, _("Green"));
 
249
    glist = g_list_append (glist, _("Blue"));
 
250
    glist = g_list_append (glist, _("Hue"));
 
251
    glist = g_list_append (glist, _("Saturation"));
 
252
    glist = g_list_append (glist, _("Lightness"));
 
253
    glist = g_list_append (glist, _("Alpha"));
 
254
 
 
255
    return glist;
 
256
}
 
257
 
 
258
/**
 
259
 * \brief Generate the list of autogap selection entries.
 
260
 */
 
261
GList * flood_autogap_dropdown_items_list() {
 
262
    GList *glist = NULL;
 
263
 
 
264
    glist = g_list_append (glist, _("None"));
 
265
    glist = g_list_append (glist, _("Small"));
 
266
    glist = g_list_append (glist, _("Medium"));
 
267
    glist = g_list_append (glist, _("Large"));
 
268
 
 
269
    return glist;
 
270
}
 
271
 
 
272
/**
 
273
 * \brief Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
 
274
 * \param check The pixel in the pixel buffer to check.
 
275
 * \param orig The original selected pixel to use as the fill target color.
 
276
 * \param merged_orig_pixel The original pixel merged with the background.
 
277
 * \param dtc The desktop background color.
 
278
 * \param threshold The fill threshold.
 
279
 * \param method The fill method to use as defined in PaintBucketChannels.
 
280
 */
 
281
static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned char *merged_orig_pixel, unsigned char *dtc, int threshold, PaintBucketChannels method) {
209
282
    int diff = 0;
210
 
    float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0};
211
 
 
212
 
    guint32 ac = 0, rc = 0, gc = 0, bc = 0;
213
 
    ExtractARGB32(check, ac, rc, gc, bc);
214
 
 
215
 
    guint32 ao = 0, ro = 0, go = 0, bo = 0;
216
 
    ExtractARGB32(orig, ao, ro, go, bo);
217
 
 
218
 
    guint32 ad = 0, rd = 0, gd = 0, bd = 0;
219
 
    ExtractARGB32(dtc, ad, rd, gd, bd);
220
 
 
221
 
    guint32 amop = 0, rmop = 0, gmop = 0, bmop = 0;
222
 
    ExtractARGB32(merged_orig_pixel, amop, rmop, gmop, bmop);
223
 
 
 
283
    float hsl_check[3], hsl_orig[3];
 
284
    
224
285
    if ((method == FLOOD_CHANNELS_H) ||
225
286
        (method == FLOOD_CHANNELS_S) ||
226
287
        (method == FLOOD_CHANNELS_L)) {
227
 
        double dac = ac;
228
 
        double dao = ao;
229
 
        sp_color_rgb_to_hsl_floatv(hsl_check, rc / dac, gc / dac, bc / dac);
230
 
        sp_color_rgb_to_hsl_floatv(hsl_orig, ro / dao, go / dao, bo / dao);
 
288
        sp_color_rgb_to_hsl_floatv(hsl_check, check[0] / 255.0, check[1] / 255.0, check[2] / 255.0);
 
289
        sp_color_rgb_to_hsl_floatv(hsl_orig, orig[0] / 255.0, orig[1] / 255.0, orig[2] / 255.0);
231
290
    }
232
291
    
233
292
    switch (method) {
234
293
        case FLOOD_CHANNELS_ALPHA:
235
 
            return abs(static_cast<int>(ac) - ao) <= threshold;
 
294
            return ((int)abs(check[3] - orig[3]) <= threshold);
236
295
        case FLOOD_CHANNELS_R:
237
 
            return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold;
 
296
            return ((int)abs(check[0] - orig[0]) <= threshold);
238
297
        case FLOOD_CHANNELS_G:
239
 
            return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold;
 
298
            return ((int)abs(check[1] - orig[1]) <= threshold);
240
299
        case FLOOD_CHANNELS_B:
241
 
            return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold;
 
300
            return ((int)abs(check[2] - orig[2]) <= threshold);
242
301
        case FLOOD_CHANNELS_RGB:
243
 
            guint32 amc, rmc, bmc, gmc;
244
 
            //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
245
 
            //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
246
 
            amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
247
 
            rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
248
 
            gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
249
 
            bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
250
 
 
251
 
            diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0));
252
 
            diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0));
253
 
            diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0));
 
302
            unsigned char merged_check[3];
 
303
            
 
304
            merge_pixel_with_background(check, dtc, merged_check);
 
305
            
 
306
            for (int i = 0; i < 3; i++) {
 
307
              diff += (int)abs(merged_check[i] - merged_orig_pixel[i]);
 
308
            }
254
309
            return ((diff / 3) <= ((threshold * 3) / 4));
255
310
        
256
311
        case FLOOD_CHANNELS_H:
296
351
    int y_limit;
297
352
    unsigned int width;
298
353
    unsigned int height;
299
 
    unsigned int stride;
300
354
    unsigned int threshold;
301
355
    unsigned int radius;
302
356
    PaintBucketChannels method;
303
 
    guint32 dtc;
304
 
    guint32 merged_orig_pixel;
 
357
    unsigned char *dtc;
 
358
    unsigned char *merged_orig_pixel;
305
359
    Geom::Rect bbox;
306
360
    Geom::Rect screen;
307
361
    unsigned int max_queue_size;
309
363
};
310
364
 
311
365
/**
312
 
 * Check if a pixel can be included in the fill.
313
 
 * @param px The rendered pixel buffer to check.
314
 
 * @param trace_t The pixel in the trace pixel buffer to check or mark.
315
 
 * @param x The X coordinate.
316
 
 * @param y The y coordinate.
317
 
 * @param orig_color The original selected pixel to use as the fill target color.
318
 
 * @param bci The bitmap_coords_info structure.
 
366
 * \brief Check if a pixel can be included in the fill.
 
367
 * \param px The rendered pixel buffer to check.
 
368
 * \param trace_t The pixel in the trace pixel buffer to check or mark.
 
369
 * \param x The X coordinate.
 
370
 * \param y The y coordinate.
 
371
 * \param orig_color The original selected pixel to use as the fill target color.
 
372
 * \param bci The bitmap_coords_info structure.
319
373
 */
320
 
inline static bool check_if_pixel_is_paintable(guchar *px, unsigned char *trace_t, int x, int y, guint32 orig_color, bitmap_coords_info bci) {
 
374
inline static bool check_if_pixel_is_paintable(guchar *px, unsigned char *trace_t, int x, int y, unsigned char *orig_color, bitmap_coords_info bci) {
321
375
    if (is_pixel_paintability_checked(trace_t)) {
322
376
        return is_pixel_paintable(trace_t);
323
377
    } else {
324
 
        guint32 pixel = get_pixel(px, x, y, bci.stride);
325
 
        if (compare_pixels(pixel, orig_color, bci.merged_orig_pixel, bci.dtc, bci.threshold, bci.method)) {
 
378
        unsigned char *t = get_pixel(px, x, y, bci.width);
 
379
        if (compare_pixels(t, orig_color, bci.merged_orig_pixel, bci.dtc, bci.threshold, bci.method)) {
326
380
            mark_pixel_paintable(trace_t);
327
381
            return true;
328
382
        } else {
333
387
}
334
388
 
335
389
/**
336
 
 * Perform the bitmap-to-vector tracing and place the traced path onto the document.
337
 
 * @param px The trace pixel buffer to trace to SVG.
338
 
 * @param desktop The desktop on which to place the final SVG path.
339
 
 * @param transform The transform to apply to the final SVG path.
340
 
 * @param union_with_selection If true, merge the final SVG path with the current selection.
 
390
 * \brief Perform the bitmap-to-vector tracing and place the traced path onto the document.
 
391
 * \param px The trace pixel buffer to trace to SVG.
 
392
 * \param desktop The desktop on which to place the final SVG path.
 
393
 * \param transform The transform to apply to the final SVG path.
 
394
 * \param union_with_selection If true, merge the final SVG path with the current selection.
341
395
 */
342
 
static void do_trace(bitmap_coords_info bci, guchar *trace_px, SPDesktop *desktop, Geom::Affine transform, unsigned int min_x, unsigned int max_x, unsigned int min_y, unsigned int max_y, bool union_with_selection) {
343
 
    SPDocument *document = desktop->getDocument();
 
396
static void do_trace(bitmap_coords_info bci, guchar *trace_px, SPDesktop *desktop, Geom::Matrix transform, unsigned int min_x, unsigned int max_x, unsigned int min_y, unsigned int max_y, bool union_with_selection) {
 
397
    SPDocument *document = sp_desktop_document(desktop);
344
398
 
345
399
    unsigned char *trace_t;
346
400
 
363
417
    std::vector<Inkscape::Trace::TracingEngineResult> results = pte.traceGrayMap(gray_map);
364
418
    gray_map->destroy(gray_map);
365
419
 
366
 
    //XML Tree being used here directly while it shouldn't be...."
367
 
    Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
 
420
    Inkscape::XML::Node *layer_repr = SP_GROUP(desktop->currentLayer())->repr;
 
421
    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
368
422
 
369
423
    long totalNodeCount = 0L;
370
424
 
427
481
            g_free(str);
428
482
        }
429
483
 
430
 
        desktop->currentLayer()->addChild(pathRepr,NULL);
 
484
        layer_repr->addChild(pathRepr, NULL);
431
485
 
432
486
        SPObject *reprobj = document->getObjectByRepr(pathRepr);
433
487
        if (reprobj) {
434
 
            SP_ITEM(reprobj)->doWriteTransform(pathRepr, transform, NULL);
 
488
            sp_item_write_transform(SP_ITEM(reprobj), pathRepr, transform, NULL);
435
489
            
436
490
            // premultiply the item transform by the accumulated parent transform in the paste layer
437
 
            Geom::Affine local (SP_GROUP(desktop->currentLayer())->i2doc_affine());
 
491
            Geom::Matrix local (sp_item_i2doc_affine(SP_GROUP(desktop->currentLayer())));
438
492
            if (!local.isIdentity()) {
439
493
                gchar const *t_str = pathRepr->attribute("transform");
440
 
                Geom::Affine item_t (Geom::identity());
 
494
                Geom::Matrix item_t (Geom::identity());
441
495
                if (t_str)
442
496
                    sp_svg_transform_read(t_str, &item_t);
443
497
                item_t *= local.inverse();
447
501
                g_free(affinestr);
448
502
            }
449
503
 
450
 
            Inkscape::Selection *selection = desktop->getSelection();
 
504
            Inkscape::Selection *selection = sp_desktop_selection(desktop);
451
505
 
452
506
            pathRepr->setPosition(-1);
453
507
 
454
508
            if (union_with_selection) {
455
 
                desktop->messageStack()->flashF( Inkscape::WARNING_MESSAGE,
456
 
                    ngettext("Area filled, path with <b>%d</b> node created and unioned with selection.","Area filled, path with <b>%d</b> nodes created and unioned with selection.",
457
 
                    SP_PATH(reprobj)->nodesInPath()), SP_PATH(reprobj)->nodesInPath() );
 
509
                desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, ngettext("Area filled, path with <b>%d</b> node created and unioned with selection.","Area filled, path with <b>%d</b> nodes created and unioned with selection.",sp_nodes_in_path(SP_PATH(reprobj))), sp_nodes_in_path(SP_PATH(reprobj)));
458
510
                selection->add(reprobj);
459
 
                sp_selected_path_union_skip_undo(desktop->getSelection(), desktop);
 
511
                sp_selected_path_union_skip_undo(desktop);
460
512
            } else {
461
 
                desktop->messageStack()->flashF( Inkscape::WARNING_MESSAGE,
462
 
                    ngettext("Area filled, path with <b>%d</b> node created.","Area filled, path with <b>%d</b> nodes created.",
463
 
                    SP_PATH(reprobj)->nodesInPath()), SP_PATH(reprobj)->nodesInPath() );
 
513
                desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, ngettext("Area filled, path with <b>%d</b> node created.","Area filled, path with <b>%d</b> nodes created.",sp_nodes_in_path(SP_PATH(reprobj))), sp_nodes_in_path(SP_PATH(reprobj)));
464
514
                selection->set(reprobj);
465
515
            }
466
516
 
472
522
}
473
523
 
474
524
/**
475
 
 * The possible return states of perform_bitmap_scanline_check().
 
525
 * \brief The possible return states of perform_bitmap_scanline_check()
476
526
 */
477
527
enum ScanlineCheckResult {
478
528
    SCANLINE_CHECK_OK,
481
531
};
482
532
 
483
533
/**
484
 
 * Determine if the provided coordinates are within the pixel buffer limits.
485
 
 * @param x The X coordinate.
486
 
 * @param y The Y coordinate.
487
 
 * @param bci The bitmap_coords_info structure.
 
534
 * \brief Determine if the provided coordinates are within the pixel buffer limits.
 
535
 * \param x The X coordinate.
 
536
 * \param y The Y coordinate.
 
537
 * \param bci The bitmap_coords_info structure.
488
538
 */
489
539
inline static bool coords_in_range(unsigned int x, unsigned int y, bitmap_coords_info bci) {
490
540
    return (x < bci.width) &&
498
548
#define PAINT_DIRECTION_ALL 15
499
549
 
500
550
/**
501
 
 * Paint a pixel or a square (if autogap is enabled) on the trace pixel buffer.
502
 
 * @param px The rendered pixel buffer to check.
503
 
 * @param trace_px The trace pixel buffer.
504
 
 * @param orig_color The original selected pixel to use as the fill target color.
505
 
 * @param bci The bitmap_coords_info structure.
506
 
 * @param original_point_trace_t The original pixel in the trace pixel buffer to check.
 
551
 * \brief Paint a pixel or a square (if autogap is enabled) on the trace pixel buffer
 
552
 * \param px The rendered pixel buffer to check.
 
553
 * \param trace_px The trace pixel buffer.
 
554
 * \param orig_color The original selected pixel to use as the fill target color.
 
555
 * \param bci The bitmap_coords_info structure.
 
556
 * \param original_point_trace_t The original pixel in the trace pixel buffer to check.
507
557
 */
508
 
inline static unsigned int paint_pixel(guchar *px, guchar *trace_px, guint32 orig_color, bitmap_coords_info bci, unsigned char *original_point_trace_t) {
 
558
inline static unsigned int paint_pixel(guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci, unsigned char *original_point_trace_t) {
509
559
    if (bci.radius == 0) {
510
560
        mark_pixel_colored(original_point_trace_t); 
511
561
        return PAINT_DIRECTION_ALL;
546
596
}
547
597
 
548
598
/**
549
 
 * Push a point to be checked onto the bottom of the rendered pixel buffer check queue.
550
 
 * @param fill_queue The fill queue to add the point to.
551
 
 * @param max_queue_size The maximum size of the fill queue.
552
 
 * @param trace_t The trace pixel buffer pixel.
553
 
 * @param x The X coordinate.
554
 
 * @param y The Y coordinate.
 
599
 * \brief Push a point to be checked onto the bottom of the rendered pixel buffer check queue.
 
600
 * \param fill_queue The fill queue to add the point to.
 
601
 * \param max_queue_size The maximum size of the fill queue.
 
602
 * \param trace_t The trace pixel buffer pixel.
 
603
 * \param x The X coordinate.
 
604
 * \param y The Y coordinate.
555
605
 */
556
606
static void push_point_onto_queue(std::deque<Geom::Point> *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) {
557
607
    if (!is_pixel_queued(trace_t)) {
563
613
}
564
614
 
565
615
/**
566
 
 * Shift a point to be checked onto the top of the rendered pixel buffer check queue.
567
 
 * @param fill_queue The fill queue to add the point to.
568
 
 * @param max_queue_size The maximum size of the fill queue.
569
 
 * @param trace_t The trace pixel buffer pixel.
570
 
 * @param x The X coordinate.
571
 
 * @param y The Y coordinate.
 
616
 * \brief Shift a point to be checked onto the top of the rendered pixel buffer check queue.
 
617
 * \param fill_queue The fill queue to add the point to.
 
618
 * \param max_queue_size The maximum size of the fill queue.
 
619
 * \param trace_t The trace pixel buffer pixel.
 
620
 * \param x The X coordinate.
 
621
 * \param y The Y coordinate.
572
622
 */
573
623
static void shift_point_onto_queue(std::deque<Geom::Point> *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) {
574
624
    if (!is_pixel_queued(trace_t)) {
580
630
}
581
631
 
582
632
/**
583
 
 * Scan a row in the rendered pixel buffer and add points to the fill queue as necessary.
584
 
 * @param fill_queue The fill queue to add the point to.
585
 
 * @param px The rendered pixel buffer.
586
 
 * @param trace_px The trace pixel buffer.
587
 
 * @param orig_color The original selected pixel to use as the fill target color.
588
 
 * @param bci The bitmap_coords_info structure.
 
633
 * \brief Scan a row in the rendered pixel buffer and add points to the fill queue as necessary.
 
634
 * \param fill_queue The fill queue to add the point to.
 
635
 * \param px The rendered pixel buffer.
 
636
 * \param trace_px The trace pixel buffer.
 
637
 * \param orig_color The original selected pixel to use as the fill target color.
 
638
 * \param bci The bitmap_coords_info structure.
589
639
 */
590
 
static ScanlineCheckResult perform_bitmap_scanline_check(std::deque<Geom::Point> *fill_queue, guchar *px, guchar *trace_px, guint32 orig_color, bitmap_coords_info bci, unsigned int *min_x, unsigned int *max_x) {
 
640
static ScanlineCheckResult perform_bitmap_scanline_check(std::deque<Geom::Point> *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci, unsigned int *min_x, unsigned int *max_x) {
591
641
    bool aborted = false;
592
642
    bool reached_screen_boundary = false;
593
643
    bool ok;
601
651
    bool currently_painting_top = false;
602
652
    bool currently_painting_bottom = false;
603
653
 
604
 
    unsigned int top_ty = (bci.y > 0) ? bci.y - 1 : 0;
 
654
    unsigned int top_ty = bci.y - 1;
605
655
    unsigned int bottom_ty = bci.y + 1;
606
656
 
607
657
    bool can_paint_top = (top_ty > 0);
697
747
}
698
748
 
699
749
/**
700
 
 * Sort the rendered pixel buffer check queue vertically.
 
750
 * \brief Sort the rendered pixel buffer check queue vertically.
701
751
 */
702
752
static bool sort_fill_queue_vertical(Geom::Point a, Geom::Point b) {
703
753
    return a[Geom::Y] > b[Geom::Y];
704
754
}
705
755
 
706
756
/**
707
 
 * Sort the rendered pixel buffer check queue horizontally.
 
757
 * \brief Sort the rendered pixel buffer check queue horizontally.
708
758
 */
709
759
static bool sort_fill_queue_horizontal(Geom::Point a, Geom::Point b) {
710
760
    return a[Geom::X] > b[Geom::X];
711
761
}
712
762
 
713
763
/**
714
 
 * Perform a flood fill operation.
715
 
 * @param event_context The event context for this tool.
716
 
 * @param event The details of this event.
717
 
 * @param union_with_selection If true, union the new fill with the current selection.
718
 
 * @param is_point_fill If false, use the Rubberband "touch selection" to get the initial points for the fill.
719
 
 * @param is_touch_fill If true, use only the initial contact point in the Rubberband "touch selection" as the fill target color.
 
764
 * \brief Perform a flood fill operation.
 
765
 * \param event_context The event context for this tool.
 
766
 * \param event The details of this event.
 
767
 * \param union_with_selection If true, union the new fill with the current selection.
 
768
 * \param is_point_fill If false, use the Rubberband "touch selection" to get the initial points for the fill.
 
769
 * \param is_touch_fill If true, use only the initial contact point in the Rubberband "touch selection" as the fill target color.
720
770
 */
721
 
static void sp_flood_do_flood_fill(ToolBase *event_context, GdkEvent *event, bool union_with_selection, bool is_point_fill, bool is_touch_fill) {
 
771
static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *event, bool union_with_selection, bool is_point_fill, bool is_touch_fill) {
722
772
    SPDesktop *desktop = event_context->desktop;
723
 
    SPDocument *document = desktop->getDocument();
724
 
 
725
 
    document->ensureUpToDate();
 
773
    SPDocument *document = sp_desktop_document(desktop);
 
774
 
 
775
    /* Create new arena */
 
776
    NRArena *arena = NRArena::create();
 
777
    unsigned dkey = sp_item_display_key_new(1);
 
778
 
 
779
    sp_document_ensure_up_to_date (document);
726
780
    
727
 
    Geom::OptRect bbox = document->getRoot()->visualBounds();
 
781
    SPItem *document_root = SP_ITEM(SP_DOCUMENT_ROOT(document));
 
782
    Geom::OptRect bbox = document_root->getBounds(Geom::identity());
728
783
 
729
784
    if (!bbox) {
730
785
        desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Area is not bounded</b>, cannot fill."));
743
798
    unsigned int height = (int)ceil(screen.height() * zoom_scale * padding);
744
799
 
745
800
    Geom::Point origin(screen.min()[Geom::X],
746
 
                       document->getHeight().value("px") - screen.height() - screen.min()[Geom::Y]);
 
801
                       sp_document_height(document) - screen.height() - screen.min()[Geom::Y]);
747
802
                    
748
 
    origin[Geom::X] += (screen.width() * ((1 - padding) / 2));
749
 
    origin[Geom::Y] += (screen.height() * ((1 - padding) / 2));
 
803
    origin[Geom::X] = origin[Geom::X] + (screen.width() * ((1 - padding) / 2));
 
804
    origin[Geom::Y] = origin[Geom::Y] + (screen.height() * ((1 - padding) / 2));
750
805
    
751
806
    Geom::Scale scale(zoom_scale, zoom_scale);
752
 
    Geom::Affine affine = scale * Geom::Translate(-origin * scale);
753
 
 
754
 
    int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
755
 
    guchar *px = g_new(guchar, stride * height);
756
 
    guint32 bgcolor, dtc;
757
 
 
758
 
    // Draw image into data block px
759
 
    { // this block limits the lifetime of Drawing and DrawingContext
760
 
        /* Create DrawingItems and set transform */
761
 
        unsigned dkey = SPItem::display_key_new(1);
762
 
        Inkscape::Drawing drawing;
763
 
        Inkscape::DrawingItem *root = document->getRoot()->invoke_show( drawing, dkey, SP_ITEM_SHOW_DISPLAY);
764
 
        root->setTransform(affine);
765
 
        drawing.setRoot(root);
766
 
 
767
 
        Geom::IntRect final_bbox = Geom::IntRect::from_xywh(0, 0, width, height);
768
 
        drawing.update(final_bbox);
769
 
 
770
 
        cairo_surface_t *s = cairo_image_surface_create_for_data(
771
 
            px, CAIRO_FORMAT_ARGB32, width, height, stride);
772
 
        Inkscape::DrawingContext dc(s, Geom::Point(0,0));
773
 
        // cairo_translate not necessary here - surface origin is at 0,0
774
 
 
775
 
        SPNamedView *nv = desktop->getNamedView();
776
 
        bgcolor = nv->pagecolor;
777
 
        // bgcolor is 0xrrggbbaa, we need 0xaarrggbb
778
 
        dtc = (bgcolor >> 8) | (bgcolor << 24);
779
 
 
780
 
        dc.setSource(bgcolor);
781
 
        dc.setOperator(CAIRO_OPERATOR_SOURCE);
782
 
        dc.paint();
783
 
        dc.setOperator(CAIRO_OPERATOR_OVER);
784
 
 
785
 
        drawing.render(dc, final_bbox);
786
 
 
787
 
        //cairo_surface_write_to_png( s, "cairo.png" );
788
 
 
789
 
        cairo_surface_flush(s);
790
 
        cairo_surface_destroy(s);
791
 
        
792
 
        // Hide items
793
 
        document->getRoot()->invoke_hide(dkey);
 
807
    Geom::Matrix affine = scale * Geom::Translate(-origin * scale);
 
808
    
 
809
    /* Create ArenaItems and set transform */
 
810
    NRArenaItem *root = sp_item_invoke_show(SP_ITEM(sp_document_root(document)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
 
811
    nr_arena_item_set_transform(NR_ARENA_ITEM(root), affine);
 
812
 
 
813
    NRGC gc(NULL);
 
814
    gc.transform.setIdentity();
 
815
    
 
816
    NRRectL final_bbox;
 
817
    final_bbox.x0 = 0;
 
818
    final_bbox.y0 = 0; //row;
 
819
    final_bbox.x1 = width;
 
820
    final_bbox.y1 = height; //row + num_rows;
 
821
    
 
822
    nr_arena_item_invoke_update(root, &final_bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
 
823
 
 
824
    guchar *px = g_new(guchar, 4 * width * height);
 
825
    
 
826
    NRPixBlock B;
 
827
    nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N,
 
828
                              final_bbox.x0, final_bbox.y0, final_bbox.x1, final_bbox.y1,
 
829
                              px, 4 * width, FALSE, FALSE );
 
830
    
 
831
    SPNamedView *nv = sp_desktop_namedview(desktop);
 
832
    unsigned long bgcolor = nv->pagecolor;
 
833
    
 
834
    unsigned char dtc[4];
 
835
    dtc[0] = NR_RGBA32_R(bgcolor);
 
836
    dtc[1] = NR_RGBA32_G(bgcolor);
 
837
    dtc[2] = NR_RGBA32_B(bgcolor);
 
838
    dtc[3] = NR_RGBA32_A(bgcolor);
 
839
    
 
840
    for (unsigned int fy = 0; fy < height; fy++) {
 
841
        guchar *p = NR_PIXBLOCK_PX(&B) + fy * B.rs;
 
842
        for (unsigned int fx = 0; fx < width; fx++) {
 
843
            for (int i = 0; i < 4; i++) { 
 
844
                *p++ = dtc[i];
 
845
            }
 
846
        }
794
847
    }
795
848
 
796
 
    // {
797
 
    //     // Dump data to png
798
 
    //     cairo_surface_t *s = cairo_image_surface_create_for_data(
799
 
    //         px, CAIRO_FORMAT_ARGB32, width, height, stride);
800
 
    //     cairo_surface_write_to_png( s, "cairo2.png" );
801
 
    //     std::cout << "  Wrote cairo2.png" << std::endl;
802
 
    // }
803
 
 
 
849
    nr_arena_item_invoke_render(NULL, root, &final_bbox, &B, NR_ARENA_ITEM_RENDER_NO_CACHE );
 
850
    nr_pixblock_release(&B);
 
851
    
 
852
    // Hide items
 
853
    sp_item_invoke_hide(SP_ITEM(sp_document_root(document)), dkey);
 
854
    
 
855
    nr_object_unref((NRObject *) arena);
 
856
    
804
857
    guchar *trace_px = g_new(guchar, width * height);
805
858
    memset(trace_px, 0x00, width * height);
806
859
    
835
888
    bci.y_limit = y_limit;
836
889
    bci.width = width;
837
890
    bci.height = height;
838
 
    bci.stride = stride;
839
891
    bci.threshold = threshold;
840
892
    bci.method = method;
841
893
    bci.bbox = *bbox;
848
900
    if (is_point_fill) {
849
901
        fill_points.push_back(Geom::Point(event->button.x, event->button.y));
850
902
    } else {
851
 
        Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
 
903
        Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(desktop);
852
904
        fill_points = r->getPoints();
853
905
    }
854
906
 
855
907
    for (unsigned int i = 0; i < fill_points.size(); i++) {
856
 
        Geom::Point pw = Geom::Point(fill_points[i][Geom::X] / zoom_scale, document->getHeight().value("px") + (fill_points[i][Geom::Y] / zoom_scale)) * affine;
 
908
        Geom::Point pw = Geom::Point(fill_points[i][Geom::X] / zoom_scale, sp_document_height(document) + (fill_points[i][Geom::Y] / zoom_scale)) * affine;
857
909
 
858
910
        pw[Geom::X] = (int)MIN(width - 1, MAX(0, pw[Geom::X]));
859
911
        pw[Geom::Y] = (int)MIN(height - 1, MAX(0, pw[Geom::Y]));
888
940
        int cx = (int)color_point[Geom::X];
889
941
        int cy = (int)color_point[Geom::Y];
890
942
 
891
 
        guint32 orig_color = get_pixel(px, cx, cy, stride);
892
 
        bci.merged_orig_pixel = compose_onto(orig_color, dtc);
 
943
        unsigned char *orig_px = get_pixel(px, cx, cy, width);
 
944
        unsigned char orig_color[4];
 
945
        for (int i = 0; i < 4; i++) { orig_color[i] = orig_px[i]; }
 
946
 
 
947
        unsigned char merged_orig[3];
 
948
 
 
949
        merge_pixel_with_background(orig_color, dtc, merged_orig);
 
950
 
 
951
        bci.merged_orig_pixel = merged_orig;
893
952
 
894
953
        unsigned char *trace_t = get_trace_pixel(trace_px, cx, cy, width);
895
954
        if (!is_pixel_checked(trace_t) && !is_pixel_colored(trace_t)) {
932
991
                        std::deque<Geom::Point>::iterator start_sort = fill_queue.begin();
933
992
                        std::deque<Geom::Point>::iterator end_sort = fill_queue.begin();
934
993
                        unsigned int sort_y = (unsigned int)cp[Geom::Y];
935
 
                        unsigned int current_y;
 
994
                        unsigned int current_y = sort_y;
936
995
                        
937
 
                        for (std::deque<Geom::Point>::iterator i = fill_queue.begin(); i != fill_queue.end(); ++i) {
 
996
                        for (std::deque<Geom::Point>::iterator i = fill_queue.begin(); i != fill_queue.end(); i++) {
938
997
                            Geom::Point current = *i;
939
998
                            current_y = (unsigned int)current[Geom::Y];
940
999
                            if (current_y != sort_y) {
1054
1113
    Geom::Point min_start = Geom::Point(min_x, min_y);
1055
1114
    
1056
1115
    affine = scale * Geom::Translate(-origin * scale - min_start);
1057
 
    Geom::Affine inverted_affine = Geom::Affine(affine).inverse();
 
1116
    Geom::Matrix inverted_affine = Geom::Matrix(affine).inverse();
1058
1117
    
1059
1118
    do_trace(bci, trace_px, desktop, inverted_affine, min_x, max_x, min_y, max_y, union_with_selection);
1060
1119
 
1061
1120
    g_free(trace_px);
1062
1121
    
1063
 
    DocumentUndo::done(document, SP_VERB_CONTEXT_PAINTBUCKET, _("Fill bounded area"));
 
1122
    sp_document_done(document, SP_VERB_CONTEXT_PAINTBUCKET, _("Fill bounded area"));
1064
1123
}
1065
1124
 
1066
 
bool FloodTool::item_handler(SPItem* item, GdkEvent* event) {
 
1125
static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
 
1126
{
1067
1127
    gint ret = FALSE;
1068
1128
 
 
1129
    SPDesktop *desktop = event_context->desktop;
 
1130
 
1069
1131
    switch (event->type) {
1070
1132
    case GDK_BUTTON_PRESS:
1071
 
        if ((event->button.state & GDK_CONTROL_MASK) && event->button.button == 1 && !this->space_panning) {
1072
 
            Geom::Point const button_w(event->button.x, event->button.y);
 
1133
        if ((event->button.state & GDK_CONTROL_MASK) && event->button.button == 1 && !event_context->space_panning) {
 
1134
            Geom::Point const button_w(event->button.x,
 
1135
                                       event->button.y);
1073
1136
            
1074
1137
            SPItem *item = sp_event_context_find_item (desktop, button_w, TRUE, TRUE);
1075
1138
            
1076
 
            // Set style
1077
 
            desktop->applyCurrentOrToolStyle(item, "/tools/paintbucket", false);
1078
 
 
1079
 
            DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_PAINTBUCKET, _("Set style on object"));
1080
 
            // Dead assignment: Value stored to 'ret' is never read
1081
 
            //ret = TRUE;
 
1139
            Inkscape::XML::Node *pathRepr = SP_OBJECT_REPR(item);
 
1140
            /* Set style */
 
1141
            sp_desktop_apply_style_tool (desktop, pathRepr, "/tools/paintbucket", false);
 
1142
            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_PAINTBUCKET, _("Set style on object"));
 
1143
            ret = TRUE;
1082
1144
        }
1083
1145
        break;
1084
 
 
1085
1146
    default:
1086
1147
        break;
1087
1148
    }
1088
1149
 
1089
 
//    if (((ToolBaseClass *) sp_flood_context_parent_class)->item_handler) {
1090
 
//        ret = ((ToolBaseClass *) sp_flood_context_parent_class)->item_handler(event_context, item, event);
1091
 
//    }
1092
 
    // CPPIFY: ret is overwritten...
1093
 
    ret = ToolBase::item_handler(item, event);
 
1150
    if (((SPEventContextClass *) parent_class)->item_handler) {
 
1151
        ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
 
1152
    }
1094
1153
 
1095
1154
    return ret;
1096
1155
}
1097
1156
 
1098
 
bool FloodTool::root_handler(GdkEvent* event) {
 
1157
static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event)
 
1158
{
1099
1159
    static bool dragging;
1100
1160
    
1101
1161
    gint ret = FALSE;
 
1162
    SPDesktop *desktop = event_context->desktop;
1102
1163
 
1103
1164
    switch (event->type) {
1104
1165
    case GDK_BUTTON_PRESS:
1105
 
        if (event->button.button == 1 && !this->space_panning) {
 
1166
        if (event->button.button == 1 && !event_context->space_panning) {
1106
1167
            if (!(event->button.state & GDK_CONTROL_MASK)) {
1107
 
                Geom::Point const button_w(event->button.x, event->button.y);
 
1168
                Geom::Point const button_w(event->button.x,
 
1169
                                           event->button.y);
1108
1170
    
1109
 
                if (Inkscape::have_viable_layer(desktop, this->defaultMessageContext())) {
 
1171
                if (Inkscape::have_viable_layer(desktop, event_context->defaultMessageContext())) {
1110
1172
                    // save drag origin
1111
 
                    this->xp = (gint) button_w[Geom::X];
1112
 
                    this->yp = (gint) button_w[Geom::Y];
1113
 
                    this->within_tolerance = true;
 
1173
                    event_context->xp = (gint) button_w[Geom::X];
 
1174
                    event_context->yp = (gint) button_w[Geom::Y];
 
1175
                    event_context->within_tolerance = true;
1114
1176
                      
1115
1177
                    dragging = true;
1116
1178
                    
1120
1182
                }
1121
1183
            }
1122
1184
        }
1123
 
 
1124
1185
    case GDK_MOTION_NOTIFY:
1125
 
        if ( dragging && ( event->motion.state & GDK_BUTTON1_MASK ) && !this->space_panning) {
1126
 
            if ( this->within_tolerance
1127
 
                 && ( abs( (gint) event->motion.x - this->xp ) < this->tolerance )
1128
 
                 && ( abs( (gint) event->motion.y - this->yp ) < this->tolerance ) ) {
 
1186
        if ( dragging
 
1187
             && ( event->motion.state & GDK_BUTTON1_MASK ) && !event_context->space_panning)
 
1188
        {
 
1189
            if ( event_context->within_tolerance
 
1190
                 && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
 
1191
                 && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
1129
1192
                break; // do not drag if we're within tolerance from origin
1130
1193
            }
1131
1194
            
1132
 
            this->within_tolerance = false;
 
1195
            event_context->within_tolerance = false;
1133
1196
            
1134
1197
            Geom::Point const motion_pt(event->motion.x, event->motion.y);
1135
1198
            Geom::Point const p(desktop->w2d(motion_pt));
1136
 
 
1137
1199
            if (Inkscape::Rubberband::get(desktop)->is_started()) {
1138
1200
                Inkscape::Rubberband::get(desktop)->move(p);
1139
 
                this->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> areas to add to fill, hold <b>Alt</b> for touch fill"));
 
1201
                event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> areas to add to fill, hold <b>Alt</b> for touch fill"));
1140
1202
                gobble_motion_events(GDK_BUTTON1_MASK);
1141
1203
            }
1142
1204
        }
1143
1205
        break;
1144
1206
 
1145
1207
    case GDK_BUTTON_RELEASE:
1146
 
        if (event->button.button == 1 && !this->space_panning) {
1147
 
            Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
1148
 
 
 
1208
        if (event->button.button == 1 && !event_context->space_panning) {
 
1209
            Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(desktop);
1149
1210
            if (r->is_started()) {
1150
1211
                // set "busy" cursor
1151
1212
                desktop->setWaitingCursor();
1152
1213
 
1153
 
                if (SP_IS_EVENT_CONTEXT(this)) { 
 
1214
                if (SP_IS_EVENT_CONTEXT(event_context)) { 
1154
1215
                    // Since setWaitingCursor runs main loop iterations, we may have already left this tool!
1155
1216
                    // So check if the tool is valid before doing anything
1156
1217
                    dragging = false;
1157
1218
 
1158
 
                    bool is_point_fill = this->within_tolerance;
 
1219
                    bool is_point_fill = event_context->within_tolerance;
1159
1220
                    bool is_touch_fill = event->button.state & GDK_MOD1_MASK;
1160
1221
                    
1161
 
                    sp_flood_do_flood_fill(this, event, event->button.state & GDK_SHIFT_MASK, is_point_fill, is_touch_fill);
 
1222
                    sp_flood_do_flood_fill(event_context, event, event->button.state & GDK_SHIFT_MASK, is_point_fill, is_touch_fill);
1162
1223
                    
1163
1224
                    desktop->clearWaitingCursor();
1164
1225
                    // restore cursor when done; note that it may already be different if e.g. user 
1169
1230
 
1170
1231
                r->stop();
1171
1232
 
1172
 
                //if (SP_IS_EVENT_CONTEXT(this)) {
1173
 
                this->defaultMessageContext()->clear();
1174
 
                //}
 
1233
                if (SP_IS_EVENT_CONTEXT(event_context)) {
 
1234
                    event_context->defaultMessageContext()->clear();
 
1235
                }
1175
1236
            }
1176
1237
        }
1177
1238
        break;
1178
1239
    case GDK_KEY_PRESS:
1179
1240
        switch (get_group0_keyval (&event->key)) {
1180
 
        case GDK_KEY_Up:
1181
 
        case GDK_KEY_Down:
1182
 
        case GDK_KEY_KP_Up:
1183
 
        case GDK_KEY_KP_Down:
 
1241
        case GDK_Up:
 
1242
        case GDK_Down:
 
1243
        case GDK_KP_Up:
 
1244
        case GDK_KP_Down:
1184
1245
            // prevent the zoom field from activation
1185
 
            if (!MOD__CTRL_ONLY(event))
 
1246
            if (!MOD__CTRL_ONLY)
1186
1247
                ret = TRUE;
1187
1248
            break;
1188
1249
        default:
1189
1250
            break;
1190
1251
        }
1191
1252
        break;
1192
 
 
1193
1253
    default:
1194
1254
        break;
1195
1255
    }
1196
1256
 
1197
1257
    if (!ret) {
1198
 
        ret = ToolBase::root_handler(event);
 
1258
        if (((SPEventContextClass *) parent_class)->root_handler) {
 
1259
            ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
 
1260
        }
1199
1261
    }
1200
1262
 
1201
1263
    return ret;
1202
1264
}
1203
1265
 
1204
 
void FloodTool::finishItem() {
1205
 
    this->message_context->clear();
1206
 
 
1207
 
    if (this->item != NULL) {
1208
 
        this->item->updateRepr();
1209
 
 
1210
 
        desktop->canvas->endForcedFullRedraws();
1211
 
 
1212
 
        desktop->getSelection()->set(this->item);
1213
 
 
1214
 
        DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_PAINTBUCKET, _("Fill bounded area"));
1215
 
 
1216
 
        this->item = NULL;
 
1266
 
 
1267
static void sp_flood_finish(SPFloodContext *rc)
 
1268
{
 
1269
    rc->_message_context->clear();
 
1270
 
 
1271
    if ( rc->item != NULL ) {
 
1272
        SPDesktop * desktop;
 
1273
 
 
1274
        desktop = SP_EVENT_CONTEXT_DESKTOP(rc);
 
1275
 
 
1276
        SP_OBJECT(rc->item)->updateRepr();
 
1277
 
 
1278
        sp_canvas_end_forced_full_redraws(desktop->canvas);
 
1279
 
 
1280
        sp_desktop_selection(desktop)->set(rc->item);
 
1281
        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_PAINTBUCKET,
 
1282
                        _("Fill bounded area"));
 
1283
 
 
1284
        rc->item = NULL;
1217
1285
    }
1218
1286
}
1219
1287
 
1220
 
void FloodTool::set_channels(gint channels) {
 
1288
void flood_channels_set_channels( gint channels )
 
1289
{
1221
1290
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1222
1291
    prefs->setInt("/tools/paintbucket/channels", channels);
1223
1292
}
1224
1293
 
1225
 
}
1226
 
}
1227
 
}
1228
 
 
1229
1294
/*
1230
1295
  Local Variables:
1231
1296
  mode:c++
1235
1300
  fill-column:99
1236
1301
  End:
1237
1302
*/
1238
 
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
 
1303
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :