X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fflood-context.cpp;h=a45ec5dfe79569b53d5e19359789447216322e76;hb=e0c38bd294fd720cebbabeda1a0d87259f68bf7f;hp=7dc832843cf02a16f5a8cacb0274fbcb76ea64ec;hpb=e6d243379ddfa3709bcbf794f60ae6498d9dbb0e;p=inkscape.git diff --git a/src/flood-context.cpp b/src/flood-context.cpp index 7dc832843..a45ec5dfe 100644 --- a/src/flood-context.cpp +++ b/src/flood-context.cpp @@ -1,21 +1,24 @@ #define __SP_FLOOD_CONTEXT_C__ -/* -* Flood fill drawing context -* -* Author: -* Lauris Kaplinski -* bulia byak -* John Bintz -* -* Copyright (C) 2006 Johan Engelen -* Copyright (C) 2000-2005 authors -* Copyright (C) 2000-2001 Ximian, Inc. -* -* Released under GNU GPL, read the file 'COPYING' for more information -*/ - +/** @file + * @brief Bucket fill drawing context, works by bitmap filling an area on a rendered version + * of the current display and then tracing the result using potrace. + */ +/* Author: + * Lauris Kaplinski + * bulia byak + * John Bintz + * + * Copyright (C) 2006 Johan Engelen + * Copyright (C) 2000-2005 authors + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H #include "config.h" +#endif #include #include @@ -40,7 +43,7 @@ #include "object-edit.h" #include "xml/repr.h" #include "xml/node-event-vector.h" -#include "prefs-utils.h" +#include "preferences.h" #include "context-fns.h" #include "rubberband.h" @@ -55,6 +58,7 @@ #include "libnr/nr-translate-matrix-ops.h" #include "libnr/nr-translate-scale-ops.h" #include "libnr/nr-matrix-ops.h" +#include <2geom/pathvector.h> #include "sp-item.h" #include "sp-root.h" #include "sp-defs.h" @@ -62,11 +66,11 @@ #include "splivarot.h" #include "livarot/Path.h" #include "livarot/Shape.h" -#include "libnr/n-art-bpath.h" #include "svg/svg.h" #include "color.h" #include "trace/trace.h" +#include "trace/imagemap.h" #include "trace/potrace/inkscape-potrace.h" static void sp_flood_context_class_init(SPFloodContextClass *klass); @@ -222,11 +226,18 @@ static void sp_flood_context_setup(SPEventContext *ec) rc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); - if (prefs_get_int_attribute("tools.paintbucket", "selcue", 0) != 0) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/paintbucket/selcue")) { rc->enableSelectionCue(); } } +/** + * \brief Merge a pixel with the background color. + * \param orig The pixel to merge with the background. + * \param bg The background color. + * \param base The pixel to merge the original and background into. + */ inline static void merge_pixel_with_background (unsigned char *orig, unsigned char *bg, unsigned char *base) @@ -239,10 +250,24 @@ merge_pixel_with_background (unsigned char *orig, unsigned char *bg, } } +/** + * \brief Get the pointer to a pixel in a pixel buffer. + * \param px The pixel buffer. + * \param x The X coordinate. + * \param y The Y coordinate. + * \param width The width of the pixel buffer. + */ inline unsigned char * get_pixel(guchar *px, int x, int y, int width) { return px + (x + y * width) * 4; } +inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width) { + return trace_px + (x + y * width); +} + +/** + * \brief Generate the list of trace channel selection entries. + */ GList * flood_channels_dropdown_items_list() { GList *glist = NULL; @@ -258,6 +283,9 @@ GList * flood_channels_dropdown_items_list() { return glist; } +/** + * \brief Generate the list of autogap selection entries. + */ GList * flood_autogap_dropdown_items_list() { GList *glist = NULL; @@ -269,6 +297,15 @@ GList * flood_autogap_dropdown_items_list() { return glist; } +/** + * \brief Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation. + * \param check The pixel in the pixel buffer to check. + * \param orig The original selected pixel to use as the fill target color. + * \param merged_orig_pixel The original pixel merged with the background. + * \param dtc The desktop background color. + * \param threshold The fill threshold. + * \param method The fill method to use as defined in PaintBucketChannels. + */ static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned char *merged_orig_pixel, unsigned char *dtc, int threshold, PaintBucketChannels method) { int diff = 0; float hsl_check[3], hsl_orig[3]; @@ -310,20 +347,30 @@ static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned c return false; } -static inline bool is_pixel_checked(unsigned char *t) { return t[0] == 1; } -static inline bool is_pixel_queued(unsigned char *t) { return t[1] == 1; } -static inline bool is_pixel_paintability_checked(unsigned char *t) { return t[2] != 0; } -static inline bool is_pixel_paintable(unsigned char *t) { return t[2] == 1; } -static inline bool is_pixel_colored(unsigned char *t) { return t[3] == 255; } +enum { + PIXEL_CHECKED = 1, + PIXEL_QUEUED = 2, + PIXEL_PAINTABLE = 4, + PIXEL_NOT_PAINTABLE = 8, + PIXEL_COLORED = 16 +}; -static inline void mark_pixel_checked(unsigned char *t) { t[0] = 1; } -static inline void mark_pixel_unchecked(unsigned char *t) { t[0] = 0; } -static inline void mark_pixel_queued(unsigned char *t) { t[1] = 1; } -static inline void mark_pixel_paintable(unsigned char *t) { t[2] = 1; } -static inline void mark_pixel_not_paintable(unsigned char *t) { t[2] = 2; } -static inline void mark_pixel_colored(unsigned char *t) { t[3] = 255; } +static inline bool is_pixel_checked(unsigned char *t) { return (*t & PIXEL_CHECKED) == PIXEL_CHECKED; } +static inline bool is_pixel_queued(unsigned char *t) { return (*t & PIXEL_QUEUED) == PIXEL_QUEUED; } +static inline bool is_pixel_paintability_checked(unsigned char *t) { + return !((*t & PIXEL_PAINTABLE) == 0) && ((*t & PIXEL_NOT_PAINTABLE) == 0); +} +static inline bool is_pixel_paintable(unsigned char *t) { return (*t & PIXEL_PAINTABLE) == PIXEL_PAINTABLE; } +static inline bool is_pixel_colored(unsigned char *t) { return (*t & PIXEL_COLORED) == PIXEL_COLORED; } + +static inline void mark_pixel_checked(unsigned char *t) { *t |= PIXEL_CHECKED; } +static inline void mark_pixel_unchecked(unsigned char *t) { *t ^= PIXEL_CHECKED; } +static inline void mark_pixel_queued(unsigned char *t) { *t |= PIXEL_QUEUED; } +static inline void mark_pixel_paintable(unsigned char *t) { *t |= PIXEL_PAINTABLE; *t ^= PIXEL_NOT_PAINTABLE; } +static inline void mark_pixel_not_paintable(unsigned char *t) { *t |= PIXEL_NOT_PAINTABLE; *t ^= PIXEL_PAINTABLE; } +static inline void mark_pixel_colored(unsigned char *t) { *t |= PIXEL_COLORED; } -static inline void clear_pixel_paintability(unsigned char *t) { t[2] = 0; } +static inline void clear_pixel_paintability(unsigned char *t) { *t ^= PIXEL_PAINTABLE; *t ^= PIXEL_NOT_PAINTABLE; } struct bitmap_coords_info { bool is_left; @@ -337,12 +384,21 @@ struct bitmap_coords_info { PaintBucketChannels method; unsigned char *dtc; unsigned char *merged_orig_pixel; - NR::Rect bbox; - NR::Rect screen; + Geom::Rect bbox; + Geom::Rect screen; unsigned int max_queue_size; unsigned int current_step; }; +/** + * \brief Check if a pixel can be included in the fill. + * \param px The rendered pixel buffer to check. + * \param trace_t The pixel in the trace pixel buffer to check or mark. + * \param x The X coordinate. + * \param y The y coordinate. + * \param orig_color The original selected pixel to use as the fill target color. + * \param bci The bitmap_coords_info structure. + */ 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) { if (is_pixel_paintability_checked(trace_t)) { return is_pixel_paintable(trace_t); @@ -358,24 +414,44 @@ inline static bool check_if_pixel_is_paintable(guchar *px, unsigned char *trace_ } } -static void do_trace(GdkPixbuf *px, SPDesktop *desktop, NR::Matrix transform, bool union_with_selection) { +/** + * \brief Perform the bitmap-to-vector tracing and place the traced path onto the document. + * \param px The trace pixel buffer to trace to SVG. + * \param desktop The desktop on which to place the final SVG path. + * \param transform The transform to apply to the final SVG path. + * \param union_with_selection If true, merge the final SVG path with the current selection. + */ +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) { SPDocument *document = sp_desktop_document(desktop); - + + unsigned char *trace_t; + + GrayMap *gray_map = GrayMapCreate((max_x - min_x + 1), (max_y - min_y + 1)); + unsigned int gray_map_y = 0; + for (unsigned int y = min_y; y <= max_y; y++) { + unsigned long *gray_map_t = gray_map->rows[gray_map_y]; + + trace_t = get_trace_pixel(trace_px, min_x, y, bci.width); + for (unsigned int x = min_x; x <= max_x; x++) { + *gray_map_t = is_pixel_colored(trace_t) ? GRAYMAP_BLACK : GRAYMAP_WHITE; + gray_map_t++; + trace_t++; + } + gray_map_y++; + } + Inkscape::Trace::Potrace::PotraceTracingEngine pte; - - pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS); - pte.setInvert(false); + pte.keepGoing = 1; + std::vector results = pte.traceGrayMap(gray_map); + gray_map->destroy(gray_map); - Glib::RefPtr pixbuf = Glib::wrap(px, true); - - std::vector 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; - double offset = prefs_get_double_attribute("tools.paintbucket", "offset", 0.0); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double offset = prefs->getDouble("/tools/paintbucket/offset", 0.0); for (unsigned int i=0 ; icreateElement("svg:path"); /* Set style */ - sp_desktop_apply_style_tool (desktop, pathRepr, "tools.paintbucket", false); + sp_desktop_apply_style_tool (desktop, pathRepr, "/tools/paintbucket", false); - NArtBpath *bpath = sp_svg_read_path(result.getPathData().c_str()); - Path *path = bpath_to_Path(bpath); - g_free(bpath); + Geom::PathVector pathv = sp_svg_read_pathv(result.getPathData().c_str()); + Path *path = new Path; + path->LoadPathVector(pathv); if (offset != 0) { @@ -440,10 +516,10 @@ static void do_trace(GdkPixbuf *px, SPDesktop *desktop, NR::Matrix transform, bo sp_item_write_transform(SP_ITEM(reprobj), pathRepr, transform, NULL); // premultiply the item transform by the accumulated parent transform in the paste layer - NR::Matrix local = sp_item_i2doc_affine(SP_GROUP(desktop->currentLayer())); - if (!local.test_identity()) { + Geom::Matrix local (sp_item_i2doc_affine(SP_GROUP(desktop->currentLayer()))); + if (!local.isIdentity()) { gchar const *t_str = pathRepr->attribute("transform"); - NR::Matrix item_t (NR::identity()); + Geom::Matrix item_t (Geom::identity()); if (t_str) sp_svg_transform_read(t_str, &item_t); item_t *= local.inverse(); @@ -458,11 +534,11 @@ static void do_trace(GdkPixbuf *px, SPDesktop *desktop, NR::Matrix transform, bo pathRepr->setPosition(-1); if (union_with_selection) { - desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, _("Area filled, path with %d nodes created and unioned with selection."), sp_nodes_in_path(SP_PATH(reprobj))); + desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, ngettext("Area filled, path with %d node created and unioned with selection.","Area filled, path with %d nodes created and unioned with selection.",sp_nodes_in_path(SP_PATH(reprobj))), sp_nodes_in_path(SP_PATH(reprobj))); selection->add(reprobj); - sp_selected_path_union_skip_undo(); + sp_selected_path_union_skip_undo(desktop); } else { - desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, _("Area filled, path with %d nodes created."), sp_nodes_in_path(SP_PATH(reprobj))); + desktop->messageStack()->flashF(Inkscape::WARNING_MESSAGE, ngettext("Area filled, path with %d node created.","Area filled, path with %d nodes created.",sp_nodes_in_path(SP_PATH(reprobj))), sp_nodes_in_path(SP_PATH(reprobj))); selection->set(reprobj); } @@ -473,12 +549,21 @@ static void do_trace(GdkPixbuf *px, SPDesktop *desktop, NR::Matrix transform, bo } } +/** + * \brief The possible return states of perform_bitmap_scanline_check() + */ enum ScanlineCheckResult { SCANLINE_CHECK_OK, SCANLINE_CHECK_ABORTED, SCANLINE_CHECK_BOUNDARY }; +/** + * \brief Determine if the provided coordinates are within the pixel buffer limits. + * \param x The X coordinate. + * \param y The Y coordinate. + * \param bci The bitmap_coords_info structure. + */ inline static bool coords_in_range(unsigned int x, unsigned int y, bitmap_coords_info bci) { return (x < bci.width) && (y < bci.height); @@ -490,6 +575,14 @@ inline static bool coords_in_range(unsigned int x, unsigned int y, bitmap_coords #define PAINT_DIRECTION_DOWN 8 #define PAINT_DIRECTION_ALL 15 +/** + * \brief Paint a pixel or a square (if autogap is enabled) on the trace pixel buffer + * \param px The rendered pixel buffer to check. + * \param trace_px The trace pixel buffer. + * \param orig_color The original selected pixel to use as the fill target color. + * \param bci The bitmap_coords_info structure. + * \param original_point_trace_t The original pixel in the trace pixel buffer to check. + */ 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) { if (bci.radius == 0) { mark_pixel_colored(original_point_trace_t); @@ -505,7 +598,7 @@ inline static unsigned int paint_pixel(guchar *px, guchar *trace_px, unsigned ch for (unsigned int ty = bci.y - bci.radius; ty <= bci.y + bci.radius; ty++) { for (unsigned int tx = bci.x - bci.radius; tx <= bci.x + bci.radius; tx++) { if (coords_in_range(tx, ty, bci)) { - trace_t = get_pixel(trace_px, tx, ty, bci.width); + trace_t = get_trace_pixel(trace_px, tx, ty, bci.width); if (!is_pixel_colored(trace_t)) { if (check_if_pixel_is_paintable(px, trace_t, tx, ty, orig_color, bci)) { mark_pixel_colored(trace_t); @@ -530,25 +623,49 @@ inline static unsigned int paint_pixel(guchar *px, guchar *trace_px, unsigned ch } } -static void push_point_onto_queue(std::deque *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) { +/** + * \brief Push a point to be checked onto the bottom of the rendered pixel buffer check queue. + * \param fill_queue The fill queue to add the point to. + * \param max_queue_size The maximum size of the fill queue. + * \param trace_t The trace pixel buffer pixel. + * \param x The X coordinate. + * \param y The Y coordinate. + */ +static void push_point_onto_queue(std::deque *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) { if (!is_pixel_queued(trace_t)) { if ((fill_queue->size() < max_queue_size)) { - fill_queue->push_back(NR::Point(x, y)); + fill_queue->push_back(Geom::Point(x, y)); mark_pixel_queued(trace_t); } } } -static void shift_point_onto_queue(std::deque *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) { +/** + * \brief Shift a point to be checked onto the top of the rendered pixel buffer check queue. + * \param fill_queue The fill queue to add the point to. + * \param max_queue_size The maximum size of the fill queue. + * \param trace_t The trace pixel buffer pixel. + * \param x The X coordinate. + * \param y The Y coordinate. + */ +static void shift_point_onto_queue(std::deque *fill_queue, unsigned int max_queue_size, unsigned char *trace_t, unsigned int x, unsigned int y) { if (!is_pixel_queued(trace_t)) { if ((fill_queue->size() < max_queue_size)) { - fill_queue->push_front(NR::Point(x, y)); + fill_queue->push_front(Geom::Point(x, y)); mark_pixel_queued(trace_t); } } } -static ScanlineCheckResult perform_bitmap_scanline_check(std::deque *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci) { +/** + * \brief Scan a row in the rendered pixel buffer and add points to the fill queue as necessary. + * \param fill_queue The fill queue to add the point to. + * \param px The rendered pixel buffer. + * \param trace_px The trace pixel buffer. + * \param orig_color The original selected pixel to use as the fill target color. + * \param bci The bitmap_coords_info structure. + */ +static ScanlineCheckResult perform_bitmap_scanline_check(std::deque *fill_queue, guchar *px, guchar *trace_px, unsigned char *orig_color, bitmap_coords_info bci, unsigned int *min_x, unsigned int *max_x) { bool aborted = false; bool reached_screen_boundary = false; bool ok; @@ -556,21 +673,19 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * bool keep_tracing; bool initial_paint = true; - unsigned char *current_trace_t = get_pixel(trace_px, bci.x, bci.y, bci.width); + unsigned char *current_trace_t = get_trace_pixel(trace_px, bci.x, bci.y, bci.width); unsigned int paint_directions; bool currently_painting_top = false; bool currently_painting_bottom = false; - unsigned int pix_width = bci.width * 4; - unsigned int top_ty = bci.y - 1; unsigned int bottom_ty = bci.y + 1; bool can_paint_top = (top_ty > 0); bool can_paint_bottom = (bottom_ty < bci.height); - NR::Point t = fill_queue->front(); + Geom::Point t = fill_queue->front(); do { ok = false; @@ -580,19 +695,22 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * keep_tracing = (bci.x < bci.width); } + *min_x = MIN(*min_x, bci.x); + *max_x = MAX(*max_x, bci.x); + if (keep_tracing) { if (check_if_pixel_is_paintable(px, current_trace_t, bci.x, bci.y, orig_color, bci)) { paint_directions = paint_pixel(px, trace_px, orig_color, bci, current_trace_t); if (bci.radius == 0) { mark_pixel_checked(current_trace_t); - if ((t[NR::X] == bci.x) && (t[NR::Y] == bci.y)) { + if ((t[Geom::X] == bci.x) && (t[Geom::Y] == bci.y)) { fill_queue->pop_front(); t = fill_queue->front(); } } if (can_paint_top) { if (paint_directions & PAINT_DIRECTION_UP) { - unsigned char *trace_t = current_trace_t - pix_width; + unsigned char *trace_t = current_trace_t - bci.width; if (!is_pixel_queued(trace_t)) { bool ok_to_paint = check_if_pixel_is_paintable(px, trace_t, bci.x, top_ty, orig_color, bci); @@ -611,7 +729,7 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * if (can_paint_bottom) { if (paint_directions & PAINT_DIRECTION_DOWN) { - unsigned char *trace_t = current_trace_t + pix_width; + unsigned char *trace_t = current_trace_t + bci.width; if (!is_pixel_queued(trace_t)) { bool ok_to_paint = check_if_pixel_is_paintable(px, trace_t, bci.x, bottom_ty, orig_color, bci); @@ -630,12 +748,12 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * if (bci.is_left) { if (paint_directions & PAINT_DIRECTION_LEFT) { - bci.x -= 1; current_trace_t -= 4; + bci.x--; current_trace_t--; ok = true; } } else { if (paint_directions & PAINT_DIRECTION_RIGHT) { - bci.x += 1; current_trace_t += 4; + bci.x++; current_trace_t++; ok = true; } } @@ -643,7 +761,7 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * initial_paint = false; } } else { - if (bci.bbox.min()[NR::X] > bci.screen.min()[NR::X]) { + if (bci.bbox.min()[Geom::X] > bci.screen.min()[Geom::X]) { aborted = true; break; } else { reached_screen_boundary = true; @@ -656,14 +774,28 @@ static ScanlineCheckResult perform_bitmap_scanline_check(std::deque * return SCANLINE_CHECK_OK; } -static bool sort_fill_queue_vertical(NR::Point a, NR::Point b) { - return a[NR::Y] > b[NR::Y]; +/** + * \brief Sort the rendered pixel buffer check queue vertically. + */ +static bool sort_fill_queue_vertical(Geom::Point a, Geom::Point b) { + return a[Geom::Y] > b[Geom::Y]; } -static bool sort_fill_queue_horizontal(NR::Point a, NR::Point b) { - return a[NR::X] > b[NR::X]; +/** + * \brief Sort the rendered pixel buffer check queue horizontally. + */ +static bool sort_fill_queue_horizontal(Geom::Point a, Geom::Point b) { + return a[Geom::X] > b[Geom::X]; } +/** + * \brief Perform a flood fill operation. + * \param event_context The event context for this tool. + * \param event The details of this event. + * \param union_with_selection If true, union the new fill with the current selection. + * \param is_point_fill If false, use the Rubberband "touch selection" to get the initial points for the fill. + * \param is_touch_fill If true, use only the initial contact point in the Rubberband "touch selection" as the fill target color. + */ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *event, bool union_with_selection, bool is_point_fill, bool is_touch_fill) { SPDesktop *desktop = event_context->desktop; SPDocument *document = sp_desktop_document(desktop); @@ -675,7 +807,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even sp_document_ensure_up_to_date (document); SPItem *document_root = SP_ITEM(SP_DOCUMENT_ROOT(document)); - NR::Maybe bbox = document_root->getBounds(NR::identity()); + Geom::OptRect bbox = document_root->getBounds(Geom::identity()); if (!bbox) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Area is not bounded, cannot fill.")); @@ -683,34 +815,37 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even } double zoom_scale = desktop->current_zoom(); + + // Render 160% of the physical display to the render pixel buffer, so that available + // fill areas off the screen can be included in the fill. double padding = 1.6; - NR::Rect screen = desktop->get_display_area(); + Geom::Rect screen = desktop->get_display_area(); - unsigned int width = (int)ceil(screen.extent(NR::X) * zoom_scale * padding); - unsigned int height = (int)ceil(screen.extent(NR::Y) * zoom_scale * padding); + unsigned int width = (int)ceil(screen.width() * zoom_scale * padding); + unsigned int height = (int)ceil(screen.height() * zoom_scale * padding); - NR::Point origin(screen.min()[NR::X], - sp_document_height(document) - screen.extent(NR::Y) - screen.min()[NR::Y]); + Geom::Point origin(screen.min()[Geom::X], + sp_document_height(document) - screen.height() - screen.min()[Geom::Y]); - origin[NR::X] = origin[NR::X] + (screen.extent(NR::X) * ((1 - padding) / 2)); - origin[NR::Y] = origin[NR::Y] + (screen.extent(NR::Y) * ((1 - padding) / 2)); + origin[Geom::X] = origin[Geom::X] + (screen.width() * ((1 - padding) / 2)); + origin[Geom::Y] = origin[Geom::Y] + (screen.height() * ((1 - padding) / 2)); - NR::scale scale(zoom_scale, zoom_scale); - NR::Matrix affine = scale * NR::translate(-origin * scale); + Geom::Scale scale(zoom_scale, zoom_scale); + Geom::Matrix affine = scale * Geom::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(NR_ARENA_ITEM(root), affine); NRGC gc(NULL); - nr_matrix_set_identity(&gc.transform); + gc.transform.setIdentity(); NRRectL final_bbox; final_bbox.x0 = 0; - final_bbox.y0 = 0;//row; + final_bbox.y0 = 0; //row; final_bbox.x1 = width; - final_bbox.y1 = height;//row + num_rows; + 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); @@ -745,22 +880,22 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even // Hide items sp_item_invoke_hide(SP_ITEM(sp_document_root(document)), dkey); - nr_arena_item_unref(root); nr_object_unref((NRObject *) arena); - guchar *trace_px = g_new(guchar, 4 * width * height); - memset(trace_px, 0x00, 4 * width * height); + guchar *trace_px = g_new(guchar, width * height); + memset(trace_px, 0x00, width * height); - std::deque fill_queue; - std::queue color_queue; + std::deque fill_queue; + std::queue color_queue; - std::vector fill_points; + std::vector fill_points; bool aborted = false; int y_limit = height - 1; - PaintBucketChannels method = (PaintBucketChannels)prefs_get_int_attribute("tools.paintbucket", "channels", 0); - int threshold = prefs_get_int_attribute_limited("tools.paintbucket", "threshold", 1, 0, 100); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + PaintBucketChannels method = (PaintBucketChannels) prefs->getInt("/tools/paintbucket/channels", 0); + int threshold = prefs->getIntLimited("/tools/paintbucket/threshold", 1, 0, 100); switch(method) { case FLOOD_CHANNELS_ALPHA: @@ -786,29 +921,29 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even bci.bbox = *bbox; bci.screen = screen; bci.dtc = dtc; - bci.radius = prefs_get_int_attribute_limited("tools.paintbucket", "autogap", 0, 0, 3); + bci.radius = prefs->getIntLimited("/tools/paintbucket/autogap", 0, 0, 3); bci.max_queue_size = (width * height) / 4; bci.current_step = 0; if (is_point_fill) { - fill_points.push_back(NR::Point(event->button.x, event->button.y)); + fill_points.push_back(Geom::Point(event->button.x, event->button.y)); } else { - Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(); + Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(desktop); fill_points = r->getPoints(); } for (unsigned int i = 0; i < fill_points.size(); i++) { - NR::Point pw = NR::Point(fill_points[i][NR::X] / zoom_scale, sp_document_height(document) + (fill_points[i][NR::Y] / zoom_scale)) * affine; + Geom::Point pw = Geom::Point(fill_points[i][Geom::X] / zoom_scale, sp_document_height(document) + (fill_points[i][Geom::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])); + pw[Geom::X] = (int)MIN(width - 1, MAX(0, pw[Geom::X])); + pw[Geom::Y] = (int)MIN(height - 1, MAX(0, pw[Geom::Y])); if (is_touch_fill) { if (i == 0) { color_queue.push(pw); } else { - unsigned char *trace_t = get_pixel(trace_px, (int)pw[NR::X], (int)pw[NR::Y], width); - push_point_onto_queue(&fill_queue, bci.max_queue_size, trace_t, (int)pw[NR::X], (int)pw[NR::Y]); + unsigned char *trace_t = get_trace_pixel(trace_px, (int)pw[Geom::X], (int)pw[Geom::Y], width); + push_point_onto_queue(&fill_queue, bci.max_queue_size, trace_t, (int)pw[Geom::X], (int)pw[Geom::Y]); } } else { color_queue.push(pw); @@ -821,12 +956,17 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even unsigned long sort_size_threshold = 5; + unsigned int min_y = height; + unsigned int max_y = 0; + unsigned int min_x = width; + unsigned int max_x = 0; + while (!color_queue.empty() && !aborted) { - NR::Point color_point = color_queue.front(); + Geom::Point color_point = color_queue.front(); color_queue.pop(); - int cx = (int)color_point[NR::X]; - int cy = (int)color_point[NR::Y]; + int cx = (int)color_point[Geom::X]; + int cy = (int)color_point[Geom::Y]; unsigned char *orig_px = get_pixel(px, cx, cy, width); unsigned char orig_color[4]; @@ -838,17 +978,17 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even bci.merged_orig_pixel = merged_orig; - unsigned char *trace_t = get_pixel(trace_px, cx, cy, width); + unsigned char *trace_t = get_trace_pixel(trace_px, cx, cy, width); if (!is_pixel_checked(trace_t) && !is_pixel_colored(trace_t)) { if (check_if_pixel_is_paintable(px, trace_px, cx, cy, orig_color, bci)) { shift_point_onto_queue(&fill_queue, bci.max_queue_size, trace_t, cx, cy); if (!first_run) { for (unsigned int y = 0; y < height; y++) { - trace_t = get_pixel(trace_px, 0, y, width); + trace_t = get_trace_pixel(trace_px, 0, y, width); for (unsigned int x = 0; x < width; x++) { clear_pixel_paintability(trace_t); - trace_t += 4; + trace_t++; } } } @@ -859,23 +999,31 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even unsigned long old_fill_queue_size = fill_queue.size(); while (!fill_queue.empty() && !aborted) { - NR::Point cp = fill_queue.front(); + Geom::Point cp = fill_queue.front(); if (bci.radius == 0) { unsigned long new_fill_queue_size = fill_queue.size(); + /* + * To reduce the number of points in the fill queue, periodically + * resort all of the points in the queue so that scanline checks + * can complete more quickly. A point cannot be checked twice + * in a normal scanline checks, so forcing scanline checks to start + * from one corner of the rendered area as often as possible + * will reduce the number of points that need to be checked and queued. + */ if (new_fill_queue_size > sort_size_threshold) { if (new_fill_queue_size > old_fill_queue_size) { std::sort(fill_queue.begin(), fill_queue.end(), sort_fill_queue_vertical); - std::deque::iterator start_sort = fill_queue.begin(); - std::deque::iterator end_sort = fill_queue.begin(); - unsigned int sort_y = (unsigned int)cp[NR::Y]; + std::deque::iterator start_sort = fill_queue.begin(); + std::deque::iterator end_sort = fill_queue.begin(); + unsigned int sort_y = (unsigned int)cp[Geom::Y]; unsigned int current_y = sort_y; - for (std::deque::iterator i = fill_queue.begin(); i != fill_queue.end(); i++) { - NR::Point current = *i; - current_y = (unsigned int)current[NR::Y]; + for (std::deque::iterator i = fill_queue.begin(); i != fill_queue.end(); i++) { + Geom::Point current = *i; + current_y = (unsigned int)current[Geom::Y]; if (current_y != sort_y) { if (start_sort != end_sort) { std::sort(start_sort, end_sort, sort_fill_queue_horizontal); @@ -898,15 +1046,18 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even fill_queue.pop_front(); - int x = (int)cp[NR::X]; - int y = (int)cp[NR::Y]; + int x = (int)cp[Geom::X]; + int y = (int)cp[Geom::Y]; + + min_y = MIN((unsigned int)y, min_y); + max_y = MAX((unsigned int)y, max_y); - unsigned char *trace_t = get_pixel(trace_px, x, y, width); + unsigned char *trace_t = get_trace_pixel(trace_px, x, y, width); if (!is_pixel_checked(trace_t)) { mark_pixel_checked(trace_t); if (y == 0) { - if (bbox->min()[NR::Y] > screen.min()[NR::Y]) { + if (bbox->min()[Geom::Y] > screen.min()[Geom::Y]) { aborted = true; break; } else { reached_screen_boundary = true; @@ -914,7 +1065,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even } if (y == y_limit) { - if (bbox->max()[NR::Y] < screen.max()[NR::Y]) { + if (bbox->max()[Geom::Y] < screen.max()[Geom::Y]) { aborted = true; break; } else { reached_screen_boundary = true; @@ -925,7 +1076,7 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even bci.x = x; bci.y = y; - ScanlineCheckResult result = perform_bitmap_scanline_check(&fill_queue, px, trace_px, orig_color, bci); + ScanlineCheckResult result = perform_bitmap_scanline_check(&fill_queue, px, trace_px, orig_color, bci, &min_x, &max_x); switch (result) { case SCANLINE_CHECK_ABORTED: @@ -939,13 +1090,13 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even } if (bci.x < width) { - trace_t += 4; + trace_t++; if (!is_pixel_checked(trace_t) && !is_pixel_queued(trace_t)) { mark_pixel_checked(trace_t); bci.is_left = false; bci.x = x + 1; - result = perform_bitmap_scanline_check(&fill_queue, px, trace_px, orig_color, bci); + result = perform_bitmap_scanline_check(&fill_queue, px, trace_px, orig_color, bci, &min_x, &max_x); switch (result) { case SCANLINE_CHECK_ABORTED: @@ -980,17 +1131,19 @@ static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *even if (reached_screen_boundary) { desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Only the visible part of the bounded area was filled. If you want to fill all of the area, undo, zoom out, and fill again.")); } + + unsigned int trace_padding = bci.radius + 1; + if (min_y > trace_padding) { min_y -= trace_padding; } + if (max_y < (y_limit - trace_padding)) { max_y += trace_padding; } + if (min_x > trace_padding) { min_x -= trace_padding; } + if (max_x < (width - 1 - trace_padding)) { max_x += trace_padding; } + + Geom::Point min_start = Geom::Point(min_x, min_y); - 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(); + affine = scale * Geom::Translate(-origin * scale - min_start); + Geom::Matrix inverted_affine = Geom::Matrix(affine).inverse(); - do_trace(pixbuf, desktop, inverted_affine, union_with_selection); + do_trace(bci, trace_px, desktop, inverted_affine, min_x, max_x, min_y, max_y, union_with_selection); g_free(trace_px); @@ -1006,14 +1159,14 @@ static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem switch (event->type) { case GDK_BUTTON_PRESS: if ((event->button.state & GDK_CONTROL_MASK) && event->button.button == 1 && !event_context->space_panning) { - NR::Point const button_w(event->button.x, - event->button.y); + Geom::Point const button_w(event->button.x, + event->button.y); SPItem *item = sp_event_context_find_item (desktop, button_w, TRUE, TRUE); Inkscape::XML::Node *pathRepr = SP_OBJECT_REPR(item); /* Set style */ - sp_desktop_apply_style_tool (desktop, pathRepr, "tools.paintbucket", false); + sp_desktop_apply_style_tool (desktop, pathRepr, "/tools/paintbucket", false); sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_PAINTBUCKET, _("Set style on object")); ret = TRUE; } @@ -1040,20 +1193,20 @@ static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEven case GDK_BUTTON_PRESS: if (event->button.button == 1 && !event_context->space_panning) { if (!(event->button.state & GDK_CONTROL_MASK)) { - NR::Point const button_w(event->button.x, - event->button.y); + Geom::Point const button_w(event->button.x, + event->button.y); if (Inkscape::have_viable_layer(desktop, event_context->defaultMessageContext())) { // save drag origin - event_context->xp = (gint) button_w[NR::X]; - event_context->yp = (gint) button_w[NR::Y]; + event_context->xp = (gint) button_w[Geom::X]; + event_context->yp = (gint) button_w[Geom::Y]; event_context->within_tolerance = true; dragging = true; - NR::Point const p(desktop->w2d(button_w)); - Inkscape::Rubberband::get()->setMode(RUBBERBAND_MODE_TOUCHPATH); - Inkscape::Rubberband::get()->start(desktop, p); + Geom::Point const p(desktop->w2d(button_w)); + Inkscape::Rubberband::get(desktop)->setMode(RUBBERBAND_MODE_TOUCHPATH); + Inkscape::Rubberband::get(desktop)->start(desktop, p); } } } @@ -1069,10 +1222,10 @@ static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEven event_context->within_tolerance = false; - NR::Point const motion_pt(event->motion.x, event->motion.y); - NR::Point const p(desktop->w2d(motion_pt)); - if (Inkscape::Rubberband::get()->is_started()) { - Inkscape::Rubberband::get()->move(p); + Geom::Point const motion_pt(event->motion.x, event->motion.y); + Geom::Point const p(desktop->w2d(motion_pt)); + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(p); event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Draw over areas to add to fill, hold Alt for touch fill")); gobble_motion_events(GDK_BUTTON1_MASK); } @@ -1081,7 +1234,7 @@ static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEven case GDK_BUTTON_RELEASE: if (event->button.button == 1 && !event_context->space_panning) { - Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(); + Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(desktop); if (r->is_started()) { // set "busy" cursor desktop->setWaitingCursor(); @@ -1162,7 +1315,8 @@ static void sp_flood_finish(SPFloodContext *rc) void flood_channels_set_channels( gint channels ) { - prefs_set_int_attribute("tools.paintbucket", "channels", channels); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/tools/paintbucket/channels", channels); } /*