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