diff --git a/src/display/CMakeLists.txt b/src/display/CMakeLists.txt index 628b60f59d81f6d000126997ec9e26f067f8c17e..16e8bbfe59b55259471b6de6d0fd37f997981430 100644 --- a/src/display/CMakeLists.txt +++ b/src/display/CMakeLists.txt @@ -68,6 +68,7 @@ set(display_SRC drawing-group.h drawing-image.h drawing-item.h + drawing-item-ptr.h drawing-paintserver.h drawing-pattern.h drawing-shape.h diff --git a/src/display/cairo-templates.h b/src/display/cairo-templates.h index 8b7493e54281b13b19a56db513492595131fd6d5..d23343e45691d47dc49be99ac18eac50c839b943 100644 --- a/src/display/cairo-templates.h +++ b/src/display/cairo-templates.h @@ -21,14 +21,13 @@ #ifdef HAVE_OPENMP #include -#include "preferences.h" // single-threaded operation if the number of pixels is below this threshold static const int OPENMP_THRESHOLD = 2048; #endif +#include #include #include -#include #include "display/nr-3dutils.h" #include "display/cairo-utils.h" @@ -76,9 +75,7 @@ void ink_cairo_surface_blend(cairo_surface_t *in1, cairo_surface_t *in2, cairo_s // OpenMP probably doesn't help much here. // It would be better to render more than 1 tile at a time. #if HAVE_OPENMP - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int numOfThreads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); - if (numOfThreads){} // inform compiler we are using it. + int numOfThreads = get_num_filter_threads(); #endif // The number of code paths here is evil. @@ -204,9 +201,7 @@ void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter guint32 *const out_data = reinterpret_cast(cairo_image_surface_get_data(out)); #if HAVE_OPENMP - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int numOfThreads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); - if (numOfThreads){} // inform compiler we are using it. + int numOfThreads = get_num_filter_threads(); #endif // this is provided just in case, to avoid problems with strict aliasing rules @@ -353,9 +348,7 @@ void ink_cairo_surface_synthesize(cairo_surface_t *out, cairo_rectangle_t const #if HAVE_OPENMP int limit = w * h; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int numOfThreads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); - if (numOfThreads){} // inform compiler we are using it. + int numOfThreads = get_num_filter_threads(); #endif if (bppout == 4) { @@ -641,62 +634,6 @@ struct SurfaceSynth { bool _alpha; }; -/* -// simple pixel accessor for image surface that handles different edge wrapping modes -class PixelAccessor { -public: - typedef PixelAccessor self; - enum EdgeMode { - EDGE_PAD, - EDGE_WRAP, - EDGE_ZERO - }; - - PixelAccessor(cairo_surface_t *s, EdgeMode e) - : _surface(s) - , _px(cairo_image_surface_get_data(s)) - , _x(0), _y(0) - , _w(cairo_image_surface_get_width(s)) - , _h(cairo_image_surface_get_height(s)) - , _stride(cairo_image_surface_get_stride(s)) - , _edge_mode(e) - , _alpha(cairo_image_surface_get_format(s) == CAIRO_FORMAT_A8) - {} - - guint32 pixelAt(int x, int y) { - // This is a lot of ifs for a single pixel access. However, branch prediction - // should help us a lot, as the result of ifs is always the same for a single image. - int real_x = x, real_y = y; - switch (_edge_mode) { - case EDGE_PAD: - real_x = CLAMP(x, 0, _w-1); - real_y = CLAMP(y, 0, _h-1); - break; - case EDGE_WRAP: - real_x %= _w; - real_y %= _h; - break; - case EDGE_ZERO: - default: - if (x < 0 || x >= _w || y < 0 || y >= _h) - return 0; - break; - } - if (_alpha) { - return *(_px + real_y*_stride + real_x) << 24; - } else { - guint32 *px = reinterpret_cast(_px +real_y*_stride + real_x*4); - return *px; - } - } -private: - cairo_surface_t *_surface; - guint8 *_px; - int _x, _y, _w, _h, _stride; - EdgeMode _edge_mode; - bool _alpha; -};*/ - // Some helpers for pixel manipulation G_GNUC_CONST inline gint32 pxclamp(gint32 v, gint32 low, gint32 high) { diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index ce1ecb820cd26449e450492e946616867094310e..e3ed741a3cb7f3f5461d99bf4d700efa1d64699d 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -11,6 +11,7 @@ #include "display/cairo-utils.h" +#include #include #include @@ -45,7 +46,7 @@ * Only the address of the structure is used, it is never initialized. See: * http://www.cairographics.org/manual/cairo-Types.html#cairo-user-data-key-t */ -cairo_user_data_key_t ink_color_interpolation_key; +static cairo_user_data_key_t ink_color_interpolation_key; namespace Inkscape { @@ -866,6 +867,18 @@ feed_pathvector_to_cairo (cairo_t *ct, Geom::PathVector const &pathv) } } +static std::atomic num_filter_threads = 4; + +int get_num_filter_threads() +{ + return num_filter_threads.load(std::memory_order_relaxed); +} + +void set_num_filter_threads(int n) +{ + num_filter_threads.store(n, std::memory_order_relaxed); +} + SPColorInterpolation get_cairo_surface_ci(cairo_surface_t *surface) { void* data = cairo_surface_get_user_data( surface, &ink_color_interpolation_key ); @@ -964,6 +977,13 @@ ink_cairo_set_hairline(cairo_t *ct) #endif } +void ink_cairo_set_dither(cairo_surface_t *surface, bool enabled) +{ +#ifdef CAIRO_HAS_DITHER + cairo_image_surface_set_dither(surface, enabled ? CAIRO_DITHER_BEST : CAIRO_DITHER_NONE); +#endif +} + /** * Create an exact copy of a surface. * Creates a surface that has the same type, content type, dimensions and contents diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h index 87bcef7bab0cdb261cc1aa61df1ffdab9df1cf01..0effcc7e482195bc7ded05bb5ffde84578113a42 100644 --- a/src/display/cairo-utils.h +++ b/src/display/cairo-utils.h @@ -88,8 +88,9 @@ public: } // namespace Inkscape -// TODO: these declarations may not be needed in the header -extern cairo_user_data_key_t ink_color_interpolation_key; +// Atomic accessors to global variable governing number of filter threads. +int get_num_filter_threads(); +void set_num_filter_threads(int); SPColorInterpolation get_cairo_surface_ci(cairo_surface_t *surface); void set_cairo_surface_ci(cairo_surface_t *surface, SPColorInterpolation cif); @@ -101,6 +102,7 @@ void ink_cairo_set_source_rgba32(cairo_t *ct, guint32 rgba); void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m); void ink_cairo_pattern_set_matrix(cairo_pattern_t *cp, Geom::Affine const &m); void ink_cairo_set_hairline(cairo_t *ct); +void ink_cairo_set_dither(cairo_surface_t *surface, bool enabled); void ink_matrix_to_2geom(Geom::Affine &, cairo_matrix_t const &); void ink_matrix_to_cairo(cairo_matrix_t &, Geom::Affine const &); diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp index ef7a5815de03cd28fe1d65e6fcca2a6b45f5c50d..a3e11fffbef7ee4fb21250fb757e61d2f2428125 100644 --- a/src/display/drawing-group.cpp +++ b/src/display/drawing-group.cpp @@ -10,33 +10,28 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include "display/drawing-group.h" -#include "display/cairo-utils.h" -#include "display/drawing-context.h" -#include "display/drawing-item.h" -#include "display/drawing-surface.h" -#include "display/drawing-text.h" -#include "display/drawing.h" +#include "drawing-group.h" +#include "cairo-utils.h" +#include "drawing-context.h" +#include "drawing-surface.h" +#include "drawing-text.h" +#include "drawing.h" #include "style.h" namespace Inkscape { DrawingGroup::DrawingGroup(Drawing &drawing) - : DrawingItem(drawing) -{} - -DrawingGroup::~DrawingGroup() -{ -} + : DrawingItem(drawing) {} /** * Set whether the group returns children from pick calls. * Previously this feature was called "transparent groups". */ -void -DrawingGroup::setPickChildren(bool p) +void DrawingGroup::setPickChildren(bool pick_children) { - _pick_children = p; + defer([=] { + _pick_children = pick_children; + }); } /** @@ -44,25 +39,16 @@ DrawingGroup::setPickChildren(bool p) * This is applied after the normal transform and mainly useful for * markers, clipping paths, etc. */ -void DrawingGroup::setChildTransform(Geom::Affine const &new_trans) +void DrawingGroup::setChildTransform(Geom::Affine const &transform) { - double constexpr EPS = 1e-18; - - Geom::Affine current; - if (_child_transform) { - current = *_child_transform; - } - - if (!Geom::are_near(current, new_trans, EPS)) { - // mark the area where the object was for redraw. + defer([=] { + auto constexpr EPS = 1e-18; + auto current = _child_transform ? *_child_transform : Geom::identity(); + if (Geom::are_near(transform, current, EPS)) return; _markForRendering(); - if (new_trans.isIdentity(EPS)) { - _child_transform.reset(); - } else { - _child_transform = std::make_unique(new_trans); - } + _child_transform = transform.isIdentity(EPS) ? nullptr : std::make_unique(transform); _markForUpdate(STATE_ALL, true); - } + }); } unsigned DrawingGroup::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) @@ -132,12 +118,7 @@ bool DrawingGroup::_canClip() return true; } -bool is_drawing_group(DrawingItem *item) -{ - return dynamic_cast(item); -} - -} // end namespace Inkscape +} // namespace Inkscape /* Local Variables: diff --git a/src/display/drawing-group.h b/src/display/drawing-group.h index 7e84b711fe1b39ebe0a9a8468f8459d085b30e3e..88464d0dca8b998fe77276662a78ff46f02bd112 100644 --- a/src/display/drawing-group.h +++ b/src/display/drawing-group.h @@ -10,8 +10,8 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_GROUP_H -#define SEEN_INKSCAPE_DISPLAY_DRAWING_GROUP_H +#ifndef INKSCAPE_DISPLAY_DRAWING_GROUP_H +#define INKSCAPE_DISPLAY_DRAWING_GROUP_H #include "display/drawing-item.h" @@ -22,14 +22,15 @@ class DrawingGroup { public: DrawingGroup(Drawing &drawing); - ~DrawingGroup() override; bool pickChildren() { return _pick_children; } - void setPickChildren(bool p); + void setPickChildren(bool); - void setChildTransform(Geom::Affine const &new_trans); + void setChildTransform(Geom::Affine const &); protected: + ~DrawingGroup() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, @@ -41,11 +42,9 @@ protected: std::unique_ptr _child_transform; }; -bool is_drawing_group(DrawingItem *item); - -} // end namespace Inkscape +} // namespace Inkscape -#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#endif // INKSCAPE_DISPLAY_DRAWING_GROUP_H /* Local Variables: diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp index d2117708919112d19e09ed935db84d2750b9c7b2..011293e6b20b2abd1b4a3dd7d7c5eac6103c6725 100644 --- a/src/display/drawing-image.cpp +++ b/src/display/drawing-image.cpp @@ -12,11 +12,10 @@ #include <2geom/bezier-curve.h> -#include "display/drawing.h" -#include "display/drawing-context.h" -#include "display/drawing-image.h" - -#include "display/cairo-utils.h" +#include "drawing.h" +#include "drawing-context.h" +#include "drawing-image.h" +#include "cairo-utils.h" #include "cairo-templates.h" namespace Inkscape { @@ -24,36 +23,39 @@ namespace Inkscape { DrawingImage::DrawingImage(Drawing &drawing) : DrawingItem(drawing) , style_image_rendering(SP_CSS_IMAGE_RENDERING_AUTO) - , _pixbuf(nullptr) -{ -} - -DrawingImage::~DrawingImage() { } -void DrawingImage::setPixbuf(std::shared_ptr pb) +void DrawingImage::setPixbuf(std::shared_ptr pixbuf) { - _pixbuf = std::move(pb); - _markForUpdate(STATE_ALL, false); + defer([this, pixbuf = std::move(pixbuf)] () mutable { + _pixbuf = std::move(pixbuf); + _markForUpdate(STATE_ALL, false); + }); } void DrawingImage::setScale(double sx, double sy) { - _scale = Geom::Scale(sx, sy); - _markForUpdate(STATE_ALL, false); + defer([=] { + _scale = Geom::Scale(sx, sy); + _markForUpdate(STATE_ALL, false); + }); } -void DrawingImage::setOrigin(Geom::Point const &o) +void DrawingImage::setOrigin(Geom::Point const &origin) { - _origin = o; - _markForUpdate(STATE_ALL, false); + defer([=] { + _origin = origin; + _markForUpdate(STATE_ALL, false); + }); } void DrawingImage::setClipbox(Geom::Rect const &box) { - _clipbox = box; - _markForUpdate(STATE_ALL, false); + defer([=] { + _clipbox = box; + _markForUpdate(STATE_ALL, false); + }); } Geom::Rect DrawingImage::bounds() const @@ -75,11 +77,15 @@ Geom::Rect DrawingImage::bounds() const void DrawingImage::setStyle(SPStyle const *style, SPStyle const *context_style) { DrawingItem::setStyle(style, context_style); + + auto image_rendering = SP_CSS_IMAGE_RENDERING_AUTO; if (_style) { - style_image_rendering = _style->image_rendering.computed; - } else { - style_image_rendering = SP_CSS_IMAGE_RENDERING_AUTO; + image_rendering = _style->image_rendering.computed; } + + defer([=] { + style_image_rendering = image_rendering; + }); } unsigned DrawingImage::_updateItem(Geom::IntRect const &, UpdateContext const &, unsigned, unsigned) diff --git a/src/display/drawing-image.h b/src/display/drawing-image.h index c909ab3a7beb7456e8b8f5126b39f7e2c183e680..79b9ea968dfcbee22597edc527f5304764f1dff0 100644 --- a/src/display/drawing-image.h +++ b/src/display/drawing-image.h @@ -10,14 +10,13 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_IMAGE_H -#define SEEN_INKSCAPE_DISPLAY_DRAWING_IMAGE_H +#ifndef INKSCAPE_DISPLAY_DRAWING_IMAGE_H +#define INKSCAPE_DISPLAY_DRAWING_IMAGE_H #include - -#include -#include #include <2geom/transforms.h> +#include +#include #include "display/drawing-item.h" @@ -29,7 +28,6 @@ class DrawingImage { public: DrawingImage(Drawing &drawing); - ~DrawingImage() override; void setStyle(SPStyle const *style, SPStyle const *context_style = nullptr) override; @@ -40,6 +38,8 @@ public: Geom::Rect bounds() const; protected: + ~DrawingImage() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) override; DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags) override; @@ -54,9 +54,9 @@ protected: Geom::Scale _scale; }; -} // end namespace Inkscape +} // namespace Inkscape -#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#endif // INKSCAPE_DISPLAY_DRAWING_IMAGE_H /* Local Variables: diff --git a/src/display/drawing-item-ptr.h b/src/display/drawing-item-ptr.h new file mode 100644 index 0000000000000000000000000000000000000000..070756794c457f5bc6ac54761af91557df9e354d --- /dev/null +++ b/src/display/drawing-item-ptr.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef INKSCAPE_DISPLAY_DRAWINGITEM_PTR_H +#define INKSCAPE_DISPLAY_DRAWINGITEM_PTR_H + +#include +#include + +namespace Inkscape { class DrawingItem; } + +/** + * Deleter object which calls the unlink() method of DrawingItem to schedule deferred destruction. + */ +struct UnlinkDeleter +{ + template + void operator()(T *t) + { + static_assert(std::is_base_of_v); + t->unlink(); + } +}; + +/** + * Smart pointer used by the Object Tree to hold items in the Display Tree, like std::unique_ptr. + * + * Upon deletion, the pointed-to object and its subtree will be destroyed immediately if not currently in use by a snapshot. + * Otherwise, destruction is deferred to after the snapshot is released. + */ +template +using DrawingItemPtr = std::unique_ptr; + +/** + * Convienence function to create a DrawingItemPtr, like std::make_unique. + */ +template +auto make_drawingitem(Args&&... args) +{ + return DrawingItemPtr(new T(std::forward(args)...)); +}; + +#endif // INKSCAPE_DISPLAY_DRAWINGITEM_PTR_H diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 4165b99075fac1098db34be941604b1110f462c9..b1dcd9ce9420e4d0a8df1abc9e4536ecbf45f098 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -71,7 +71,6 @@ DrawingItem::DrawingItem(Drawing &drawing) , _fill_pattern(nullptr) , _stroke_pattern(nullptr) , _item(nullptr) - , _cache(nullptr) , _state(0) , _child_type(ChildType::ORPHAN) , _background_new(0) @@ -93,62 +92,23 @@ DrawingItem::DrawingItem(Drawing &drawing) DrawingItem::~DrawingItem() { // Unactivate if active. - if (drawing().getCanvasItemDrawing()) { - if (drawing().getCanvasItemDrawing()->get_active() == this) { - drawing().getCanvasItemDrawing()->set_active(nullptr); + if (auto itemdrawing = _drawing.getCanvasItemDrawing()) { + if (itemdrawing->get_active() == this) { + itemdrawing->set_active(nullptr); } } else { // Can happen, e.g. in Eraser tool. // std::cerr << "DrawingItem::~DrawingItem: Missing CanvasItemDrawing!" << std::endl; } - //if (!_children.empty()) { - // g_warning("Removing item with children"); - //} + // Remove from the set of cached items and delete cache. + _setCached(false, true); - // remove from the set of cached items and delete cache - setCached(false, true); - // remove this item from parent's children list - // due to the effect of clearChildren(), this only happens for the top-level deleted item - if (_parent) { - _markForRendering(); - } - switch (_child_type) { - case ChildType::NORMAL: { - ChildrenList::iterator ithis = _parent->_children.iterator_to(*this); - _parent->_children.erase(ithis); - } break; - case ChildType::CLIP: - // we cannot call setClip(NULL) or setMask(NULL), - // because that would be an endless loop - _parent->_clip = nullptr; - break; - case ChildType::MASK: - _parent->_mask = nullptr; - break; - case ChildType::ROOT: - _drawing._root = nullptr; - break; - case ChildType::FILL: - _parent->_fill_pattern = nullptr; - break; - case ChildType::STROKE: - _parent->_stroke_pattern = nullptr; - break; - default: ; - } - - if (_parent) { - bool propagate = _child_type == ChildType::CLIP || _child_type == ChildType::MASK; - _parent->_markForUpdate(STATE_ALL, propagate); - } - clearChildren(); - delete _stroke_pattern; - delete _fill_pattern; + _children.clear_and_dispose([] (auto c) { delete c; }); delete _clip; delete _mask; - _filter.reset(); - if (_style) sp_style_unref(_style); + delete static_cast(_fill_pattern); + delete static_cast(_stroke_pattern); } DrawingItem *DrawingItem::parent() const @@ -169,118 +129,119 @@ bool DrawingItem::isAncestorOf(DrawingItem *item) const void DrawingItem::appendChild(DrawingItem *item) { - item->_parent = this; + // Ok to perform non-deferred modification of child, because not part of rendering tree yet. assert(item->_child_type == ChildType::ORPHAN); + item->_parent = this; item->_child_type = ChildType::NORMAL; - _children.push_back(*item); - - // This ensures that _markForUpdate() called on the child will recurse to this item - item->_state = STATE_ALL; - // Because _markForUpdate recurses through ancestors, we can simply call it - // on the just-added child. This has the additional benefit that we do not - // rely on the appended child being in the default non-updated state. - // We set propagate to true, because the child might have descendants of its own. - item->_markForUpdate(STATE_ALL, true); + + defer([=] { + _children.push_back(*item); + + // This ensures that _markForUpdate() called on the child will recurse to this item + item->_state = STATE_ALL; + // Because _markForUpdate recurses through ancestors, we can simply call it + // on the just-added child. This has the additional benefit that we do not + // rely on the appended child being in the default non-updated state. + // We set propagate to true, because the child might have descendants of its own. + item->_markForUpdate(STATE_ALL, true); + }); } void DrawingItem::prependChild(DrawingItem *item) { - item->_parent = this; + // See appendChild for explanations. assert(item->_child_type == ChildType::ORPHAN); + item->_parent = this; item->_child_type = ChildType::NORMAL; - _children.push_front(*item); - // See appendChild for explanation - item->_state = STATE_ALL; - item->_markForUpdate(STATE_ALL, true); + + defer([=] { + _children.push_front(*item); + item->_state = STATE_ALL; + item->_markForUpdate(STATE_ALL, true); + }); } -/// Delete all regular children of this item (not mask or clip). +// Clear this node's ordinary children, deleting them and their descendants without otherwise changing them in any way. void DrawingItem::clearChildren() { - if (_children.empty()) return; - - _markForRendering(); - // prevent children from referencing the parent during deletion - // this way, children won't try to remove themselves from a list - // from which they have already been removed by clear_and_dispose - for (auto & i : _children) { - i._parent = NULL; - i._child_type = ChildType::ORPHAN; - } - _children.clear_and_dispose(DeleteDisposer()); - _markForUpdate(STATE_ALL, false); + defer([=] { + if (_children.empty()) return; + _markForRendering(); + _children.clear_and_dispose([] (auto c) { delete c; }); + _markForUpdate(STATE_ALL, false); + }); } -/// Set the incremental transform for this item -void DrawingItem::setTransform(Geom::Affine const &new_trans) +void DrawingItem::setTransform(Geom::Affine const &transform) { - double constexpr EPS = 1e-18; + defer([=] { + auto constexpr EPS = 1e-18; + auto current = _transform ? *_transform : Geom::identity(); + if (Geom::are_near(transform, current, EPS)) return; - Geom::Affine current; - if (_transform) { - current = *_transform; - } - - if (!Geom::are_near(current, new_trans, EPS)) { - // mark the area where the object was for redraw. _markForRendering(); - if (new_trans.isIdentity(EPS)) { - _transform.reset(); - } else { - _transform = std::make_unique(new_trans); - } + _transform = transform.isIdentity(EPS) ? nullptr : std::make_unique(transform); _markForUpdate(STATE_ALL, true); - } + }); } void DrawingItem::setOpacity(float opacity) { - if (_opacity != opacity) { + defer([=] { + if (opacity == _opacity) return; _opacity = opacity; _markForRendering(); - } + }); } -void DrawingItem::setAntialiasing(unsigned a) +void DrawingItem::setAntialiasing(unsigned antialias) { - if (_antialias != a) { - _antialias = a; + defer([=] { + if (_antialias == antialias) return; + _antialias = antialias; _markForRendering(); - } + }); } void DrawingItem::setIsolation(bool isolation) { - _isolation = isolation; - //if( isolation != 0 ) std::cout << "isolation: " << isolation << std::endl; - _markForRendering(); + defer([=] { + if (isolation == _isolation) return; + _isolation = isolation; + _markForRendering(); + }); } -void DrawingItem::setBlendMode(SPBlendMode mix_blend_mode) +void DrawingItem::setBlendMode(SPBlendMode blend_mode) { - _blend_mode = mix_blend_mode; - //if( mix_blend_mode != 0 ) std::cout << "setBlendMode: " << mix_blend_mode << std::endl; - _markForRendering(); + defer([=] { + if (blend_mode == _blend_mode) return; + _blend_mode = blend_mode; + _markForRendering(); + }); } -void DrawingItem::setVisible(bool v) +void DrawingItem::setVisible(bool visible) { - if (_visible != v) { - _visible = v; + defer([=] { + if (visible == _visible) return; + _visible = visible; _markForRendering(); - } + }); } void DrawingItem::setSensitive(bool sensitive) { - _sensitive = sensitive; + defer([=] { // Must be deferred, since in bitfield. + _sensitive = sensitive; + }); } /** * Enable / disable storing the rendering in memory. * Calling setCached(false, true) will also remove the persistent status */ -void DrawingItem::setCached(bool cached, bool persistent) +void DrawingItem::_setCached(bool cached, bool persistent) { static bool const cache_env = getenv("_INKSCAPE_DISABLE_CACHE"); if (cache_env) { @@ -302,8 +263,7 @@ void DrawingItem::setCached(bool cached, bool persistent) _drawing._cached_items.insert(this); } else { _drawing._cached_items.erase(this); - delete _cache; - _cache = nullptr; + _cache.reset(); if (_has_cache_iterator) { _drawing._candidate_items.erase(_cache_iterator); _has_cache_iterator = false; @@ -318,53 +278,47 @@ void DrawingItem::setCached(bool cached, bool persistent) */ void DrawingItem::setStyle(SPStyle const *style, SPStyle const *context_style) { - // std::cout << "DrawingItem::setStyle: " << name() << " " << style - // << " " << context_style << std::endl; - - _markForRendering(); + // Ok to not defer setting the style pointer, because the pointer itself is only read by SPObject-side code. + _style = style; + if (context_style) { + _context_style = context_style; + } else if (_parent) { + _context_style = _parent->_context_style; + } - if (style != _style) { - if (style) sp_style_ref(style); - if (_style) sp_style_unref(_style); - _style = style; + // Copy required information out of style. + bool background_new = false; + bool vector_effect_size = false; + bool vector_effect_rotate = false; + bool vector_effect_fixed = false; + if (style) { + background_new = style->enable_background.set && style->enable_background.value == SP_CSS_BACKGROUND_NEW; + vector_effect_size = _style->vector_effect.size; + vector_effect_rotate = _style->vector_effect.rotate; + vector_effect_fixed = _style->vector_effect.fixed; } - if (style && style->enable_background.set) { - bool _background_new_check = _background_new; - if (style->enable_background.value == SP_CSS_BACKGROUND_NEW) { - _background_new = true; - } - if (style->enable_background.value == SP_CSS_BACKGROUND_ACCUMULATE) { - _background_new = false; - } - if (_background_new_check != _background_new) { + // Defer setting the style information on the DrawingItem. + defer([=] { + _markForRendering(); + + if (background_new != _background_new) { + _background_new = background_new; _markForUpdate(STATE_BACKGROUND, true); } - } - - if (context_style != nullptr) { - _context_style = context_style; - } else if (_parent != nullptr) { - _context_style = _parent->_context_style; - } - if (_style) { - style_vector_effect_size = _style->vector_effect.size; - style_vector_effect_rotate = _style->vector_effect.rotate; - style_vector_effect_fixed = _style->vector_effect.fixed; - } else { - style_vector_effect_size = false; - style_vector_effect_rotate = false; - style_vector_effect_fixed = false; - } + style_vector_effect_size = vector_effect_size; + style_vector_effect_rotate = vector_effect_rotate; + style_vector_effect_fixed = vector_effect_fixed; - _markForUpdate(STATE_ALL, false); + _markForUpdate(STATE_ALL, false); + }); } /** * Recursively update children style. * The purpose of this call is to update fill and stroke for markers that have elements with - * fill/stroke property values of 'context-fill' or 'context-stroke'. Marker styling is not + * fill/stroke property values of 'context-fill' or 'context-stroke'. Marker styling is not * updated like other 'clones' as marker instances are not included the SP object tree. * Note: this is a virtual function. */ @@ -378,79 +332,97 @@ void DrawingItem::setChildrenStyle(SPStyle const *context_style) void DrawingItem::setClip(DrawingItem *item) { - _markForRendering(); - delete _clip; - _clip = item; if (item) { - item->_parent = this; assert(item->_child_type == ChildType::ORPHAN); + item->_parent = this; item->_child_type = ChildType::CLIP; } - _markForUpdate(STATE_ALL, true); + + defer([=] { + _markForRendering(); + delete _clip; + _clip = item; + _markForUpdate(STATE_ALL, true); + }); } void DrawingItem::setMask(DrawingItem *item) { - _markForRendering(); - delete _mask; - _mask = item; if (item) { - item->_parent = this; assert(item->_child_type == ChildType::ORPHAN); + item->_parent = this; item->_child_type = ChildType::MASK; } - _markForUpdate(STATE_ALL, true); + + defer([=] { + _markForRendering(); + delete _mask; + _mask = item; + _markForUpdate(STATE_ALL, true); + }); } void DrawingItem::setFillPattern(DrawingPattern *pattern) { - _markForRendering(); - delete _fill_pattern; - _fill_pattern = pattern; if (pattern) { - pattern->_parent = this; assert(pattern->_child_type == ChildType::ORPHAN); + pattern->_parent = this; pattern->_child_type = ChildType::FILL; } - _markForUpdate(STATE_ALL, false); + + defer([=] { + _markForRendering(); + delete static_cast(_fill_pattern); + _fill_pattern = pattern; + _markForUpdate(STATE_ALL, false); + }); } void DrawingItem::setStrokePattern(DrawingPattern *pattern) { - _markForRendering(); - delete _stroke_pattern; - _stroke_pattern = pattern; if (pattern) { - pattern->_parent = this; assert(pattern->_child_type == ChildType::ORPHAN); + pattern->_parent = this; pattern->_child_type = ChildType::STROKE; } - _markForUpdate(STATE_ALL, false); + + defer([=] { + _markForRendering(); + delete static_cast(_stroke_pattern); + _stroke_pattern = pattern; + _markForUpdate(STATE_ALL, false); + }); } -/// Move this item to the given place in the Z order of siblings. -/// Does nothing if the item has no parent. -void DrawingItem::setZOrder(unsigned z) +/// Move this item to the given place in the Z order of siblings. Does nothing if the item is not a normal child. +void DrawingItem::setZOrder(unsigned zorder) { - if (!_parent) return; + if (_child_type != ChildType::NORMAL) return; - ChildrenList::iterator it = _parent->_children.iterator_to(*this); - _parent->_children.erase(it); + defer([=] { + auto it = _parent->_children.iterator_to(*this); + _parent->_children.erase(it); - ChildrenList::iterator i = _parent->_children.begin(); - std::advance(i, std::min(z, unsigned(_parent->_children.size()))); - _parent->_children.insert(i, *this); - _markForRendering(); + auto it2 = _parent->_children.begin(); + std::advance(it2, std::min(zorder, _parent->_children.size())); + _parent->_children.insert(it2, *this); + _markForRendering(); + }); } void DrawingItem::setItemBounds(Geom::OptRect const &bounds) { - _item_bbox = bounds; + defer([=] { + _item_bbox = bounds; + }); } -void DrawingItem::setFilterRenderer(std::unique_ptr renderer) +void DrawingItem::setFilterRenderer(std::unique_ptr filter) { - _filter = std::move(renderer); + defer([=, filter = std::move(filter)] () mutable { + _filter = std::move(filter); + _markForRendering(); + }); } /** @@ -620,7 +592,7 @@ void DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, un // Destroy cache for this item - outside of canvas or invisible. // The opposite transition (invisible -> visible or object // entering the canvas) is handled during the render phase - setCached(false, true); + _setCached(false, true); } } } @@ -634,7 +606,7 @@ void DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, un if (_stroke_pattern) { _stroke_pattern->update(area, child_ctx, flags, reset); } - if (!is_drawing_group(this) || (_filter && filters)) { + if (!dynamic_cast(this) || (_filter && filters)) { _markForRendering(); } } @@ -704,9 +676,9 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec iarea = carea; _filter->area_enlarge(*iarea, this); iarea.intersectWith(_drawbox); - setCached(false, true); + _setCached(false, true); } else { - setCached(true, true); + _setCached(true, true); } } // carea is the area to paint @@ -722,8 +694,7 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec // Bypass in case of pattern, see below. if (_cached && !(flags & RENDER_BYPASS_CACHE)) { if (_cache && _cache->device_scale() != device_scale) { - delete _cache; - _cache = nullptr; + _cache.reset(); } if (_cache) { @@ -741,7 +712,7 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec Geom::OptIntRect cl = _cacheRect(); if (!cl) cl = carea; - _cache = new DrawingCache(*cl, device_scale); + _cache = std::make_unique(*cl, device_scale); } } else { // if our caching was turned off after the last update, it was already deleted in setCached() @@ -757,7 +728,7 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec || _isolation == SP_CSS_ISOLATION_ISOLATE // 6. it is isolated || _child_type == ChildType::ROOT; // 7. is root, need isolation from background if (_prev_nir && !needs_intermediate_rendering) { - setCached(false, true); + _setCached(false, true); } _prev_nir = needs_intermediate_rendering; needs_intermediate_rendering |= !!_cache; // 8. it is to be cached @@ -787,6 +758,7 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec DrawingSurface intermediate(*carea, device_scale); DrawingContext ict(intermediate); cairo_set_antialias(ict.raw(), cairo_get_antialias(dc.raw())); // propagate antialias setting + ink_cairo_set_dither(intermediate.raw(), _drawing.useDithering()); // This path fails for patterns/hatches when stepping the pattern to handle overflows. // The offsets are applied to drawing context (dc) but they are not copied to the @@ -845,6 +817,7 @@ unsigned DrawingItem::render(DrawingContext &dc, RenderContext &rc, Geom::IntRec if (bg_root) { DrawingSurface bg(*carea, device_scale); DrawingContext bgdc(bg); + ink_cairo_set_dither(bg.raw(), _drawing.useDithering()); bg_root->render(bgdc, rc, *carea, flags | RENDER_FILTER_BACKGROUND, this); _filter->render(this, ict, &bgdc, rc); rendered = true; @@ -1016,7 +989,7 @@ DrawingItem *DrawingItem::pick(Geom::Point const &p, double delta, unsigned flag } // For debugging -Glib::ustring DrawingItem::name() +Glib::ustring DrawingItem::name() const { if (_item) { if (_item->getId()) @@ -1029,7 +1002,7 @@ Glib::ustring DrawingItem::name() } // For debugging: Print drawing tree structure. -void DrawingItem::recursivePrintTree(unsigned level) +void DrawingItem::recursivePrintTree(unsigned level) const { if (level == 0) { std::cout << "Display Item Tree" << std::endl; @@ -1225,6 +1198,49 @@ void apply_antialias(DrawingContext &dc, int antialias) } } +// Remove this node from its parent, then delete it. +void DrawingItem::unlink() +{ + defer([=] { + // This only happens for the top-level deleted item. + if (_parent) { + _markForRendering(); + } + + switch (_child_type) { + case ChildType::NORMAL: { + auto it = _parent->_children.iterator_to(*this); + _parent->_children.erase(it); + break; + } + case ChildType::CLIP: + _parent->_clip = nullptr; + break; + case ChildType::MASK: + _parent->_mask = nullptr; + break; + case ChildType::FILL: + _parent->_fill_pattern = nullptr; + break; + case ChildType::STROKE: + _parent->_stroke_pattern = nullptr; + break; + case ChildType::ROOT: + _drawing._root = nullptr; + break; + default: + break; + } + + if (_parent) { + bool propagate = _child_type == ChildType::CLIP || _child_type == ChildType::MASK; + _parent->_markForUpdate(STATE_ALL, propagate); + } + + delete this; + }); +} + } // namespace Inkscape /* diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index b4b2a821d9c7d43dbaedf85168d99aa056832829..720db81c2c70d86c703368a3938ab11a8616f12d 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -26,10 +26,7 @@ #include "style-enums.h" -namespace Glib { -class ustring; -} // namespace Glib - +namespace Glib { class ustring; } class SPStyle; class SPItem; @@ -40,11 +37,7 @@ class DrawingCache; class DrawingItem; class DrawingPattern; class DrawingContext; -class RenderContext; - -namespace Filters { -class Filter; -} // namespace Filters +namespace Filters { class Filter; } struct RenderContext { @@ -72,7 +65,7 @@ struct InvalidItemException : std::exception char const *what() const noexcept override { return "Invalid item in drawing"; } }; -class DrawingItem : boost::noncopyable +class DrawingItem { public: enum RenderFlags @@ -104,7 +97,9 @@ public: }; DrawingItem(Drawing &drawing); - virtual ~DrawingItem(); + DrawingItem(DrawingItem const &) = delete; + DrawingItem &operator=(DrawingItem const &) = delete; + void unlink(); /// Unlink this node and its subtree from the rendering tree and destroy. Geom::OptIntRect const &bbox() const { return _bbox; } Geom::OptIntRect const &drawbox() const { return _drawbox; } @@ -120,16 +115,14 @@ public: void clearChildren(); bool visible() const { return _visible; } - void setVisible(bool v); + void setVisible(bool visible); bool sensitive() const { return _sensitive; } - void setSensitive(bool v); - bool cached() const { return _cached; } - void setCached(bool c, bool persistent = false); + void setSensitive(bool sensitive); virtual void setStyle(SPStyle const *style, SPStyle const *context_style = nullptr); virtual void setChildrenStyle(SPStyle const *context_style); void setOpacity(float opacity); - void setAntialiasing(unsigned a); + void setAntialiasing(unsigned antialias); unsigned antialiasing() const { return _antialias; } void setIsolation(bool isolation); // CSS Compositing and Blending void setBlendMode(SPBlendMode blend_mode); @@ -138,9 +131,8 @@ public: void setMask(DrawingItem *item); void setFillPattern(DrawingPattern *pattern); void setStrokePattern(DrawingPattern *pattern); - void setZOrder(unsigned z); + void setZOrder(unsigned zorder); void setItemBounds(Geom::OptRect const &bounds); - void setFilterBounds(Geom::OptRect const &bounds); void setFilterRenderer(std::unique_ptr renderer); void setKey(unsigned key) { _key = key; } @@ -153,8 +145,8 @@ public: void clip(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area); DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags = 0); - virtual Glib::ustring name(); // For debugging - void recursivePrintTree(unsigned level = 0); // For debugging + Glib::ustring name() const; // For debugging + void recursivePrintTree(unsigned level = 0) const; // For debugging protected: enum class ChildType : unsigned char @@ -172,12 +164,14 @@ protected: RENDER_OK = 0, RENDER_STOP = 1 }; + virtual ~DrawingItem(); // Private to prevent deletion of items that are still in use by a snapshot. void _renderOutline(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags); void _markForUpdate(unsigned state, bool propagate); void _markForRendering(); void _invalidateFilterBackground(Geom::IntRect const &area); double _cacheScore(); Geom::OptIntRect _cacheRect(); + void _setCached(bool cached, bool persistent = false); virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) { return 0; } virtual unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, @@ -187,27 +181,25 @@ protected: virtual bool _canClip() { return false; } virtual void _dropPatternCache() {} - // member variables start here - Drawing &_drawing; DrawingItem *_parent; - typedef boost::intrusive::list_member_hook<> ListHook; + using ListHook = boost::intrusive::list_member_hook<>; ListHook _child_hook; - typedef boost::intrusive::list< + using ChildrenList = boost::intrusive::list< DrawingItem, boost::intrusive::member_hook - > ChildrenList; + >; ChildrenList _children; - unsigned _key; ///< Some SPItems can have more than one DrawingItem; - /// this value is a hack used to distinguish between them + // Todo: Try to get rid of all of these variables, moving them into the object tree. + unsigned _key; ///< Auxiliary key used by the object tree for showing clips/masks/patterns. + SPItem *_item; ///< Used to associate DrawingItems with SPItems that created them SPStyle const *_style; // Not used by DrawingGlyphs SPStyle const *_context_style; // Used for 'context-fill', 'context-stroke' - - float _opacity; + float _opacity; std::unique_ptr _transform; ///< Incremental transform from parent to this item's coords Geom::Affine _ctm; ///< Total transform from item coords to display coords Geom::OptIntRect _bbox; ///< Bounding box in display (pixel) coords including stroke @@ -221,8 +213,7 @@ protected: DrawingPattern *_fill_pattern; DrawingPattern *_stroke_pattern; std::unique_ptr _filter; - SPItem *_item; ///< Used to associate DrawingItems with SPItems that created them - DrawingCache *_cache; + std::unique_ptr _cache; bool _prev_nir = false; CacheList::iterator _cache_iterator; @@ -250,12 +241,15 @@ protected: bool _isolation : 1; SPBlendMode _blend_mode; - friend class Drawing; -}; + template + void defer(F &&f) + { + // Introduce artificial dependence on a template parameter to allow definition with Drawing forward-declared. + auto &drawing = static_cast 0), Drawing&>>(_drawing); + drawing.defer(std::forward(f)); + } -struct DeleteDisposer -{ - void operator()(DrawingItem *item) { delete item; } + friend class Drawing; }; /// Apply antialias setting to Cairo. diff --git a/src/display/drawing-pattern.cpp b/src/display/drawing-pattern.cpp index cae2b829f16199eb4a3b858c1dcc98992076b48c..28a3700deac29f9fbe874308df2425ca6ab20c5f 100644 --- a/src/display/drawing-pattern.cpp +++ b/src/display/drawing-pattern.cpp @@ -11,27 +11,22 @@ */ #include -#include "preferences.h" -#include "display/cairo-utils.h" -#include "display/drawing-context.h" -#include "display/drawing-pattern.h" -#include "display/drawing-surface.h" +#include "cairo-utils.h" +#include "drawing-context.h" +#include "drawing-pattern.h" +#include "drawing-surface.h" +#include "drawing.h" #include "helper/geom.h" #include "ui/util.h" namespace Inkscape { -DrawingPattern::Surface::Surface(Geom::IntRect const &rect, int device_scale) +DrawingPattern::Surface::Surface(Geom::IntRect const &rect, int device_scale, bool dither) : rect(rect) , surface(Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, rect.width() * device_scale, rect.height() * device_scale)) { cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale); - #ifdef CAIRO_HAS_DITHER - auto prefs = Inkscape::Preferences::get(); - if (prefs->getBool("/options/dithering/value", true)) { - cairo_image_surface_set_dither(surface->cobj(), CAIRO_DITHER_BEST); - } - #endif + ink_cairo_set_dither(surface->cobj(), dither); } DrawingPattern::DrawingPattern(Drawing &drawing) @@ -40,42 +35,33 @@ DrawingPattern::DrawingPattern(Drawing &drawing) { } -DrawingPattern::~DrawingPattern() -{ -} - -void DrawingPattern::setPatternToUserTransform(Geom::Affine const &new_trans) +void DrawingPattern::setPatternToUserTransform(Geom::Affine const &transform) { - double constexpr EPS = 1e-18; - - Geom::Affine current; - if (_pattern_to_user) { - current = *_pattern_to_user; - } - - if (!Geom::are_near(current, new_trans, EPS)) { - // mark the area where the object was for redraw. + defer([=] { + auto constexpr EPS = 1e-18; + auto current = _pattern_to_user ? *_pattern_to_user : Geom::identity(); + if (Geom::are_near(transform, current, EPS)) return; _markForRendering(); - if (new_trans.isIdentity(EPS)) { - _pattern_to_user.reset(); - } else { - _pattern_to_user = std::make_unique(new_trans); - } + _pattern_to_user = transform.isIdentity(EPS) ? nullptr : std::make_unique(transform); _markForUpdate(STATE_ALL, true); - } + }); } void DrawingPattern::setTileRect(Geom::Rect const &tile_rect) { - _tile_rect = tile_rect; - _markForUpdate(STATE_ALL, true); + defer([=] { + _tile_rect = tile_rect; + _markForUpdate(STATE_ALL, true); + }); } void DrawingPattern::setOverflow(Geom::Affine const &initial_transform, int steps, Geom::Affine const &step_transform) { - _overflow_initial_transform = initial_transform; - _overflow_steps = steps; - _overflow_step_transform = step_transform; + defer([=] { + _overflow_initial_transform = initial_transform; + _overflow_steps = steps; + _overflow_step_transform = step_transform; + }); } cairo_pattern_t *DrawingPattern::renderPattern(RenderContext &rc, Geom::IntRect const &area, float opacity, int device_scale) @@ -168,7 +154,7 @@ cairo_pattern_t *DrawingPattern::renderPattern(RenderContext &rc, Geom::IntRect } // Create a new surface covering the expanded rectangle. - auto surface = Surface(expanded, device_scale); + auto surface = Surface(expanded, device_scale, _drawing.useDithering()); auto cr = Cairo::Context::create(surface.surface); cr->translate(-surface.rect.left(), -surface.rect.top()); diff --git a/src/display/drawing-pattern.h b/src/display/drawing-pattern.h index e09f2b527a7030263238f228656d98b8dfce2dbc..cead1866859bd562890df6d0a626fda6d223e4de 100644 --- a/src/display/drawing-pattern.h +++ b/src/display/drawing-pattern.h @@ -34,17 +34,18 @@ class DrawingPattern { public: DrawingPattern(Drawing &drawing); - ~DrawingPattern() override; /** * Set the transformation from pattern to user coordinate systems. * @see SPPattern description for explanation of coordinate systems. */ - void setPatternToUserTransform(Geom::Affine const &new_trans); + void setPatternToUserTransform(Geom::Affine const &); + /** * Set the tile rect position and dimensions in content coordinate system */ - void setTileRect(Geom::Rect const &tile_rect); + void setTileRect(Geom::Rect const &); + /** * Turn on overflow rendering. * @@ -52,6 +53,7 @@ public: * a translation transform is applied. */ void setOverflow(Geom::Affine const &initial_transform, int steps, Geom::Affine const &step_transform); + /** * Render the pattern. * @@ -60,6 +62,8 @@ public: cairo_pattern_t *renderPattern(RenderContext &rc, Geom::IntRect const &area, float opacity, int device_scale); protected: + ~DrawingPattern() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; void _dropPatternCache() override; @@ -78,7 +82,7 @@ protected: struct Surface { - Surface(Geom::IntRect const &rect, int device_scale); + Surface(Geom::IntRect const &rect, int device_scale, bool dither); Geom::IntRect rect; Cairo::RefPtr surface; }; @@ -87,8 +91,6 @@ protected: std::vector surfaces; }; -bool is_drawing_group(DrawingItem const *item); - } // namespace Inkscape #endif // INKSCAPE_DISPLAY_DRAWING_PATTERN_H diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp index c4b782c7b2c498c1ec12d89f159843adfbd28cb9..b031277a05fb27e85d23a33160df9d54d838e5ce 100644 --- a/src/display/drawing-shape.cpp +++ b/src/display/drawing-shape.cpp @@ -16,16 +16,15 @@ #include <2geom/path-sink.h> #include <2geom/svg-path-parser.h> -#include "drawing-shape.h" - #include "style.h" -#include "display/cairo-utils.h" -#include "display/curve.h" -#include "display/drawing.h" -#include "display/drawing-context.h" -#include "display/drawing-group.h" -#include "display/control/canvas-item-drawing.h" +#include "cairo-utils.h" +#include "curve.h" +#include "drawing.h" +#include "drawing-context.h" +#include "drawing-group.h" +#include "drawing-shape.h" +#include "control/canvas-item-drawing.h" #include "helper/geom-curves.h" #include "helper/geom.h" @@ -47,40 +46,49 @@ DrawingShape::DrawingShape(Drawing &drawing) { } -DrawingShape::~DrawingShape() -{ -} - void DrawingShape::setPath(std::shared_ptr curve) { - _markForRendering(); - _curve = std::move(curve); - _markForUpdate(STATE_ALL, false); + defer([this, curve = std::move(curve)] () mutable { + _markForRendering(); + _curve = std::move(curve); + _markForUpdate(STATE_ALL, false); + }); } void DrawingShape::setStyle(SPStyle const *style, SPStyle const *context_style) { DrawingItem::setStyle(style, context_style); - _nrstyle.set(_style, _context_style); - if (_style) { - style_vector_effect_stroke = _style->vector_effect.stroke; - style_stroke_extensions_hairline = _style->stroke_extensions.hairline; - style_clip_rule = _style->clip_rule.computed; - style_fill_rule = _style->fill_rule.computed; - style_opacity = _style->opacity.value; - } else { - style_vector_effect_stroke = false; - style_stroke_extensions_hairline = false; - style_clip_rule = SP_WIND_RULE_EVENODD; - style_fill_rule = SP_WIND_RULE_EVENODD; - style_opacity = SP_SCALE24_MAX; + + auto vector_effect_stroke = false; + auto stroke_extensions_hairline = false; + auto clip_rule = SP_WIND_RULE_EVENODD; + auto fill_rule = SP_WIND_RULE_EVENODD; + auto opacity = SP_SCALE24_MAX; + if (style) { + vector_effect_stroke = style->vector_effect.stroke; + stroke_extensions_hairline = style->stroke_extensions.hairline; + clip_rule = style->clip_rule.value; + fill_rule = style->fill_rule.value; + opacity = style->opacity.value; } + + defer([=, nrstyle = NRStyle(_style)] () mutable { + _nrstyle = std::move(nrstyle); + style_vector_effect_stroke = vector_effect_stroke; + style_stroke_extensions_hairline = stroke_extensions_hairline; + style_clip_rule = clip_rule; + style_fill_rule = fill_rule; + style_opacity = opacity; + }); } void DrawingShape::setChildrenStyle(SPStyle const *context_style) { DrawingItem::setChildrenStyle(context_style); - _nrstyle.set(_style, _context_style); + + defer([this, nrstyle = NRStyle(_style, _context_style)] () mutable { + _nrstyle = std::move(nrstyle); + }); } unsigned DrawingShape::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h index 669c953e52beb45ec3d9fcd7aa86718f38f607d4..2a7fbba2b59fcb0547b17d1ce812edb62f940092 100644 --- a/src/display/drawing-shape.h +++ b/src/display/drawing-shape.h @@ -10,14 +10,12 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_SHAPE_H -#define SEEN_INKSCAPE_DISPLAY_DRAWING_SHAPE_H +#ifndef INKSCAPE_DISPLAY_DRAWING_SHAPE_H +#define INKSCAPE_DISPLAY_DRAWING_SHAPE_H #include "display/drawing-item.h" #include "display/nr-style.h" -#include - class SPStyle; class SPCurve; @@ -28,13 +26,14 @@ class DrawingShape { public: DrawingShape(Drawing &drawing); - ~DrawingShape() override; void setPath(std::shared_ptr curve); void setStyle(SPStyle const *style, SPStyle const *context_style = nullptr) override; void setChildrenStyle(SPStyle const *context_style) override; protected: + ~DrawingShape() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at) override; void _clipItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area) override; @@ -58,9 +57,9 @@ protected: unsigned _repick_after; }; -} // end namespace Inkscape +} // namespace Inkscape -#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#endif // INKSCAPE_DISPLAY_DRAWING_SHAPE_H /* Local Variables: diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp index ae27a58a2a82e35bdbe7ae59df7e6370b5ca67ed..da7eba9e18d0db3d1d8740cdd56caec7fd8a3e84 100644 --- a/src/display/drawing-surface.cpp +++ b/src/display/drawing-surface.cpp @@ -15,7 +15,6 @@ #include "3rdparty/cairo/src/cairo.h" #endif -#include "preferences.h" #include "display/drawing-surface.h" #include "display/drawing-context.h" #include "display/cairo-utils.h" @@ -187,11 +186,6 @@ DrawingSurface::createRawContext() _pixels[X] * _device_scale, _pixels[Y] * _device_scale); cairo_surface_set_device_scale(_surface, _device_scale, _device_scale); -#ifdef CAIRO_HAS_DITHER - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if(prefs->getBool("/options/dithering/value", true)) - cairo_image_surface_set_dither(_surface, CAIRO_DITHER_BEST); -#endif } cairo_t *ct = cairo_create(_surface); if (_scale != Geom::Scale::identity()) { diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index c6892116da8443a90129503ac06442b4ae5ec963..73d0e6fab2e08c5fe647d67e3d98bfde27c2fd76 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -14,11 +14,11 @@ #include "style.h" -#include "display/cairo-utils.h" -#include "display/drawing-context.h" -#include "display/drawing-surface.h" -#include "display/drawing-text.h" -#include "display/drawing.h" +#include "cairo-utils.h" +#include "drawing-context.h" +#include "drawing-surface.h" +#include "drawing-text.h" +#include "drawing.h" #include "helper/geom.h" @@ -29,38 +29,42 @@ namespace Inkscape { DrawingGlyphs::DrawingGlyphs(Drawing &drawing) : DrawingItem(drawing) - , _font(nullptr) , _glyph(0) { } -DrawingGlyphs::~DrawingGlyphs() -{ -} - void DrawingGlyphs::setGlyph(std::shared_ptr font, int glyph, Geom::Affine const &trans) { - _markForRendering(); + defer([=, font = std::move(font)] { + _markForRendering(); - setTransform(trans); + assert(!_drawing.snapshotted()); + setTransform(trans); - _font = std::move(font); - _glyph = glyph; + _font_data = font->share_data(); + _glyph = glyph; - // Load pathvectors and pixbufs in advance. - if (_font) { - pathvec = _font->PathVector(_glyph); - pathvec_ref = _font->PathVector(42); + design_units = 1.0; + pathvec = nullptr; + pathvec_ref = nullptr; + pixbuf = nullptr; - if (_font->FontHasSVG()) { - pixbuf = _font->PixBuf(_glyph); + // Load pathvectors and pixbufs in advance, as must be done on main thread. + if (font) { + design_units = font->GetDesignUnits(); + pathvec = font->PathVector(_glyph); + pathvec_ref = font->PathVector(42); + + if (font->FontHasSVG()) { + pixbuf = font->PixBuf(_glyph); + } } - } - _markForUpdate(STATE_ALL, false); + _markForUpdate(STATE_ALL, false); + }); } -void DrawingGlyphs::setStyle(SPStyle const */*style*/, SPStyle const */*context_style*/) +void DrawingGlyphs::setStyle(SPStyle const *, SPStyle const *) { std::cerr << "DrawingGlyphs: Use parent style" << std::endl; } @@ -72,7 +76,7 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext throw InvalidItemException(); } - if (!_font) { + if (!pathvec) { return STATE_ALL; } @@ -196,7 +200,7 @@ DrawingItem *DrawingGlyphs::_pickItem(Geom::Point const &p, double /*delta*/, un ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE; bool outline = flags & PICK_OUTLINE; - if (_font && _bbox && (outline || !invisible)) { + if (pathvec && _bbox && (outline || !invisible)) { // With text we take a simple approach: pick if the point is in a character bbox Geom::Rect expanded(_pick_bbox); // FIXME, why expand by delta? When is the next line needed? @@ -216,53 +220,57 @@ DrawingText::DrawingText(Drawing &drawing) { } -DrawingText::~DrawingText() = default; - -void DrawingText::clear() -{ - _markForRendering(); - _children.clear_and_dispose(DeleteDisposer()); -} - bool DrawingText::addComponent(std::shared_ptr const &font, int glyph, Geom::Affine const &trans, float width, float ascent, float descent, float phase_length) { -/* original, did not save a glyph for white space characters, causes problems for text-decoration - if (!font || !font->PathVector(glyph)) { + // original, did not save a glyph for white space characters, causes problems for text-decoration + /*if (!font || !font->PathVector(glyph)) { return false; - } -*/ + }*/ if (!font) return false; - _markForRendering(); - DrawingGlyphs *ng = new DrawingGlyphs(_drawing); - ng->setGlyph(font, glyph, trans); - ng->_width = width; // used especially when _drawable = false, otherwise, it is the advance of the font - ng->_asc = ascent; // of font, not of this one character - ng->_dsc = descent; // of font, not of this one character - ng->_pl = phase_length; // used for phase of dots, dashes, and wavy - appendChild(ng); + defer([=, font = std::move(font)] () mutable { + _markForRendering(); + auto ng = new DrawingGlyphs(_drawing); + assert(!_drawing.snapshotted()); + ng->setGlyph(font, glyph, trans); + ng->_width = width; // used especially when _drawable = false, otherwise, it is the advance of the font + ng->_asc = ascent; // of font, not of this one character + ng->_dsc = descent; // of font, not of this one character + ng->_pl = phase_length; // used for phase of dots, dashes, and wavy + appendChild(ng); + }); + return true; } void DrawingText::setStyle(SPStyle const *style, SPStyle const *context_style) { DrawingGroup::setStyle(style, context_style); - _nrstyle.set(_style, _context_style); + + auto vector_effect_stroke = false; + auto stroke_extensions_hairline = false; + auto clip_rule = SP_WIND_RULE_EVENODD; if (_style) { - style_vector_effect_stroke = _style->vector_effect.stroke; - style_stroke_extensions_hairline = _style->stroke_extensions.hairline; - style_clip_rule = _style->clip_rule.computed; - } else { - style_vector_effect_stroke = false; - style_stroke_extensions_hairline = false; - style_clip_rule = SP_WIND_RULE_EVENODD; + vector_effect_stroke = _style->vector_effect.stroke; + stroke_extensions_hairline = _style->stroke_extensions.hairline; + clip_rule = _style->clip_rule.computed; } + + defer([=, nrstyle = NRStyle(_style, _context_style)] () mutable { + _nrstyle = std::move(nrstyle); + style_vector_effect_stroke = vector_effect_stroke; + style_stroke_extensions_hairline = stroke_extensions_hairline; + style_clip_rule = clip_rule; + }); } void DrawingText::setChildrenStyle(SPStyle const *context_style) { DrawingGroup::setChildrenStyle(context_style); - _nrstyle.set(_style, _context_style); + + defer([this, nrstyle = NRStyle(_style, _context_style)] () mutable { + _nrstyle = std::move(nrstyle); + }); } unsigned DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) @@ -606,29 +614,25 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, RenderContext &rc, Geom::I if (g->_ctm.isSingular()) continue; dc.transform(g->_ctm); if (g->pathvec) { - if (g->_font->FontHasSVG()) { - if (g->pixbuf) { - // Geom::OptRect box = bounds_exact(*g->pathvec); - // if (box) { - // Inkscape::DrawingContext::Save save(dc); - // dc.newPath(); - // dc.rectangle(*box); - // dc.setLineWidth(0.01); - // dc.setSource(0x8080ffff); - // dc.stroke(); - // } - { - // pixbuf is in font design units, scale to embox. - double scale = g->_font->GetDesignUnits(); - if (scale <= 0) scale = 1000; - Inkscape::DrawingContext::Save save(dc); - dc.translate(0, 1); - dc.scale(1.0 / scale, -1.0 / scale); - dc.setSource(g->pixbuf->getSurfaceRaw(), 0, 0); - dc.paint(1); - } - } else { - dc.path(*g->pathvec); + if (g->pixbuf) { + // Geom::OptRect box = bounds_exact(*g->pathvec); + // if (box) { + // Inkscape::DrawingContext::Save save(dc); + // dc.newPath(); + // dc.rectangle(*box); + // dc.setLineWidth(0.01); + // dc.setSource(0x8080ffff); + // dc.stroke(); + // } + { + // pixbuf is in font design units, scale to embox. + double scale = g->design_units; + if (scale <= 0) scale = 1000; + Inkscape::DrawingContext::Save save(dc); + dc.translate(0, 1); + dc.scale(1.0 / scale, -1.0 / scale); + dc.setSource(g->pixbuf->getSurfaceRaw(), 0, 0); + dc.paint(1); } } else { dc.path(*g->pathvec); diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h index 785af679741b858f9bfed482255d97e11e57782d..9511cfd675fceadf09d6a4151dde2bbf057f81d5 100644 --- a/src/display/drawing-text.h +++ b/src/display/drawing-text.h @@ -10,8 +10,8 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_TEXT_H -#define SEEN_INKSCAPE_DISPLAY_DRAWING_TEXT_H +#ifndef INKSCAPE_DISPLAY_DRAWING_TEXT_H +#define INKSCAPE_DISPLAY_DRAWING_TEXT_H #include #include "display/drawing-group.h" @@ -29,17 +29,18 @@ class DrawingGlyphs { public: DrawingGlyphs(Drawing &drawing); - ~DrawingGlyphs() override; void setGlyph(std::shared_ptr font, int glyph, Geom::Affine const &trans); void setStyle(SPStyle const *style, SPStyle const *context_style = nullptr) override; // Not to be used Geom::IntRect getPickBox() const { return _pick_bbox; }; protected: + ~DrawingGlyphs() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags) override; - std::shared_ptr _font; + std::shared_ptr _font_data; // keeps alive pathvec, pathvec_ref, and pixbuf int _glyph; float _width; // These three are used to set up bounding box float _asc; // @@ -47,6 +48,7 @@ protected: float _pl; // phase length Geom::IntRect _pick_bbox; + double design_units; Geom::PathVector const *pathvec; // pathvector of actual glyph Geom::PathVector const *pathvec_ref; // pathvector of reference glyph 42 Inkscape::Pixbuf const *pixbuf; // pixbuf, if SVG font @@ -59,15 +61,14 @@ class DrawingText { public: DrawingText(Drawing &drawing); - ~DrawingText() override; - void clear(); - bool addComponent(std::shared_ptr const &font, int glyph, Geom::Affine const &trans, - float width, float ascent, float descent, float phase_length); + bool addComponent(std::shared_ptr const &font, int glyph, Geom::Affine const &trans, float width, float ascent, float descent, float phase_length); void setStyle(SPStyle const *style, SPStyle const *context_style = nullptr) override; void setChildrenStyle(SPStyle const *context_style) override; protected: + ~DrawingText() override = default; + unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override; unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, @@ -87,9 +88,9 @@ protected: friend class DrawingGlyphs; }; -} // end namespace Inkscape +} // namespace Inkscape -#endif // !SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#endif // INKSCAPE_DISPLAY_DRAWING_TEXT_H /* Local Variables: diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index d8d1182c10909459c106527fb6189bc74699fbbd..f5453c34a8d97e345e3dd0c0d4601a2cba5dce41 100644 --- a/src/display/drawing.cpp +++ b/src/display/drawing.cpp @@ -12,7 +12,7 @@ */ #include - +#include #include "display/drawing.h" #include "display/control/canvas-item-drawing.h" #include "nr-filter-gaussian.h" @@ -42,6 +42,12 @@ static auto rendermode_to_renderflags(RenderMode mode) } } +static auto default_numthreads() +{ + auto ret = std::thread::hardware_concurrency(); + return ret == 0 ? 4 : ret; // Sensible fallback if not reported. +} + Drawing::Drawing(Inkscape::CanvasItemDrawing *canvas_item_drawing) : _canvas_item_drawing(canvas_item_drawing) , _grayscale_matrix(std::vector(grayscale_matrix.begin(), grayscale_matrix.end())) @@ -67,106 +73,146 @@ void Drawing::setRoot(DrawingItem *root) void Drawing::setRenderMode(RenderMode mode) { assert(mode != RenderMode::OUTLINE_OVERLAY && "Drawing::setRenderMode: OUTLINE_OVERLAY is not a true render mode"); - if (mode == _rendermode) return; - _root->_markForRendering(); - _rendermode = mode; - _root->_markForUpdate(DrawingItem::STATE_ALL, true); - _clearCache(); + + defer([=] { + if (mode == _rendermode) return; + _root->_markForRendering(); + _rendermode = mode; + _root->_markForUpdate(DrawingItem::STATE_ALL, true); + _clearCache(); + }); } void Drawing::setColorMode(ColorMode mode) { - if (mode == _colormode) return; - _colormode = mode; - if (_rendermode != RenderMode::OUTLINE || _image_outline_mode) { - _root->_markForRendering(); - } + defer([=] { + if (mode == _colormode) return; + _colormode = mode; + if (_rendermode != RenderMode::OUTLINE || _image_outline_mode) { + _root->_markForRendering(); + } + }); } void Drawing::setOutlineOverlay(bool outlineoverlay) { - if (outlineoverlay == _outlineoverlay) return; - _outlineoverlay = outlineoverlay; - _root->_markForUpdate(DrawingItem::STATE_ALL, true); + defer([=] { + if (outlineoverlay == _outlineoverlay) return; + _outlineoverlay = outlineoverlay; + _root->_markForUpdate(DrawingItem::STATE_ALL, true); + }); } void Drawing::setGrayscaleMatrix(double value_matrix[20]) { - _grayscale_matrix = Filters::FilterColorMatrix::ColorMatrixMatrix(std::vector(value_matrix, value_matrix + 20)); - if (_rendermode != RenderMode::OUTLINE) { - _root->_markForRendering(); - } + defer([=] { + _grayscale_matrix = Filters::FilterColorMatrix::ColorMatrixMatrix(std::vector(value_matrix, value_matrix + 20)); + if (_rendermode != RenderMode::OUTLINE) { + _root->_markForRendering(); + } + }); } void Drawing::setClipOutlineColor(uint32_t col) { - _clip_outline_color = col; - if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { - _root->_markForRendering(); - } + defer([=] { + _clip_outline_color = col; + if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { + _root->_markForRendering(); + } + }); } void Drawing::setMaskOutlineColor(uint32_t col) { - _mask_outline_color = col; - if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { - _root->_markForRendering(); - } + defer([=] { + _mask_outline_color = col; + if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { + _root->_markForRendering(); + } + }); } void Drawing::setImageOutlineColor(uint32_t col) { - _image_outline_color = col; - if ((_rendermode == RenderMode::OUTLINE || _outlineoverlay) && !_image_outline_mode) { - _root->_markForRendering(); - } + defer([=] { + _image_outline_color = col; + if ((_rendermode == RenderMode::OUTLINE || _outlineoverlay) && !_image_outline_mode) { + _root->_markForRendering(); + } + }); } void Drawing::setImageOutlineMode(bool enabled) { - _image_outline_mode = enabled; - if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { - _root->_markForRendering(); - } + defer([=] { + _image_outline_mode = enabled; + if (_rendermode == RenderMode::OUTLINE || _outlineoverlay) { + _root->_markForRendering(); + } + }); } void Drawing::setFilterQuality(int quality) { - _filter_quality = quality; - if (!(_rendermode == RenderMode::OUTLINE || _rendermode == RenderMode::NO_FILTERS)) { - _root->_markForUpdate(DrawingItem::STATE_ALL, true); - _clearCache(); - } + defer([=] { + _filter_quality = quality; + if (!(_rendermode == RenderMode::OUTLINE || _rendermode == RenderMode::NO_FILTERS)) { + _root->_markForUpdate(DrawingItem::STATE_ALL, true); + _clearCache(); + } + }); } void Drawing::setBlurQuality(int quality) { - _blur_quality = quality; - if (!(_rendermode == RenderMode::OUTLINE || _rendermode == RenderMode::NO_FILTERS)) { - _root->_markForUpdate(DrawingItem::STATE_ALL, true); - _clearCache(); - } + defer([=] { + _blur_quality = quality; + if (!(_rendermode == RenderMode::OUTLINE || _rendermode == RenderMode::NO_FILTERS)) { + _root->_markForUpdate(DrawingItem::STATE_ALL, true); + _clearCache(); + } + }); +} + +void Drawing::setDithering(bool use_dithering) +{ + defer([=] { + _use_dithering = use_dithering; + #ifdef CAIRO_HAS_DITHER + if (_rendermode != RenderMode::OUTLINE) { + _root->_markForUpdate(DrawingItem::STATE_ALL, true); + _clearCache(); + } + #endif + }); } void Drawing::setCacheBudget(size_t bytes) { - _cache_budget = bytes; - _pickItemsForCaching(); + defer([=] { + _cache_budget = bytes; + _pickItemsForCaching(); + }); } void Drawing::setCacheLimit(Geom::OptIntRect const &rect) { - _cache_limit = rect; - for (auto item : _cached_items) { - item->_markForUpdate(DrawingItem::STATE_CACHE, false); - } + defer([=] { + _cache_limit = rect; + for (auto item : _cached_items) { + item->_markForUpdate(DrawingItem::STATE_CACHE, false); + } + }); } void Drawing::setClip(std::optional &&clip) { - if (clip == _clip) return; - _clip = std::move(clip); - _root->_markForRendering(); + defer([=] { + if (clip == _clip) return; + _clip = std::move(clip); + _root->_markForRendering(); + }); } void Drawing::update(Geom::IntRect const &area, Geom::Affine const &affine, unsigned flags, unsigned reset) @@ -207,6 +253,19 @@ DrawingItem *Drawing::pick(Geom::Point const &p, double delta, unsigned flags) return _root->pick(p, delta, flags); } +void Drawing::snapshot() +{ + assert(!_snapshotted); + _snapshotted = true; +} + +void Drawing::unsnapshot() +{ + assert(_snapshotted); + _snapshotted = false; // Unsnapshot before replaying log so further work is not deferred. + _funclog(); +} + void Drawing::_pickItemsForCaching() { // Build sorted list of items that should be cached. @@ -226,12 +285,12 @@ void Drawing::_pickItemsForCaching() to_cache.begin(), to_cache.end(), std::back_inserter(to_uncache)); for (auto item : to_uncache) { - item->setCached(false); + item->_setCached(false); } // Cache all items that should be cached (no-op if already cached). for (auto item : to_cache) { - item->setCached(true); + item->_setCached(true); } } @@ -241,7 +300,7 @@ void Drawing::_clearCache() std::vector to_uncache; std::copy(_cached_items.begin(), _cached_items.end(), std::back_inserter(to_uncache)); for (auto item : to_uncache) { - item->setCached(false, true); + item->_setCached(false, true); } } @@ -256,6 +315,7 @@ void Drawing::_loadPrefs() _image_outline_mode = prefs->getBool ("/options/rendering/imageinoutlinemode", false); _filter_quality = prefs->getIntLimited("/options/filterquality/value", 0, Filters::FILTER_QUALITY_WORST, Filters::FILTER_QUALITY_BEST); _blur_quality = prefs->getInt ("/options/blurquality/value", 0); + _use_dithering = prefs->getBool ("/options/dithering/value", true); _cursor_tolerance = prefs->getDouble ("/options/cursortolerance/value", 1.0); // Enable caching only for the Canvas's drawing, since only it is persistent. @@ -265,6 +325,9 @@ void Drawing::_loadPrefs() _cache_budget = 0; } + // Set the global variable governing the number of filter threads, and track it too. (This is ugly, but hopefully transitional.) + set_num_filter_threads(prefs->getIntLimited("/options/threading/numthreads", default_numthreads(), 1, 256)); + // Similarly, enable preference tracking only for the Canvas's drawing. if (_canvas_item_drawing) { std::unordered_map> actions; @@ -276,8 +339,10 @@ void Drawing::_loadPrefs() actions.emplace("/options/rendering/imageinoutlinemode", [this] (auto &entry) { setImageOutlineMode(entry.getBool(false)); }); actions.emplace("/options/filterquality/value", [this] (auto &entry) { setFilterQuality(entry.getIntLimited(0, Filters::FILTER_QUALITY_WORST, Filters::FILTER_QUALITY_BEST)); }); actions.emplace("/options/blurquality/value", [this] (auto &entry) { setBlurQuality(entry.getInt(0)); }); + actions.emplace("/options/dithering/value", [this] (auto &entry) { setDithering(entry.getBool(true)); }); actions.emplace("/options/cursortolerance/value", [this] (auto &entry) { setCursorTolerance(entry.getDouble(1.0)); }); actions.emplace("/options/renderingcache/size", [this] (auto &entry) { setCacheBudget((1 << 20) * entry.getIntLimited(64, 0, 4096)); }); + actions.emplace("/options/threading/numthreads", [this] (auto &entry) { set_num_filter_threads(entry.getIntLimited(default_numthreads(), 1, 256)); }); _pref_tracker = Inkscape::Preferences::PreferencesObserver::create("/options", [actions = std::move(actions)] (auto &entry) { auto it = actions.find(entry.getPath()); diff --git a/src/display/drawing.h b/src/display/drawing.h index 1c50af44fff6cd203f5dccf7dcb407f22191e71a..22dedc5596e10c76bd8dcf61e40a58ecca00e0bc 100644 --- a/src/display/drawing.h +++ b/src/display/drawing.h @@ -25,6 +25,7 @@ #include "display/rendermode.h" #include "nr-filter-colormatrix.h" #include "preferences.h" +#include "util/funclog.h" namespace Inkscape { @@ -54,6 +55,7 @@ public: void setImageOutlineMode(bool); void setFilterQuality(int); void setBlurQuality(int); + void setDithering(bool); void setCursorTolerance(double tol) { _cursor_tolerance = tol; } void setCacheBudget(size_t bytes); void setCacheLimit(Geom::OptIntRect const &rect); @@ -69,6 +71,7 @@ public: bool imageOutlineMode() const { return _image_outline_mode; } int filterQuality() const { return _filter_quality; } int blurQuality() const { return _blur_quality; } + bool useDithering() const { return _use_dithering; } double cursorTolerance() const { return _cursor_tolerance; } Geom::OptIntRect const &cacheLimit() const { return _cache_limit; } @@ -77,6 +80,10 @@ public: void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0, int antialiasing_override = -1); DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags); + void snapshot(); + void unsnapshot(); + bool snapshotted() const { return _snapshotted; } + // Convenience void averageColor(Geom::IntRect const &area, double &R, double &G, double &B, double &A); void setExact(); @@ -100,14 +107,34 @@ private: bool _image_outline_mode; ///< Always draw images as images, even in outline mode. int _filter_quality; int _blur_quality; + bool _use_dithering; double _cursor_tolerance; size_t _cache_budget; ///< Maximum allowed size of cache. Geom::OptIntRect _cache_limit; std::optional _clip; - using CandidateList = std::list; - std::set _cached_items; // modified by DrawingItem::setCached() - CandidateList _candidate_items; // keep this list always sorted with std::greater + std::set _cached_items; // modified by DrawingItem::_setCached() + CacheList _candidate_items; // keep this list always sorted with std::greater + + /* + * Simple cacheline separator compatible with x86 (64 bytes) and M* (128 bytes). + * Ideally alignas(std::hardware_destructive_interference_size) could be used instead, + * but this is extremely painful to make work across all supported platforms/compilers. + */ + char cacheline_separator[127]; + + bool _snapshotted = false; + Util::FuncLog _funclog; + + template + void defer(F &&f) + { + if (!_snapshotted) { + f(); + } else { + _funclog.emplace(std::forward(f)); + } + } friend class DrawingItem; }; diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index 2f066428505df0aca12d62a44d403d7db6415514..4e04bfd62ff0f198cce9dca64c29d3ede7ba12bb 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -34,7 +34,6 @@ #include "display/nr-filter-slot.h" #include <2geom/affine.h> #include "util/fixed_point.h" -#include "preferences.h" #ifndef INK_UNUSED #define INK_UNUSED(x) ((void)(x)) @@ -598,14 +597,8 @@ void FilterGaussian::render_cairo(FilterSlot &slot) const bytes_per_pixel = 4; break; } -#if HAVE_OPENMP - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); -#else - int threads = 1; -#endif - int quality = slot.get_blurquality(); + int threads = get_num_filter_threads(); int x_step = 1 << _effect_subsample_step_log2(deviation_x_orig, quality); int y_step = 1 << _effect_subsample_step_log2(deviation_y_orig, quality); bool resampling = x_step > 1 || y_step > 1; diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index d597428e726c17ec66f8635415a65b54288fc1a3..60c2b4b0b46d582b1f7394349c6dbad8280b13ce 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -70,6 +70,7 @@ void FilterImage::render_cairo(FilterSlot &slot) const Geom::Rect sa = slot.get_slot_area(); cairo_surface_t *out = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, sa.width() * device_scale, sa.height() * device_scale); cairo_surface_set_device_scale(out, device_scale, device_scale); + ink_cairo_set_dither(out, item->drawing().useDithering()); Inkscape::DrawingContext dc(out, sa.min()); Geom::Affine user2pb = slot.get_units().get_matrix_user2pb(); diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp index 2c3cf9c61bf01c8271d79f97bf312ac025d25bad..1cc2aadea7bc2f113ae5a9c88c6cc80752c377ef 100644 --- a/src/display/nr-filter-morphology.cpp +++ b/src/display/nr-filter-morphology.cpp @@ -67,10 +67,7 @@ void morphologicalFilter1D(cairo_surface_t * const input, cairo_surface_t * cons #if HAVE_OPENMP int limit = w * h; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - int numOfThreads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); - (void) numOfThreads; // suppress unused variable warning - #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(numOfThreads) + #pragma omp parallel for if(limit > OPENMP_THRESHOLD) num_threads(get_num_filter_threads()) #endif // HAVE_OPENMP for (int i = 0; i < h; ++i) { // TODO: Store position and value in one 32 bit integer? 24 bits should be enough for a position, it would be quite strange to have an image with a width/height of more than 16 million(!). diff --git a/src/display/nr-filter-slot.cpp b/src/display/nr-filter-slot.cpp index 8855b552fe7e4971941bc72fbd999ec77841e633..55e7a46eb59e953699d954d3b52dc2f0ddf2d679 100644 --- a/src/display/nr-filter-slot.cpp +++ b/src/display/nr-filter-slot.cpp @@ -16,26 +16,27 @@ #include #include <2geom/transforms.h> -#include "display/cairo-utils.h" -#include "display/drawing-context.h" -#include "display/nr-filter-types.h" -#include "display/nr-filter-gaussian.h" -#include "display/nr-filter-slot.h" -#include "display/nr-filter-units.h" +#include "cairo-utils.h" +#include "drawing-context.h" +#include "drawing-surface.h" +#include "nr-filter-types.h" +#include "nr-filter-gaussian.h" +#include "nr-filter-slot.h" +#include "nr-filter-units.h" namespace Inkscape { namespace Filters { -FilterSlot::FilterSlot(DrawingContext *bgdc, DrawingContext &graphic, FilterUnits const &u, RenderContext &rc) +FilterSlot::FilterSlot(DrawingContext *bgdc, DrawingContext &graphic, FilterUnits const &units, RenderContext &rc, int blurquality) : _source_graphic(graphic.rawTarget()) , _background_ct(bgdc ? bgdc->raw() : nullptr) , _source_graphic_area(graphic.targetLogicalBounds().roundOutwards()) // fixme , _background_area(bgdc ? bgdc->targetLogicalBounds().roundOutwards() : Geom::IntRect()) // fixme - , _units(u) + , _units(units) , _last_out(NR_FILTER_SOURCEGRAPHIC) - , filterquality(FILTER_QUALITY_BEST) - , blurquality(BLUR_QUALITY_BEST) + , _blurquality(blurquality) , rc(rc) + , device_scale(graphic.surface()->device_scale()) { using Geom::X; using Geom::Y; @@ -253,36 +254,6 @@ Geom::Rect FilterSlot::get_primitive_area(int slot_nr) const return s->second; } -int FilterSlot::get_slot_count() const -{ - return _slots.size(); -} - -void FilterSlot::set_quality(FilterQuality q) -{ - filterquality = q; -} - -void FilterSlot::set_blurquality(int q) -{ - blurquality = q; -} - -int FilterSlot::get_blurquality() const -{ - return blurquality; -} - -void FilterSlot::set_device_scale(int s) -{ - device_scale = s; -} - -int FilterSlot::get_device_scale() const -{ - return device_scale; -} - Geom::Rect FilterSlot::get_slot_area() const { return Geom::Rect::from_xywh(_slot_x, _slot_y, _slot_w, _slot_h); diff --git a/src/display/nr-filter-slot.h b/src/display/nr-filter-slot.h index 31114cbe97f0beb5dd00a0c9b68ee5749d7dd008..ecd9f00573a8a078e96a4891bad5a3d70dc726e5 100644 --- a/src/display/nr-filter-slot.h +++ b/src/display/nr-filter-slot.h @@ -16,8 +16,8 @@ */ #include -#include "display/nr-filter-types.h" -#include "display/nr-filter-units.h" +#include "nr-filter-types.h" +#include "nr-filter-units.h" extern "C" { typedef struct _cairo cairo_t; @@ -31,14 +31,14 @@ class RenderContext; namespace Filters { -class FilterSlot +class FilterSlot final { public: /** Creates a new FilterSlot object. */ - FilterSlot(DrawingContext *bgdc, DrawingContext &graphic, FilterUnits const &u, RenderContext &rc); + FilterSlot(DrawingContext *bgdc, DrawingContext &graphic, FilterUnits const &units, RenderContext &rc, int blurquality); /** Destroys the FilterSlot object and all its contents */ - virtual ~FilterSlot(); + ~FilterSlot(); /** Returns the pixblock in specified slot. * Parameter 'slot' may be either an positive integer or one of @@ -61,22 +61,13 @@ public: Geom::Rect get_primitive_area(int slot) const; /** Returns the number of slots in use. */ - int get_slot_count() const; - - /** Sets the filtering quality. Affects used interpolation methods */ - void set_quality(FilterQuality q); - - /** Sets the gaussian filtering quality. Affects used interpolation methods */ - void set_blurquality(int q); + int get_slot_count() const { return _slots.size(); } /** Gets the gaussian filtering quality. Affects used interpolation methods */ - int get_blurquality() const; - - /** Sets the device scale; for high DPI monitors. */ - void set_device_scale(int s); + int get_blurquality() const { return _blurquality; } /** Gets the device scale; for high DPI monitors. */ - int get_device_scale() const; + int get_device_scale() const { return device_scale; } FilterUnits const &get_units() const { return _units; } Geom::Rect get_slot_area() const; @@ -99,8 +90,7 @@ private: Geom::IntRect _background_area; ///< needed to extract background FilterUnits const &_units; int _last_out; - FilterQuality filterquality; - int blurquality; + int _blurquality; int device_scale; RenderContext &rc; diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 4c9d77ea0265637f63d4157e9a7a813c40629d2e..315195ebe6c7232d419d6e6b9845f1fc10cf15e4 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -142,10 +142,7 @@ int Filter::render(Inkscape::DrawingItem const *item, DrawingContext &graphic, D } } - FilterSlot slot(bgdc, graphic, units, rc); - slot.set_quality(filterquality); - slot.set_blurquality(blurquality); - slot.set_device_scale(graphic.surface()->device_scale()); + auto slot = FilterSlot(bgdc, graphic, units, rc, blurquality); for (auto &i : primitives) { i->render_cairo(slot); diff --git a/src/display/nr-style.h b/src/display/nr-style.h index ccca68394170b484b2d15f62836a9ff3adccb330..5e47a8bb6647ee9ac3bb3d258f03dfaa4db75c38 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -34,6 +34,7 @@ class RenderContext; struct NRStyle { NRStyle(); + explicit NRStyle(SPStyle const *style, SPStyle const *context_style = nullptr) { set(style, context_style); } enum PaintType { diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp index 827cb3770eb569ad8591e72fe26c9039512aeaba..82d85e9c1b06699711337e51eb79989ac48228fc 100644 --- a/src/helper/pixbuf-ops.cpp +++ b/src/helper/pixbuf-ops.cpp @@ -13,7 +13,9 @@ */ #include <2geom/transforms.h> +#include +#include "helper/pixbuf-ops.h" #include "helper/png-write.h" #include "display/cairo-utils.h" #include "display/drawing.h" @@ -23,15 +25,11 @@ #include "object/sp-defs.h" #include "object/sp-use.h" #include "util/units.h" +#include "util/scope_exit.h" #include "inkscape.h" -#include "helper/pixbuf-ops.h" - -#include - /** - generates a bitmap from given items - the bitmap is stored in RAM and not written to file + Generates a bitmap from given items. The bitmap is stored in RAM and not written to file. @param document Inkscape document. @param area Export area in document units. @param dpi Resolution. @@ -44,7 +42,7 @@ Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document, double dpi, std::vector items, bool opaque, - unsigned int* checkerboard_color, + uint32_t const *checkerboard_color, double device_scale) { // Geometry @@ -66,6 +64,7 @@ Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document, // Drawing Inkscape::Drawing drawing; // New drawing for offscreen rendering. drawing.setRoot(document->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY)); + auto invoke_hide_guard = scope_exit([&] { document->getRoot()->invoke_hide(dkey); }); drawing.root()->setTransform(affine); drawing.setExact(); // Maximum quality for blurs. @@ -75,7 +74,7 @@ Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document, document->getRoot()->invoke_hide_except(dkey, items); } - Geom::IntRect final_area = Geom::IntRect::from_xywh(0, 0, width, height); + auto final_area = Geom::IntRect::from_xywh(0, 0, width, height); drawing.update(final_area); if (opaque) { @@ -89,45 +88,35 @@ Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document, // Rendering cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - Inkscape::Pixbuf* pixbuf = nullptr; - - if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS) { - Inkscape::DrawingContext dc(surface, Geom::Point(0,0)); - - if (checkerboard_color) { - guint rgba = *checkerboard_color; - auto pattern = ink_cairo_pattern_create_checkerboard(rgba); - dc.save(); - dc.transform(Geom::Scale(device_scale)); - dc.setOperator(CAIRO_OPERATOR_SOURCE); - dc.setSource(pattern); - dc.paint(); - dc.restore(); - cairo_pattern_destroy(pattern); - } - // render items - drawing.render(dc, final_area, Inkscape::DrawingItem::RENDER_BYPASS_CACHE); - - if (device_scale != 1.0) { - cairo_surface_set_device_scale(surface, device_scale, device_scale); - } - - pixbuf = new Inkscape::Pixbuf(surface); - - } else { - - long long size = - (long long) height * - (long long) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + long long size = (long long)height * (long long)cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); g_warning("sp_generate_internal_bitmap: not enough memory to create pixel buffer. Need %lld.", size); cairo_surface_destroy(surface); + return nullptr; } - // Return to previous state. - document->getRoot()->invoke_hide(dkey); + Inkscape::DrawingContext dc(surface, Geom::Point(0, 0)); + + if (checkerboard_color) { + auto pattern = ink_cairo_pattern_create_checkerboard(*checkerboard_color); + dc.save(); + dc.transform(Geom::Scale(device_scale)); + dc.setOperator(CAIRO_OPERATOR_SOURCE); + dc.setSource(pattern); + dc.paint(); + dc.restore(); + cairo_pattern_destroy(pattern); + } + + // render items + drawing.render(dc, final_area, Inkscape::DrawingItem::RENDER_BYPASS_CACHE); + + if (device_scale != 1.0) { + cairo_surface_set_device_scale(surface, device_scale, device_scale); + } - return pixbuf; + return new Inkscape::Pixbuf(surface); } /* diff --git a/src/helper/pixbuf-ops.h b/src/helper/pixbuf-ops.h index c57654869a7e184031906aa232c48b9df1519aaa..b6cc87a9c73803b5730bcbe03d895205b2d67516 100644 --- a/src/helper/pixbuf-ops.h +++ b/src/helper/pixbuf-ops.h @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#ifndef __SP_PIXBUF_OPS_H__ -#define __SP_PIXBUF_OPS_H__ +#ifndef INKSCAPE_HELPER_PIXBUF_OPS_H +#define INKSCAPE_HELPER_PIXBUF_OPS_H /* * Helpers for SPItem -> gdk_pixbuf related stuff @@ -13,7 +13,9 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#include +#include +#include +#include <2geom/forward.h> class SPDocument; class SPItem; @@ -22,8 +24,8 @@ namespace Inkscape { class Pixbuf; } Inkscape::Pixbuf *sp_generate_internal_bitmap(SPDocument *document, Geom::Rect const &area, double dpi, - std::vector items = std::vector(), + std::vector items = {}, bool set_opaque = false, - unsigned int* checkerboard_color = nullptr, + uint32_t const *checkerboard_color = nullptr, double device_scale = 1.0); -#endif +#endif // INKSCAPE_HELPER_PIXBUF_OPS_H diff --git a/src/libnrtype/font-instance.cpp b/src/libnrtype/font-instance.cpp index 99f821ffb114d8272bebcbe52eba8533173bbb86..304d62d1bafa689061de7078e0727fd1bf5ef649 100644 --- a/src/libnrtype/font-instance.cpp +++ b/src/libnrtype/font-instance.cpp @@ -193,8 +193,9 @@ void FontInstance::init_face() FT_Select_Charmap(face, ft_encoding_unicode); FT_Select_Charmap(face, ft_encoding_symbol); - readOpenTypeSVGTable(hb_font, openTypeSVGGlyphs); - readOpenTypeFvarAxes(face, openTypeVarAxes); + data = std::make_shared(); + readOpenTypeSVGTable(hb_font, data->openTypeSVGGlyphs); + readOpenTypeFvarAxes(face, data->openTypeVarAxes); #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 8 // 2.8 does not seem to work even though it has some support. @@ -222,7 +223,7 @@ void FontInstance::init_face() auto regex = Glib::Regex::create("(\\w{4})=([-+]?\\d*\\.?\\d+([eE][-+]?\\d+)?)"); Glib::MatchInfo matchInfo; - FT_UInt num_axis = openTypeVarAxes.size(); + FT_UInt num_axis = data->openTypeVarAxes.size(); std::vector w(num_axis, 0); auto tokens = Glib::Regex::split_simple(",", variations); @@ -241,8 +242,8 @@ void FontInstance::init_face() if (name == "slnt") name = "Slant" ; // 'font-style' if (name == "ital") name = "Italic" ; // 'font-style' - auto it = openTypeVarAxes.find(name); - if (it != openTypeVarAxes.end()) { + auto it = data->openTypeVarAxes.find(name); + if (it != data->openTypeVarAxes.end()) { it->second.set_val = value; w[it->second.index] = value * 65536; } @@ -389,7 +390,7 @@ FontGlyph const *FontInstance::LoadGlyph(int glyph_id) return nullptr; // bitmap font } - if (auto it = glyphs.find(glyph_id); it != glyphs.end()) { + if (auto it = data->glyphs.find(glyph_id); it != data->glyphs.end()) { return it->second.get(); // already loaded } @@ -457,7 +458,7 @@ FontGlyph const *FontInstance::LoadGlyph(int glyph_id) } } - auto ret = glyphs.emplace(glyph_id, std::move(n_g)); + auto ret = data->glyphs.emplace(glyph_id, std::move(n_g)); return ret.first->second.get(); } @@ -527,8 +528,8 @@ Geom::PathVector const *FontInstance::PathVector(int glyph_id) Inkscape::Pixbuf const *FontInstance::PixBuf(int glyph_id) { - auto glyph_iter = openTypeSVGGlyphs.find(glyph_id); - if (glyph_iter == openTypeSVGGlyphs.end()) { + auto glyph_iter = data->openTypeSVGGlyphs.find(glyph_id); + if (glyph_iter == data->openTypeSVGGlyphs.end()) { return nullptr; // out of range } @@ -649,15 +650,15 @@ double FontInstance::Advance(int glyph_id, bool vertical) std::map const &FontInstance::get_opentype_tables() { - if (!openTypeTables) { + if (!data->openTypeTables) { auto hb_font = pango_font_get_hb_font(p_font); assert(hb_font); - openTypeTables.emplace(); - readOpenTypeGsubTable(hb_font, *openTypeTables); + data->openTypeTables.emplace(); + readOpenTypeGsubTable(hb_font, *data->openTypeTables); } - return *openTypeTables; + return *data->openTypeTables; } /* diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 735309a69a7b6e5732be030ff732bee0221c2f7e..cfa709e2ab2ac6c47ac5b68fcc0630a588862d83 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -66,8 +66,8 @@ public: Geom::PathVector const *PathVector(int glyph_id); // Return font has SVG OpenType enties. - bool FontHasSVG() const { return openTypeSVGGlyphs.size() > 0; }; - auto const &get_opentype_varaxes() const { return openTypeVarAxes; } + bool FontHasSVG() const { return data->openTypeSVGGlyphs.size() > 0; }; + auto const &get_opentype_varaxes() const { return data->openTypeVarAxes; } // Return the font's OpenType tables, possibly loading them on-demand. std::map const &get_opentype_tables(); @@ -79,6 +79,9 @@ public: // Horizontal advance if 'vertical' is false, vertical advance if true. double Advance(int glyph_id, bool vertical); + // Return a shared pointer that will keep alive the pathvector and pixbuf data, but nothing else. + std::shared_ptr share_data() const { return data; } + double GetTypoAscent() const { return _ascent; } double GetTypoDescent() const { return _descent; } double GetXHeight() const { return _xheight; } @@ -141,25 +144,30 @@ private: // Baselines double _baselines[SP_CSS_BASELINE_SIZE]; - /* - * Tables - */ + struct Data + { + /* + * Tables + */ - // Map of SVG in OpenType glyphs - std::map openTypeSVGGlyphs; + // Map of SVG in OpenType glyphs + std::map openTypeSVGGlyphs; - // Maps for font variations. - std::map openTypeVarAxes; // Axes with ranges + // Maps for font variations. + std::map openTypeVarAxes; // Axes with ranges - // Map of OpenType tables found in font. Transparently lazy-loaded. - std::optional> openTypeTables; + // Map of OpenType tables found in font. Transparently lazy-loaded. + std::optional> openTypeTables; - /* - * Glyphs - */ + /* + * Glyphs + */ + + // Lookup table mapping pango glyph ids to glyphs. + std::unordered_map> glyphs; + }; - // Lookup table mapping pango glyph ids to glyphs. - std::unordered_map> glyphs; + std::shared_ptr data; }; #endif // LIBNRTYPE_FONT_INSTANCE_H diff --git a/src/object/filters/image.cpp b/src/object/filters/image.cpp index 5835066d2c5fbca4f3e042c7f20db3c16dcc5802..644960e2093a82e0cab192c5824c77e159521633 100644 --- a/src/object/filters/image.cpp +++ b/src/object/filters/image.cpp @@ -276,7 +276,7 @@ void SPFeImage::destroy_view(View &v) if (type == ELEM) { elem->invoke_hide(v.inner_key); } else if (type == IMAGE) { - delete v.child; + v.child->unlink(); } // Defensive-coding measure: clear filter renderer immediately. diff --git a/src/object/sp-clippath.cpp b/src/object/sp-clippath.cpp index a7f33370d1a5f80ba2763acdca7d6c0b453cacf9..67085eb46674241aa37bbf3d1b7a9f937381ab1b 100644 --- a/src/object/sp-clippath.cpp +++ b/src/object/sp-clippath.cpp @@ -155,7 +155,7 @@ Inkscape::XML::Node *SPClipPath::write(Inkscape::XML::Document *xml_doc, Inkscap Inkscape::DrawingItem *SPClipPath::show(Inkscape::Drawing &drawing, unsigned key, Geom::OptRect const &bbox) { - views.emplace_back(std::make_unique(drawing), bbox, key); + views.emplace_back(make_drawingitem(drawing), bbox, key); auto &v = views.back(); auto root = v.drawingitem.get(); @@ -239,7 +239,7 @@ char const *SPClipPath::create(std::vector &reprs, SPDocum return id; } -SPClipPath::View::View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key) +SPClipPath::View::View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key) : drawingitem(std::move(drawingitem)) , bbox(bbox) , key(key) {} diff --git a/src/object/sp-clippath.h b/src/object/sp-clippath.h index 2fea9f1f76b6c2c3a0dce0178a3d4e4ffd315c6a..b7b5fb0fe7045aef6c28a70ef95cf37404f1bf49 100644 --- a/src/object/sp-clippath.h +++ b/src/object/sp-clippath.h @@ -22,6 +22,7 @@ #include <2geom/rect.h> #include "sp-object-group.h" #include "uri-references.h" +#include "display/drawing-item-ptr.h" namespace Inkscape { class Drawing; @@ -65,10 +66,10 @@ private: struct View { - std::unique_ptr drawingitem; + DrawingItemPtr drawingitem; Geom::OptRect bbox; unsigned key; - View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key); + View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key); }; std::vector views; void update_view(View &v); diff --git a/src/object/sp-factory.cpp b/src/object/sp-factory.cpp index d3e28f7f42d231188b661759d3884646c7de6462..125e6e86e9205d03325d568a4d930a5ee70d4141 100644 --- a/src/object/sp-factory.cpp +++ b/src/object/sp-factory.cpp @@ -94,204 +94,201 @@ #include "filters/tile.h" #include "filters/turbulence.h" -#include #include namespace { class Factory { - std::unordered_map> map; - public: - Factory() + SPObject *create(std::string const &id) const + { + auto it = map.find(id); + + if (it == map.end()) { + std::cerr << "WARNING: unknown type: " << id << std::endl; + return nullptr; + } + + return it->second(); + } + +private: + using Func = SPObject*(*)(); + + template + static Func constexpr make = [] () -> SPObject* { return new T; }; + static Func constexpr null = [] () -> SPObject* { return nullptr; }; + + std::unordered_map const map = { // primary - map.emplace("inkscape:box3d", [] { return new SPBox3D; }); - map.emplace("inkscape:box3dside", [] { return new Box3DSide; }); - map.emplace("svg:color-profile", [] { return new Inkscape::ColorProfile; }); - map.emplace("inkscape:persp3d", [] { return new Persp3D; }); - map.emplace("svg:a", [] { return new SPAnchor; }); - map.emplace("svg:clipPath", [] { return new SPClipPath; }); - map.emplace("svg:defs", [] { return new SPDefs; }); - map.emplace("svg:desc", [] { return new SPDesc; }); - map.emplace("svg:ellipse", [] { + { "inkscape:box3d", make }, + { "inkscape:box3dside", make }, + { "svg:color-profile", make }, + { "inkscape:persp3d", make }, + { "svg:a", make }, + { "svg:clipPath", make }, + { "svg:defs", make }, + { "svg:desc", make }, + { "svg:ellipse", [] () -> SPObject* { auto e = new SPGenericEllipse; e->type = SP_GENERIC_ELLIPSE_ELLIPSE; return e; - }); - map.emplace("svg:circle", [] { + }}, + { "svg:circle", [] () -> SPObject* { auto c = new SPGenericEllipse; c->type = SP_GENERIC_ELLIPSE_CIRCLE; return c; - }); - map.emplace("arc", [] { + }}, + { "arc", [] () -> SPObject* { auto a = new SPGenericEllipse; a->type = SP_GENERIC_ELLIPSE_ARC; return a; - }); - map.emplace("svg:filter", [] { return new SPFilter; }); - map.emplace("svg:flowDiv", [] { return new SPFlowdiv; }); - map.emplace("svg:flowSpan", [] { return new SPFlowtspan; }); - map.emplace("svg:flowPara", [] { return new SPFlowpara; }); - map.emplace("svg:flowLine", [] { return new SPFlowline; }); - map.emplace("svg:flowRegionBreak", [] { return new SPFlowregionbreak; }); - map.emplace("svg:flowRegion", [] { return new SPFlowregion; }); - map.emplace("svg:flowRegionExclude", [] { return new SPFlowregionExclude; }); - map.emplace("svg:flowRoot", [] { return new SPFlowtext; }); - map.emplace("svg:font", [] { return new SPFont; }); - map.emplace("svg:font-face", [] { return new SPFontFace; }); - map.emplace("svg:glyph", [] { return new SPGlyph; }); - map.emplace("svg:hkern", [] { return new SPHkern; }); - map.emplace("svg:vkern", [] { return new SPVkern; }); - map.emplace("sodipodi:guide", [] { return new SPGuide; }); - map.emplace("inkscape:page", [] { return new SPPage; }); - map.emplace("svg:hatch", [] { return new SPHatch; }); - map.emplace("svg:hatchpath", [] { return new SPHatchPath; }); - map.emplace("svg:hatchPath", [] { + }}, + { "svg:filter", make }, + { "svg:flowDiv", make }, + { "svg:flowSpan", make }, + { "svg:flowPara", make }, + { "svg:flowLine", make }, + { "svg:flowRegionBreak", make }, + { "svg:flowRegion", make }, + { "svg:flowRegionExclude", make }, + { "svg:flowRoot", make }, + { "svg:font", make }, + { "svg:font-face", make }, + { "svg:glyph", make }, + { "svg:hkern", make }, + { "svg:vkern", make }, + { "sodipodi:guide", make }, + { "inkscape:page", make }, + { "svg:hatch", make }, + { "svg:hatchpath", make }, + { "svg:hatchPath", [] () -> SPObject* { std::cerr << "Warning: has been renamed " << std::endl; return new SPHatchPath; - }); - map.emplace("svg:image", [] { return new SPImage; }); - map.emplace("svg:g", [] { return new SPGroup; }); - map.emplace("svg:line", [] { return new SPLine; }); - map.emplace("svg:linearGradient", [] { return new SPLinearGradient; }); - map.emplace("svg:marker", [] { return new SPMarker; }); - map.emplace("svg:mask", [] { return new SPMask; }); - map.emplace("svg:mesh", [] { // SVG 2 old + }}, + { "svg:image", make }, + { "svg:g", make }, + { "svg:line", make }, + { "svg:linearGradient", make }, + { "svg:marker", make }, + { "svg:mask", make }, + { "svg:mesh", [] () -> SPObject* { // SVG 2 old std::cerr << "Warning: has been renamed ." << std::endl; std::cerr << "Warning: has been repurposed as a shape that tightly wraps a ." << std::endl; return new SPMeshGradient; - }); - map.emplace("svg:meshGradient", [] { // SVG 2 old + }}, + { "svg:meshGradient", [] () -> SPObject* { // SVG 2 old std::cerr << "Warning: has been renamed " << std::endl; return new SPMeshGradient; - }); - map.emplace("svg:meshgradient", [] { // SVG 2 + }}, + { "svg:meshgradient", [] () -> SPObject* { // SVG 2 return new SPMeshGradient; - }); - map.emplace("svg:meshPatch", [] { + }}, + { "svg:meshPatch", [] () -> SPObject* { std::cerr << "Warning: and have been renamed and " << std::endl; return new SPMeshpatch; - }); - map.emplace("svg:meshpatch", [] { return new SPMeshpatch; }); - map.emplace("svg:meshRow", [] { return new SPMeshrow; }); - map.emplace("svg:meshrow", [] { return new SPMeshrow; }); - map.emplace("svg:metadata", [] { return new SPMetadata; }); - map.emplace("svg:missing-glyph", [] { return new SPMissingGlyph; }); - map.emplace("sodipodi:namedview", [] { return new SPNamedView; }); - map.emplace("inkscape:offset", [] { return new SPOffset; }); - map.emplace("svg:path", [] { return new SPPath; }); - map.emplace("svg:pattern", [] { return new SPPattern; }); - map.emplace("svg:polygon", [] { return new SPPolygon; }); - map.emplace("svg:polyline", [] { return new SPPolyLine; }); - map.emplace("svg:radialGradient", [] { return new SPRadialGradient; }); - map.emplace("svg:rect", [] { return new SPRect; }); - map.emplace("rect", [] { return new SPRect; } ); // LPE rect; - map.emplace("svg:svg", [] { return new SPRoot; }); - map.emplace("svg:script", [] { return new SPScript; }); - map.emplace("svg:solidColor", [] { + }}, + { "svg:meshpatch", make }, + { "svg:meshRow", make }, + { "svg:meshrow", make }, + { "svg:metadata", make }, + { "svg:missing-glyph", make }, + { "sodipodi:namedview", make }, + { "inkscape:offset", make }, + { "svg:path", make }, + { "svg:pattern", make }, + { "svg:polygon", make }, + { "svg:polyline", make }, + { "svg:radialGradient", make }, + { "svg:rect", make }, + { "rect", make }, // LPE rect; + { "svg:svg", make }, + { "svg:script", make }, + { "svg:solidColor", [] () -> SPObject* { std::cerr << "Warning: has been renamed " << std::endl; return new SPSolidColor; - }); - map.emplace("svg:solidColor", [] { + }}, + { "svg:solidColor", [] () -> SPObject* { std::cerr << "Warning: has been renamed " << std::endl; return new SPSolidColor; - }); - map.emplace("svg:solidcolor", [] { return new SPSolidColor; }); - map.emplace("spiral", [] { return new SPSpiral; }); - map.emplace("star", [] { return new SPStar; }); - map.emplace("svg:stop", [] { return new SPStop; }); - map.emplace("string", [] { return new SPString; }); - map.emplace("svg:style", [] { return new SPStyleElem; }); - map.emplace("svg:switch", [] { return new SPSwitch; }); - map.emplace("svg:symbol", [] { return new SPSymbol; }); - map.emplace("inkscape:tag", [] { return new SPTag; }); - map.emplace("inkscape:tagref", [] { return new SPTagUse; }); - map.emplace("svg:text", [] { return new SPText; }); - map.emplace("svg:title", [] { return new SPTitle; }); - map.emplace("svg:tref", [] { return new SPTRef; }); - map.emplace("svg:tspan", [] { return new SPTSpan; }); - map.emplace("svg:textPath", [] { return new SPTextPath; }); - map.emplace("svg:use", [] { return new SPUse; }); - map.emplace("inkscape:path-effect", [] { return new LivePathEffectObject; }); + }}, + { "svg:solidcolor", make }, + { "spiral", make }, + { "star", make }, + { "svg:stop", make }, + { "string", make }, + { "svg:style", make }, + { "svg:switch", make }, + { "svg:symbol", make }, + { "inkscape:tag", make }, + { "inkscape:tagref", make }, + { "svg:text", make }, + { "svg:title", make }, + { "svg:tref", make }, + { "svg:tspan", make }, + { "svg:textPath", make }, + { "svg:use", make }, + { "inkscape:path-effect", make }, // filters - map.emplace("svg:feBlend", [] { return new SPFeBlend; }); - map.emplace("svg:feColorMatrix", [] { return new SPFeColorMatrix; }); - map.emplace("svg:feComponentTransfer", [] { return new SPFeComponentTransfer; }); - map.emplace("svg:feFuncR", [] { return new SPFeFuncNode(SPFeFuncNode::R); }); - map.emplace("svg:feFuncG", [] { return new SPFeFuncNode(SPFeFuncNode::G); }); - map.emplace("svg:feFuncB", [] { return new SPFeFuncNode(SPFeFuncNode::B); }); - map.emplace("svg:feFuncA", [] { return new SPFeFuncNode(SPFeFuncNode::A); }); - map.emplace("svg:feComposite", [] { return new SPFeComposite; }); - map.emplace("svg:feConvolveMatrix", [] { return new SPFeConvolveMatrix; }); - map.emplace("svg:feDiffuseLighting", [] { return new SPFeDiffuseLighting; }); - map.emplace("svg:feDisplacementMap", [] { return new SPFeDisplacementMap; }); - map.emplace("svg:feDistantLight", [] { return new SPFeDistantLight; }); - map.emplace("svg:feFlood", [] { return new SPFeFlood; }); - map.emplace("svg:feGaussianBlur", [] { return new SPGaussianBlur; }); - map.emplace("svg:feImage", [] { return new SPFeImage; }); - map.emplace("svg:feMerge", [] { return new SPFeMerge; }); - map.emplace("svg:feMergeNode", [] { return new SPFeMergeNode; }); - map.emplace("svg:feMorphology", [] { return new SPFeMorphology; }); - map.emplace("svg:feOffset", [] { return new SPFeOffset; }); - map.emplace("svg:fePointLight", [] { return new SPFePointLight; }); - map.emplace("svg:feSpecularLighting", [] { return new SPFeSpecularLighting; }); - map.emplace("svg:feSpotLight", [] { return new SPFeSpotLight; }); - map.emplace("svg:feTile", [] { return new SPFeTile; }); - map.emplace("svg:feTurbulence", [] { return new SPFeTurbulence; }); - map.emplace("inkscape:grid", [] { return new SPObject; }); // TODO wtf + { "svg:feBlend", make }, + { "svg:feColorMatrix", make }, + { "svg:feComponentTransfer", make }, + { "svg:feFuncR", [] () -> SPObject* { return new SPFeFuncNode(SPFeFuncNode::R); }}, + { "svg:feFuncG", [] () -> SPObject* { return new SPFeFuncNode(SPFeFuncNode::G); }}, + { "svg:feFuncB", [] () -> SPObject* { return new SPFeFuncNode(SPFeFuncNode::B); }}, + { "svg:feFuncA", [] () -> SPObject* { return new SPFeFuncNode(SPFeFuncNode::A); }}, + { "svg:feComposite", make }, + { "svg:feConvolveMatrix", make }, + { "svg:feDiffuseLighting", make }, + { "svg:feDisplacementMap", make }, + { "svg:feDistantLight", make }, + { "svg:feFlood", make }, + { "svg:feGaussianBlur", make }, + { "svg:feImage", make }, + { "svg:feMerge", make }, + { "svg:feMergeNode", make }, + { "svg:feMorphology", make }, + { "svg:feOffset", make }, + { "svg:fePointLight", make }, + { "svg:feSpecularLighting", make }, + { "svg:feSpotLight", make }, + { "svg:feTile", make }, + { "svg:feTurbulence", make }, + { "inkscape:grid", make }, // TODO wtf // ignore - map.emplace("rdf:RDF", [] { return nullptr; } ); // no SP node yet - map.emplace("inkscape:clipboard", [] { return nullptr; } ); // SP node not necessary - map.emplace("inkscape:templateinfo", [] { return nullptr; } ); // metadata for templates - map.emplace("inkscape:_templateinfo", [] { return nullptr; } ); // metadata for templates - map.emplace("", [] { return nullptr; } ); // comments - } - - SPObject *create(std::string const &id) - { - auto it = map.find(id); - - if (it == map.end()) { - std::cerr << "WARNING: unknown type: " << id << std::endl; - return nullptr; - } - - return it->second(); - } + { "rdf:RDF", null }, // no SP node yet + { "inkscape:clipboard", null }, // SP node not necessary + { "inkscape:templateinfo", null }, // metadata for templates + { "inkscape:_templateinfo", null }, // metadata for templates + { "", null } // comments + }; }; } // namespace SPObject *SPFactory::createObject(std::string const &id) { - static Factory factory; + static Factory const factory; return factory.create(id); } std::string NodeTraits::get_type_string(Inkscape::XML::Node const &node) { - std::string name; - switch (node.type()) { - case Inkscape::XML::NodeType::TEXT_NODE: - name = "string"; - break; - - case Inkscape::XML::NodeType::ELEMENT_NODE: { - auto sptype = node.attribute("sodipodi:type"); - name = sptype ? sptype : node.name(); - break; - } - default: - name = ""; - break; + case Inkscape::XML::NodeType::TEXT_NODE: + return "string"; + case Inkscape::XML::NodeType::ELEMENT_NODE: + if (auto sptype = node.attribute("sodipodi:type")) { + return sptype; + } + return node.name(); + default: + return ""; } - - return name; } /* diff --git a/src/object/sp-filter.h b/src/object/sp-filter.h index 25485dc2ce7b9be6081bb84eb3bb38d4c08c5700..9f6a07a9631099674056b16580c7e099f25a664e 100644 --- a/src/object/sp-filter.h +++ b/src/object/sp-filter.h @@ -26,9 +26,7 @@ namespace Inkscape { class Drawing; class DrawingItem; -namespace Filters { -class Filter; -} // namespace Filters +namespace Filters { class Filter; } } // namespace Inkscape class SPFilterReference; diff --git a/src/object/sp-flowtext.cpp b/src/object/sp-flowtext.cpp index 2ecb502f35eabf54097b6265af61dadbba89b201..a8d49c9b60b64b9612d4d9f14f03586910f22e8b 100644 --- a/src/object/sp-flowtext.cpp +++ b/src/object/sp-flowtext.cpp @@ -111,7 +111,7 @@ void SPFlowtext::update(SPCtx* ctx, unsigned int flags) { Geom::OptRect pbox = this->geometricBounds(); for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); g->setStyle(style); // pass the bbox of the flowtext object as paintbox (used for paintserver fills) @@ -133,7 +133,7 @@ void SPFlowtext::modified(unsigned int flags) { Geom::OptRect pbox = geometricBounds(); for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); g->setStyle(style); layout.show(g, pbox); @@ -325,7 +325,7 @@ void SPFlowtext::hide(unsigned key) { for (auto &v : views) { if (v.key == key) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); } } diff --git a/src/object/sp-hatch-path.cpp b/src/object/sp-hatch-path.cpp index 0e358f501e62877a4ec4bfaa798afa2165829aa9..6cf0cdb03e36edfb94eb5585fa8a0e343b70b1cd 100644 --- a/src/object/sp-hatch-path.cpp +++ b/src/object/sp-hatch-path.cpp @@ -16,11 +16,9 @@ #include #include <2geom/path.h> +#include "style.h" #include "svg/svg.h" -#include "display/cairo-utils.h" #include "display/curve.h" -#include "display/drawing-context.h" -#include "display/drawing-surface.h" #include "display/drawing.h" #include "display/drawing-shape.h" #include "helper/geom.h" @@ -29,13 +27,7 @@ #include "sp-hatch-path.h" #include "svg/css-ostringstream.h" -SPHatchPath::SPHatchPath() - : offset() - , _display() - , _continuous(false) -{ - offset.unset(); -} +SPHatchPath::SPHatchPath() = default; SPHatchPath::~SPHatchPath() = default; @@ -52,11 +44,7 @@ void SPHatchPath::build(SPDocument *doc, Inkscape::XML::Node *repr) void SPHatchPath::release() { - for (auto &iter : _display) { - delete iter.arenaitem; - iter.arenaitem = nullptr; - } - + views.clear(); SPObject::release(); } @@ -105,15 +93,15 @@ void SPHatchPath::update(SPCtx *ctx, unsigned int flags) double const aw = (ictx) ? 1.0 / ictx->i2vp.descrim() : 1.0; style->stroke_width.computed = style->stroke_width.value * aw; - for (auto &iter : _display) { - iter.arenaitem->setStyle(style); + for (auto &v : views) { + v.drawingitem->setStyle(style); } } } if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) { - for (auto &iter : _display) { - _updateView(iter); + for (auto &v : views) { + _updateView(v); } } } @@ -125,22 +113,24 @@ bool SPHatchPath::isValid() const Inkscape::DrawingItem *SPHatchPath::show(Inkscape::Drawing &drawing, unsigned int key, Geom::OptInterval extents) { - Inkscape::DrawingShape *s = new Inkscape::DrawingShape(drawing); - _display.push_front({s, extents, key}); + views.emplace_back(make_drawingitem(drawing), extents, key); + auto &v = views.back(); + auto s = v.drawingitem.get(); - _updateView(_display.front()); + _updateView(v); return s; } void SPHatchPath::hide(unsigned int key) { - for (ViewIterator iter = _display.begin(); iter != _display.end(); ++iter) { - if (iter->key == key) { - delete iter->arenaitem; - _display.erase(iter); - return; - } + auto it = std::find_if(views.begin(), views.end(), [=] (auto &v) { + return v.key == key; + }); + + if (it != views.end()) { + views.erase(it); + return; } g_assert_not_reached(); @@ -148,9 +138,9 @@ void SPHatchPath::hide(unsigned int key) void SPHatchPath::setStripExtents(unsigned int key, Geom::OptInterval const &extents) { - for (auto &iter : _display) { - if (iter.key == key) { - iter.extents = extents; + for (auto &v : views) { + if (v.key == key) { + v.extents = extents; break; } } @@ -179,9 +169,9 @@ Geom::Interval SPHatchPath::bounds() const SPCurve SPHatchPath::calculateRenderCurve(unsigned key) const { - for (auto const &iter : _display) { - if (iter.key == key) { - return _calculateRenderCurve(iter); + for (auto const &v : views) { + if (v.key == key) { + return _calculateRenderCurve(v); } } g_assert_not_reached(); @@ -204,10 +194,10 @@ void SPHatchPath::_updateView(View &view) auto calculated_curve = _calculateRenderCurve(view); Geom::Affine offset_transform = Geom::Translate(offset.computed, 0); - view.arenaitem->setTransform(offset_transform); + view.drawingitem->setTransform(offset_transform); style->fill.setNone(); - view.arenaitem->setStyle(style); - view.arenaitem->setPath(std::make_shared(std::move(calculated_curve))); + view.drawingitem->setStyle(style); + view.drawingitem->setPath(std::make_shared(std::move(calculated_curve))); } SPCurve SPHatchPath::_calculateRenderCurve(View const &view) const @@ -278,6 +268,11 @@ void SPHatchPath::_readHatchPathVector(char const *str, Geom::PathVector &pathv, } } +SPHatchPath::View::View(DrawingItemPtr drawingitem, Geom::OptInterval const &extents, unsigned key) + : drawingitem(std::move(drawingitem)) + , extents(extents) + , key(key) {} + /* Local Variables: mode:c++ diff --git a/src/object/sp-hatch-path.h b/src/object/sp-hatch-path.h index 76945f26b6c405e22c282bbe6d4060016c3bbff6..da60589862bba8dcf43c67239fbc2eb2a6c501f2 100644 --- a/src/object/sp-hatch-path.h +++ b/src/object/sp-hatch-path.h @@ -16,7 +16,7 @@ #ifndef SEEN_SP_HATCH_PATH_H #define SEEN_SP_HATCH_PATH_H -#include +#include #include #include #include @@ -27,6 +27,7 @@ #include "svg/svg-length.h" #include "object/sp-object.h" #include "display/curve.h" +#include "display/drawing-item-ptr.h" namespace Inkscape { @@ -63,14 +64,12 @@ protected: private: struct View { - Inkscape::DrawingShape *arenaitem; + DrawingItemPtr drawingitem; Geom::OptInterval extents; unsigned key; + View(DrawingItemPtr drawingitem, Geom::OptInterval const &extents, unsigned key); }; - - using ViewIterator = std::list::iterator; - using ConstViewIterator = std::list::const_iterator; - std::list _display; + std::vector views; gdouble _repeatLength() const; void _updateView(View &view); @@ -79,7 +78,7 @@ private: void _readHatchPathVector(char const *str, Geom::PathVector &pathv, bool &continous_join); std::optional _curve; - bool _continuous; + bool _continuous = false; }; #endif // SEEN_SP_HATCH_PATH_H diff --git a/src/object/sp-hatch.cpp b/src/object/sp-hatch.cpp index 69140338d561b7d48fcdd99a2c912f1f354c5d81..47f505dc64cf588da532a7b6a743f6a14084c9e9 100644 --- a/src/object/sp-hatch.cpp +++ b/src/object/sp-hatch.cpp @@ -21,13 +21,11 @@ #include <2geom/transforms.h> #include +#include "style.h" #include "attributes.h" #include "bad-uri-exception.h" #include "document.h" -#include "display/cairo-utils.h" -#include "display/drawing-context.h" -#include "display/drawing-surface.h" #include "display/drawing.h" #include "display/drawing-pattern.h" @@ -38,21 +36,12 @@ #include "svg/svg.h" SPHatch::SPHatch() - : SPPaintServer(), - href(), - ref(nullptr), // avoiding 'this' in initializer list + : ref(nullptr), // avoiding 'this' in initializer list _hatchUnits(UNITS_OBJECTBOUNDINGBOX), _hatchUnits_set(false), _hatchContentUnits(UNITS_USERSPACEONUSE), _hatchContentUnits_set(false), - _hatchTransform(Geom::identity()), - _hatchTransform_set(false), - _x(), - _y(), - _pitch(), - _rotate(), - _modified_connection(), - _display() + _hatchTransform_set(false) { ref = new SPHatchReference(this); ref->changedSignal().connect(sigc::mem_fun(*this, &SPHatch::_onRefChanged)); @@ -91,14 +80,14 @@ void SPHatch::release() document->removeResource("hatch", this); } - std::vector children(hatchPaths()); - for (auto &view_iter : _display) { + auto children = hatchPaths(); + for (auto &v : views) { for (auto child : children) { - child->hide(view_iter.key); + child->hide(v.key); } - delete view_iter.arenaitem; - view_iter.arenaitem = nullptr; + v.drawingitem.reset(); } + views.clear(); if (ref) { _modified_connection.disconnect(); @@ -117,13 +106,13 @@ void SPHatch::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) SPHatchPath *path_child = dynamic_cast(document->getObjectByRepr(child)); if (path_child) { - for (auto & iter : _display) { - Geom::OptInterval extents = _calculateStripExtents(iter.bbox); - Inkscape::DrawingItem *ac = path_child->show(iter.arenaitem->drawing(), iter.key, extents); + for (auto &v : views) { + Geom::OptInterval extents = _calculateStripExtents(v.bbox); + auto ac = path_child->show(v.drawingitem->drawing(), v.key, extents); path_child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (ac) { - iter.arenaitem->prependChild(ac); + v.drawingitem->prependChild(ac); } } } @@ -294,9 +283,9 @@ void SPHatch::update(SPCtx* ctx, unsigned int flags) for (auto child : children) { sp_object_ref(child, nullptr); - for (auto &view_iter : _display) { - Geom::OptInterval strip_extents = _calculateStripExtents(view_iter.bbox); - child->setStripExtents(view_iter.key, strip_extents); + for (auto &v : views) { + Geom::OptInterval strip_extents = _calculateStripExtents(v.bbox); + child->setStripExtents(v.key, strip_extents); } if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { @@ -306,8 +295,8 @@ void SPHatch::update(SPCtx* ctx, unsigned int flags) sp_object_unref(child, nullptr); } - for (auto &iter : _display) { - _updateView(iter); + for (auto &v : views) { + _updateView(v); } } @@ -360,17 +349,17 @@ void SPHatch::_onRefChanged(SPObject *old_ref, SPObject *ref) } if (old_shown != new_shown) { - for (auto & iter : _display) { - Geom::OptInterval extents = _calculateStripExtents(iter.bbox); + for (auto &v : views) { + Geom::OptInterval extents = _calculateStripExtents(v.bbox); for (auto child : oldhatchPaths) { - child->hide(iter.key); + child->hide(v.key); } for (auto child : newhatchPaths) { - Inkscape::DrawingItem *cai = child->show(iter.arenaitem->drawing(), iter.key, extents); + auto cai = child->show(v.drawingitem->drawing(), v.key, extents); child->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); if (cai) { - iter.arenaitem->appendChild(cai); + v.drawingitem->appendChild(cai); } } @@ -549,12 +538,14 @@ bool SPHatch::isValid() const bool valid = false; if (pitch() > 0) { - std::vector children(hatchPaths()); + auto children = hatchPaths(); if (!children.empty()) { valid = true; - for (ConstChildIterator iter = children.begin(); (iter != children.end()) && valid; ++iter) { - SPHatchPath const *child = *iter; - valid = child->isValid(); + for (auto c : children) { + valid = c->isValid(); + if (!valid) { + break; + } } } } @@ -564,10 +555,11 @@ bool SPHatch::isValid() const Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned key, Geom::OptRect const &bbox) { - Inkscape::DrawingPattern *ai = new Inkscape::DrawingPattern(drawing); - _display.push_front({ai, bbox, key}); + views.emplace_back(make_drawingitem(drawing), bbox, key); + auto &v = views.back(); + auto ai = v.drawingitem.get(); - std::vector children(hatchPaths()); + auto children = hatchPaths(); Geom::OptInterval extents = _calculateStripExtents(bbox); for (auto child : children) { @@ -577,8 +569,7 @@ Inkscape::DrawingPattern *SPHatch::show(Inkscape::Drawing &drawing, unsigned key } } - View &view = _display.front(); - _updateView(view); + _updateView(v); return ai; } @@ -591,12 +582,13 @@ void SPHatch::hide(unsigned int key) child->hide(key); } - for (ViewIterator iter = _display.begin(); iter != _display.end(); ++iter) { - if (iter->key == key) { - delete iter->arenaitem; - _display.erase(iter); - return; - } + auto it = std::find_if(views.begin(), views.end(), [=] (auto &v) { + return v.key == key; + }); + + if (it != views.end()) { + views.erase(it); + return; } g_assert_not_reached(); @@ -605,7 +597,7 @@ void SPHatch::hide(unsigned int key) Geom::Interval SPHatch::bounds() const { Geom::Interval result; - std::vector children(hatchPaths()); + auto children = hatchPaths(); for (auto child : children) { if (result.extent() == 0) { @@ -620,9 +612,9 @@ Geom::Interval SPHatch::bounds() const SPHatch::RenderInfo SPHatch::calculateRenderInfo(unsigned key) const { RenderInfo info; - for (auto const &iter : _display) { - if (iter.key == key) { - return _calculateRenderInfo(iter); + for (auto const &v : views) { + if (v.key == key) { + return _calculateRenderInfo(v); } } g_assert_not_reached(); @@ -638,11 +630,11 @@ void SPHatch::_updateView(View &view) //as drawing whole strips in left-to-right order. - view.arenaitem->setChildTransform(info.child_transform); - view.arenaitem->setPatternToUserTransform(info.pattern_to_user_transform); - view.arenaitem->setTileRect(info.tile_rect); - view.arenaitem->setStyle(style); - view.arenaitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, info.overflow_step_transform); + view.drawingitem->setChildTransform(info.child_transform); + view.drawingitem->setPatternToUserTransform(info.pattern_to_user_transform); + view.drawingitem->setTileRect(info.tile_rect); + view.drawingitem->setStyle(style); + view.drawingitem->setOverflow(info.overflow_initial_transform, info.overflow_steps, info.overflow_step_transform); } SPHatch::RenderInfo SPHatch::_calculateRenderInfo(View const &view) const @@ -744,14 +736,19 @@ Geom::OptInterval SPHatch::_calculateStripExtents(Geom::OptRect const &bbox) con void SPHatch::setBBox(unsigned int key, Geom::OptRect const &bbox) { - for (auto & iter : _display) { - if (iter.key == key) { - iter.bbox = bbox; + for (auto &v : views) { + if (v.key == key) { + v.bbox = bbox; break; } } } +SPHatch::View::View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key) + : drawingitem(std::move(drawingitem)) + , bbox(bbox) + , key(key) {} + /* Local Variables: mode:c++ diff --git a/src/object/sp-hatch.h b/src/object/sp-hatch.h index c4b64d40671d828cbc4c2a2b3edb23ba2eb3b284..fe25a472eb950cf86b31a9968f3d4a4341331745 100644 --- a/src/object/sp-hatch.h +++ b/src/object/sp-hatch.h @@ -16,7 +16,7 @@ #ifndef SEEN_SP_HATCH_H #define SEEN_SP_HATCH_H -#include +#include #include #include #include @@ -25,6 +25,7 @@ #include "svg/svg-angle.h" #include "sp-paint-server.h" #include "uri-references.h" +#include "display/drawing-item-ptr.h" class SPHatchReference; class SPHatchPath; @@ -33,9 +34,7 @@ class SPItem; namespace Inkscape { class Drawing; class DrawingPattern; -namespace XML { -class Node; -} // namespace XML +namespace XML { class Node; } } // namespace Inkscape #define SP_HATCH(obj) (dynamic_cast((SPObject *)obj)) @@ -103,15 +102,12 @@ protected: private: struct View { - Inkscape::DrawingPattern *arenaitem; + DrawingItemPtr drawingitem; Geom::OptRect bbox; unsigned key; + View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key); }; - - using ChildIterator = std::vector::iterator; - using ConstChildIterator = std::vector::const_iterator; - using ViewIterator = std::list::iterator; - using ConstViewIterator = std::list::const_iterator; + std::vector views; static bool _hasHatchPatchChildren(SPHatch const *hatch); @@ -151,8 +147,6 @@ private: SVGAngle _rotate; sigc::connection _modified_connection; - - std::list _display; }; class SPHatchReference : public Inkscape::URIReference diff --git a/src/object/sp-image.cpp b/src/object/sp-image.cpp index e3e832ae0a3394966a4df7dcc34715c387ee64ae..b73138acf8a68013442cb1a121f16a2aa57dd0ca 100644 --- a/src/object/sp-image.cpp +++ b/src/object/sp-image.cpp @@ -429,13 +429,12 @@ void SPImage::modified(unsigned int flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - auto img = dynamic_cast(v.drawingitem); + auto img = dynamic_cast(v.drawingitem.get()); img->setStyle(style); } } } - Inkscape::XML::Node *SPImage::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags ) { if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { repr = xml_doc->createElement("svg:image"); @@ -667,7 +666,7 @@ sp_image_update_arenaitem (SPImage *image, Inkscape::DrawingImage *ai) static void sp_image_update_canvas_image(SPImage *image) { for (auto &v : image->views) { - sp_image_update_arenaitem(image, dynamic_cast(v.drawingitem)); + sp_image_update_arenaitem(image, dynamic_cast(v.drawingitem.get())); } } diff --git a/src/object/sp-item-group.cpp b/src/object/sp-item-group.cpp index 8be26cb1ab6345b0c71eb61b4706649f625650af..b93c1a189eb8e50c0c7aa003f1eb9810dbc82407 100644 --- a/src/object/sp-item-group.cpp +++ b/src/object/sp-item-group.cpp @@ -172,7 +172,7 @@ void SPGroup::update(SPCtx *ctx, unsigned int flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - Inkscape::DrawingGroup *group = dynamic_cast(v.drawingitem); + auto group = dynamic_cast(v.drawingitem.get()); if( this->parent ) { this->context_style = this->parent->context_style; } @@ -192,7 +192,7 @@ void SPGroup::modified(guint flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - Inkscape::DrawingGroup *group = dynamic_cast(v.drawingitem); + auto group = dynamic_cast(v.drawingitem.get()); group->setStyle(this->style); } } @@ -803,7 +803,7 @@ void SPGroup::setLayerDisplayMode(unsigned int dkey, SPGroup::LayerMode mode) { void SPGroup::_updateLayerMode(unsigned int display_key) { for (auto &v : views) { if (!display_key || v.key == display_key) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); if (g) { g->setPickChildren(effectiveLayerMode(v.key) == SPGroup::LAYER); } @@ -834,9 +834,9 @@ void SPGroup::scaleChildItemsRec(Geom::Scale const &sc, Geom::Point const &p, bo if (defsgroup) defsgroup->scaleChildItemsRec(sc, p, false); } - } else if ( SPItem *item = dynamic_cast(&o) ) { - SPGroup *group = dynamic_cast(item); - if (group && !dynamic_cast(item)) { + } else if (auto item = dynamic_cast(&o)) { + auto group = dynamic_cast(item); + if (group && !dynamic_cast(item)) { /* Using recursion breaks clipping because transforms are applied in coordinates for draws but nothing in defs is changed instead change the transform on the entire group, and the transform diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index 22eef19f85d10db6149757a96e36b941cf658142..46115dfb9b0d7a3e64418837c5dba322e2cfae40 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -66,6 +66,11 @@ //#define OBJECT_TRACE +SPItemView::SPItemView(unsigned flags, unsigned key, DrawingItemPtr drawingitem) + : flags(flags) + , key(key) + , drawingitem(std::move(drawingitem)) {} + SPItem::SPItem() { sensitive = TRUE; @@ -171,16 +176,16 @@ void SPItem::setHidden(bool hide) { updateRepr(); } -bool SPItem::isHidden(unsigned display_key) const { - if (!isEvaluated()) +bool SPItem::isHidden(unsigned display_key) const +{ + if (!isEvaluated()) { return true; + } for (auto &v : views) { if (v.key == display_key) { - g_assert(v.drawingitem != nullptr); - for ( Inkscape::DrawingItem *arenaitem = v.drawingitem ; - arenaitem ; arenaitem = arenaitem->parent() ) - { - if (!arenaitem->visible()) { + g_assert(v.drawingitem); + for (auto di = v.drawingitem.get(); di; di = di->parent()) { + if (!di->visible()) { return true; } } @@ -471,9 +476,9 @@ void SPItem::release() stroke_ps->hide(v.drawingitem->key() + ITEM_KEY_STROKE); } if (filter) { - filter->hide(v.drawingitem); + filter->hide(v.drawingitem.get()); } - delete v.drawingitem; + v.drawingitem.reset(); } views.clear(); } @@ -605,7 +610,7 @@ void SPItem::clip_ref_changed(SPObject *old_clip, SPObject *clip) if (clipPath) { Geom::OptRect bbox = geometricBounds(); for (auto &v : views) { - auto clip_key = SPItem::ensure_key(v.drawingitem) + ITEM_KEY_CLIP; + auto clip_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_CLIP; auto ai = clipPath->show(v.drawingitem->drawing(), clip_key, bbox); v.drawingitem->setClip(ai); } @@ -632,7 +637,7 @@ void SPItem::mask_ref_changed(SPObject *old_mask, SPObject *mask) if (maskItem) { Geom::OptRect bbox = geometricBounds(); for (auto &v : views) { - auto mask_key = SPItem::ensure_key(v.drawingitem) + ITEM_KEY_MASK; + auto mask_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_MASK; auto ai = maskItem->show(v.drawingitem->drawing(), mask_key, bbox); v.drawingitem->setMask(ai); } @@ -658,7 +663,7 @@ void SPItem::fill_ps_ref_changed(SPObject *old_ps, SPObject *ps) if (new_fill_ps) { Geom::OptRect bbox = geometricBounds(); for (auto &v : views) { - auto fill_key = SPItem::ensure_key(v.drawingitem) + ITEM_KEY_FILL; + auto fill_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_FILL; auto pi = new_fill_ps->show(v.drawingitem->drawing(), fill_key, bbox); v.drawingitem->setFillPattern(pi); } @@ -678,7 +683,7 @@ void SPItem::stroke_ps_ref_changed(SPObject *old_ps, SPObject *ps) if (new_stroke_ps) { Geom::OptRect bbox = geometricBounds(); for (auto &v : views) { - auto stroke_key = SPItem::ensure_key(v.drawingitem) + ITEM_KEY_STROKE; + auto stroke_key = SPItem::ensure_key(v.drawingitem.get()) + ITEM_KEY_STROKE; auto pi = new_stroke_ps->show(v.drawingitem->drawing(), stroke_key, bbox); v.drawingitem->setStrokePattern(pi); } @@ -690,14 +695,14 @@ void SPItem::filter_ref_changed(SPObject *old_obj, SPObject *obj) auto old_filter = dynamic_cast(old_obj); if (old_filter) { for (auto &v : views) { - old_filter->hide(v.drawingitem); + old_filter->hide(v.drawingitem.get()); } } auto new_filter = dynamic_cast(obj); if (new_filter) { for (auto &v : views) { - new_filter->show(v.drawingitem); + new_filter->show(v.drawingitem.get()); } } } @@ -1209,7 +1214,7 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned ai->setBlendMode(style->mix_blend_mode.value); ai->setVisible(!isHidden()); ai->setSensitive(sensitive); - views.push_back({flags, key, ai}); + views.emplace_back(flags, key, DrawingItemPtr(ai)); if (auto clip = getClipObject()) { auto clip_key = SPItem::ensure_key(ai) + ITEM_KEY_CLIP; @@ -1265,9 +1270,10 @@ void SPItem::invoke_hide(unsigned key) stroke_ps->hide(ai_key + ITEM_KEY_STROKE); } if (auto filter = style->getFilter()) { - filter->hide(v.drawingitem); + filter->hide(v.drawingitem.get()); } - delete v.drawingitem; + + v.drawingitem.reset(); *it = std::move(views.back()); views.pop_back(); @@ -1775,10 +1781,9 @@ Inkscape::DrawingItem *SPItem::get_arenaitem(unsigned key) { for (auto &v : views) { if (v.key == key) { - return v.drawingitem; + return v.drawingitem.get(); } } - return nullptr; } diff --git a/src/object/sp-item.h b/src/object/sp-item.h index 5d912e12008e2ff84f2a996a2ba6969159a77d50..abb0d6648aa24d0c65b4ae337becfe7898ab0492 100644 --- a/src/object/sp-item.h +++ b/src/object/sp-item.h @@ -28,7 +28,7 @@ #include "sp-object.h" #include "sp-marker-loc.h" - +#include "display/drawing-item-ptr.h" #include "xml/repr.h" class SPGroup; @@ -90,7 +90,8 @@ struct SPItemView { unsigned flags; unsigned key; - Inkscape::DrawingItem *drawingitem; + DrawingItemPtr drawingitem; + SPItemView(unsigned flags, unsigned key, DrawingItemPtr drawingitem); }; enum SPItemKey diff --git a/src/object/sp-marker.cpp b/src/object/sp-marker.cpp index 48fb4f99b94063045a2ba0720385b1e1c550987c..169c2d14152ee1b15aef261e5f5a0ccb9601acd1 100644 --- a/src/object/sp-marker.cpp +++ b/src/object/sp-marker.cpp @@ -31,28 +31,19 @@ #include "sp-defs.h" #include "display/drawing-group.h" +#include "display/drawing-item-ptr.h" #include "object/object-set.h" #include "svg/css-ostringstream.h" #include "svg/svg.h" #include "ui/icon-names.h" #include "xml/repr.h" - using Inkscape::DocumentUndo; using Inkscape::ObjectSet; -class SPMarkerView { - -public: - - SPMarkerView() = default;; - ~SPMarkerView() { - for (auto & item : items) { - delete item; - } - items.clear(); - } - std::vector items; +struct SPMarkerView +{ + std::vector> items; }; SPMarker::SPMarker() : SPGroup(), SPViewBox(), @@ -115,16 +106,14 @@ void SPMarker::build(SPDocument *document, Inkscape::XML::Node *repr) { */ void SPMarker::release() { - std::map::iterator it; - for (it = views_map.begin(); it != views_map.end(); ++it) { - SPGroup::hide( it->first ); + for (auto &it : views_map) { + SPGroup::hide(it.first); } views_map.clear(); SPGroup::release(); } - void SPMarker::set(SPAttr key, const gchar* value) { switch (key) { case SPAttr::MARKERUNITS: @@ -227,11 +216,10 @@ void SPMarker::update(SPCtx *ctx, guint flags) { SPGroup::update((SPCtx *) &rctx, flags); // As last step set additional transform of drawing group - std::map::iterator it; - for (it = views_map.begin(); it != views_map.end(); ++it) { - for (auto & item : it->second.items) { + for (auto &it : views_map) { + for (auto &item : it.second.items) { if (item) { - Inkscape::DrawingGroup *g = dynamic_cast(item); + auto g = dynamic_cast(item.get()); g->setChildTransform(this->c2p); } } @@ -410,7 +398,7 @@ void SPMarker::print(SPPrintContext* /*ctx*/) { void sp_marker_show_dimension (SPMarker *marker, unsigned int key, unsigned int size) { - std::map::iterator it = marker->views_map.find(key); + auto it = marker->views_map.find(key); if (it != marker->views_map.end()) { if (it->second.items.size() != size ) { // Need to change size of vector! (We should not really need to do this.) @@ -444,29 +432,30 @@ sp_marker_show_instance ( SPMarker *marker, Inkscape::DrawingItem *parent, return nullptr; } - std::map::iterator it = marker->views_map.find(key); + auto it = marker->views_map.find(key); if (it == marker->views_map.end()) { // Key not found return nullptr; } - SPMarkerView *view = &(it->second); + SPMarkerView *view = &it->second; if (pos >= view->items.size() ) { // Position index too large, doesn't exist. return nullptr; } // If not already created - if (view->items[pos] == nullptr) { + if (!view->items[pos]) { /* Parent class ::show method */ - view->items[pos] = marker->private_show(parent->drawing(), key, SP_ITEM_REFERENCE_FLAGS); + view->items[pos].reset(marker->private_show(parent->drawing(), key, SP_ITEM_REFERENCE_FLAGS)); if (view->items[pos]) { /* fixme: Position (Lauris) */ - parent->prependChild(view->items[pos]); - Inkscape::DrawingGroup *g = dynamic_cast(view->items[pos]); - if (g) g->setChildTransform(marker->c2p); + parent->prependChild(view->items[pos].get()); + if (auto g = dynamic_cast(view->items[pos].get())) { + g->setChildTransform(marker->c2p); + } } } @@ -489,7 +478,7 @@ sp_marker_show_instance ( SPMarker *marker, Inkscape::DrawingItem *parent, view->items[pos]->setTransform(m); } - return view->items[pos]; + return view->items[pos].get(); } /** diff --git a/src/object/sp-mask.cpp b/src/object/sp-mask.cpp index 6d96d0f7b11b2ecda135e489fe9e8579e8501d57..588e4406e50a63b30d49894c129696e41971282a 100644 --- a/src/object/sp-mask.cpp +++ b/src/object/sp-mask.cpp @@ -220,7 +220,7 @@ char const *SPMask::create(std::vector &reprs, SPDocument Inkscape::DrawingItem *SPMask::show(Inkscape::Drawing &drawing, unsigned key, Geom::OptRect const &bbox) { - views.emplace_back(std::make_unique(drawing), bbox, key); + views.emplace_back(make_drawingitem(drawing), bbox, key); auto &v = views.back(); auto root = v.drawingitem.get(); @@ -266,7 +266,7 @@ void SPMask::setBBox(unsigned key, Geom::OptRect const &bbox) update_view(v); } -SPMask::View::View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key) +SPMask::View::View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key) : drawingitem(std::move(drawingitem)) , bbox(bbox) , key(key) {} diff --git a/src/object/sp-mask.h b/src/object/sp-mask.h index 92a248c00624c7b4cb6822264efdb208cbbbca3a..d73795d09fe4cd6a95110f7cdde9c53aae550941 100644 --- a/src/object/sp-mask.h +++ b/src/object/sp-mask.h @@ -19,6 +19,7 @@ #include <2geom/rect.h> #include "sp-object-group.h" #include "uri-references.h" +#include "display/drawing-item-ptr.h" #include "xml/node.h" namespace Inkscape { @@ -67,10 +68,10 @@ private: struct View { - std::unique_ptr drawingitem; + DrawingItemPtr drawingitem; Geom::OptRect bbox; unsigned key; - View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key); + View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key); }; std::vector views; void update_view(View &v); diff --git a/src/object/sp-object.h b/src/object/sp-object.h index 77bd0c9802c9f1977c2352a7466a831e5e1ff15e..5f6f7d3ab5291367865c6cf61f078a839604188f 100644 --- a/src/object/sp-object.h +++ b/src/object/sp-object.h @@ -167,6 +167,8 @@ public: }; SPObject(); + SPObject(SPObject const &) = delete; + SPObject &operator=(SPObject const &) = delete; virtual ~SPObject(); unsigned int cloned : 1; @@ -181,9 +183,6 @@ public: SPObject *parent; /* Our parent (only one allowed) */ private: - SPObject(const SPObject&); - SPObject& operator=(const SPObject&); - char *id; /* Our very own unique id */ Inkscape::XML::Node *repr; /* Our xml representation */ diff --git a/src/object/sp-pattern.cpp b/src/object/sp-pattern.cpp index 0cb5e049dec197261a774db320cfb1646ff74216..a21a7faa5f70138a883be06429ff8a5c4da1425d 100644 --- a/src/object/sp-pattern.cpp +++ b/src/object/sp-pattern.cpp @@ -680,7 +680,7 @@ bool SPPattern::isValid() const Inkscape::DrawingPattern *SPPattern::show(Inkscape::Drawing &drawing, unsigned key, Geom::OptRect const &bbox) { - views.emplace_back(std::make_unique(drawing), bbox, key); + views.emplace_back(make_drawingitem(drawing), bbox, key); auto &v = views.back(); auto root = v.drawingitem.get(); @@ -724,7 +724,7 @@ void SPPattern::setBBox(unsigned key, Geom::OptRect const &bbox) update_view(v); } -SPPattern::View::View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key) +SPPattern::View::View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key) : drawingitem(std::move(drawingitem)) , bbox(bbox) , key(key) {} diff --git a/src/object/sp-pattern.h b/src/object/sp-pattern.h index c1134250ae345b58777b84954058a171a873e4a5..5cc3557f374e28fcf91a9c0ad695699dffe687f1 100644 --- a/src/object/sp-pattern.h +++ b/src/object/sp-pattern.h @@ -24,6 +24,7 @@ #include "sp-paint-server.h" #include "uri-references.h" #include "viewbox.h" +#include "display/drawing-item-ptr.h" class SPPattern; class SPItem; @@ -163,10 +164,10 @@ private: struct View { - std::unique_ptr drawingitem; + DrawingItemPtr drawingitem; Geom::OptRect bbox; unsigned key; - View(std::unique_ptr drawingitem, Geom::OptRect const &bbox, unsigned key); + View(DrawingItemPtr drawingitem, Geom::OptRect const &bbox, unsigned key); }; std::vector views; void update_view(View &v); diff --git a/src/object/sp-root.cpp b/src/object/sp-root.cpp index 8ed924600c118bef59bab054efe20074c37f5401..d77ea6c9fd5d87e659fc2db5650a5112f8512218 100644 --- a/src/object/sp-root.cpp +++ b/src/object/sp-root.cpp @@ -287,7 +287,7 @@ void SPRoot::update(SPCtx *ctx, guint flags) /* As last step set additional transform of drawing group */ for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); g->setChildTransform(c2p); } } diff --git a/src/object/sp-shape.cpp b/src/object/sp-shape.cpp index 826a814cf1b6d38515dc58d2b6a6b8be91054620..9e5803e338e5c2c3ae2831907a1a8d808d268c7d 100644 --- a/src/object/sp-shape.cpp +++ b/src/object/sp-shape.cpp @@ -138,7 +138,7 @@ void SPShape::update(SPCtx* ctx, guint flags) { this->style->stroke_width.computed = this->style->stroke_width.value * aw; for (auto &v : views) { - auto sh = dynamic_cast(v.drawingitem); + auto sh = dynamic_cast(v.drawingitem.get()); if (hasMarkers()) { context_style = style; sh->setStyle(style, context_style); @@ -157,7 +157,7 @@ void SPShape::update(SPCtx* ctx, guint flags) { /* But on the other hand - how can we know that parent does not tie style and transform */ for (auto &v : views) { if (flags & SP_OBJECT_MODIFIED_FLAG) { - auto sh = static_cast(v.drawingitem); + auto sh = static_cast(v.drawingitem.get()); sh->setPath(_curve); } } @@ -167,7 +167,7 @@ void SPShape::update(SPCtx* ctx, guint flags) { /* Dimension marker views */ for (auto &v : views) { - SPItem::ensure_key(v.drawingitem); + SPItem::ensure_key(v.drawingitem.get()); for (int i = 0; i < SP_MARKER_LOC_QTY; i++) { if (_marker[i]) { sp_marker_show_dimension(_marker[i], v.drawingitem->key() + ITEM_KEY_MARKERS + i, numberOfMarkers(i)); @@ -177,12 +177,12 @@ void SPShape::update(SPCtx* ctx, guint flags) { /* Update marker views */ for (auto &v : views) { - sp_shape_update_marker_view (this, v.drawingitem); + sp_shape_update_marker_view (this, v.drawingitem.get()); } // Marker selector needs this here or marker previews are not rendered. for (auto &v : views) { - auto sh = static_cast(v.drawingitem); + auto sh = static_cast(v.drawingitem.get()); sh->setChildrenStyle(this->context_style); // Resolve 'context-xxx' in children. } } @@ -432,7 +432,7 @@ void SPShape::modified(unsigned int flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - Inkscape::DrawingShape *sh = dynamic_cast(v.drawingitem); + Inkscape::DrawingShape *sh = dynamic_cast(v.drawingitem.get()); if (hasMarkers()) { this->context_style = this->style; sh->setStyle(this->style, this->context_style); diff --git a/src/object/sp-symbol.cpp b/src/object/sp-symbol.cpp index 51c6694b9a4f449964c2a29e96521cd58956f361..51c38b4f1e66606dbd195b67a044695c1160e172 100644 --- a/src/object/sp-symbol.cpp +++ b/src/object/sp-symbol.cpp @@ -213,7 +213,7 @@ void SPSymbol::update(SPCtx *ctx, guint flags) { // As last step set additional transform of drawing group for (auto &v : views) { - Inkscape::DrawingGroup *g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); g->setChildTransform(this->c2p); } } else { diff --git a/src/object/sp-text.cpp b/src/object/sp-text.cpp index 0421a548f308c3c1bdfa8c43fbbd856b0a7e6aee..3e6767d1a41aa08c451dc494540537ed5e9bf5c3 100644 --- a/src/object/sp-text.cpp +++ b/src/object/sp-text.cpp @@ -195,7 +195,7 @@ void SPText::update(SPCtx *ctx, guint flags) { Geom::OptRect paintbox = this->geometricBounds(); for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); g->setStyle(style, parent->style); // pass the bbox of this as paintbox (used for paintserver fills) @@ -221,7 +221,7 @@ void SPText::modified(guint flags) { Geom::OptRect paintbox = geometricBounds(); for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); g->setStyle(style, parent->style); layout.show(g, paintbox); @@ -317,7 +317,7 @@ void SPText::hide(unsigned key) { for (auto &v : views) { if (v.key == key) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); _clearFlow(g); } } diff --git a/src/object/sp-use.cpp b/src/object/sp-use.cpp index ac81fdc6cbc02c033ca59bb13dd9992c9eb68835..b0d4892beb2a45c0f2d7153c1245ee9aac73de51 100644 --- a/src/object/sp-use.cpp +++ b/src/object/sp-use.cpp @@ -687,7 +687,7 @@ void SPUse::update(SPCtx *ctx, unsigned flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); context_style = style; g->setStyle(style, context_style); } @@ -695,7 +695,7 @@ void SPUse::update(SPCtx *ctx, unsigned flags) { /* As last step set additional transform of arena group */ for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); auto t = Geom::Translate(x.computed, y.computed); g->setChildTransform(t); } @@ -711,7 +711,7 @@ void SPUse::modified(unsigned int flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (auto &v : views) { - auto g = dynamic_cast(v.drawingitem); + auto g = dynamic_cast(v.drawingitem.get()); context_style = style; g->setStyle(style, context_style); } diff --git a/src/ui/dialog/export-batch.cpp b/src/ui/dialog/export-batch.cpp index 23a2c4430cba1b19a41f115a7a874e77ecefaea1..3c438117c115fc447abfc5df6842dfefaec5b311 100644 --- a/src/ui/dialog/export-batch.cpp +++ b/src/ui/dialog/export-batch.cpp @@ -122,7 +122,7 @@ void BatchItem::refresh(bool hide, guint32 bg_color) _preview.setDbox(b.left(), b.right(), b.top(), b.bottom()); } - _preview.set_background_color(bg_color); + _preview.setBackgroundColor(bg_color); // When hiding the preview, we show the items as a checklist // So all items must be packed differently on refresh. @@ -387,7 +387,7 @@ void BatchExport::refreshPreview() selected = std::vector(sels.begin(), sels.end()); } } - val->refreshHide(selected); + val->refreshHide(std::move(selected)); } val->refresh(!preview, _bgnd_color_picker->get_current_color()); } diff --git a/src/ui/dialog/export-batch.h b/src/ui/dialog/export-batch.h index 7d3088762378e0518de00ed86b870d10092ed059..6c13e22f2c494e42a7bc5968fcc838bb75c570d2 100644 --- a/src/ui/dialog/export-batch.h +++ b/src/ui/dialog/export-batch.h @@ -53,7 +53,7 @@ public: SPPage *getPage() { return _page; } bool isActive() { return _selector.get_active(); } void refresh(bool hide, guint32 bg_color); - void refreshHide(const std::vector &list) { _preview.refreshHide(list); } + void refreshHide(std::vector &&list) { _preview.refreshHide(std::move(list)); } void setDocument(SPDocument *doc) { _preview.setDocument(doc); } private: diff --git a/src/ui/dialog/export-single.cpp b/src/ui/dialog/export-single.cpp index d2f5c6a3d38174664674168b8f118601fe3c56c2..1ade5e859515c0582d2b82b4081779c7a29f1e93 100644 --- a/src/ui/dialog/export-single.cpp +++ b/src/ui/dialog/export-single.cpp @@ -962,8 +962,8 @@ void SingleExport::refreshPreview() float y0 = unit->convert(spin_buttons[SPIN_Y0]->get_value(), "px"); float y1 = unit->convert(spin_buttons[SPIN_Y1]->get_value(), "px"); preview->setDbox(x0, x1, y0, y1); - preview->set_background_color(_bgnd_color_picker->get_current_color()); - preview->refreshHide(selected); + preview->setBackgroundColor(_bgnd_color_picker->get_current_color()); + preview->refreshHide(std::move(selected)); preview->queueRefresh(); } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 815f174d0fd9be9d1117fc5a7041201f3bc3a412..237511c7b564f64067ede1bc874bc0a1556aae27 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -2655,7 +2655,7 @@ void InkscapePreferences::initPageBehavior() void InkscapePreferences::initPageRendering() { /* threaded blur */ //related comments/widgets/functions should be renamed and option should be moved elsewhere when inkscape is fully multi-threaded - _filter_multi_threaded.init("/options/threading/numthreads", 1.0, 8.0, 1.0, 2.0, 4.0, true, false); + _filter_multi_threaded.init("/options/threading/numthreads", 1.0, 32.0, 1.0, 2.0, 4.0, true, false); _page_rendering.add_line( false, _("Number of _Threads:"), _filter_multi_threaded, "", _("Configure number of processors/threads to use when rendering filters"), false, reset_icon()); // rendering cache diff --git a/src/ui/svg-renderer.cpp b/src/ui/svg-renderer.cpp index 13664852d4aac0758f0ffed2d59eb57be81de858..8b06a2ccbfe5916164a691a1fb9b9b56ca481d5b 100644 --- a/src/ui/svg-renderer.cpp +++ b/src/ui/svg-renderer.cpp @@ -90,9 +90,8 @@ Inkscape::Pixbuf* svg_renderer::do_render(double scale) { auto dpi = 96 * scale; auto area = Geom::Rect(0, 0, w, h); - unsigned int color = _checkerboard.value_or(0); - return sp_generate_internal_bitmap(_document.get(), area, dpi, std::vector(), false, - _checkerboard.has_value() ? &color : nullptr, scale); + auto checkerboard_ptr = _checkerboard ? &*_checkerboard : nullptr; + return sp_generate_internal_bitmap(_document.get(), area, dpi, {}, false, checkerboard_ptr, scale); } Glib::RefPtr svg_renderer::render(double scale) { diff --git a/src/ui/svg-renderer.h b/src/ui/svg-renderer.h index 3a74abc4b6945e24283ebd82e29d84b85868c4a6..aed6bf0d27f67a9a85a44f3dc512c9435ba843b7 100644 --- a/src/ui/svg-renderer.h +++ b/src/ui/svg-renderer.h @@ -2,6 +2,7 @@ #ifndef SEEN_SVG_RENDERER_H #define SEEN_SVG_RENDERER_H +#include #include #include #include @@ -36,7 +37,7 @@ public: Cairo::RefPtr render_surface(double scale); // if set, draw checkerboard pattern before image - void set_checkerboard_color(unsigned int rgba); + void set_checkerboard_color(uint32_t rgba); double get_width_px() const; double get_height_px() const; @@ -45,7 +46,7 @@ private: Pixbuf* do_render(double scale); std::shared_ptr _document; SPRoot* _root = nullptr; - std::optional _checkerboard; + std::optional _checkerboard; }; } diff --git a/src/ui/widget/export-preview.cpp b/src/ui/widget/export-preview.cpp index 5e08032a37815fd24cd79a08d4d8cbc28ce14f9a..2ab4dc3293682c7683c6af8afb1fa6c172050f04 100644 --- a/src/ui/widget/export-preview.cpp +++ b/src/ui/widget/export-preview.cpp @@ -9,16 +9,9 @@ #include "export-preview.h" -#include -#include -#include -#include - +#include "document.h" #include "display/cairo-utils.h" -#include "inkscape.h" -#include "object/sp-defs.h" #include "object/sp-item.h" -#include "object/sp-namedview.h" #include "object/sp-root.h" #include "util/preview.h" @@ -32,60 +25,60 @@ void ExportPreview::resetPixels() show(); } +void ExportPreview::setSize(int newSize) +{ + size = newSize; + resetPixels(); +} + ExportPreview::~ExportPreview() { - if (drawing && _document) { + refresh_conn.disconnect(); + if (drawing) { _document->getRoot()->invoke_hide(visionkey); } - if (timer) { - timer->stop(); - } - if (renderTimer) { - renderTimer->stop(); - } - _item = nullptr; - _document = nullptr; } void ExportPreview::setItem(SPItem *item) { _item = item; - _dbox = Geom::OptRect(); + _dbox = {}; } + void ExportPreview::setDbox(double x0, double x1, double y0, double y1) { if (!_document) { return; } - if ((x1 - x0 == 0) || (y1 - y0) == 0) { + if (x1 == x0 || y1 == y0) { return; } _item = nullptr; - _dbox = Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)) * _document->dt2doc(); + _dbox = Geom::Rect(x0, y0, x1, y1) * _document->dt2doc(); } void ExportPreview::setDocument(SPDocument *document) { if (drawing) { - if (_document) { - _document->getRoot()->invoke_hide(visionkey); - } + _document->getRoot()->invoke_hide(visionkey); drawing.reset(); + _item = nullptr; } _document = document; if (_document) { - drawing = std::make_unique(); + drawing = std::make_shared(); visionkey = SPItem::display_key_new(1); - DrawingItem *ai = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY); - if (ai) { - drawing->setRoot(ai); + if (auto di = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY)) { + drawing->setRoot(di); + } else { + drawing.reset(); } } } -void ExportPreview::refreshHide(std::vector const &list) +void ExportPreview::refreshHide(std::vector &&list) { - _hidden_excluded = list; + _hidden_excluded = std::move(list); _hidden_requested = true; } @@ -96,11 +89,12 @@ void ExportPreview::performHide() if (drawing) { _document->getRoot()->invoke_hide(visionkey); } - drawing = std::make_unique(); + drawing = std::make_shared(); visionkey = SPItem::display_key_new(1); - DrawingItem *ai = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY); - if (ai) { - drawing->setRoot(ai); + if (auto di = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY)) { + drawing->setRoot(di); + } else { + drawing.reset(); } isLastHide = false; } @@ -111,62 +105,30 @@ void ExportPreview::performHide() } } -void ExportPreview::queueRefresh() +static bool debug_busyloop() { - if (drawing == nullptr) { - return; - } - if (!pending) { - pending = true; - if (!timer) { - timer = std::make_unique(); - } - Glib::signal_idle().connect(sigc::mem_fun(*this, &ExportPreview::refreshCB), Glib::PRIORITY_DEFAULT_IDLE); - } + static bool enabled = std::getenv("INKSCAPE_DEBUG_EXPORTDIALOG_BUSYLOOP"); + return enabled; } -bool ExportPreview::refreshCB() +void ExportPreview::queueRefresh() { - bool callAgain = true; - if (!timer) { - timer = std::make_unique(); - } - if (timer->elapsed() > minDelay) { - callAgain = false; - refreshPreview(); - pending = false; + if (!drawing || refresh_conn.connected() || dest) { + return; } - return callAgain; -} -void ExportPreview::refreshPreview() -{ - auto document = _document; - if (!timer) { - timer = std::make_unique(); - } - if (timer->elapsed() < minDelay) { - // Do not refresh too quickly - queueRefresh(); - } else if (document) { - renderPreview(); - timer->reset(); - } + refresh_conn = Glib::signal_timeout().connect([this] { renderPreview(); return false; }, debug_busyloop() ? 1 : delay_msecs); } /* -This is main function which finally render preview. Call this after setting document, item and dbox. -If dbox is given it will use it. -if item is given and not dbox then item is used -If both are not given then simply we do nothing. -*/ + * This is the main function which finally renders the preview. Call this after setting document, item and dbox. + * If dbox is given it will use it. + * if item is given and not dbox then item is used. + * If both are not given then we simply do nothing. + */ void ExportPreview::renderPreview() { - if (!renderTimer) { - renderTimer = std::make_unique(); - } - renderTimer->reset(); - if (drawing == nullptr) { + if (!drawing || dest) { return; } @@ -174,24 +136,23 @@ void ExportPreview::renderPreview() performHide(); _hidden_requested = false; } - if (_document) { - GdkPixbuf *pb = nullptr; - if (_item) { - pb = Inkscape::UI::PREVIEW::render_preview(_document, *drawing, _bg_color, _item, size, size); - } else if (_dbox) { - pb = Inkscape::UI::PREVIEW::render_preview(_document, *drawing, _bg_color, nullptr, size, size, &_dbox); - } - if (pb) { - set(Glib::wrap(pb)); + + dest = UI::Preview::render_preview(_document, drawing, _bg_color, _item, size, size, _dbox ? &_dbox : nullptr, [this] (Cairo::RefPtr surface, int elapsed_msecs) { + if (surface) { + set(Gdk::Pixbuf::create(surface, 0, 0, surface->get_width(), surface->get_height())); show(); } - } + delay_msecs = std::max(100, elapsed_msecs * 3); + dest.close(); - renderTimer->stop(); - minDelay = std::max(0.1, renderTimer->elapsed() * 3.0); + if (debug_busyloop()) { + renderPreview(); + } + }); } -void ExportPreview::set_background_color(guint32 bg_color) { +void ExportPreview::setBackgroundColor(uint32_t bg_color) +{ _bg_color = bg_color; } diff --git a/src/ui/widget/export-preview.h b/src/ui/widget/export-preview.h index 738ebbac703e1a342659419acd0c63d2ffea0339..1f0bf9a6fa14b35c78773a5f880e46463ebf9a39 100644 --- a/src/ui/widget/export-preview.h +++ b/src/ui/widget/export-preview.h @@ -7,76 +7,69 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SP_EXPORT_PREVIEW_H -#define SP_EXPORT_PREVIEW_H +#ifndef INKSCAPE_UI_WIDGET_EXPORT_PREVIEW_H +#define INKSCAPE_UI_WIDGET_EXPORT_PREVIEW_H +#include +#include <2geom/rect.h> #include -#include - -#include "desktop.h" -#include "document.h" #include "display/drawing.h" +#include "async/channel.h" +class SPDocument; class SPObject; class SPItem; namespace Inkscape { +class Drawing; + namespace UI { namespace Dialog { -class ExportPreview : public Gtk::Image +class ExportPreview final : public Gtk::Image { public: ExportPreview() = default; + ExportPreview(BaseObjectType *cobj, Glib::RefPtr const &) : Gtk::Image(cobj) {} ~ExportPreview() override; - ExportPreview(BaseObjectType* cobject, const Glib::RefPtr &) - : Gtk::Image(cobject) - { - } + + void setDocument(SPDocument *document); + void refreshHide(std::vector &&list = {}); + void setItem(SPItem *item); + void setDbox(double x0, double x1, double y0, double y1); + void queueRefresh(); + void resetPixels(); + void setSize(int newSize); + void setBackgroundColor(uint32_t bg_color); private: int size = 128; // size of preview image bool isLastHide = false; - bool pending = false; + sigc::connection refresh_conn; bool _hidden_requested = false; SPDocument *_document = nullptr; SPItem *_item = nullptr; Geom::OptRect _dbox; - std::unique_ptr drawing; - std::unique_ptr timer; - std::unique_ptr renderTimer; - gdouble minDelay = 0.1; - guint32 _bg_color = 0; - unsigned int visionkey = 0; + std::shared_ptr drawing; // drawing implies _document + int delay_msecs = 100; + uint32_t _bg_color = 0; + unsigned visionkey = 0; - std::vector _hidden_excluded; -public: - void setDocument(SPDocument *document); - void refreshHide(std::vector const &list = {}); - void setItem(SPItem *item); - void setDbox(double x0, double x1, double y0, double y1); - void queueRefresh(); - void resetPixels(); + std::vector _hidden_excluded; - void setSize(int newSize) - { - size = newSize; - resetPixels(); - } + Async::Channel::Dest dest; - void set_background_color(guint32 bg_color); -private: - void refreshPreview(); void renderPreview(); - bool refreshCB(); void performHide(); }; + } // namespace Dialog } // namespace UI } // namespace Inkscape -#endif + +#endif // INKSCAPE_UI_WIDGET_EXPORT_PREVIEW_H /* Local Variables: diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index d38b63d2c31e72dabea4bed057983e6310cfe0c2..62c3ba293c3cfb9b94756182567ba1a506815587 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -33,6 +33,7 @@ set(util_SRC pool.h preview.h reference.h + scope_exit.h share.h signal-blocker.h statics.h diff --git a/src/util/preview.cpp b/src/util/preview.cpp index 018fc256164ef5e95db1ddcb5bfa7ea0a66d6c00..e79f90a617c39613368d82982b47a625f1ee3335 100644 --- a/src/util/preview.cpp +++ b/src/util/preview.cpp @@ -15,22 +15,21 @@ #include "preview.h" -#include "desktop.h" #include "document.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" #include "object/sp-namedview.h" #include "object/sp-root.h" -#include "page-manager.h" +#include "async/async.h" namespace Inkscape { namespace UI { -namespace PREVIEW { +namespace Preview { -GdkPixbuf *render_preview(SPDocument *doc, Inkscape::Drawing &drawing, guint32 bg_color, SPItem *item, - unsigned width_in, unsigned height_in, Geom::OptRect *dboxIn) +Async::Channel::Dest render_preview(SPDocument *doc, std::shared_ptr drawing, uint32_t bg, SPItem *item, + unsigned width_in, unsigned height_in, Geom::OptRect const *dboxIn, std::function, int)> &&onfinished) { - if (auto name = (item ? item->getId() : nullptr)) { + if (auto name = item ? item->getId() : nullptr) { // Get item even if it's in another document. if (item->document != doc) { item = dynamic_cast(doc->getObjectById(name)); @@ -47,12 +46,12 @@ GdkPixbuf *render_preview(SPDocument *doc, Inkscape::Drawing &drawing, guint32 b dbox = doc->preferredBounds(); } } else if (doc->getRoot()) { - // If we still dont have a dbox we will use document coordinates. + // If we still don't have a dbox we will use document coordinates. dbox = doc->getRoot()->documentVisualBounds(); } - // If we still dont have anything to render then return - if (!dbox) return nullptr; + // If we still dont have anything to render then return. + if (!dbox) return {}; // Calculate a scaling factor for the requested bounding box. double sf = 1.0; @@ -64,49 +63,64 @@ GdkPixbuf *render_preview(SPDocument *doc, Inkscape::Drawing &drawing, guint32 b ibox = scaled_box.roundOutwards(); } - // Resize the contents to the available space with a scale factor - drawing.root()->setTransform(Geom::Scale(sf)); - drawing.update(); + // Resize the contents to the available space with a scale factor. + drawing->root()->setTransform(Geom::Scale(sf)); + drawing->update(); - Geom::IntPoint pdim(width_in, height_in); + auto pdim = Geom::IntPoint(width_in, height_in); // The unsigned width/height can wrap around when negative. int dx = ((int)width_in - ibox.width()) / 2; int dy = ((int)height_in - ibox.height()) / 2; - Geom::IntRect area = Geom::IntRect::from_xywh(ibox.min() - Geom::IntPoint(dx, dy), pdim); + auto area = Geom::IntRect::from_xywh(ibox.min() - Geom::IntPoint(dx, dy), pdim); /* Actual renderable area */ Geom::IntRect ua = *Geom::intersect(ibox, area); - /* Render */ - cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ua.width(), ua.height()); - Inkscape::DrawingContext dc(s, ua.min()); - cairo_t *cr = cairo_create(s); - cairo_rectangle(cr, 0, 0, ua.width(), ua.height()); + auto [src, dst] = Async::Channel::create(); + drawing->snapshot(); - guint32 bg = bg_color; + Async::fire_and_forget([ua, bg, drawing = std::move(drawing), onfinished = std::move(onfinished), src = std::move(src)] { - // We always use checkerboard to indicate transparency. - if (SP_RGBA32_A_F(bg) < 1.0) { - auto pattern = ink_cairo_pattern_create_checkerboard(bg, false); - auto background = Cairo::RefPtr(new Cairo::Pattern(pattern)); - cairo_set_source(cr, background->cobj()); - cairo_fill(cr); - } - // We always draw the background on top to indicate partial backgrounds - auto background = Cairo::SolidPattern::create_rgba( - SP_RGBA32_R_F(bg), SP_RGBA32_G_F(bg), - SP_RGBA32_B_F(bg), SP_RGBA32_A_F(bg)); - cairo_set_source(cr, background->cobj()); - cairo_fill(cr); - - cairo_save(cr); - cairo_destroy(cr); - - drawing.render(dc, ua); - cairo_surface_flush(s); - return ink_pixbuf_create_from_cairo_surface(s); + auto start_time = g_get_monotonic_time(); + + auto surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, ua.width(), ua.height()); + + { + auto cr = Cairo::Context::create(surface); + cr->rectangle(0, 0, ua.width(), ua.height()); + + // We always use checkerboard to indicate transparency. + if (SP_RGBA32_A_F(bg) < 1.0) { + auto pattern = ink_cairo_pattern_create_checkerboard(bg, false); + auto background = Cairo::RefPtr(new Cairo::Pattern(pattern, true)); + cr->set_source(background); + cr->fill(); + } + + // We always draw the background on top to indicate partial backgrounds. + cr->set_source_rgba(SP_RGBA32_R_F(bg), SP_RGBA32_G_F(bg), SP_RGBA32_B_F(bg), SP_RGBA32_A_F(bg)); + cr->fill(); + } + + { + // Render drawing. + auto dc = Inkscape::DrawingContext(surface->cobj(), ua.min()); + drawing->render(dc, ua); + } + + surface->flush(); + + int const elapsed_msecs = (g_get_monotonic_time() - start_time) / 1000; + + src.run([drawing = std::move(drawing), onfinished = std::move(onfinished), surface = std::move(surface), elapsed_msecs] { + drawing->unsnapshot(); + onfinished(std::move(surface), elapsed_msecs); + }); + }); + + return std::move(dst); } -} // namespace PREVIEW +} // namespace Preview } // namespace UI } // namespace Inkscape diff --git a/src/util/preview.h b/src/util/preview.h index 872e222e234b263b0f61936a6a6ba7f0dd297036..40bf2a63702ed096291950da78ad8a91dd49c333 100644 --- a/src/util/preview.h +++ b/src/util/preview.h @@ -17,27 +17,47 @@ * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ -#ifndef SP_PREVIEW_UTIL_H -#define SP_PREVIEW_UTIL_H +#ifndef INKSCAPE_UTIL_PREVIEW_H +#define INKSCAPE_UTIL_PREVIEW_H -#include -#include +#include +#include #include "display/drawing.h" +#include "async/channel.h" class SPDocument; class SPItem; namespace Inkscape { namespace UI { -namespace PREVIEW { +namespace Preview { -// takes doc, drawing, icon, and icon name to produce pixels -GdkPixbuf *render_preview(SPDocument *doc, Inkscape::Drawing &drawing, guint32 bg_color, SPItem *item, - unsigned width_in, unsigned height_in, Geom::OptRect *dboxIn = nullptr); +/** + * Launch a background task to render a drawing to a surface. + * + * If the area to render is invalid, nothing is returned and no action is taken. + * Otherwise, first the drawing is snapshotted, then an async task is launched to render the drawing to a surface. + * Upon completion, the drawing is unsnapshotted on the calling thread and the result passed to onfinished(). + * If the return object is destroyed before this happens, then the drawing will instead be destroyed on an unspecified + * thread while still in the snapshotted state. + * + * Contracts: (This isn't Rust, so we need a comment instead, and great trust in the caller.) + * + * - The caller must ensure onfinished() remains valid to call during the lifetime of the return object. + * (This is the same as for sigc::slots and connections.) + * + * - The caller must not call drawing->unsnapshot(), or any other method that bypasses snapshotting. + * However, it is ok to modify or destroy drawing in any other way, because the background task has shared + * ownership of the drawing (=> Sync), and snapshotting prevents modification of the data being read by the + * background task (=> Send/const). + */ +Async::Channel::Dest render_preview(SPDocument *doc, std::shared_ptr drawing, uint32_t bg_color, SPItem *item, + unsigned width_in, unsigned height_in, Geom::OptRect const *dboxIn, + std::function pixbuf, int msecs_elapsed)> &&onfinished); -} // namespace PREVIEW +} // namespace Preview } // namespace UI } // namespace Inkscape -#endif +#endif // INKSCAPE_UTIL_PREVIEW_H diff --git a/src/util/scope_exit.h b/src/util/scope_exit.h new file mode 100644 index 0000000000000000000000000000000000000000..725f2b2c07306bcf1f11dd80ef38c8dfed7c43ca --- /dev/null +++ b/src/util/scope_exit.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Run code on scope exit. + */ + +#ifndef INKSCAPE_UTIL_SCOPE_EXIT_H +#define INKSCAPE_UTIL_SCOPE_EXIT_H + +#include + +// Todo: (C++23?) Replace with now-standardised version. +template +class scope_exit +{ +public: + scope_exit(F &&f) : f(std::forward(f)) {} + scope_exit(scope_exit const &) = delete; + scope_exit &operator=(scope_exit const &) = delete; + ~scope_exit() { f(); } + +private: + std::decay_t f; +}; + +#endif // INKSCAPE_UTIL_SCOPE_EXIT_H