Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
drawing.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
6 * Authors:
7 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
8 * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
9 *
10 * Copyright (C) 2011-2012 Authors
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include "drawing.h"
15
16#include <array>
17#include <thread>
18
19#include "cairo-utils.h"
21#include "drawing-context.h"
22#include "nr-filter-gaussian.h"
23#include "nr-filter-types.h"
24#include "threading.h"
25
26namespace Inkscape {
27
28// Hardcoded grayscale color matrix values as default.
29static auto constexpr grayscale_matrix = std::array{
30 0.21, 0.72, 0.072, 0.0, 0.0,
31 0.21, 0.72, 0.072, 0.0, 0.0,
32 0.21, 0.72, 0.072, 0.0, 0.0,
33 0.0 , 0.0 , 0.0 , 1.0, 0.0
34};
35
45
46static auto default_numthreads()
47{
48 auto ret = std::thread::hardware_concurrency();
49 return ret == 0 ? 4 : ret; // Sensible fallback if not reported.
50}
51
53 : _canvas_item_drawing(canvas_item_drawing)
54 , _grayscale_matrix(std::vector<double>(grayscale_matrix.begin(), grayscale_matrix.end()))
55{
56 _loadPrefs();
57}
58
60{
61 delete _root;
62}
63
73
75{
76 assert(mode != RenderMode::OUTLINE_OVERLAY && "Drawing::setRenderMode: OUTLINE_OVERLAY is not a true render mode");
77
78 defer([=, this] {
79 if (mode == _rendermode) return;
84 });
85}
86
88{
89 defer([=, this] {
90 if (mode == _colormode) return;
94 }
95 });
96}
97
98void Drawing::setOutlineOverlay(bool outlineoverlay)
99{
100 defer([=, this] {
101 if (outlineoverlay == _outlineoverlay) return;
102 _outlineoverlay = outlineoverlay;
104 });
105}
106
107void Drawing::setGrayscaleMatrix(double value_matrix[20])
108{
109 defer([=, this] {
110 _grayscale_matrix = Filters::FilterColorMatrix::ColorMatrixMatrix(std::vector<double>(value_matrix, value_matrix + 20));
113 }
114 });
115}
116
118{
119 defer([=, this] {
123 }
124 });
125}
126
128{
129 defer([=, this] {
133 }
134 });
135}
136
138{
139 defer([=, this] {
143 }
144 });
145}
146
148{
149 defer([=, this] {
150 _image_outline_mode = enabled;
153 }
154 });
155}
156
158{
159 defer([=, this] {
160 _filter_quality = quality;
163 _clearCache();
164 }
165 });
166}
167
168void Drawing::setBlurQuality(int quality)
169{
170 defer([=, this] {
171 _blur_quality = quality;
174 _clearCache();
175 }
176 });
177}
178
179void Drawing::setDithering(bool use_dithering)
180{
181 defer([=, this] {
182 _use_dithering = use_dithering;
183 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 18, 0)
186 _clearCache();
187 }
188 #endif
189 });
190}
191
192void Drawing::setCacheBudget(size_t bytes)
193{
194 defer([=, this] {
195 _cache_budget = bytes;
197 });
198}
199
201{
202 defer([=, this] {
203 _cache_limit = rect;
204 for (auto item : _cached_items) {
205 item->_markForUpdate(DrawingItem::STATE_CACHE, false);
206 }
207 });
208}
209
210void Drawing::setClip(std::optional<Geom::PathVector> &&clip)
211{
212 defer([=, this] {
213 if (clip == _clip) return;
214 _clip = std::move(clip);
216 });
217}
218
219void Drawing::setAntialiasingOverride(std::optional<Antialiasing> antialiasing_override)
220{
221 defer([=, this] {
222 _antialiasing_override = antialiasing_override;
224 _clearCache();
225 });
226}
227
228void Drawing::update(Geom::IntRect const &area, Geom::Affine const &affine, unsigned flags, unsigned reset)
229{
230 if (_root) {
231 _root->update(area, { affine }, flags, reset);
232 }
233 if (flags & DrawingItem::STATE_CACHE) {
234 // Process the updated cache scores.
236 }
237}
238
239void Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags) const
240{
242
243 auto rc = RenderContext{
244 .outline_color = 0xff,
245 .antialiasing_override = _antialiasing_override,
246 .dithering = _use_dithering
247 };
249
250 if (_clip) {
251 dc.save();
252 dc.path(*_clip * _root->_ctm);
253 dc.clip();
254 }
255 _root->render(dc, rc, area, flags);
256 if (_clip) {
257 dc.restore();
258 }
259}
260
261DrawingItem *Drawing::pick(Geom::Point const &p, double delta, unsigned flags)
262{
263 return _root->pick(p, delta, flags);
264}
265
267{
268 assert(!_snapshotted);
269 _snapshotted = true;
270}
271
273{
274 assert(_snapshotted);
275 _snapshotted = false; // Unsnapshot before replaying log so further work is not deferred.
276 _funclog();
277}
278
280{
281 // Build sorted list of items that should be cached.
282 std::vector<DrawingItem*> to_cache;
283 size_t used = 0;
284 for (auto &rec : _candidate_items) {
285 if (used + rec.cache_size > _cache_budget) break;
286 to_cache.emplace_back(rec.item);
287 used += rec.cache_size;
288 }
289 std::sort(to_cache.begin(), to_cache.end());
290
291 // Uncache the items that are cached but should not be cached.
292 // Note: setCached() modifies _cached_items, so the temporary container is necessary.
293 std::vector<DrawingItem*> to_uncache;
294 std::set_difference(_cached_items.begin(), _cached_items.end(),
295 to_cache.begin(), to_cache.end(),
296 std::back_inserter(to_uncache));
297 for (auto item : to_uncache) {
298 item->_setCached(false);
299 }
300
301 // Cache all items that should be cached (no-op if already cached).
302 for (auto item : to_cache) {
303 item->_setCached(true);
304 }
305}
306
308{
309 // Note: setCached() modifies _cached_items, so the temporary container is necessary.
310 std::vector<DrawingItem*> to_uncache;
311 std::copy(_cached_items.begin(), _cached_items.end(), std::back_inserter(to_uncache));
312 for (auto item : to_uncache) {
313 item->_setCached(false, true);
314 }
315}
316
318{
319 auto prefs = Inkscape::Preferences::get();
320
321 // Set the initial values of preferences.
322 _clip_outline_color = prefs->getIntLimited("/options/wireframecolors/clips", 0x00ff00ff, 0, 0xffffffff); // Green clip outlines by default.
323 _mask_outline_color = prefs->getIntLimited("/options/wireframecolors/masks", 0x0000ffff, 0, 0xffffffff); // Blue mask outlines by default.
324 _image_outline_color = prefs->getIntLimited("/options/wireframecolors/images", 0xff0000ff, 0, 0xffffffff); // Red image outlines by default.
325 _image_outline_mode = prefs->getBool ("/options/rendering/imageinoutlinemode", false);
326 _filter_quality = prefs->getIntLimited("/options/filterquality/value", 0, Filters::FILTER_QUALITY_WORST, Filters::FILTER_QUALITY_BEST);
327 _blur_quality = prefs->getInt ("/options/blurquality/value", 0);
328 _use_dithering = prefs->getBool ("/options/dithering/value", true);
329 _cursor_tolerance = prefs->getDouble ("/options/cursortolerance/value", 1.0);
330 _select_zero_opacity = prefs->getBool ("/options/selection/zeroopacity", false);
331
332 // Enable caching only for the Canvas's drawing, since only it is persistent.
334 // Preference is stored in MiB; convert to bytes, taking care not to overflow.
335 _cache_budget = (size_t{1} << 20) * prefs->getIntLimited("/options/renderingcache/size", 64, 0, 4096);
336 } else {
337 _cache_budget = 0;
338 }
339
340 // Set the global variable governing the number of threads, and track it too. (This is ugly, but hopefully
341 // transitional.)
342 set_num_dispatch_threads(prefs->getIntLimited("/options/threading/numthreads", default_numthreads(), 1, 256));
343
344 // Similarly, enable preference tracking only for the Canvas's drawing.
346 std::unordered_map<std::string, std::function<void (Preferences::Entry const &)>> actions;
347
348 // Todo: (C++20) Eliminate this repetition by baking the preference metadata into the variables themselves using structural templates.
349 actions.emplace("/options/wireframecolors/clips", [this] (auto &entry) { setClipOutlineColor (entry.getIntLimited(0x00ff00ff, 0, 0xffffffff)); });
350 actions.emplace("/options/wireframecolors/masks", [this] (auto &entry) { setMaskOutlineColor (entry.getIntLimited(0x0000ffff, 0, 0xffffffff)); });
351 actions.emplace("/options/wireframecolors/images", [this] (auto &entry) { setImageOutlineColor(entry.getIntLimited(0xff0000ff, 0, 0xffffffff)); });
352 actions.emplace("/options/rendering/imageinoutlinemode", [this] (auto &entry) { setImageOutlineMode(entry.getBool(false)); });
353 actions.emplace("/options/filterquality/value", [this] (auto &entry) { setFilterQuality(entry.getIntLimited(0, Filters::FILTER_QUALITY_WORST, Filters::FILTER_QUALITY_BEST)); });
354 actions.emplace("/options/blurquality/value", [this] (auto &entry) { setBlurQuality(entry.getInt(0)); });
355 actions.emplace("/options/dithering/value", [this] (auto &entry) { setDithering(entry.getBool(true)); });
356 actions.emplace("/options/cursortolerance/value", [this] (auto &entry) { setCursorTolerance(entry.getDouble(1.0)); });
357 actions.emplace("/options/selection/zeroopacity", [this] (auto &entry) { setSelectZeroOpacity(entry.getBool(false)); });
358 actions.emplace("/options/renderingcache/size", [this] (auto &entry) { setCacheBudget((1 << 20) * entry.getIntLimited(64, 0, 4096)); });
359 actions.emplace("/options/threading/numthreads", [this](auto &entry) {
360 set_num_dispatch_threads(entry.getIntLimited(default_numthreads(), 1, 256));
361 });
362
363 _pref_tracker = Inkscape::Preferences::PreferencesObserver::create("/options", [actions = std::move(actions)] (auto &entry) {
364 auto it = actions.find(entry.getPath());
365 if (it == actions.end()) return;
366 it->second(entry);
367 });
368 }
369}
370
371/*
372 * Return average color over area. Used by Calligraphic, Dropper, and Spray tools.
373 */
375{
376 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, area.width(), area.height());
377 auto dc = Inkscape::DrawingContext(surface->cobj(), area.min());
378 render(dc, area);
380}
381
382/*
383 * Return the average color inside the given path.
384 */
386{
387 auto area = path.boundsExact();
388 if (!area || area->hasZeroArea()) {
389 return Colors::Color(0x0); // Transparent black sRGB
390 }
391
392 // Scale the graphic so there's a predictable number of pixels to choose from
393 static constexpr auto width = 200.0;
394 static constexpr auto height = 200.0;
395
396 auto affine = Geom::Scale(width / area->width(), height / area->height());
397 auto offset = area->min() * affine;
398
399 // Build a mask of pixels to ignore
400 auto mask = Cairo::ImageSurface::create(Cairo::Surface::Format::A8, width, height);
401 auto dc_mask = Inkscape::DrawingContext(mask->cobj(), offset);
402 dc_mask.scale(affine);
403
404 dc_mask.setFillRule(evenodd ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING);
405 dc_mask.path(path);
406 dc_mask.clip();
407 dc_mask.setSource(1, 1, 1, 1);
408 dc_mask.setOperator(CAIRO_OPERATOR_SOURCE);
409 dc_mask.paint();
410
411 // Render the output, no need to clip as the mask will say what values to use
412 auto image = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width, height);
413 auto dc = Inkscape::DrawingContext(image->cobj(), offset);
414 dc.scale(affine);
415 render(dc, area->roundOutwards());
416
417 return ink_cairo_surface_average_color(image->cobj(), mask->cobj());
418}
419
420/*
421 * Convenience function to set high quality options for export.
422 */
428
429/*
430 * Set the opacity of the drawing root drawing-item
431 */
432void Drawing::setOpacity(double opacity)
433{
434 _root->setOpacity(opacity);
435}
436
437} // namespace Inkscape
438
439/*
440 Local Variables:
441 mode:c++
442 c-file-style:"stroustrup"
443 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
444 indent-tabs-mode:nil
445 fill-column:99
446 End:
447*/
448// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Colors::Color ink_cairo_surface_average_color(cairo_surface_t *surface, cairo_surface_t *mask)
Get the average color from the given surface.
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
3x3 matrix representing an affine transformation.
Definition affine.h:70
Axis-aligned generic rectangle that can be empty.
Axis aligned, non-empty, generic rectangle.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
Sequence of subpaths.
Definition pathvector.h:122
OptRect boundsExact() const
Two-dimensional point that doubles as a vector.
Definition point.h:66
Scaling from the origin.
Definition transforms.h:150
Minimal wrapper over Cairo.
void path(Geom::PathVector const &pv)
SVG drawing item for display.
Antialiasing _antialias
antialiasing level (default is Good)
unsigned render(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags=0, DrawingItem const *stop_at=nullptr) const
Rasterize items.
DrawingItem * pick(Geom::Point const &p, double delta, unsigned flags=0)
Get the item under the specified point.
void setOpacity(float opacity)
void _markForUpdate(unsigned state, bool propagate)
Marks the item as needing a recomputation of internal data.
void update(Geom::IntRect const &area=Geom::IntRect::infinite(), UpdateContext const &ctx=UpdateContext(), unsigned flags=STATE_ALL, unsigned reset=0)
Update derived data before operations.
Geom::Affine _ctm
Total transform from item coords to display coords.
void _markForRendering()
Marks the current visual bounding box of the item for redrawing.
void setBlurQuality(int)
Definition drawing.cpp:168
void _pickItemsForCaching()
Definition drawing.cpp:279
uint32_t _image_outline_color
Definition drawing.h:112
void setGrayscaleMatrix(double[20])
Definition drawing.cpp:107
DrawingItem * _root
Definition drawing.h:102
uint32_t _clip_outline_color
Definition drawing.h:110
CanvasItemDrawing * _canvas_item_drawing
Definition drawing.h:103
void setOpacity(double opacity=1.0)
Definition drawing.cpp:432
void setSelectZeroOpacity(bool select_zero_opacity)
Definition drawing.h:61
uint32_t _mask_outline_color
Definition drawing.h:111
size_t _cache_budget
Maximum allowed size of cache.
Definition drawing.h:118
CacheList _candidate_items
Definition drawing.h:125
void setImageOutlineColor(uint32_t)
Definition drawing.cpp:137
void setDithering(bool)
Definition drawing.cpp:179
void setRenderMode(RenderMode)
Definition drawing.cpp:74
void setRoot(DrawingItem *root)
Definition drawing.cpp:64
DrawingItem * root()
Definition drawing.h:46
std::optional< Geom::PathVector > _clip
Definition drawing.h:120
void setMaskOutlineColor(uint32_t)
Definition drawing.cpp:127
void setClip(std::optional< Geom::PathVector > &&clip)
Definition drawing.cpp:210
double _cursor_tolerance
Definition drawing.h:117
RenderMode _rendermode
Definition drawing.h:106
void setClipOutlineColor(uint32_t)
Definition drawing.cpp:117
void setColorMode(ColorMode)
Definition drawing.cpp:87
void defer(F &&f)
Definition drawing.h:138
Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_matrix
Definition drawing.h:109
Colors::Color averageColor(Geom::IntRect const &area) const
Definition drawing.cpp:374
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
DrawingItem * pick(Geom::Point const &p, double delta, unsigned flags)
Definition drawing.cpp:261
ColorMode _colormode
Definition drawing.h:107
void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags=0) const
Definition drawing.cpp:239
Util::FuncLog _funclog
Definition drawing.h:135
void setCacheLimit(Geom::OptIntRect const &rect)
Definition drawing.cpp:200
void setCursorTolerance(double tol)
Definition drawing.h:60
std::unique_ptr< Preferences::PreferencesObserver > _pref_tracker
Definition drawing.h:104
void setImageOutlineMode(bool)
Definition drawing.cpp:147
Drawing(CanvasItemDrawing *drawing=nullptr)
Definition drawing.cpp:52
void setFilterQuality(int)
Definition drawing.cpp:157
bool _select_zero_opacity
Definition drawing.h:121
std::optional< Antialiasing > _antialiasing_override
Definition drawing.h:122
void setAntialiasingOverride(std::optional< Antialiasing > antialiasing_override)
Definition drawing.cpp:219
Geom::OptIntRect _cache_limit
Definition drawing.h:119
bool _image_outline_mode
Always draw images as images, even in outline mode.
Definition drawing.h:113
void setOutlineOverlay(bool)
Definition drawing.cpp:98
void setCacheBudget(size_t bytes)
Definition drawing.cpp:192
std::set< DrawingItem * > _cached_items
Definition drawing.h:124
Data type representing a typeless value of a preference.
static std::unique_ptr< PreferencesObserver > create(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
static Preferences * get()
Access the singleton Preferences object.
RootCluster root
RectangularCluster rc
Cairo drawing context with Inkscape extensions.
SVG drawing for display.
std::unique_ptr< Magick::Image > image
SPItem * item
double offset
Geom::Point end
Helper class to stream background task notifications as a series of messages.
static auto rendermode_to_renderflags(RenderMode mode)
Definition drawing.cpp:36
static auto default_numthreads()
Definition drawing.cpp:46
void set_num_dispatch_threads(int num_dispatch_threads)
Definition threading.cpp:26
static auto constexpr grayscale_matrix
Definition drawing.cpp:29
void apply_antialias(DrawingContext &dc, Antialiasing antialias)
Apply antialias setting to Cairo.
STL namespace.
static T clip(T const &v, T const &a, T const &b)
@ BLUR_QUALITY_BEST
TODO: insert short description here.
int mode
bool used
std::uint32_t outline_color
int delta
double height
double width