diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 464635b437e23f01605aaf7e1310ce539c66240e..75da2e4f2579be49b8c5f0c7a031e66b8668480f 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 81ed1e4655c88cc61a04dac23ae8a721ceeced21..708f6b6d5ec0ab150162c4ae9185d4ccfd7a48b4 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 bf8653007f534a1c68e036e1f73dfcef6108f4f5..ae27a58a2a82e35bdbe7ae59df7e6370b5ca67ed 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 5f43967017029abddbe81709e9b9cdc07cb534d0..c30609582b6ce2229d70d94dc4a59eb58a728786 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 5b936f20be5cef5ee00ca3326a205251e12ca46b..709050ebe682b3a1211cf4efaa045998656b8518 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 8c6cba93ea1fcddb64eac761457c78a7638672c6..188fbe7e2d5e194a1cc8c85c590f1f986451e30a 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 6389caa669c08d8aa1f7db4597fd7a50eac75cd7..d39a0829461855e85eb9d9cc7991f6f488669b42 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 5c309996fcf8d0231830f8fd936ca48adae22fd4..c8530c488f8fe95916ec31d2f6146397650734a4 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 1d0359a9bb88d772062ff92e2e440aef23c06b9e..bbd8b32e765e1a75c6dfcbb2441032ccee588644 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);