From 7782d19c8d4400d479c17b7ac45255824fe5445a Mon Sep 17 00:00:00 2001 From: Jabier Arraiza Date: Wed, 13 Jul 2022 12:37:13 +0200 Subject: [PATCH] Improve dragging rendering as block tile and allow caching --- src/display/drawing-item.cpp | 45 ++++++++++++++++----------------- src/display/drawing-item.h | 2 +- src/display/drawing-surface.cpp | 4 +++ src/display/drawing.cpp | 9 +++---- src/display/drawing.h | 2 +- src/object/sp-item-group.cpp | 8 +++--- src/object/sp-item.cpp | 11 +++++++- src/object/sp-lpe-item.cpp | 8 +++--- src/ui/widget/canvas.cpp | 32 ++++++++++++++++++++--- 9 files changed, 76 insertions(+), 45 deletions(-) diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 464635b437..75da2e4f25 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -219,12 +219,13 @@ DrawingItem::clearChildren() void DrawingItem::setTransform(Geom::Affine const &new_trans) { + double constexpr EPS = 1e-18; + Geom::Affine current; if (_transform) { current = *_transform; } - - if (!Geom::are_near(current, new_trans, 1e-18)) { + if (!Geom::are_near(current, new_trans, EPS)) { // mark the area where the object was for redraw. _markForRendering(); delete _transform; @@ -676,6 +677,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag { bool outline = _drawing.outline(); bool render_filters = _drawing.renderFilters(); + bool forcecache = _filter && render_filters; // stop_at is handled in DrawingGroup, but this check is required to handle the case // where a filtered item with background-accessing filter has enable-background: new if (this == stop_at) { @@ -698,8 +700,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag } // carea is the area to paint - Geom::OptIntRect carea = Geom::intersect(area, _drawbox); - + Geom::OptIntRect carea = area & _drawbox; if (!carea) { return RENDER_OK; } @@ -711,7 +712,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag Geom::OptIntRect iarea = carea; // expand carea to contain the dependent area of filters. - if (_filter && render_filters) { + if (forcecache) { iarea = _cacheRect(); if (!iarea) { iarea = carea; @@ -723,7 +724,9 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag } } - if (!iarea) { + // carea is the area to paint + carea = iarea & _drawbox; + if (!carea) { return RENDER_OK; } @@ -743,7 +746,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag if (_cache) { _cache->prepare(); dc.setOperator(ink_css_blend_to_cairo_operator(_mix_blend_mode)); - _cache->paintFromCache(dc, carea, _filter && render_filters); + _cache->paintFromCache(dc, carea, forcecache); if (!carea) { dc.setSource(0, 0, 0, 0); return RENDER_OK; @@ -752,9 +755,10 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag // There is no cache. This could be because caching of this item // was just turned on after the last update phase, or because // we were previously outside of the canvas. - if (iarea) { - _cache = new DrawingCache(*iarea, device_scale); - } + Geom::OptIntRect cl = _cacheRect(); + if (!cl) + cl = carea; + _cache = new DrawingCache(*cl, device_scale); } } else { // if our caching was turned off after the last update, it was already @@ -778,7 +782,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag setCached(false, true); } _prev_nir = needs_intermediate_rendering; - nir |= (_cache != nullptr); // 5. it is to be cached + nir |= (_cache != nullptr); // 8. it is to be cached /* How the rendering is done. * @@ -799,11 +803,11 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag if ((flags & RENDER_FILTER_BACKGROUND) || !needs_intermediate_rendering) { dc.setOperator(ink_css_blend_to_cairo_operator(SP_CSS_BLEND_NORMAL)); - return _renderItem(dc, *iarea, flags & ~RENDER_FILTER_BACKGROUND, stop_at); + return _renderItem(dc, *carea, flags & ~RENDER_FILTER_BACKGROUND, stop_at); } - DrawingSurface intermediate(*iarea, device_scale); + DrawingSurface intermediate(*carea, device_scale); DrawingContext ict(intermediate); // This path fails for patterns/hatches when stepping the pattern to handle overflows. @@ -852,7 +856,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag // 3. Render object itself ict.pushGroup(); - render_result = _renderItem(ict, *iarea, flags, stop_at); + render_result = _renderItem(ict, *carea, flags, stop_at); // 4. Apply filter. if (_filter && render_filters) { @@ -863,9 +867,9 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag if (bg_root->_background_new) break; } if (bg_root) { - DrawingSurface bg(*iarea, device_scale); + DrawingSurface bg(*carea, device_scale); DrawingContext bgdc(bg); - bg_root->render(bgdc, *iarea, flags | RENDER_FILTER_BACKGROUND, this); + bg_root->render(bgdc, *carea, flags | RENDER_FILTER_BACKGROUND, this); _filter->render(this, ict, &bgdc); rendered = true; } @@ -886,16 +890,11 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag // 6. Paint the completed rendering onto the base context (or into cache) if (_cached && _cache) { DrawingContext cachect(*_cache); - cachect.rectangle(*iarea); + cachect.rectangle(*carea); cachect.setOperator(CAIRO_OPERATOR_SOURCE); cachect.setSource(&intermediate); cachect.fill(); - Geom::OptIntRect cl = _cacheRect(); - if (_filter && render_filters && cl) { - _cache->markClean(*cl); - } else { - _cache->markClean(*iarea); - } + _cache->markClean(*carea); } dc.rectangle(*carea); diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 81ed1e4655..708f6b6d5e 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -213,7 +213,7 @@ protected: Inkscape::Filters::Filter *_filter; SPItem *_item; ///< Used to associate DrawingItems with SPItems that created them DrawingCache *_cache; - bool _prev_nir; + bool _prev_nir = false; CacheList::iterator _cache_iterator; diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp index bf8653007f..ae27a58a2a 100644 --- a/src/display/drawing-surface.cpp +++ b/src/display/drawing-surface.cpp @@ -323,6 +323,10 @@ void DrawingCache::paintFromCache(DrawingContext &dc, Geom::OptIntRect &area, bo cairo_region_subtract(dirty_region, _clean_region); if (is_filter && !cairo_region_is_empty(dirty_region)) { // To allow fast panning on high zoom on filters + cairo_region_destroy(cache_region); + cairo_region_destroy(dirty_region); + cairo_region_destroy(_clean_region); + _clean_region = cairo_region_create(); return; } if (cairo_region_is_empty(dirty_region)) { diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index 5f43967017..c30609582b 100644 --- a/src/display/drawing.cpp +++ b/src/display/drawing.cpp @@ -137,14 +137,11 @@ Drawing::cacheLimit() const return _cache_limit; } void -Drawing::setCacheLimit(Geom::OptIntRect const &r, bool update_cache) +Drawing::setCacheLimit(Geom::OptIntRect const &r) { _cache_limit = r; - if (update_cache) { - for (auto _cached_item : _cached_items) - { - _cached_item->_markForUpdate(DrawingItem::STATE_CACHE, false); - } + for (auto _cached_item : _cached_items) { + _cached_item->_markForUpdate(DrawingItem::STATE_CACHE, false); } } void diff --git a/src/display/drawing.h b/src/display/drawing.h index 5b936f20be..709050ebe6 100644 --- a/src/display/drawing.h +++ b/src/display/drawing.h @@ -67,7 +67,7 @@ public: bool getOutlineSensitive() const { return _outline_sensitive; }; Geom::OptIntRect const &cacheLimit() const; - void setCacheLimit(Geom::OptIntRect const &r, bool update_cache = true); + void setCacheLimit(Geom::OptIntRect const &r); void setCacheBudget(size_t bytes); OutlineColors const &colors() const { return _colors; } diff --git a/src/object/sp-item-group.cpp b/src/object/sp-item-group.cpp index 8c6cba93ea..188fbe7e2d 100644 --- a/src/object/sp-item-group.cpp +++ b/src/object/sp-item-group.cpp @@ -1008,15 +1008,13 @@ void SPGroup::update_patheffect(bool write) { lpe_item->update_patheffect(write); // update satellites if (!lpe_item->hasPathEffect()) { - gchar *classes = g_strdup(lpe_item->getAttribute("class")); - if (classes) { - Glib::ustring classdata = classes; + if (auto classes = lpe_item->getAttribute("class")) { + auto classdata = Glib::ustring(classes); size_t pos = classdata.find("UnoptimicedTransforms"); - if ( pos != std::string::npos ) { + if (pos != Glib::ustring::npos) { lpe_item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } - g_free(classes); } } } diff --git a/src/object/sp-item.cpp b/src/object/sp-item.cpp index 6389caa669..d39a082946 100644 --- a/src/object/sp-item.cpp +++ b/src/object/sp-item.cpp @@ -1568,12 +1568,21 @@ void SPItem::doWriteTransform(Geom::Affine const &transform, Geom::Affine const if (lpeitem) { lpeitem->notifyTransform(transform); } + bool unoptimized = false; + if (auto classes = getAttribute("class")) { + auto classdata = Glib::ustring(classes); + size_t pos = classdata.find("UnoptimicedTransforms"); + if (pos != Glib::ustring::npos) { + unoptimized = true; + } + } if ( // run the object's set_transform (i.e. embed transform) only if: (dynamic_cast(this) && firstChild() && dynamic_cast(firstChild())) || (!preserve && // user did not chose to preserve all transforms (!clip_ref || !clip_ref->getObject()) && // the object does not have a clippath (!mask_ref || !mask_ref->getObject()) && // the object does not have a mask - !(!transform.isTranslation() && style && style->getFilter())) + !(!transform.isTranslation() && style && style->getFilter()) && + !unoptimized) // the object does not have a filter, or the transform is translation (which is supposed to not affect filters) ) { diff --git a/src/object/sp-lpe-item.cpp b/src/object/sp-lpe-item.cpp index 5c309996fc..c8530c488f 100755 --- a/src/object/sp-lpe-item.cpp +++ b/src/object/sp-lpe-item.cpp @@ -357,15 +357,13 @@ bool SPLPEItem::optimizeTransforms() } } // LPEs with satellites (and his satellites) has this class auto - gchar *classes = g_strdup(getAttribute("class")); - if (classes) { - Glib::ustring classdata = classes; + if (auto classes = getAttribute("class")) { + auto classdata = Glib::ustring(classes); size_t pos = classdata.find("UnoptimicedTransforms"); - if ( pos != std::string::npos ) { + if (pos != Glib::ustring::npos) { return false; } } - g_free(classes); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); return !prefs->getBool("/options/preservetransform/value", false); } diff --git a/src/ui/widget/canvas.cpp b/src/ui/widget/canvas.cpp index 1d0359a9bb..bbd8b32e76 100644 --- a/src/ui/widget/canvas.cpp +++ b/src/ui/widget/canvas.cpp @@ -2296,6 +2296,10 @@ CanvasPrivate::on_idle() // Recreate the store at the current affine so that it covers the visible region. _store_rect = q->get_area_world(); _store_rect.expandBy(pad); + Geom::IntRect expanded = _store_rect; + Geom::IntPoint expansion(expanded.width()/2, expanded.height()/2); + expanded.expandBy(expansion); + q->_drawing->setCacheLimit(expanded); _store_affine = q->_affine; int desired_width = _store_rect.width() * _device_scale; int desired_height = _store_rect.height() * _device_scale; @@ -2342,6 +2346,10 @@ CanvasPrivate::on_idle() // Recreate the store, but keep re-usable content from the old store. auto store_rect = q->get_area_world(); store_rect.expandBy(pad); + Geom::IntRect expanded = _store_rect; + Geom::IntPoint expansion(expanded.width()/2, expanded.height()/2); + expanded.expandBy(expansion); + q->_drawing->setCacheLimit(expanded); auto backing_store = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, store_rect.width() * _device_scale, store_rect.height() * _device_scale); cairo_surface_set_device_scale(backing_store->cobj(), _device_scale, _device_scale); // No C++ API! @@ -2522,12 +2530,26 @@ CanvasPrivate::on_idle() paint_region = Cairo::Region::create(); } + Geom::OptIntRect dragged = Geom::OptIntRect(); + if (q->_grabbed_canvas_item) { + dragged = q->_grabbed_canvas_item->get_bounds().roundOutwards(); + if (dragged) { + (*dragged).expandBy(prefs.pad); + dragged = dragged & visible_rect; + if (dragged) { + paint_region->subtract(geom_to_cairo(*dragged)); + } + } + } // Get the list of rectangles to paint, coarsened to avoid fragmentation. auto rects = coarsen(paint_region, std::min(prefs.coarsener_min_size, prefs.new_bisector_size / 2), std::min(prefs.coarsener_glue_size, prefs.new_bisector_size / 2), prefs.coarsener_min_fullness); - + if (dragged) { + // this become the first after look for cursor + rects.push_back(*dragged); + } // Ensure that all the rectangles lie within the visible rect (and therefore within the store). #ifndef NDEBUG for (auto &rect : rects) { @@ -2542,6 +2564,7 @@ CanvasPrivate::on_idle() std::make_heap(rects.begin(), rects.end(), cmp); // Process rectangles until none left or timed out. + bool start = true; while (!rects.empty()) { // Extract the closest rectangle to the mouse. std::pop_heap(rects.begin(), rects.end(), cmp); @@ -2550,12 +2573,14 @@ CanvasPrivate::on_idle() // Cull empty rectangles. if (rect.width() == 0 || rect.height() == 0) { + start = false; continue; } // Cull rectangles that lie entirely inside the clean region. // (These can be generated by coarsening; they must be discarded to avoid getting stuck re-rendering the same rectangles.) if (clean_region->contains_rectangle(geom_to_cairo(rect)) == Cairo::REGION_OVERLAP_IN) { + start = false; continue; } @@ -2567,13 +2592,14 @@ CanvasPrivate::on_idle() // If the rectangle needs bisecting, bisect it and put it back on the heap. auto axis = prefs.use_new_bisector ? new_bisector(rect) : old_bisector(rect); - if (axis) { + if (axis && !(dragged && start)) { int mid = rect[*axis].middle(); auto lo = rect; lo[*axis].setMax(mid); add_rect(lo); auto hi = rect; hi[*axis].setMin(mid); add_rect(hi); + start = false; continue; } - + start = false; // Paint the rectangle. paint_rect_internal(rect); -- GitLab