Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
flood-tool.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
7/* Author:
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * bulia byak <buliabyak@users.sf.net>
10 * John Bintz <jcoswell@coswellproductions.org>
11 * Jon A. Cruz <jon@joncruz.org>
12 * Abhishek Sharma
13 *
14 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
15 * Copyright (C) 2000-2005 authors
16 * Copyright (C) 2000-2001 Ximian, Inc.
17 *
18 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
19 */
20
21#include "flood-tool.h"
22
23#include <cmath>
24#include <cstdint>
25#include <queue>
26
27#include <gdk/gdkkeysyms.h>
28#include <glibmm/i18n.h>
29
30#include <2geom/pathvector.h>
31
32#include "async/progress.h"
33#include "context-fns.h"
34#include "desktop-style.h"
35#include "desktop.h"
36#include "document-undo.h"
37#include "document.h"
38#include "layer-manager.h"
39#include "message-context.h"
40#include "message-stack.h"
41#include "rubberband.h"
42#include "selection.h"
43#include "page-manager.h"
44
45#include "display/cairo-utils.h"
47#include "display/drawing.h"
48
49#include "livarot/Path.h"
50#include "livarot/Shape.h"
51
52#include "object/sp-namedview.h"
53#include "object/sp-path.h"
54#include "object/sp-root.h"
55
56#include "svg/svg.h"
57
58#include "trace/imagemap.h"
60
61#include "ui/icon-names.h"
62#include "ui/shape-editor.h"
63#include "ui/widget/canvas.h" // Canvas area
65
67
71
72using namespace Inkscape::Colors;
73
74namespace Inkscape::UI::Tools {
75
76// Must match PaintBucketChannels enum
77std::vector<char const *> const FloodTool::channel_list = {
78 N_("Visible Colors"),
79 N_("Red"),
80 N_("Green"),
81 N_("Blue"),
82 N_("Hue"),
83 N_("Saturation"),
84 N_("Lightness"),
85 N_("Alpha")
86};
87
88std::vector<char const *> const FloodTool::gap_list = {
89 NC_("Flood autogap", "None"),
90 NC_("Flood autogap", "Small"),
91 NC_("Flood autogap", "Medium"),
92 NC_("Flood autogap", "Large")
93};
94
96 : ToolBase(desktop, "/tools/paintbucket", "flood.svg")
97{
98 // TODO: Why does the flood tool use a hardcoded tolerance instead of a pref?
99 tolerance = 4;
100
102
103 if (auto item = desktop->getSelection()->singleItem()) {
105 }
106
108 sigc::mem_fun(*this, &FloodTool::selection_changed)
109 );
110
111 auto prefs = Preferences::get();
112 if (prefs->getBool("/tools/paintbucket/selcue")) {
114 }
115}
116
118{
119 sel_changed_connection.disconnect();
120
121 delete shape_editor;
122 shape_editor = nullptr;
123
124 // Fixme: This is necessary because we do not grab.
125 if (item) {
126 finishItem();
127 }
128}
129
135{
137 shape_editor->set_item(selection->singleItem());
138}
139
140inline static uint32_t compose_onto(uint32_t px, uint32_t bg)
141{
142 uint32_t ap = 0, rp = 0, gp = 0, bp = 0;
143 uint32_t rb = 0, gb = 0, bb = 0;
144 ExtractARGB32(px, ap, rp, gp, bp);
145 ExtractRGB32(bg, rb, gb, bb);
146
147 uint32_t ro = (255 - ap) * rb + 255 * rp; ro = (ro + 127) / 255;
148 uint32_t go = (255 - ap) * gb + 255 * gp; go = (go + 127) / 255;
149 uint32_t bo = (255 - ap) * bb + 255 * bp; bo = (bo + 127) / 255;
150
151 return AssembleARGB32(255, ro, go, bo);
152}
153
161inline uint32_t get_pixel(unsigned char *px, int x, int y, int stride)
162{
163 return *reinterpret_cast<uint32_t*>(px + y * stride + x * 4);
164}
165
166inline unsigned char *get_trace_pixel(unsigned char *trace_px, int x, int y, int width)
167{
168 return trace_px + (x + y * width);
169}
170
180static bool compare_uint32(uint32_t a, uint32_t b, uint32_t d)
181{
182 return std::abs(static_cast<int64_t>(a) - static_cast<int64_t>(b)) <= static_cast<int64_t>(d);
183}
184
194static bool compare_pixels(uint32_t check, uint32_t orig, uint32_t merged_orig_pixel, uint32_t dtc, int threshold, PaintBucketChannels method)
195{
196 uint32_t ac = 0, rc = 0, gc = 0, bc = 0;
197 ExtractARGB32(check, ac, rc, gc, bc);
198
199 uint32_t ao = 0, ro = 0, go = 0, bo = 0;
200 ExtractARGB32(orig, ao, ro, go, bo);
201
202 uint32_t ad = 0, rd = 0, gd = 0, bd = 0;
203 ExtractARGB32(dtc, ad, rd, gd, bd);
204
205 uint32_t amop = 0, rmop = 0, gmop = 0, bmop = 0;
206 ExtractARGB32(merged_orig_pixel, amop, rmop, gmop, bmop);
207
208 auto hsl_orig = Color(Space::Type::HSL, {0, 0, 0});
209 auto hsl_check = hsl_orig;
210
211 if ((method == FLOOD_CHANNELS_H) ||
212 (method == FLOOD_CHANNELS_S) ||
213 (method == FLOOD_CHANNELS_L)) {
214 double dao = ao;
215 double dac = ac;
216 hsl_orig.set(Color(Space::Type::RGB, {ro / dao, go / dao, bo / dao}), true);
217 hsl_check.set(Color(Space::Type::RGB, {rc / dac, gc / dac, bc / dac}), true);
218 }
219
220 switch (method) {
222 return compare_uint32(ac, ao, threshold);
223 case FLOOD_CHANNELS_R:
224 return compare_uint32(ac ? unpremul_alpha(rc, ac) : 0,
225 ao ? unpremul_alpha(ro, ao) : 0,
226 threshold);
227 case FLOOD_CHANNELS_G:
228 return compare_uint32(ac ? unpremul_alpha(gc, ac) : 0,
229 ao ? unpremul_alpha(go, ao) : 0,
230 threshold);
231 case FLOOD_CHANNELS_B:
232 return compare_uint32(ac ? unpremul_alpha(bc, ac) : 0,
233 ao ? unpremul_alpha(bo, ao) : 0,
234 threshold);
236 {
237 uint32_t amc, rmc, bmc, gmc;
238 //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
239 //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
240 amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
241 rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
242 gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
243 bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
244
245 int diff = 0; // The total difference between each of the 3 color components
246 diff += std::abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(rmop, amop) : 0));
247 diff += std::abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(gmop, amop) : 0));
248 diff += std::abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(bmop, amop) : 0));
249 return ((diff / 3) <= ((threshold * 3) / 4));
250 }
251 case FLOOD_CHANNELS_H:
252 return ((int)(fabs(hsl_check[0] - hsl_orig[0]) * 100.0) <= threshold);
253 case FLOOD_CHANNELS_S:
254 return ((int)(fabs(hsl_check[1] - hsl_orig[1]) * 100.0) <= threshold);
255 case FLOOD_CHANNELS_L:
256 return ((int)(fabs(hsl_check[2] - hsl_orig[2]) * 100.0) <= threshold);
257 }
258
259 return false;
260}
261
262static constexpr unsigned char PIXEL_CHECKED = 1;
263static constexpr unsigned char PIXEL_QUEUED = 2;
264static constexpr unsigned char PIXEL_PAINTABLE = 4;
265static constexpr unsigned char PIXEL_NOT_PAINTABLE = 8;
266static constexpr unsigned char PIXEL_COLORED = 16;
267
268static inline bool is_pixel_checked(unsigned char *t) { return (*t & PIXEL_CHECKED) == PIXEL_CHECKED; }
269static inline bool is_pixel_queued(unsigned char *t) { return (*t & PIXEL_QUEUED) == PIXEL_QUEUED; }
270static inline bool is_pixel_paintability_checked(unsigned char *t) {
271 return !((*t & PIXEL_PAINTABLE) == 0) && ((*t & PIXEL_NOT_PAINTABLE) == 0);
272}
273static inline bool is_pixel_paintable(unsigned char *t) { return (*t & PIXEL_PAINTABLE) == PIXEL_PAINTABLE; }
274static inline bool is_pixel_colored(unsigned char *t) { return (*t & PIXEL_COLORED) == PIXEL_COLORED; }
275
276static inline void mark_pixel_checked(unsigned char *t) { *t |= PIXEL_CHECKED; }
277static inline void mark_pixel_queued(unsigned char *t) { *t |= PIXEL_QUEUED; }
278static inline void mark_pixel_paintable(unsigned char *t) { *t |= PIXEL_PAINTABLE; *t ^= PIXEL_NOT_PAINTABLE; }
279static inline void mark_pixel_not_paintable(unsigned char *t) { *t |= PIXEL_NOT_PAINTABLE; *t ^= PIXEL_PAINTABLE; }
280static inline void mark_pixel_colored(unsigned char *t) { *t |= PIXEL_COLORED; }
281
282static inline void clear_pixel_paintability(unsigned char *t) { *t ^= PIXEL_PAINTABLE; *t ^= PIXEL_NOT_PAINTABLE; }
283
284struct BitmapCoordsInfo
285{
286 bool is_left;
287 unsigned int x;
288 unsigned int y;
289 int y_limit;
290 unsigned int width;
291 unsigned int height;
292 unsigned int stride;
293 unsigned int threshold;
294 unsigned int radius;
295 PaintBucketChannels method;
296 uint32_t dtc;
297 uint32_t merged_orig_pixel;
298 Geom::Rect bbox;
299 Geom::Rect screen;
300 unsigned int max_queue_size;
301 unsigned int current_step;
302};
303
313inline static bool check_if_pixel_is_paintable(unsigned char *px, unsigned char *trace_t, int x, int y, uint32_t orig_color, BitmapCoordsInfo const &bci)
314{
315 if (is_pixel_paintability_checked(trace_t)) {
316 return is_pixel_paintable(trace_t);
317 } else {
318 uint32_t pixel = get_pixel(px, x, y, bci.stride);
319 if (compare_pixels(pixel, orig_color, bci.merged_orig_pixel, bci.dtc, bci.threshold, bci.method)) {
320 mark_pixel_paintable(trace_t);
321 return true;
322 } else {
324 return false;
325 }
326 }
327}
328
336static void do_trace(BitmapCoordsInfo const &bci, unsigned char *trace_px, SPDesktop *desktop, Geom::Affine const &transform, unsigned min_x, unsigned max_x, unsigned min_y, unsigned max_y, bool union_with_selection)
337{
338 SPDocument *document = desktop->getDocument();
339
340 unsigned char *trace_t;
341
342 auto gray_map = Trace::GrayMap(max_x - min_x + 1, max_y - min_y + 1);
343 unsigned gray_map_y = 0;
344 for (unsigned y = min_y; y <= max_y; y++) {
345 auto gray_map_t = gray_map.row(gray_map_y);
346
347 trace_t = get_trace_pixel(trace_px, min_x, y, bci.width);
348 for (unsigned x = min_x; x <= max_x; x++) {
350 gray_map_t++;
351 trace_t++;
352 }
353 gray_map_y++;
354 }
355
357 auto progress = Async::ProgressAlways<double>();
358 auto results = pte.traceGrayMap(gray_map, progress);
359
360 // XML Tree being used here directly while it shouldn't be...."
362
364 double offset = prefs->getDouble("/tools/paintbucket/offset", 0.0);
365
366 for (auto result : results) {
367
368 Inkscape::XML::Node *pathRepr = xml_doc->createElement("svg:path");
369 /* Set style */
370 sp_desktop_apply_style_tool (desktop, pathRepr, "/tools/paintbucket", false);
371
372 Path path;
373 path.LoadPathVector(result.path);
374
375 if (offset != 0) {
376 Shape path_shape;
377 path.ConvertWithBackData(0.03);
378 path.Fill(&path_shape, 0);
379
380 Shape expanded_path_shape;
381 expanded_path_shape.ConvertToShape(&path_shape, fill_nonZero);
382 path_shape.MakeOffset(&expanded_path_shape, offset * desktop->current_zoom(), join_round, 4);
383 expanded_path_shape.ConvertToShape(&path_shape, fill_positive);
384
385 Path expanded_path;
386 expanded_path_shape.ConvertToForme(&expanded_path);
387 expanded_path.ConvertEvenLines(1.0);
388 expanded_path.Simplify(1.0);
389
390 auto str = expanded_path.svg_dump_path();
391 if (!str.empty()) {
392 pathRepr->setAttribute("d", str.c_str());
393 } else {
394 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Too much inset</b>, the result is empty."));
395 Inkscape::GC::release(pathRepr);
396 return;
397 }
398 } else {
399 pathRepr->setAttribute("d", path.svg_dump_path().c_str());
400 }
401
402 auto layer = desktop->layerManager().currentLayer();
403 layer->addChild(pathRepr, nullptr);
404
405 SPObject *reprobj = document->getObjectByRepr(pathRepr);
406 if (reprobj) {
407 cast<SPItem>(reprobj)->doWriteTransform(transform);
408
409 // premultiply the item transform by the accumulated parent transform in the paste layer
410 Geom::Affine local (layer->i2doc_affine());
411 if (!local.isIdentity()) {
412 gchar const *t_str = pathRepr->attribute("transform");
413 Geom::Affine item_t (Geom::identity());
414 if (t_str)
415 sp_svg_transform_read(t_str, &item_t);
416 item_t *= local.inverse();
417 // (we're dealing with unattached repr, so we write to its attr instead of using sp_item_set_transform)
418 pathRepr->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(item_t));
419 }
420
422
423 pathRepr->setPosition(-1);
424
425 if (union_with_selection) {
427 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.",
428 cast<SPPath>(reprobj)->nodesInPath()), cast<SPPath>(reprobj)->nodesInPath() );
429 selection->add(reprobj);
430 selection->pathUnion(true);
431 } else {
433 ngettext("Area filled, path with <b>%d</b> node created.","Area filled, path with <b>%d</b> nodes created.",
434 cast<SPPath>(reprobj)->nodesInPath()), cast<SPPath>(reprobj)->nodesInPath() );
435 selection->set(reprobj);
436 }
437
438 }
439
440 Inkscape::GC::release(pathRepr);
441
442 }
443}
444
449{
450 OK,
451 ABORTED,
453};
454
461inline static bool coords_in_range(unsigned x, unsigned y, BitmapCoordsInfo const &bci)
462{
463 return x < bci.width && y < bci.height;
464}
465
466static constexpr int PAINT_DIRECTION_LEFT = 1;
467static constexpr int PAINT_DIRECTION_RIGHT = 2;
468static constexpr int PAINT_DIRECTION_UP = 4;
469static constexpr int PAINT_DIRECTION_DOWN = 8;
470static constexpr int PAINT_DIRECTION_ALL = 15;
471
480inline static unsigned paint_pixel(unsigned char *px, unsigned char *trace_px, uint32_t orig_color, BitmapCoordsInfo const &bci, unsigned char *original_point_trace_t)
481{
482 if (bci.radius == 0) {
483 mark_pixel_colored(original_point_trace_t);
484 return PAINT_DIRECTION_ALL;
485 } else {
486 unsigned char *trace_t;
487
488 bool can_paint_up = true;
489 bool can_paint_down = true;
490 bool can_paint_left = true;
491 bool can_paint_right = true;
492
493 for (unsigned int ty = bci.y - bci.radius; ty <= bci.y + bci.radius; ty++) {
494 for (unsigned int tx = bci.x - bci.radius; tx <= bci.x + bci.radius; tx++) {
495 if (coords_in_range(tx, ty, bci)) {
496 trace_t = get_trace_pixel(trace_px, tx, ty, bci.width);
497 if (!is_pixel_colored(trace_t)) {
498 if (check_if_pixel_is_paintable(px, trace_t, tx, ty, orig_color, bci)) {
499 mark_pixel_colored(trace_t);
500 } else {
501 if (tx < bci.x) { can_paint_left = false; }
502 if (tx > bci.x) { can_paint_right = false; }
503 if (ty < bci.y) { can_paint_up = false; }
504 if (ty > bci.y) { can_paint_down = false; }
505 }
506 }
507 }
508 }
509 }
510
511 unsigned int paint_directions = 0;
512 if (can_paint_left) { paint_directions += PAINT_DIRECTION_LEFT; }
513 if (can_paint_right) { paint_directions += PAINT_DIRECTION_RIGHT; }
514 if (can_paint_up) { paint_directions += PAINT_DIRECTION_UP; }
515 if (can_paint_down) { paint_directions += PAINT_DIRECTION_DOWN; }
516
517 return paint_directions;
518 }
519}
520
529static void push_point_onto_queue(std::deque<Geom::Point> &fill_queue, unsigned max_queue_size, unsigned char *trace_t, unsigned x, unsigned y)
530{
531 if (!is_pixel_queued(trace_t) && fill_queue.size() < max_queue_size) {
532 fill_queue.emplace_back(x, y);
533 mark_pixel_queued(trace_t);
534 }
535}
536
545static void shift_point_onto_queue(std::deque<Geom::Point> &fill_queue, unsigned max_queue_size, unsigned char *trace_t, unsigned x, unsigned y)
546{
547 if (!is_pixel_queued(trace_t) && fill_queue.size() < max_queue_size) {
548 fill_queue.emplace_front(x, y);
549 mark_pixel_queued(trace_t);
550 }
551}
552
561static ScanlineCheckResult perform_bitmap_scanline_check(std::deque<Geom::Point> &fill_queue, unsigned char *px, unsigned char *trace_px, uint32_t orig_color, BitmapCoordsInfo bci, unsigned *min_x, unsigned *max_x)
562{
563 bool aborted = false;
564 bool reached_screen_boundary = false;
565 bool ok;
566
567 bool keep_tracing;
568 bool initial_paint = true;
569
570 unsigned char *current_trace_t = get_trace_pixel(trace_px, bci.x, bci.y, bci.width);
571 unsigned int paint_directions;
572
573 bool currently_painting_top = false;
574 bool currently_painting_bottom = false;
575
576 unsigned int top_ty = (bci.y > 0) ? bci.y - 1 : 0;
577 unsigned int bottom_ty = bci.y + 1;
578
579 bool can_paint_top = (top_ty > 0);
580 bool can_paint_bottom = (bottom_ty < bci.height);
581
582 Geom::Point front_of_queue = fill_queue.empty() ? Geom::Point() : fill_queue.front();
583
584 do {
585 ok = false;
586 if (bci.is_left) {
587 keep_tracing = bci.x != 0;
588 } else {
589 keep_tracing = bci.x < bci.width;
590 }
591
592 *min_x = std::min(*min_x, bci.x);
593 *max_x = std::max(*max_x, bci.x);
594
595 if (keep_tracing) {
596 if (check_if_pixel_is_paintable(px, current_trace_t, bci.x, bci.y, orig_color, bci)) {
597 paint_directions = paint_pixel(px, trace_px, orig_color, bci, current_trace_t);
598 if (bci.radius == 0) {
599 mark_pixel_checked(current_trace_t);
600 if (!fill_queue.empty() && front_of_queue == Geom::IntPoint(bci.x, bci.y)) {
601 fill_queue.pop_front();
602 front_of_queue = fill_queue.empty() ? Geom::Point() : fill_queue.front();
603 }
604 }
605
606 if (can_paint_top) {
607 if (paint_directions & PAINT_DIRECTION_UP) {
608 unsigned char *trace_t = current_trace_t - bci.width;
609 if (!is_pixel_queued(trace_t)) {
610 bool ok_to_paint = check_if_pixel_is_paintable(px, trace_t, bci.x, top_ty, orig_color, bci);
611
612 if (initial_paint) { currently_painting_top = !ok_to_paint; }
613
614 if (ok_to_paint && (!currently_painting_top)) {
615 currently_painting_top = true;
616 push_point_onto_queue(fill_queue, bci.max_queue_size, trace_t, bci.x, top_ty);
617 }
618 if ((!ok_to_paint) && currently_painting_top) {
619 currently_painting_top = false;
620 }
621 }
622 }
623 }
624
625 if (can_paint_bottom) {
626 if (paint_directions & PAINT_DIRECTION_DOWN) {
627 unsigned char *trace_t = current_trace_t + bci.width;
628 if (!is_pixel_queued(trace_t)) {
629 bool ok_to_paint = check_if_pixel_is_paintable(px, trace_t, bci.x, bottom_ty, orig_color, bci);
630
631 if (initial_paint) { currently_painting_bottom = !ok_to_paint; }
632
633 if (ok_to_paint && (!currently_painting_bottom)) {
634 currently_painting_bottom = true;
635 push_point_onto_queue(fill_queue, bci.max_queue_size, trace_t, bci.x, bottom_ty);
636 }
637 if ((!ok_to_paint) && currently_painting_bottom) {
638 currently_painting_bottom = false;
639 }
640 }
641 }
642 }
643
644 if (bci.is_left) {
645 if (paint_directions & PAINT_DIRECTION_LEFT) {
646 bci.x--; current_trace_t--;
647 ok = true;
648 }
649 } else {
650 if (paint_directions & PAINT_DIRECTION_RIGHT) {
651 bci.x++; current_trace_t++;
652 ok = true;
653 }
654 }
655
656 initial_paint = false;
657 }
658 } else {
659 if (bci.bbox.min()[Geom::X] > bci.screen.min()[Geom::X]) {
660 aborted = true; break;
661 } else {
662 reached_screen_boundary = true;
663 }
664 }
665 } while (ok);
666
667 if (aborted) { return ScanlineCheckResult::ABORTED; }
668 if (reached_screen_boundary) { return ScanlineCheckResult::BOUNDARY; }
670}
671
675static bool sort_fill_queue_vertical(Geom::Point const &a, Geom::Point const &b)
676{
677 return a.y() > b.y();
678}
679
684{
685 return a.x() > b.x();
686}
687
696static void sp_flood_do_flood_fill(SPDesktop *desktop, Geom::Point const &cursor_pos,
697 bool union_with_selection, bool is_point_fill, bool is_touch_fill)
698{
699 auto const document = desktop->getDocument();
700 document->ensureUpToDate();
701
702 auto const bbox = document->getRoot()->visualBounds();
703 if (!bbox) {
704 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Area is not bounded</b>, cannot fill."));
705 return;
706 }
707
708 // Render 160% of the physical display to the render pixel buffer, so that available
709 // fill areas off the screen can be included in the fill.
710 constexpr double padding = 1.6;
711
712 // image space is world space with an offset
713 Geom::Rect const screen_world = desktop->getCanvas()->get_area_world();
714 Geom::Rect const screen = screen_world * desktop->w2d();
715 Geom::IntPoint const img_dims = (screen_world.dimensions() * padding).ceil();
716 Geom::Affine const world2img = Geom::Translate((img_dims - screen_world.dimensions()) / 2.0 - screen_world.min());
717 Geom::Affine const doc2img = desktop->doc2dt() * desktop->d2w() * world2img;
718
719 auto const width = img_dims.x();
720 auto const height = img_dims.y();
721
722 auto const stride = Cairo::ImageSurface::format_stride_for_width(Cairo::Surface::Format::ARGB32, width);
723 // TODO: C++20: *once* Apple+AppImage support it: Use std::make_unique_for_overwrite()
724 auto const px = std::make_unique<unsigned char[]>(stride * height);
725 uint32_t dtc;
726
727 // Draw image into data block px
728 { // this block limits the lifetime of Drawing and DrawingContext
729 // Create DrawingItems and set transform.
730 Drawing drawing;
731 unsigned dkey = SPItem::display_key_new(1);
732 auto root = document->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY);
733 root->setTransform(doc2img);
734 drawing.setRoot(root);
735
736 auto const final_bbox = Geom::IntRect::from_xywh(0, 0, width, height);
737 drawing.update(final_bbox);
738
739 auto surf = Cairo::ImageSurface::create(px.get(), Cairo::Surface::Format::ARGB32, width, height, stride);
740 auto dc = DrawingContext(surf->cobj(), Geom::Point());
741 // cairo_translate not necessary here - surface origin is at 0,0
742
743 // make color transparent for 'alpha' flood mode to work
744 auto bgcolor = document->getPageManager().getBackgroundColor();
745 bgcolor.setOpacity(0.0);
746
747 dc.setSource(bgcolor.toARGB());
748 dc.setOperator(CAIRO_OPERATOR_SOURCE);
749 dc.paint();
750 dc.setOperator(CAIRO_OPERATOR_OVER);
751
752 drawing.render(dc, final_bbox);
753
754 if constexpr (false) surf->write_to_png("cairo.png");
755
756 surf->flush();
757
758 // Hide items
759 document->getRoot()->invoke_hide(dkey);
760 }
761
762 if constexpr (false) {
763 // Dump data to png
764 auto surf = Cairo::ImageSurface::create(px.get(), Cairo::Surface::Format::ARGB32, width, height, stride);
765 surf->write_to_png("cairo2.png");
766 std::cout << " Wrote cairo2.png" << std::endl;
767 }
768
769 auto const trace_px = std::make_unique<unsigned char[]>(width * height);
770
771 std::deque<Geom::Point> fill_queue;
772 std::queue<Geom::Point> color_queue;
773
774 bool aborted = false;
775 int y_limit = height - 1;
776
777 auto prefs = Preferences::get();
778 auto const method = static_cast<PaintBucketChannels>(prefs->getInt("/tools/paintbucket/channels", 0));
779 int threshold = prefs->getIntLimited("/tools/paintbucket/threshold", 1, 0, 100);
780
781 switch (method) {
784 case FLOOD_CHANNELS_R:
785 case FLOOD_CHANNELS_G:
786 case FLOOD_CHANNELS_B:
787 threshold = (255 * threshold) / 100;
788 break;
789 default:
790 break;
791 }
792
793 BitmapCoordsInfo bci;
794
795 bci.y_limit = y_limit;
796 bci.width = width;
797 bci.height = height;
798 bci.stride = stride;
799 bci.threshold = threshold;
800 bci.method = method;
801 bci.bbox = *bbox;
802 bci.screen = screen;
803 bci.dtc = dtc;
804 bci.radius = prefs->getIntLimited("/tools/paintbucket/autogap", 0, 0, 3);
805 bci.max_queue_size = (width * height) / 4;
806 bci.current_step = 0;
807
808 auto const fill_points = [&] () -> std::vector<Geom::Point> {
809 if (is_point_fill) {
810 return { cursor_pos };
811 } else {
813 }
814 }();
815
816 auto const img_max_indices = Geom::Rect::from_xywh(0, 0, width - 1, height - 1);
817
818 for (unsigned i = 0; i < fill_points.size(); i++) {
819 auto const pw = img_max_indices.clamp(fill_points[i] * world2img);
820
821 if (is_touch_fill) {
822 if (i == 0) {
823 color_queue.emplace(pw);
824 } else {
825 auto trace_t = get_trace_pixel(trace_px.get(), (int)pw.x(), (int)pw.y(), width);
826 push_point_onto_queue(fill_queue, bci.max_queue_size, trace_t, (int)pw.x(), (int)pw.y());
827 }
828 } else {
829 color_queue.emplace(pw);
830 }
831 }
832
833 bool reached_screen_boundary = false;
834
835 bool first_run = true;
836
837 size_t sort_size_threshold = 5;
838
839 unsigned int min_y = height;
840 unsigned int max_y = 0;
841 unsigned int min_x = width;
842 unsigned int max_x = 0;
843
844 while (!color_queue.empty() && !aborted) {
845 Geom::Point color_point = color_queue.front();
846 color_queue.pop();
847
848 int cx = (int)color_point[Geom::X];
849 int cy = (int)color_point[Geom::Y];
850
851 uint32_t orig_color = get_pixel(px.get(), cx, cy, stride);
852 bci.merged_orig_pixel = compose_onto(orig_color, dtc);
853
854 unsigned char *trace_t = get_trace_pixel(trace_px.get(), cx, cy, width);
855 if (!is_pixel_checked(trace_t) && !is_pixel_colored(trace_t)) {
856 if (check_if_pixel_is_paintable(px.get(), trace_px.get(), cx, cy, orig_color, bci)) {
857 shift_point_onto_queue(fill_queue, bci.max_queue_size, trace_t, cx, cy);
858
859 if (!first_run) {
860 for (unsigned int y = 0; y < height; y++) {
861 trace_t = get_trace_pixel(trace_px.get(), 0, y, width);
862 for (unsigned int x = 0; x < width; x++) {
864 trace_t++;
865 }
866 }
867 }
868 first_run = false;
869 }
870 }
871
872 unsigned long old_fill_queue_size = fill_queue.size();
873
874 while (!fill_queue.empty() && !aborted) {
875 Geom::Point cp = fill_queue.front();
876
877 if (bci.radius == 0) {
878 auto const new_fill_queue_size = fill_queue.size();
879
880 /*
881 * To reduce the number of points in the fill queue, periodically
882 * resort all of the points in the queue so that scanline checks
883 * can complete more quickly. A point cannot be checked twice
884 * in a normal scanline checks, so forcing scanline checks to start
885 * from one corner of the rendered area as often as possible
886 * will reduce the number of points that need to be checked and queued.
887 */
888 if (new_fill_queue_size > sort_size_threshold) {
889 if (new_fill_queue_size > old_fill_queue_size) {
890 std::sort(fill_queue.begin(), fill_queue.end(), sort_fill_queue_vertical);
891
892 auto start_sort = fill_queue.begin();
893 auto end_sort = fill_queue.begin();
894 unsigned int sort_y = (unsigned int)cp[Geom::Y];
895 unsigned int current_y;
896
897 for (auto i = fill_queue.begin(); i != fill_queue.end(); ++i) {
898 Geom::Point current = *i;
899 current_y = (unsigned int)current[Geom::Y];
900 if (current_y != sort_y) {
901 if (start_sort != end_sort) {
902 std::sort(start_sort, end_sort, sort_fill_queue_horizontal);
903 }
904 sort_y = current_y;
905 start_sort = i;
906 }
907 end_sort = i;
908 }
909 if (start_sort != end_sort) {
910 std::sort(start_sort, end_sort, sort_fill_queue_horizontal);
911 }
912
913 cp = fill_queue.front();
914 }
915 }
916
917 old_fill_queue_size = new_fill_queue_size;
918 }
919
920 fill_queue.pop_front();
921
922 int x = (int)cp[Geom::X];
923 int y = (int)cp[Geom::Y];
924
925 min_y = MIN((unsigned int)y, min_y);
926 max_y = MAX((unsigned int)y, max_y);
927
928 unsigned char *trace_t = get_trace_pixel(trace_px.get(), x, y, width);
929 if (!is_pixel_checked(trace_t)) {
930 mark_pixel_checked(trace_t);
931
932 if (y == 0) {
933 if (bbox->min()[Geom::Y] > screen.min()[Geom::Y]) {
934 aborted = true; break;
935 } else {
936 reached_screen_boundary = true;
937 }
938 }
939
940 if (y == y_limit) {
941 if (bbox->max()[Geom::Y] < screen.max()[Geom::Y]) {
942 aborted = true; break;
943 } else {
944 reached_screen_boundary = true;
945 }
946 }
947
948 bci.is_left = true;
949 bci.x = x;
950 bci.y = y;
951
952 ScanlineCheckResult result = perform_bitmap_scanline_check(fill_queue, px.get(), trace_px.get(), orig_color, bci, &min_x, &max_x);
953
954 switch (result) {
956 aborted = true;
957 break;
959 reached_screen_boundary = true;
960 break;
961 default:
962 break;
963 }
964
965 if (bci.x < width) {
966 trace_t++;
967 if (!is_pixel_checked(trace_t) && !is_pixel_queued(trace_t)) {
968 mark_pixel_checked(trace_t);
969 bci.is_left = false;
970 bci.x = x + 1;
971
972 result = perform_bitmap_scanline_check(fill_queue, px.get(), trace_px.get(), orig_color, bci, &min_x, &max_x);
973
974 switch (result) {
976 aborted = true;
977 break;
979 reached_screen_boundary = true;
980 break;
981 default:
982 break;
983 }
984 }
985 }
986 }
987
988 bci.current_step++;
989
990 if (bci.current_step > bci.max_queue_size) {
991 aborted = true;
992 }
993 }
994 }
995
996 if (aborted) {
997 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Area is not bounded</b>, cannot fill."));
998 return;
999 }
1000
1001 if (reached_screen_boundary) {
1002 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Only the visible part of the bounded area was filled.</b> If you want to fill all of the area, undo, zoom out, and fill again."));
1003 }
1004
1005 unsigned int trace_padding = bci.radius + 1;
1006 if (min_y > trace_padding) { min_y -= trace_padding; }
1007 if (max_y < (y_limit - trace_padding)) { max_y += trace_padding; }
1008 if (min_x > trace_padding) { min_x -= trace_padding; }
1009 if (max_x < (width - 1 - trace_padding)) { max_x += trace_padding; }
1010
1011 Geom::Affine inverted_affine = Geom::Translate(min_x, min_y) * doc2img.inverse();
1012
1013 do_trace(bci, trace_px.get(), desktop, inverted_affine, min_x, max_x, min_y, max_y, union_with_selection);
1014
1015 DocumentUndo::done(document, _("Fill bounded area"), INKSCAPE_ICON("color-fill"));
1016}
1017
1019{
1020 bool ret = false;
1021
1022 inspect_event(event,
1023 [&] (ButtonPressEvent const &event) {
1024 if (event.num_press == 1 && event.button == 1 && event.modifiers & GDK_CONTROL_MASK) {
1025 auto const button_w = event.pos;
1026
1027 auto item = sp_event_context_find_item(_desktop, button_w, true, true);
1028
1029 // Set style
1030 _desktop->applyCurrentOrToolStyle(item, "/tools/paintbucket", false);
1031
1032 DocumentUndo::done(_desktop->getDocument(), _("Set style on object"), INKSCAPE_ICON("color-fill"));
1033 ret = true;
1034 }
1035 },
1036 [&] (CanvasEvent const &event) {}
1037 );
1038
1039 return ret || ToolBase::item_handler(item, event);
1040}
1041
1043{
1044 bool ret = false;
1045
1046 inspect_event(event,
1047 [&] (ButtonPressEvent const &event) {
1048 if (event.num_press == 1 && event.button == 1 && !(event.modifiers & GDK_CONTROL_MASK)) {
1049 if (have_viable_layer(_desktop, defaultMessageContext())) {
1050 // save drag origin
1051 saveDragOrigin(event.pos);
1052
1053 dragging = true;
1054
1055 auto const p = _desktop->w2d(event.pos);
1056 auto rubberband = Rubberband::get(_desktop);
1057 rubberband->setMode(Rubberband::Mode::TOUCHPATH);
1058 rubberband->setHandle(RUBBERBAND_TOUCHPATH_FLOOD);
1059 rubberband->start(_desktop, p);
1060 }
1061 }
1062 },
1063
1064 [&] (MotionEvent const &event) {
1065 if (dragging && event.modifiers & GDK_BUTTON1_MASK) {
1066 if (!checkDragMoved(event.pos)) {
1067 return;
1068 }
1069
1070 auto const p = _desktop->w2d(event.pos);
1071
1072 if (Rubberband::get(_desktop)->isStarted()) {
1073 Rubberband::get(_desktop)->move(p);
1074 defaultMessageContext()->set(NORMAL_MESSAGE, _("<b>Draw over</b> areas to add to fill, hold <b>Alt</b> for touch fill"));
1075 gobble_motion_events(GDK_BUTTON1_MASK);
1076 }
1077 }
1078 },
1079
1080 [&] (ButtonReleaseEvent const &event) {
1081 if (event.button == 1) {
1082 auto r = Rubberband::get(_desktop);
1083
1084 if (r->isStarted()) {
1085 dragging = false;
1086 bool is_point_fill = within_tolerance;
1087 bool is_touch_fill = event.modifiers & GDK_ALT_MASK;
1088
1089 // It's possible for the user to sneakily change the tool while the
1090 // Gtk main loop has control, so we save the current desktop address:
1091 SPDesktop* current_desktop = _desktop;
1092
1093 current_desktop->setWaitingCursor();
1094 sp_flood_do_flood_fill(current_desktop, event.pos,
1095 event.modifiers & GDK_SHIFT_MASK,
1096 is_point_fill, is_touch_fill);
1097 current_desktop->clearWaitingCursor();
1098 r->stop();
1099
1100 // We check whether our object was deleted by SPDesktop::setTool()
1101 // TODO: fix SPDesktop so that it doesn't kill us before we're done
1102 ToolBase *current_context = current_desktop->getTool();
1103
1104 if (current_context == (ToolBase*)this) { // We're still alive
1105 this->defaultMessageContext()->clear();
1106 } // else just return without dereferencing `this`.
1107 ret = true;
1108 }
1109 }
1110 },
1111
1112 [&] (KeyPressEvent const &event) {
1113 switch (get_latin_keyval(event)) {
1114 case GDK_KEY_Up:
1115 case GDK_KEY_Down:
1116 case GDK_KEY_KP_Up:
1117 case GDK_KEY_KP_Down:
1118 // prevent the zoom field from activating
1119 if (!mod_ctrl_only(event)) {
1120 ret = true;
1121 }
1122 break;
1123 default:
1124 break;
1125 }
1126 },
1127
1128 [&] (CanvasEvent const &event) {}
1129 );
1130
1131 return ret || ToolBase::root_handler(event);
1132}
1133
1134void FloodTool::finishItem()
1135{
1136 message_context->clear();
1137
1138 if (item) {
1139 item->updateRepr();
1140
1141 _desktop->getSelection()->set(item);
1142 DocumentUndo::done(_desktop->getDocument(), _("Fill bounded area"), INKSCAPE_ICON("color-fill"));
1143
1144 item = nullptr;
1145 }
1146}
1147
1148void FloodTool::set_channels(int channels)
1149{
1150 auto prefs = Preferences::get();
1151 prefs->setInt("/tools/paintbucket/channels", channels);
1152}
1153
1154} // namespace Inkscape::UI::Tools
1155
1156/*
1157 Local Variables:
1158 mode:c++
1159 c-file-style:"stroustrup"
1160 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1161 indent-tabs-mode:nil
1162 fill-column:99
1163 End:
1164*/
1165// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
@ fill_nonZero
Definition LivarotDefs.h:69
@ fill_positive
Definition LivarotDefs.h:70
@ join_round
Definition LivarotDefs.h:62
TODO: insert short description here.
TODO: insert short description here.
Cairo integration helpers.
G_GNUC_CONST guint32 unpremul_alpha(const guint32 color, const guint32 alpha)
Inkscape canvas widget.
3x3 matrix representing an affine transformation.
Definition affine.h:70
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Definition affine.cpp:109
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
static CRect from_xywh(C x, C y, C w, C h)
Create rectangle from origin and dimensions.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint dimensions() const
Get rectangle's width and height as a point.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Two-dimensional point with integer coordinates.
Definition int-point.h:57
constexpr IntCoord x() const noexcept
Definition int-point.h:77
constexpr IntCoord y() const noexcept
Definition int-point.h:79
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
Translation by a vector.
Definition transforms.h:115
A dummy Progress object that never reports cancellation.
Definition progress.h:146
bool set(unsigned int index, double value)
Set a specific channel in the color.
Definition color.cpp:350
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Minimal wrapper over Cairo.
void setRoot(DrawingItem *root)
Definition drawing.cpp:64
void update(Geom::IntRect const &area=Geom::IntRect::infinite(), Geom::Affine const &affine=Geom::identity(), unsigned flags=DrawingItem::STATE_ALL, unsigned reset=0)
Definition drawing.cpp:228
void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags=0) const
Definition drawing.cpp:239
SPGroup * currentLayer() const
Returns current top layer.
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
MessageId flashF(MessageType type, char const *format,...) G_GNUC_PRINTF(3
temporarily pushes a message onto the stack using printf-like formatting
void pathUnion(bool skip_undo=false, bool silent=false)
SPItem * singleItem()
Returns a single selected item.
Preference storage class.
Definition preferences.h:66
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
static Preferences * get()
Access the singleton Preferences object.
std::vector< Geom::Point > getPoints() const
static Rubberband * get(SPDesktop *desktop)
void move(Geom::Point const &p)
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void add(XML::Node *repr)
Add an XML node's SPObject to the set of selected objects.
Definition selection.h:107
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
sigc::connection connectChanged(sigc::slot< void(Selection *)> slot)
Connects a slot to be notified of selection changes.
Definition selection.h:179
TraceResult traceGrayMap(GrayMap const &grayMap, Async::Progress< double > &progress)
This allows routines that already generate GrayMaps to skip image filtering, increasing performance.
void set_item(SPItem *item)
void unset_item(bool keep_knotholder=false)
FloodTool(SPDesktop *desktop)
void selection_changed(Selection *selection)
Callback that processes the "changed" signal on the selection; destroys old and creates new knotholde...
static std::vector< char const * > const channel_list
Definition flood-tool.h:40
bool root_handler(CanvasEvent const &event) override
sigc::connection sel_changed_connection
Definition flood-tool.h:33
bool item_handler(SPItem *item, CanvasEvent const &event) override
Handles item specific events.
static std::vector< char const * > const gap_list
Definition flood-tool.h:41
Base class for Event processors.
Definition tool-base.h:107
virtual bool root_handler(CanvasEvent const &event)
virtual bool item_handler(SPItem *item, CanvasEvent const &event)
Handles item specific events.
void enableSelectionCue(bool enable=true)
Enables/disables the ToolBase's SelCue.
Geom::IntRect get_area_world() const
Return the area shown in the canvas in world coordinates.
Definition canvas.cpp:1560
Interface for refcounted XML nodes.
Definition node.h:80
virtual void setPosition(int pos)=0
Set the position of this node in parent's child order.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Path and its polyline approximation.
Definition Path.h:93
void ConvertWithBackData(double threshhold, bool relative=false)
Creates a polyline approximation of the path.
void Fill(Shape *dest, int pathID=-1, bool justAdd=false, bool closeIfNeeded=true, bool invert=false)
Fills the shape with the polyline approximation stored in this object.
std::string svg_dump_path() const
Definition Path.cpp:577
void ConvertEvenLines(double treshhold)
Creates a polyline approximation of the path.
void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation)
Load a lib2geom Geom::PathVector in this path object.
void Simplify(double treshhold)
Simplify the path.
To do: update description of desktop.
Definition desktop.h:149
Inkscape::UI::Widget::Canvas * getCanvas() const
Definition desktop.h:190
double current_zoom() const
Definition desktop.h:335
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Geom::Affine const & d2w() const
Transformation from desktop to window coordinates.
Definition desktop.h:419
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Inkscape::UI::Tools::ToolBase * getTool() const
Definition desktop.h:187
SPDocument * doc() const
Definition desktop.h:159
void clearWaitingCursor()
Definition desktop.cpp:1166
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
Geom::Affine const & doc2dt() const
Definition desktop.cpp:1337
void setWaitingCursor()
Definition desktop.cpp:1155
Geom::Affine const & w2d() const
Transformation from window to desktop coordinates (zoom/rotate).
Definition desktop.h:416
Typed SVG document implementation.
Definition document.h:103
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Base class for visual SVG elements.
Definition sp-item.h:109
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition sp-item.cpp:1246
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void addChild(Inkscape::XML::Node *child, Inkscape::XML::Node *prev=nullptr)
A class to store/manipulate directed graphs.
Definition Shape.h:65
void ConvertToForme(Path *dest)
Extract contours from a directed graph.
Definition ShapeMisc.cpp:42
int MakeOffset(Shape *of, double dec, JoinType join, double miter, bool do_profile=false, double cx=0, double cy=0, double radius=0, Geom::Affine *i2doc=nullptr)
int ConvertToShape(Shape *a, FillRule directed=fill_nonZero, bool invert=false)
Using a given fill rule, find all intersections in the shape given, create a new intersection free sh...
RectangularCluster rd
RootCluster root
RectangularCluster rc
Css & result
Geom::Point orig
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
Editable view implementation.
static char const *const current
Definition dir-util.cpp:71
TODO: insert short description here.
Cairo drawing context with Inkscape extensions.
SVG drawing for display.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Macro for icon names used in Inkscape.
SPItem * item
TODO: insert short description here.
double offset
Interface for locally managing a current status message.
Raw stack of active status messages.
Affine identity()
Create an identity matrix.
Definition affine.h:210
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
Definition profile.cpp:24
void ExtractARGB32(guint32 px, guint32 &a, guint32 &r, guint32 &g, guint &b)
void ExtractRGB32(guint32 px, guint32 &r, guint32 &g, guint &b)
guint AssembleARGB32(guint32 a, guint32 r, guint32 g, guint32 b)
static R & release(R &r)
Decrements the reference count of a anchored object.
uint32_t get_pixel(unsigned char *px, int x, int y, int stride)
Get the pointer to a pixel in a pixel buffer.
static void clear_pixel_paintability(unsigned char *t)
static void do_trace(BitmapCoordsInfo const &bci, unsigned char *trace_px, SPDesktop *desktop, Geom::Affine const &transform, unsigned min_x, unsigned max_x, unsigned min_y, unsigned max_y, bool union_with_selection)
Perform the bitmap-to-vector tracing and place the traced path onto the document.
static constexpr int PAINT_DIRECTION_ALL
static void mark_pixel_queued(unsigned char *t)
static bool is_pixel_paintable(unsigned char *t)
static bool compare_pixels(uint32_t check, uint32_t orig, uint32_t merged_orig_pixel, uint32_t dtc, int threshold, PaintBucketChannels method)
Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in th...
static constexpr unsigned char PIXEL_QUEUED
static bool compare_uint32(uint32_t a, uint32_t b, uint32_t d)
Check whether two unsigned integers are close to each other.
static bool check_if_pixel_is_paintable(unsigned char *px, unsigned char *trace_t, int x, int y, uint32_t orig_color, BitmapCoordsInfo const &bci)
Check if a pixel can be included in the fill.
static bool is_pixel_queued(unsigned char *t)
static void mark_pixel_paintable(unsigned char *t)
static void mark_pixel_colored(unsigned char *t)
static bool sort_fill_queue_horizontal(Geom::Point const &a, Geom::Point const &b)
Sort the rendered pixel buffer check queue horizontally.
static constexpr int PAINT_DIRECTION_DOWN
static constexpr int PAINT_DIRECTION_LEFT
static ScanlineCheckResult perform_bitmap_scanline_check(std::deque< Geom::Point > &fill_queue, unsigned char *px, unsigned char *trace_px, uint32_t orig_color, BitmapCoordsInfo bci, unsigned *min_x, unsigned *max_x)
Scan a row in the rendered pixel buffer and add points to the fill queue as necessary.
static constexpr int PAINT_DIRECTION_RIGHT
static void sp_flood_do_flood_fill(SPDesktop *desktop, Geom::Point const &cursor_pos, bool union_with_selection, bool is_point_fill, bool is_touch_fill)
Perform a flood fill operation.
static constexpr unsigned char PIXEL_NOT_PAINTABLE
static unsigned paint_pixel(unsigned char *px, unsigned char *trace_px, uint32_t orig_color, BitmapCoordsInfo const &bci, unsigned char *original_point_trace_t)
Paint a pixel or a square (if autogap is enabled) on the trace pixel buffer.
static constexpr unsigned char PIXEL_COLORED
unsigned char * get_trace_pixel(unsigned char *trace_px, int x, int y, int width)
static constexpr int PAINT_DIRECTION_UP
void gobble_motion_events(unsigned mask)
Definition tool-base.h:244
static void mark_pixel_not_paintable(unsigned char *t)
static void shift_point_onto_queue(std::deque< Geom::Point > &fill_queue, unsigned max_queue_size, unsigned char *trace_t, unsigned x, unsigned y)
Shift a point to be checked onto the top of the rendered pixel buffer check queue.
static uint32_t compose_onto(uint32_t px, uint32_t bg)
static bool coords_in_range(unsigned x, unsigned y, BitmapCoordsInfo const &bci)
Determine if the provided coordinates are within the pixel buffer limits.
static bool is_pixel_colored(unsigned char *t)
static bool is_pixel_paintability_checked(unsigned char *t)
ScanlineCheckResult
The possible return states of perform_bitmap_scanline_check().
static void push_point_onto_queue(std::deque< Geom::Point > &fill_queue, unsigned max_queue_size, unsigned char *trace_t, unsigned x, unsigned y)
Push a point to be checked onto the bottom of the rendered pixel buffer check queue.
unsigned get_latin_keyval(GtkEventControllerKey const *const controller, unsigned const keyval, unsigned const keycode, GdkModifierType const state, unsigned *consumed_modifiers)
Return the keyval corresponding to the event controller key in Latin group.
static void mark_pixel_checked(unsigned char *t)
static bool sort_fill_queue_vertical(Geom::Point const &a, Geom::Point const &b)
Sort the rendered pixel buffer check queue vertically.
static constexpr unsigned char PIXEL_CHECKED
static bool is_pixel_checked(unsigned char *t)
static constexpr unsigned char PIXEL_PAINTABLE
void inspect_event(E &&event, Fs... funcs)
Perform pattern-matching on a CanvasEvent.
bool mod_ctrl_only(unsigned modifiers)
@ NORMAL_MESSAGE
Definition message.h:26
@ WARNING_MESSAGE
Definition message.h:28
PathVector - a sequence of subpaths.
int stride
Inkscape::ShapeEditor This is a container class which contains a knotholder for shapes.
SPRoot: SVG <svg> implementation.
A mouse button (left/right/middle) is pressed.
A mouse button (left/right/middle) is released.
Abstract base class for events.
unsigned modifiers
The modifiers mask immediately before the event.
A key has been pressed.
Movement of the mouse pointer.
static unsigned long constexpr WHITE
Definition imagemap.h:46
static unsigned long constexpr BLACK
Definition imagemap.h:45
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
bool sp_svg_transform_read(gchar const *str, Geom::Affine *transform)
std::string sp_svg_transform_write(Geom::Affine const &transform)
SPDesktop * desktop
double height
double width