1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3  * @file
4  * Canvas item belonging to an SVG drawing element.
5  *//*
6  * Authors:
7  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
8  *
9  * Copyright (C) 2011 Authors
10  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11  */
12 
13 #include <climits>
14 
15 #include "display/drawing-context.h"
16 #include "display/drawing-group.h"
17 #include "display/drawing-item.h"
18 #include "display/drawing-pattern.h"
19 #include "display/drawing-surface.h"
20 #include "display/drawing-text.h"
21 #include "display/drawing.h"
22 
23 #include "display/cairo-utils.h"
24 #include "display/cairo-templates.h"
25 
26 #include "display/control/canvas-item-drawing.h"
27 #include "ui/widget/canvas.h" // Mark area for redrawing.
28 
29 #include "nr-filter.h"
30 #include "preferences.h"
31 #include "style.h"
32 
33 
34 #include "object/sp-item.h"
35 
36 namespace Inkscape {
37 /**
38  * @class DrawingItem
39  * SVG drawing item for display.
40  *
41  * This was previously known as NRArenaItem. It represents the renderable
42  * portion of the SVG document. Typically this is created by the SP tree,
43  * in particular the show() virtual function.
44  *
45  * @section ObjectLifetime Object lifetime
46  * Deleting a DrawingItem will cause all of its children to be deleted as well.
47  * This can lead to nasty surprises if you hold references to things
48  * which are children of what is being deleted. Therefore, in the SP tree,
49  * you always need to delete the item views of children before deleting
50  * the view of the parent. Do not call delete on things returned from show()
51  * - this will cause dangling pointers inside the SPItem and lead to a crash.
52  * Use the corresponding hide() method.
53  *
54  * Outside of the SP tree, you should not use any references after the root node
55  * has been deleted.
56  */
57 
DrawingItem(Drawing & drawing)58 DrawingItem::DrawingItem(Drawing &drawing)
59     : _drawing(drawing)
60     , _parent(nullptr)
61     , _key(0)
62     , _style(nullptr)
63     , _context_style(nullptr)
64     , _opacity(1.0)
65     , _transform(nullptr)
66     , _clip(nullptr)
67     , _mask(nullptr)
68     , _fill_pattern(nullptr)
69     , _stroke_pattern(nullptr)
70     , _filter(nullptr)
71     , _item(nullptr)
72     , _cache(nullptr)
73     , _state(0)
74     , _child_type(CHILD_ORPHAN)
75     , _background_new(0)
76     , _background_accumulate(0)
77     , _visible(true)
78     , _sensitive(true)
79     , _cached(0)
80     , _cached_persistent(0)
81     , _has_cache_iterator(0)
82     , _propagate(0)
83     //    , _renders_opacity(0)
84     , _pick_children(0)
85     , _antialias(2)
86     , _prev_nir(false)
87     , _isolation(SP_CSS_ISOLATION_AUTO)
88     , _mix_blend_mode(SP_CSS_BLEND_NORMAL)
89 {}
90 
~DrawingItem()91 DrawingItem::~DrawingItem()
92 {
93     // Unactivate if active.
94     if (drawing().getCanvasItemDrawing()) {
95         if (drawing().getCanvasItemDrawing()->get_active() == this) {
96             drawing().getCanvasItemDrawing()->set_active(nullptr);
97         }
98     } else {
99         // Can happen, e.g. in Eraser tool.
100         // std::cerr << "DrawingItem::~DrawingItem: Missing CanvasItemDrawing!" << std::endl;
101     }
102 
103     //if (!_children.empty()) {
104     //    g_warning("Removing item with children");
105     //}
106 
107     // remove from the set of cached items and delete cache
108     setCached(false, true);
109     // remove this item from parent's children list
110     // due to the effect of clearChildren(), this only happens for the top-level deleted item
111     if (_parent) {
112         _markForRendering();
113     }
114     switch (_child_type) {
115     case CHILD_NORMAL: {
116         ChildrenList::iterator ithis = _parent->_children.iterator_to(*this);
117         _parent->_children.erase(ithis);
118         } break;
119     case CHILD_CLIP:
120         // we cannot call setClip(NULL) or setMask(NULL),
121         // because that would be an endless loop
122         _parent->_clip = nullptr;
123         break;
124     case CHILD_MASK:
125         _parent->_mask = nullptr;
126         break;
127     case CHILD_ROOT:
128         _drawing._root = nullptr;
129         break;
130     case CHILD_FILL_PATTERN:
131         _parent->_fill_pattern = nullptr;
132         break;
133     case CHILD_STROKE_PATTERN:
134         _parent->_stroke_pattern = nullptr;
135         break;
136     default: ;
137     }
138 
139     if (_parent) {
140         _parent->_markForUpdate(STATE_ALL, false);
141     }
142     clearChildren();
143     delete _transform;
144     delete _stroke_pattern;
145     delete _fill_pattern;
146     delete _clip;
147     delete _mask;
148     delete _filter;
149     if(_style)
150         sp_style_unref(_style);
151 }
152 
153 DrawingItem *
parent() const154 DrawingItem::parent() const
155 {
156     // initially I wanted to return NULL if we are a clip or mask child,
157     // but the previous behavior was just to return the parent regardless of child type
158     return _parent;
159 }
160 
161 /// Returns true if item is among the descendants. Will return false if item == this.
162 bool
isAncestorOf(DrawingItem * item) const163 DrawingItem::isAncestorOf(DrawingItem *item) const
164 {
165     for (DrawingItem *i = item->_parent; i; i = i->_parent) {
166         if (i == this) return true;
167     }
168     return false;
169 }
170 
171 void
appendChild(DrawingItem * item)172 DrawingItem::appendChild(DrawingItem *item)
173 {
174     item->_parent = this;
175     assert(item->_child_type == CHILD_ORPHAN);
176     item->_child_type = CHILD_NORMAL;
177     _children.push_back(*item);
178 
179     // This ensures that _markForUpdate() called on the child will recurse to this item
180     item->_state = STATE_ALL;
181     // Because _markForUpdate recurses through ancestors, we can simply call it
182     // on the just-added child. This has the additional benefit that we do not
183     // rely on the appended child being in the default non-updated state.
184     // We set propagate to true, because the child might have descendants of its own.
185     item->_markForUpdate(STATE_ALL, true);
186 }
187 
188 void
prependChild(DrawingItem * item)189 DrawingItem::prependChild(DrawingItem *item)
190 {
191     item->_parent = this;
192     assert(item->_child_type == CHILD_ORPHAN);
193     item->_child_type = CHILD_NORMAL;
194     _children.push_front(*item);
195     // See appendChild for explanation
196     item->_state = STATE_ALL;
197     item->_markForUpdate(STATE_ALL, true);
198 }
199 
200 /// Delete all regular children of this item (not mask or clip).
201 void
clearChildren()202 DrawingItem::clearChildren()
203 {
204     if (_children.empty()) return;
205 
206     _markForRendering();
207     // prevent children from referencing the parent during deletion
208     // this way, children won't try to remove themselves from a list
209     // from which they have already been removed by clear_and_dispose
210     for (auto & i : _children) {
211         i._parent = NULL;
212         i._child_type = CHILD_ORPHAN;
213     }
214     _children.clear_and_dispose(DeleteDisposer());
215     _markForUpdate(STATE_ALL, false);
216 }
217 
218 /// Set the incremental transform for this item
219 void
setTransform(Geom::Affine const & new_trans)220 DrawingItem::setTransform(Geom::Affine const &new_trans)
221 {
222     Geom::Affine current;
223     if (_transform) {
224         current = *_transform;
225     }
226 
227     if (!Geom::are_near(current, new_trans, 1e-18)) {
228         // mark the area where the object was for redraw.
229         _markForRendering();
230         delete _transform;
231         if (new_trans.isIdentity()) {
232             _transform = nullptr;
233         } else {
234             _transform = new Geom::Affine(new_trans);
235         }
236         _markForUpdate(STATE_ALL, true);
237     }
238 }
239 
240 void
setOpacity(float opacity)241 DrawingItem::setOpacity(float opacity)
242 {
243     if (_opacity != opacity) {
244         _opacity = opacity;
245         _markForRendering();
246     }
247 }
248 
249 void
setAntialiasing(unsigned a)250 DrawingItem::setAntialiasing(unsigned a)
251 {
252     if (_antialias != a) {
253         _antialias = a;
254         _markForRendering();
255     }
256 }
257 
258 void
setIsolation(bool isolation)259 DrawingItem::setIsolation(bool isolation)
260 {
261     _isolation = isolation;
262     //if( isolation != 0 ) std::cout << "isolation: " << isolation << std::endl;
263     _markForRendering();
264 }
265 
266 void
setBlendMode(SPBlendMode mix_blend_mode)267 DrawingItem::setBlendMode(SPBlendMode mix_blend_mode)
268 {
269     _mix_blend_mode = mix_blend_mode;
270     //if( mix_blend_mode != 0 ) std::cout << "setBlendMode: " << mix_blend_mode << std::endl;
271     _markForRendering();
272 }
273 
274 void
setVisible(bool v)275 DrawingItem::setVisible(bool v)
276 {
277     if (_visible != v) {
278         _visible = v;
279         _markForRendering();
280     }
281 }
282 
283 /// This is currently unused
284 void
setSensitive(bool s)285 DrawingItem::setSensitive(bool s)
286 {
287     _sensitive = s;
288 }
289 
290 /**
291  * Enable / disable storing the rendering in memory.
292  * Calling setCached(false, true) will also remove the persistent status
293  */
294 void
setCached(bool cached,bool persistent)295 DrawingItem::setCached(bool cached, bool persistent)
296 {
297     static const char *cache_env = getenv("_INKSCAPE_DISABLE_CACHE");
298     if (cache_env) return;
299 
300     if (_cached_persistent && !persistent)
301         return;
302 
303     _cached = cached;
304     _cached_persistent = persistent ? cached : false;
305     if (cached) {
306         _drawing._cached_items.insert(this);
307     } else {
308         _drawing._cached_items.erase(this);
309         delete _cache;
310         _cache = nullptr;
311         if (_has_cache_iterator) {
312             _drawing._candidate_items.erase(_cache_iterator);
313             _has_cache_iterator = false;
314         }
315     }
316 }
317 
318 /**
319  * Process information related to the new style.
320  *
321  * Note: _style is not used by DrawingGlyphs which uses its parent style.
322  */
323 void
setStyle(SPStyle * style,SPStyle * context_style)324 DrawingItem::setStyle(SPStyle *style, SPStyle *context_style)
325 {
326     // std::cout << "DrawingItem::setStyle: " << name() << " " << style
327     //           << " " << context_style << std::endl;
328 
329     if( style != _style ) {
330         if (style) sp_style_ref(style);
331         if (_style) sp_style_unref(_style);
332         _style = style;
333     }
334 
335     if (style && style->filter.set && style->getFilter()) {
336         if (!_filter) {
337             int primitives = style->getFilter()->primitive_count();
338             _filter = new Inkscape::Filters::Filter(primitives);
339         }
340         style->getFilter()->build_renderer(_filter);
341     } else {
342         // no filter set for this group
343         delete _filter;
344         _filter = nullptr;
345     }
346 
347     if (style && style->enable_background.set) {
348         bool _background_new_check = _background_new;
349         if (style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
350             _background_new = true;
351         }
352         if (style->enable_background.value == SP_CSS_BACKGROUND_ACCUMULATE) {
353             _background_new = false;
354         }
355         if (_background_new_check != _background_new) {
356             _markForUpdate(STATE_BACKGROUND, true);
357         }
358     }
359 
360     if (context_style != nullptr) {
361         _context_style = context_style;
362     } else if (_parent != nullptr) {
363         _context_style = _parent->_context_style;
364     }
365 
366     _markForUpdate(STATE_ALL, false);
367 }
368 
369 
370 /**
371  * Recursively update children style.
372  * The purpose of this call is to update fill and stroke for markers that have elements with
373  * fill/stroke property values of 'context-fill' or 'context-stroke'.  Marker styling is not
374  * updated like other 'clones' as marker instances are not included the SP object tree.
375  * Note: this is a virtual function.
376  */
377 void
setChildrenStyle(SPStyle * context_style)378 DrawingItem::setChildrenStyle(SPStyle* context_style)
379 {
380     _context_style = context_style;
381     for (auto & i : _children) {
382         i.setChildrenStyle( context_style );
383     }
384 }
385 
386 
387 void
setClip(DrawingItem * item)388 DrawingItem::setClip(DrawingItem *item)
389 {
390     _markForRendering();
391     delete _clip;
392     _clip = item;
393     if (item) {
394         item->_parent = this;
395         assert(item->_child_type == CHILD_ORPHAN);
396         item->_child_type = CHILD_CLIP;
397     }
398     _markForUpdate(STATE_ALL, true);
399 }
400 
401 void
setMask(DrawingItem * item)402 DrawingItem::setMask(DrawingItem *item)
403 {
404     _markForRendering();
405     delete _mask;
406     _mask = item;
407         if (item) {
408         item->_parent = this;
409         assert(item->_child_type == CHILD_ORPHAN);
410         item->_child_type = CHILD_MASK;
411     }
412     _markForUpdate(STATE_ALL, true);
413 }
414 
415 void
setFillPattern(DrawingPattern * pattern)416 DrawingItem::setFillPattern(DrawingPattern *pattern)
417 {
418     _markForRendering();
419     delete _fill_pattern;
420     _fill_pattern = pattern;
421     if (pattern) {
422         pattern->_parent = this;
423         assert(pattern->_child_type == CHILD_ORPHAN);
424         pattern->_child_type = CHILD_FILL_PATTERN;
425     }
426     _markForUpdate(STATE_ALL, true);
427 }
428 
429 void
setStrokePattern(DrawingPattern * pattern)430 DrawingItem::setStrokePattern(DrawingPattern *pattern)
431 {
432     _markForRendering();
433     delete _stroke_pattern;
434     _stroke_pattern = pattern;
435     if (pattern) {
436         pattern->_parent = this;
437         assert(pattern->_child_type == CHILD_ORPHAN);
438         pattern->_child_type = CHILD_STROKE_PATTERN;
439     }
440     _markForUpdate(STATE_ALL, true);
441 }
442 
443 /// Move this item to the given place in the Z order of siblings.
444 /// Does nothing if the item has no parent.
445 void
setZOrder(unsigned z)446 DrawingItem::setZOrder(unsigned z)
447 {
448     if (!_parent) return;
449 
450     ChildrenList::iterator it = _parent->_children.iterator_to(*this);
451     _parent->_children.erase(it);
452 
453     ChildrenList::iterator i = _parent->_children.begin();
454     std::advance(i, std::min(z, unsigned(_parent->_children.size())));
455     _parent->_children.insert(i, *this);
456     _markForRendering();
457 }
458 
459 void
setItemBounds(Geom::OptRect const & bounds)460 DrawingItem::setItemBounds(Geom::OptRect const &bounds)
461 {
462     _item_bbox = bounds;
463 }
464 
465 /**
466  * Update derived data before operations.
467  * The purpose of this call is to recompute internal data which depends
468  * on the attributes of the object, but is not directly settable by the user.
469  * Precomputing this data speeds up later rendering, because some items
470  * can be omitted.
471  *
472  * Currently this method handles updating the visual and geometric bounding boxes
473  * in pixels, storing the total transformation from item space to the screen
474  * and cache invalidation.
475  *
476  * @param area Area to which the update should be restricted. Only takes effect
477  *             if the bounding box is known.
478  * @param ctx A structure to store cascading state.
479  * @param flags Which internal data should be recomputed. This can be any combination
480  *              of StateFlags.
481  * @param reset State fields that should be reset before processing them. This is
482  *              a means to force a recomputation of internal data even if the item
483  *              considers it up to date. Mainly for internal use, such as
484  *              propagating bounding box recomputation to children when the item's
485  *              transform changes.
486  */
487 void
update(Geom::IntRect const & area,UpdateContext const & ctx,unsigned flags,unsigned reset)488 DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
489 {
490     bool render_filters = _drawing.renderFilters();
491     bool outline = _drawing.outline();
492 
493     // Set reset flags according to propagation status
494     reset |= _propagate_state;
495     _propagate_state = 0;
496 
497     _state &= ~reset; // reset state of this item
498 
499     if ((~_state & flags) == 0) return;  // nothing to do
500 
501     // TODO this might be wrong
502     if (_state & STATE_BBOX) {
503         // we have up-to-date bbox
504         if (!area.intersects(outline ? _bbox : _drawbox)) return;
505     }
506 
507     // compute which elements need an update
508     unsigned to_update = _state ^ flags;
509 
510     // this needs to be called before we recurse into children
511     if (to_update & STATE_BACKGROUND) {
512         _background_accumulate = _background_new;
513         if (_child_type == CHILD_NORMAL && _parent->_background_accumulate)
514             _background_accumulate = true;
515     }
516 
517     UpdateContext child_ctx(ctx);
518     if (_transform) {
519         child_ctx.ctm = *_transform * ctx.ctm;
520     }
521 
522     // Vector effects
523     if (_style) {
524 
525         if (_style->vector_effect.fixed) {
526             child_ctx.ctm.setTranslation(Geom::Point(0,0));
527         }
528 
529         if (_style->vector_effect.size) {
530             double value = sqrt(child_ctx.ctm.det());
531             if (value > 0 ) {
532                 child_ctx.ctm[0] = child_ctx.ctm[0]/value;
533                 child_ctx.ctm[1] = child_ctx.ctm[1]/value;
534                 child_ctx.ctm[2] = child_ctx.ctm[2]/value;
535                 child_ctx.ctm[3] = child_ctx.ctm[3]/value;
536             }
537         }
538 
539         if (_style->vector_effect.rotate) {
540             double value = sqrt(child_ctx.ctm.det());
541             child_ctx.ctm[0] = value;
542             child_ctx.ctm[1] = 0;
543             child_ctx.ctm[2] = 0;
544             child_ctx.ctm[3] = value;
545         }
546     }
547 
548     /* Remember the transformation matrix */
549     Geom::Affine ctm_change = _ctm.inverse() * child_ctx.ctm;
550     _ctm = child_ctx.ctm;
551 
552     // update _bbox and call this function for children
553     _state = _updateItem(area, child_ctx, flags, reset);
554 
555     if (to_update & STATE_BBOX) {
556         // compute drawbox
557         if (_filter && render_filters) {
558             Geom::OptRect enlarged = _filter->filter_effect_area(_item_bbox);
559             if (enlarged) {
560                 *enlarged *= ctm();
561                 _drawbox = enlarged->roundOutwards();
562             } else {
563                 _drawbox = Geom::OptIntRect();
564             }
565         } else {
566             _drawbox = _bbox;
567         }
568 
569         // Clipping
570         if (_clip) {
571             _clip->update(area, child_ctx, flags, reset);
572             if (outline) {
573                 _bbox.unionWith(_clip->_bbox);
574             } else {
575                 _drawbox.intersectWith(_clip->_bbox);
576             }
577         }
578         // Masking
579         if (_mask) {
580             _mask->update(area, child_ctx, flags, reset);
581             if (outline) {
582                 _bbox.unionWith(_mask->_bbox);
583             } else {
584                 // for masking, we need full drawbox of mask
585                 _drawbox.intersectWith(_mask->_drawbox);
586             }
587         }
588     }
589     if (to_update & STATE_CACHE) {
590         // Update cache score for this item
591         if (_has_cache_iterator) {
592             // remove old score information
593             _drawing._candidate_items.erase(_cache_iterator);
594             _has_cache_iterator = false;
595         }
596         double score = _cacheScore();
597         if (score >= _drawing._cache_score_threshold) {
598             CacheRecord cr;
599             cr.score = score;
600             // if _cacheRect() is empty, a negative score will be returned from _cacheScore(),
601             // so this will not execute (cache score threshold must be positive)
602             cr.cache_size = _cacheRect()->area() * 4;
603             cr.item = this;
604             auto it = std::lower_bound(_drawing._candidate_items.begin(), _drawing._candidate_items.end(), cr,
605                                        std::greater<CacheRecord>());
606             _cache_iterator = _drawing._candidate_items.insert(it, cr);
607             _has_cache_iterator = true;
608         }
609 
610         /* Update cache if enabled.
611          * General note: here we only tell the cache how it has to transform
612          * during the render phase. The transformation is deferred because
613          * after the update the item can have its caching turned off,
614          * e.g. because its filter was removed. This way we avoid tempoerarily
615          * using more memory than the cache budget */
616         if (_cache) {
617             Geom::OptIntRect cl = _cacheRect();
618             if (_visible && cl && _has_cache_iterator) { // never create cache for invisible items
619                 // this takes care of invalidation on transform
620                 _cache->scheduleTransform(*cl, ctm_change);
621             } else {
622                 // Destroy cache for this item - outside of canvas or invisible.
623                 // The opposite transition (invisible -> visible or object
624                 // entering the canvas) is handled during the render phase
625                 setCached(false, true);
626             }
627         }
628     }
629 
630     if (to_update & STATE_RENDER) {
631         // now that we know drawbox, dirty the corresponding rect on canvas
632         // unless filtered, groups do not need to render by themselves, only their members
633         if (_fill_pattern) {
634             _fill_pattern->update(area, child_ctx, flags, reset);
635         }
636         if (_stroke_pattern) {
637             _stroke_pattern->update(area, child_ctx, flags, reset);
638         }
639         if (!is_drawing_group(this) || (_filter && render_filters)) {
640             _markForRendering();
641         }
642     }
643 }
644 
645 struct MaskLuminanceToAlpha {
operator ()Inkscape::MaskLuminanceToAlpha646     guint32 operator()(guint32 in) {
647         guint r = 0, g = 0, b = 0;
648         Display::ExtractRGB32(in, r, g, b);
649         // the operation of unpremul -> luminance-to-alpha -> multiply by alpha
650         // is equivalent to luminance-to-alpha on premultiplied color values
651         // original computation in double: r*0.2125 + g*0.7154 + b*0.0721
652         guint32 ao = r*109 + g*366 + b*37; // coeffs add up to 512
653         return ((ao + 256) << 15) & 0xff000000; // equivalent to ((ao + 256) / 512) << 24
654     }
655 };
656 
657 /**
658  * Rasterize items.
659  * This method submits the drawing operations required to draw this item
660  * to the supplied DrawingContext, restricting drawing the specified area.
661  *
662  * This method does some common tasks and calls the item-specific rendering
663  * function, _renderItem(), to render e.g. paths or bitmaps.
664  *
665  * @param flags Rendering options. This deals mainly with cache control.
666  */
667 unsigned
render(DrawingContext & dc,Geom::IntRect const & area,unsigned flags,DrawingItem * stop_at)668 DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, DrawingItem *stop_at)
669 {
670     bool outline = _drawing.outline();
671     bool render_filters = _drawing.renderFilters();
672     // stop_at is handled in DrawingGroup, but this check is required to handle the case
673     // where a filtered item with background-accessing filter has enable-background: new
674     if (this == stop_at) {
675         return RENDER_STOP;
676     }
677 
678     // If we are invisible, return immediately
679     if (!_visible) {
680         return RENDER_OK;
681     }
682 
683     if (_ctm.isSingular(1e-18)) {
684         return RENDER_OK;
685     }
686 
687     // TODO convert outline rendering to a separate virtual function
688     if (outline) {
689         _renderOutline(dc, area, flags);
690         return RENDER_OK;
691     }
692 
693     // carea is the area to paint
694     Geom::OptIntRect carea = Geom::intersect(area, _drawbox);
695 
696     if (!carea) {
697         return RENDER_OK;
698     }
699     // iarea is the bounding box for intermediate rendering
700     // Note 1: Pixels inside iarea but outside carea are invalid
701     //         (incomplete filter dependence region).
702     // Note 2: We only need to render carea of clip and mask, but
703     //         iarea of the object.
704 
705     Geom::OptIntRect iarea = carea;
706     // expand carea to contain the dependent area of filters.
707     if (_filter && render_filters) {
708         iarea = _cacheRect();
709         if (!iarea) {
710             iarea = carea;
711             _filter->area_enlarge(*iarea, this);
712             iarea.intersectWith(_drawbox);
713             setCached(false, true);
714         } else {
715             setCached(true, true);
716         }
717     }
718 
719     if (!iarea) {
720         return RENDER_OK;
721     }
722 
723     // Device scale for HiDPI screens (typically 1 or 2)
724     int device_scale = dc.surface()->device_scale();
725 
726     _applyAntialias(dc, _antialias);
727 
728     // Render from cache if possible
729     // Bypass in case of pattern, see below.
730     if (_cached && !(flags & RENDER_BYPASS_CACHE)) {
731         if (_cache) {
732             _cache->prepare();
733             dc.setOperator(ink_css_blend_to_cairo_operator(_mix_blend_mode));
734             _cache->paintFromCache(dc, carea, _filter && render_filters);
735             if (!carea) {
736                 dc.setSource(0, 0, 0, 0);
737                 return RENDER_OK;
738             }
739         } else {
740             // There is no cache. This could be because caching of this item
741             // was just turned on after the last update phase, or because
742             // we were previously outside of the canvas.
743             if (iarea) {
744                 _cache = new DrawingCache(*iarea, device_scale);
745             }
746         }
747     } else {
748         // if our caching was turned off after the last update, it was already
749         // deleted in setCached()
750     }
751 
752     // determine whether this shape needs intermediate rendering.
753     bool needs_intermediate_rendering = false;
754     bool &nir = needs_intermediate_rendering;
755     bool needs_opacity = (_opacity < 0.995);
756 
757     // this item needs an intermediate rendering if:
758     nir |= (_clip != nullptr);                       // 1. it has a clipping path
759     nir |= (_mask != nullptr);                       // 2. it has a mask
760     nir |= (_filter != nullptr && render_filters);   // 3. it has a filter
761     nir |= needs_opacity;                            // 4. it is non-opaque
762     nir |= (_mix_blend_mode != SP_CSS_BLEND_NORMAL); // 5. it has blend mode
763     nir |= (_isolation == SP_CSS_ISOLATION_ISOLATE); // 6. it is isolated
764     nir |= !parent();                                // 7. is root, need isolation from background
765     if (_prev_nir && !needs_intermediate_rendering) {
766         setCached(false, true);
767     }
768     _prev_nir = needs_intermediate_rendering;
769     nir |= (_cache != nullptr);                      // 5. it is to be cached
770 
771     /* How the rendering is done.
772      *
773      * Clipping, masking and opacity are done by rendering them to a surface
774      * and then compositing the object's rendering onto it with the IN operator.
775      * The object itself is rendered to a group.
776      *
777      * Opacity is done by rendering the clipping path with an alpha
778      * value corresponding to the opacity. If there is no clipping path,
779      * the entire intermediate surface is painted with alpha corresponding
780      * to the opacity value.
781      *
782      */
783     // Short-circuit the simple case.
784     // We also use this path for filter background rendering, because masking, clipping,
785     // filters and opacity do not apply when rendering the ancestors of the filtered
786     // element
787 
788     if ((flags & RENDER_FILTER_BACKGROUND) || !needs_intermediate_rendering) {
789         dc.setOperator(ink_css_blend_to_cairo_operator(SP_CSS_BLEND_NORMAL));
790         return _renderItem(dc, *iarea, flags & ~RENDER_FILTER_BACKGROUND, stop_at);
791     }
792 
793 
794     DrawingSurface intermediate(*iarea, device_scale);
795     DrawingContext ict(intermediate);
796 
797     // This path fails for patterns/hatches when stepping the pattern to handle overflows.
798     // The offsets are applied to drawing context (dc) but they are not copied to the
799     // intermediate context. Something like this is needed:
800     // Copy cairo matrix from dc to intermediate, needed for patterns/hatches
801     // cairo_matrix_t cairo_matrix;
802     // cairo_get_matrix(dc.raw(), &cairo_matrix);
803     // cairo_set_matrix(ict.raw(), &cairo_matrix);
804     // For the moment we disable caching for patterns,
805     //   see https://gitlab.com/inkscape/inkscape/-/issues/309
806 
807     unsigned render_result = RENDER_OK;
808 
809     // 1. Render clipping path with alpha = opacity.
810     ict.setSource(0,0,0,_opacity);
811     // Since clip can be combined with opacity, the result could be incorrect
812     // for overlapping clip children. To fix this we use the SOURCE operator
813     // instead of the default OVER.
814     ict.setOperator(CAIRO_OPERATOR_SOURCE);
815     ict.paint();
816     if (_clip) {
817         ict.pushGroup();
818         _clip->setAntialiasing(_antialias); // propagate antialias setting
819         _clip->clip(ict, *carea);
820         ict.popGroupToSource();
821         ict.setOperator(CAIRO_OPERATOR_IN);
822         ict.paint();
823     }
824     ict.setOperator(CAIRO_OPERATOR_OVER); // reset back to default
825 
826     // 2. Render the mask if present and compose it with the clipping path + opacity.
827     if (_mask) {
828         ict.pushGroup();
829         _mask->setAntialiasing(_antialias); // propagate antialias setting
830         _mask->render(ict, *carea, flags);
831 
832         cairo_surface_t *mask_s = ict.rawTarget();
833         // Convert mask's luminance to alpha
834         ink_cairo_surface_filter(mask_s, mask_s, MaskLuminanceToAlpha());
835         ict.popGroupToSource();
836         ict.setOperator(CAIRO_OPERATOR_IN);
837         ict.paint();
838         ict.setOperator(CAIRO_OPERATOR_OVER);
839     }
840 
841     // 3. Render object itself
842     ict.pushGroup();
843     render_result = _renderItem(ict, *iarea, flags, stop_at);
844 
845     // 4. Apply filter.
846     if (_filter && render_filters) {
847         bool rendered = false;
848         if (_filter->uses_background() && _background_accumulate) {
849             DrawingItem *bg_root = this;
850             for (; bg_root; bg_root = bg_root->_parent) {
851                 if (bg_root->_background_new) break;
852             }
853             if (bg_root) {
854                 DrawingSurface bg(*iarea, device_scale);
855                 DrawingContext bgdc(bg);
856                 bg_root->render(bgdc, *iarea, flags | RENDER_FILTER_BACKGROUND, this);
857                 _filter->render(this, ict, &bgdc);
858                 rendered = true;
859             }
860         }
861         if (!rendered) {
862             _filter->render(this, ict, nullptr);
863         }
864         // Note that because the object was rendered to a group,
865         // the internals of the filter need to use cairo_get_group_target()
866         // instead of cairo_get_target().
867     }
868 
869     // 5. Render object inside the composited mask + clip
870     ict.popGroupToSource();
871     ict.setOperator(CAIRO_OPERATOR_IN);
872     ict.paint();
873 
874     // 6. Paint the completed rendering onto the base context (or into cache)
875     if (_cached && _cache) {
876         DrawingContext cachect(*_cache);
877         cachect.rectangle(*iarea);
878         cachect.setOperator(CAIRO_OPERATOR_SOURCE);
879         cachect.setSource(&intermediate);
880         cachect.fill();
881         Geom::OptIntRect cl = _cacheRect();
882         if (_filter && render_filters && cl) {
883             _cache->markClean(*cl);
884         } else {
885             _cache->markClean(*iarea);
886         }
887     }
888 
889     dc.rectangle(*carea);
890     dc.setSource(&intermediate);
891     // 7. Render blend mode
892     dc.setOperator(ink_css_blend_to_cairo_operator(_mix_blend_mode));
893     dc.fill();
894     dc.setSource(0,0,0,0);
895     // Web isolation only works if parent doesnt have transform
896 
897 
898     // the call above is to clear a ref on the intermediate surface held by dc
899 
900     return render_result;
901 }
902 
903 void
_renderOutline(DrawingContext & dc,Geom::IntRect const & area,unsigned flags)904 DrawingItem::_renderOutline(DrawingContext &dc, Geom::IntRect const &area, unsigned flags)
905 {
906     // intersect with bbox rather than drawbox, as we want to render things outside
907     // of the clipping path as well
908     Geom::OptIntRect carea = Geom::intersect(area, _bbox);
909     if (!carea) return;
910 
911     // just render everything: item, clip, mask
912     // First, render the object itself
913     _renderItem(dc, *carea, flags, nullptr);
914 
915     // render clip and mask, if any
916     guint32 saved_rgba = _drawing.outlinecolor; // save current outline color
917     // render clippath as an object, using a different color
918     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
919     if (_clip) {
920         _drawing.outlinecolor = prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff); // green clips
921         _clip->render(dc, *carea, flags);
922     }
923     // render mask as an object, using a different color
924     if (_mask) {
925         _drawing.outlinecolor = prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); // blue masks
926         _mask->render(dc, *carea, flags);
927     }
928     _drawing.outlinecolor = saved_rgba; // restore outline color
929 }
930 
931 /**
932  * Rasterize the clipping path.
933  * This method submits drawing operations required to draw a basic filled shape
934  * of the item to the supplied drawing context. Rendering is limited to the
935  * given area. The rendering of the clipped object is composited into
936  * the result of this call using the IN operator. See the implementation
937  * of render() for details.
938  */
939 void
clip(Inkscape::DrawingContext & dc,Geom::IntRect const & area)940 DrawingItem::clip(Inkscape::DrawingContext &dc, Geom::IntRect const &area)
941 {
942     // don't bother if the object does not implement clipping (e.g. DrawingImage)
943     if (!_canClip()) return;
944     if (!_visible) return;
945     if (!area.intersects(_bbox)) return;
946 
947     _applyAntialias(dc, _antialias);
948 
949     dc.setSource(0,0,0,1);
950     dc.pushGroup();
951     // rasterize the clipping path
952     _clipItem(dc, area);
953     if (_clip) {
954         // The item used as the clipping path itself has a clipping path.
955         // Render this item's clipping path onto a temporary surface, then composite it
956         // with the item using the IN operator
957         dc.pushGroup();
958         _clip->clip(dc, area);
959         dc.popGroupToSource();
960         dc.setOperator(CAIRO_OPERATOR_IN);
961         dc.paint();
962     }
963     dc.popGroupToSource();
964     dc.setOperator(CAIRO_OPERATOR_OVER);
965     dc.paint();
966     dc.setSource(0,0,0,0);
967 }
968 
969 /**
970  * Get the item under the specified point.
971  * Searches the tree for the first item in the Z-order which is closer than
972  * @a delta to the given point. The pick should be visual - for example
973  * an object with a thick stroke should pick on the entire area of the stroke.
974  * @param p Search point
975  * @param delta Maximum allowed distance from the point
976  * @param sticky Whether the pick should ignore visibility and sensitivity.
977  *               When false, only visible and sensitive objects are considered.
978  *               When true, invisible and insensitive objects can also be picked.
979  */
980 DrawingItem *
pick(Geom::Point const & p,double delta,unsigned flags)981 DrawingItem::pick(Geom::Point const &p, double delta, unsigned flags)
982 {
983     // Sometimes there's no BBOX in state, reason unknown (bug 992817)
984     // I made this not an assert to remove the warning
985     if (!(_state & STATE_BBOX) || !(_state & STATE_PICK)) {
986         g_warning("Invalid state when picking: STATE_BBOX = %d, STATE_PICK = %d",
987                   _state & STATE_BBOX, _state & STATE_PICK);
988         return nullptr;
989     }
990     // ignore invisible and insensitive items unless sticky
991     if (!(flags & PICK_STICKY) && !(_visible && _sensitive)) {
992         return nullptr;
993     }
994 
995     bool outline = _drawing.outline() || _drawing.outlineOverlay() || _drawing.getOutlineSensitive();
996 
997     if (!_drawing.outline() && !_drawing.outlineOverlay() && !_drawing.getOutlineSensitive()) {
998         // pick inside clipping path; if NULL, it means the object is clipped away there
999         if (_clip) {
1000             DrawingItem *cpick = _clip->pick(p, delta, flags | PICK_AS_CLIP);
1001             if (!cpick) {
1002                 return nullptr;
1003             }
1004         }
1005         // same for mask
1006         if (_mask) {
1007             DrawingItem *mpick = _mask->pick(p, delta, flags);
1008             if (!mpick) {
1009                 return nullptr;
1010             }
1011         }
1012     }
1013 
1014     Geom::OptIntRect box = (outline || (flags & PICK_AS_CLIP)) ? _bbox : _drawbox;
1015     if (!box) {
1016         return nullptr;
1017     }
1018 
1019     Geom::Rect expanded = *box;
1020     expanded.expandBy(delta);
1021     DrawingGlyphs *dglyps = dynamic_cast<DrawingGlyphs *>(this);
1022     if (dglyps && !(flags & PICK_AS_CLIP)) {
1023         expanded = (Geom::Rect)dglyps->getPickBox();
1024     }
1025 
1026     if (expanded.contains(p)) {
1027         return _pickItem(p, delta, flags);
1028     }
1029     return nullptr;
1030 }
1031 
1032 // For debugging
1033 Glib::ustring
name()1034 DrawingItem::name()
1035 {
1036     if (_item) {
1037         if (_item->getId())
1038             return _item->getId();
1039         else
1040             return "No object id";
1041     } else {
1042         return "No associated object";
1043     }
1044 }
1045 
1046 // For debugging: Print drawing tree structure.
1047 void
recursivePrintTree(unsigned level)1048 DrawingItem::recursivePrintTree( unsigned level )
1049 {
1050     if (level == 0) {
1051         std::cout << "Display Item Tree" << std::endl;
1052     }
1053     std::cout << "DI: ";
1054     for (unsigned i = 0; i < level; ++i) {
1055         std::cout << "  ";
1056     }
1057     std::cout << name() << std::endl;
1058     for (auto & i : _children) {
1059         i.recursivePrintTree( level+1 );
1060     }
1061 }
1062 
1063 
1064 /**
1065  * Marks the current visual bounding box of the item for redrawing.
1066  * This is called whenever the object changes its visible appearance.
1067  * For some cases (such as setting opacity) this is enough, but for others
1068  * _markForUpdate() also needs to be called.
1069  */
1070 void
_markForRendering()1071 DrawingItem::_markForRendering()
1072 {
1073     // TODO: this function does too much work when a large subtree
1074     // is invalidated - fix
1075 
1076     bool outline = _drawing.outline();
1077     Geom::OptIntRect dirty = outline ? _bbox : _drawbox;
1078     if (!dirty) return;
1079 
1080     // dirty the caches of all parents
1081     DrawingItem *bkg_root = nullptr;
1082 
1083     for (DrawingItem *i = this; i; i = i->_parent) {
1084         if (i != this && i->_filter) {
1085             i->_filter->area_enlarge(*dirty, i);
1086         }
1087         if (i->_cache) {
1088             i->_cache->markDirty(*dirty);
1089         }
1090         if (i->_background_accumulate) {
1091             bkg_root = i;
1092         }
1093     }
1094 
1095     if (bkg_root && bkg_root->_parent && bkg_root->_parent->_parent) {
1096         bkg_root->_invalidateFilterBackground(*dirty);
1097     }
1098 
1099     //_drawing.signal_request_render.emit(*dirty);
1100     if (drawing().getCanvasItemDrawing()) {
1101         Geom::Rect area = *dirty;
1102         drawing().getCanvasItemDrawing()->get_canvas()->redraw_area(area);
1103     } else {
1104         // Can happen, e.g. Icon Preview dialog.
1105         // std::cerr << "DrawingItem::_markForRendering: Missing CanvasItemDrawing!" << std::endl;
1106     }
1107 
1108 }
1109 
1110 void
_invalidateFilterBackground(Geom::IntRect const & area)1111 DrawingItem::_invalidateFilterBackground(Geom::IntRect const &area)
1112 {
1113     if (!_drawbox.intersects(area)) return;
1114 
1115     if (_cache && _filter && _filter->uses_background()) {
1116         _cache->markDirty(area);
1117     }
1118 
1119     for (auto & i : _children) {
1120         i._invalidateFilterBackground(area);
1121     }
1122 }
1123 
1124 /**
1125  * Marks the item as needing a recomputation of internal data.
1126  *
1127  * This mechanism avoids traversing the entire rendering tree (which could be vast)
1128  * on every trivial state changed in any item. Only items marked as needing
1129  * an update (having some bits in their _state unset) will be traversed
1130  * during the update call.
1131  *
1132  * The _propagate variable is another optimization. We use it to specify that
1133  * all children should also have the corresponding flags unset before checking
1134  * whether they need to be traversed. This way there is one less traversal
1135  * of the tree. Without this we would need to unset state bits in all children.
1136  * With _propagate we do this during the update call, when we have to recurse
1137  * into children anyway.
1138  */
1139 void
_markForUpdate(unsigned flags,bool propagate)1140 DrawingItem::_markForUpdate(unsigned flags, bool propagate)
1141 {
1142     if (propagate) {
1143         _propagate_state |= flags;
1144     }
1145 
1146     if (_state & flags) {
1147         unsigned oldstate = _state;
1148         _state &= ~flags;
1149         if (oldstate != _state && _parent) {
1150             // If we actually reset anything in state, recurse on the parent.
1151             _parent->_markForUpdate(flags, false);
1152         } else {
1153             // If nothing changed, it means our ancestors are already invalidated
1154             // up to the root. Do not bother recursing, because it won't change anything.
1155             // Also do this if we are the root item, because we have no more ancestors
1156             // to invalidate.
1157             if (drawing().getCanvasItemDrawing()) {
1158                 drawing().getCanvasItemDrawing()->request_update();
1159             } else {
1160                 // Can happen, e.g. Eraser tool.
1161                 // std::cerr << "DrawingItem::_markForUpdate: Missing CanvasItemDrawing!" << std::endl;
1162             }
1163         }
1164     }
1165 }
1166 
1167 /**
1168  * Compute the caching score.
1169  *
1170  * Higher scores mean the item is more aggressively prioritized for automatic
1171  * caching by Inkscape::Drawing.
1172  */
1173 double
_cacheScore()1174 DrawingItem::_cacheScore()
1175 {
1176     Geom::OptIntRect cache_rect = _cacheRect();
1177     if (!cache_rect) return -1.0;
1178     // a crude first approximation:
1179     // the basic score is the number of pixels in the drawbox
1180     double score = cache_rect->area();
1181     // this is multiplied by the filter complexity and its expansion
1182     if (_filter &&_drawing.renderFilters()) {
1183         score *= _filter->complexity(_ctm);
1184         Geom::IntRect ref_area = Geom::IntRect::from_xywh(0, 0, 16, 16);
1185         Geom::IntRect test_area = ref_area;
1186         Geom::IntRect limit_area(0, INT_MIN, 16, INT_MAX);
1187         _filter->area_enlarge(test_area, this);
1188         // area_enlarge never shrinks the rect, so the result of intersection below
1189         // must be non-empty
1190         score *= double((test_area & limit_area)->area()) / ref_area.area();
1191     }
1192     // if the object is clipped, add 1/2 of its bbox pixels
1193     if (_clip && _clip->_bbox) {
1194         score += _clip->_bbox->area() * 0.5;
1195     }
1196     // if masked, add mask score
1197     if (_mask) {
1198         score += _mask->_cacheScore();
1199     }
1200     //g_message("caching score: %f", score);
1201     return score;
1202 }
1203 
expandByScale(Geom::IntRect & rect,double scale)1204 inline void expandByScale(Geom::IntRect &rect, double scale)
1205 {
1206     double fraction = (scale - 1) / 2;
1207     rect.expandBy(rect.width() * fraction, rect.height() * fraction);
1208 }
1209 
1210 
_cacheRect()1211 Geom::OptIntRect DrawingItem::_cacheRect()
1212 {
1213     Geom::OptIntRect r = _drawbox & _drawing.cacheLimit();
1214     if (_filter && _drawing.cacheLimit() && _drawing.renderFilters() && r && r != _drawbox) {
1215         // we check unfiltered item is emought inside the cache area to  render properly
1216         Geom::OptIntRect canvas = r;
1217         expandByScale(*canvas, 0.5);
1218         Geom::OptIntRect valid = Geom::intersect(canvas, _bbox);
1219         if (!valid && _bbox) {
1220             valid = _bbox;
1221             // contract the item _bbox to get reduced size to render. $ seems good enought
1222             expandByScale(*valid, 0.5);
1223             // now we get the nearest point to cache area
1224             Geom::IntPoint center = (*_drawing.cacheLimit()).midpoint();
1225             Geom::IntPoint nearest = (*valid).nearestEdgePoint(center);
1226             r.expandTo(nearest);
1227         }
1228         return _drawbox & r;
1229     }
1230     return r;
1231 }
1232 
1233 // apply antialias setting to cairo
_applyAntialias(DrawingContext & dc,unsigned _antialias)1234 void DrawingItem::_applyAntialias(DrawingContext &dc, unsigned _antialias)
1235 {
1236     switch(_antialias) {
1237         case 0:
1238             cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE);
1239             break;
1240         case 1:
1241             cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_FAST);
1242             break;
1243         case 2:
1244             cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_GOOD);
1245             break;
1246         case 3:
1247             cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_BEST);
1248             break;
1249         default: // should not happen
1250             g_assert_not_reached();
1251     }
1252 }
1253 
1254 } // end namespace Inkscape
1255 
1256 /*
1257   Local Variables:
1258   mode:c++
1259   c-file-style:"stroustrup"
1260   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1261   indent-tabs-mode:nil
1262   fill-column:99
1263   End:
1264 */
1265 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1266