summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/display/drawing-item.cpp197
-rw-r--r--src/display/drawing-item.h8
-rw-r--r--src/display/drawing-shape.cpp20
-rw-r--r--src/display/nr-filter-blend.cpp11
-rw-r--r--src/display/nr-filter-blend.h1
-rw-r--r--src/display/nr-filter-flood.h1
-rw-r--r--src/display/nr-filter-merge.cpp11
-rw-r--r--src/display/nr-filter-merge.h1
-rw-r--r--src/display/nr-filter-primitive.h9
-rw-r--r--src/display/nr-filter-turbulence.h1
-rw-r--r--src/display/nr-filter.cpp10
-rw-r--r--src/display/nr-filter.h3
12 files changed, 188 insertions, 85 deletions
diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp
index ac0d1be1e..f28894c14 100644
--- a/src/display/drawing-item.cpp
+++ b/src/display/drawing-item.cpp
@@ -56,6 +56,8 @@ DrawingItem::DrawingItem(Drawing &drawing)
, _cache(NULL)
, _state(0)
, _child_type(CHILD_ORPHAN)
+ , _background_new(0)
+ , _background_accumulate(0)
, _visible(true)
, _sensitive(true)
, _cached(0)
@@ -300,10 +302,9 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne
bool outline = _drawing.outline();
// Set reset flags according to propagation status
- if (_propagate) {
- reset |= ~_state;
- _propagate = FALSE;
- }
+ reset |= _propagate_state;
+ _propagate_state = 0;
+
_state &= ~reset; // reset state of this item
if ((~_state & flags) == 0) return; // nothing to do
@@ -323,80 +324,91 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne
_ctm = child_ctx.ctm;
// update _bbox
- unsigned old_state = _state;
+ unsigned to_update = _state ^ flags;
_state = _updateItem(area, child_ctx, flags, reset);
- // compute drawbox
- if (_filter && render_filters && _item_bbox) {
- _drawbox = _filter->compute_drawbox(this, *_item_bbox);
- } else {
- _drawbox = _bbox;
- }
-
- // Clipping
- if (_clip) {
- _clip->update(area, child_ctx, flags, reset);
- if (outline) {
- _bbox.unionWith(_clip->_bbox);
+ if (to_update & STATE_BBOX) {
+ // compute drawbox
+ if (_filter && render_filters && _item_bbox) {
+ _drawbox = _filter->compute_drawbox(this, *_item_bbox);
} else {
- _drawbox.intersectWith(_clip->_bbox);
+ _drawbox = _bbox;
}
- }
- // masking
- if (_mask) {
- _mask->update(area, child_ctx, flags, reset);
- if (outline) {
- _bbox.unionWith(_mask->_bbox);
- } else {
- // for masking, we need full drawbox of mask
- _drawbox.intersectWith(_mask->_drawbox);
+
+ // Clipping
+ if (_clip) {
+ _clip->update(area, child_ctx, flags, reset);
+ if (outline) {
+ _bbox.unionWith(_clip->_bbox);
+ } else {
+ _drawbox.intersectWith(_clip->_bbox);
+ }
+ }
+ // Masking
+ if (_mask) {
+ _mask->update(area, child_ctx, flags, reset);
+ if (outline) {
+ _bbox.unionWith(_mask->_bbox);
+ } else {
+ // for masking, we need full drawbox of mask
+ _drawbox.intersectWith(_mask->_drawbox);
+ }
}
}
- // Update cache score for this item
- if (_has_cache_iterator) {
- // remove old score information
- _drawing._candidate_items.erase(_cache_iterator);
- _has_cache_iterator = false;
- }
- double score = _cacheScore();
- if (score >= _drawing._cache_score_threshold) {
- CacheRecord cr;
- cr.score = score;
- // if _cacheRect() is empty, a negative score will be returned from _cacheScore(),
- // so this will not execute (cache score threshold must be positive)
- cr.cache_size = _cacheRect()->area() * 4;
- cr.item = this;
- _drawing._candidate_items.push_back(cr);
- _cache_iterator = --_drawing._candidate_items.end();
- _has_cache_iterator = true;
- }
-
- /* Update cache if enabled.
- * General note: here we only tell the cache how it has to transform
- * during the render phase. The transformation is deferred because
- * after the update the item can have its caching turned off,
- * e.g. because its filter was removed. This way we avoid tempoerarily
- * using more memory than the cache budget */
- if (_cache) {
- Geom::OptIntRect cl = _cacheRect();
- if (_visible && cl) { // never create cache for invisible items
- // this takes care of invalidation on transform
- _cache->scheduleTransform(*cl, ctm_change);
- } else {
- // Destroy cache for this item - outside of canvas or invisible.
- // The opposite transition (invisible -> visible or object
- // entering the canvas) is handled during the render phase
- delete _cache;
- _cache = NULL;
+ if (to_update & STATE_CACHE) {
+ // Update cache score for this item
+ if (_has_cache_iterator) {
+ // remove old score information
+ _drawing._candidate_items.erase(_cache_iterator);
+ _has_cache_iterator = false;
+ }
+ double score = _cacheScore();
+ if (score >= _drawing._cache_score_threshold) {
+ CacheRecord cr;
+ cr.score = score;
+ // if _cacheRect() is empty, a negative score will be returned from _cacheScore(),
+ // so this will not execute (cache score threshold must be positive)
+ cr.cache_size = _cacheRect()->area() * 4;
+ cr.item = this;
+ _drawing._candidate_items.push_back(cr);
+ _cache_iterator = --_drawing._candidate_items.end();
+ _has_cache_iterator = true;
+ }
+
+ /* Update cache if enabled.
+ * General note: here we only tell the cache how it has to transform
+ * during the render phase. The transformation is deferred because
+ * after the update the item can have its caching turned off,
+ * e.g. because its filter was removed. This way we avoid tempoerarily
+ * using more memory than the cache budget */
+ if (_cache) {
+ Geom::OptIntRect cl = _cacheRect();
+ if (_visible && cl) { // never create cache for invisible items
+ // this takes care of invalidation on transform
+ _cache->scheduleTransform(*cl, ctm_change);
+ } else {
+ // Destroy cache for this item - outside of canvas or invisible.
+ // The opposite transition (invisible -> visible or object
+ // entering the canvas) is handled during the render phase
+ delete _cache;
+ _cache = NULL;
+ }
}
}
- // now that we know drawbox, dirty the corresponding rect on canvas
- // unless filtered, groups do not need to render by themselves, only their members
- if (!is_drawing_group(this) || (_filter && render_filters)) {
- // mark for rendering if the item becomes renderable
- if ((old_state ^ _state) & STATE_RENDER) {
+ if (to_update & STATE_BACKGROUND) {
+ // Update _background_accumulate flag
+ // The code below correctly passes information from _background_new down the tree
+ _background_accumulate = _background_new;
+ if (_child_type == CHILD_NORMAL && _parent->_background_accumulate)
+ _background_accumulate = true;
+ }
+
+ if (to_update & STATE_RENDER) {
+ // now that we know drawbox, dirty the corresponding rect on canvas
+ // unless filtered, groups do not need to render by themselves, only their members
+ if (!is_drawing_group(this) || (_filter && render_filters)) {
_markForRendering();
}
}
@@ -720,14 +732,37 @@ DrawingItem::_markForRendering()
if (!dirty) return;
// dirty the caches of all parents
+ DrawingItem *bkg_root = NULL;
+
for (DrawingItem *i = this; i; i = i->_parent) {
if (i->_cached && i->_cache) {
i->_cache->markDirty(*dirty);
}
+ if (i->_background_accumulate) {
+ bkg_root = i;
+ }
+ }
+
+ if (bkg_root) {
+ bkg_root->_invalidateFilterBackground(*dirty);
}
_drawing.signal_request_render.emit(*dirty);
}
+void
+DrawingItem::_invalidateFilterBackground(Geom::IntRect const &area)
+{
+ if (!_drawbox.intersects(area)) return;
+
+ if (_cache && _filter && _filter->uses_background()) {
+ _cache->markDirty(area);
+ }
+
+ for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
+ i->_invalidateFilterBackground(area);
+ }
+}
+
/** @brief Marks the item as needing a recomputation of internal data.
*
* This mechanism avoids traversing the entire rendering tree (which could be vast)
@@ -745,10 +780,9 @@ DrawingItem::_markForRendering()
void
DrawingItem::_markForUpdate(unsigned flags, bool propagate)
{
- // we can't simply assign because a previous markForUpdate call
- // could have had propagate=true even if this one has propagate=false
- if (propagate)
- _propagate = true;
+ if (propagate) {
+ _propagate_state |= flags;
+ }
if (_state & flags) {
_state &= ~flags;
@@ -780,16 +814,23 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style)
_filter = NULL;
}
- /*
- if (style && style->enable_background.set
- && style->enable_background.value == SP_CSS_BACKGROUND_NEW) {
- _background_new = true;
- }*/
+ if (style && style->enable_background.set) {
+ if (style->enable_background.value == SP_CSS_BACKGROUND_NEW && !_background_new) {
+ _background_new = true;
+ _markForUpdate(STATE_BACKGROUND, true);
+ } else if (style->enable_background.value == SP_CSS_BACKGROUND_ACCUMULATE && _background_new) {
+ _background_new = false;
+ _markForUpdate(STATE_BACKGROUND, true);
+ }
+ }
- // TODO: STATE_ALL unsets too much
_markForUpdate(STATE_ALL, false);
}
+/** @brief Compute the caching score.
+ *
+ * Higher scores mean the item is more aggresively prioritized for automatic
+ * caching by Inkscape::Drawing. */
double
DrawingItem::_cacheScore()
{
diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h
index a50e3ef03..6d142c061 100644
--- a/src/display/drawing-item.h
+++ b/src/display/drawing-item.h
@@ -62,7 +62,8 @@ public:
STATE_CACHE = (1<<1), // cache extents and clean area are up-to-date
STATE_PICK = (1<<2), // can process pick requests
STATE_RENDER = (1<<3), // can be rendered
- STATE_ALL = (1<<4)-1
+ STATE_BACKGROUND = (1<<4), // filter background data is up to date
+ STATE_ALL = (1<<5)-1
};
enum PickFlags {
PICK_NORMAL = 0, // normal pick
@@ -123,6 +124,7 @@ protected:
void _renderOutline(DrawingContext &ct, Geom::IntRect const &area, unsigned flags);
void _markForUpdate(unsigned state, bool propagate);
void _markForRendering();
+ void _invalidateFilterBackground(Geom::IntRect const &area);
void _setStyleCommon(SPStyle *&_style, SPStyle *style);
double _cacheScore();
Geom::OptIntRect _cacheRect();
@@ -166,7 +168,11 @@ protected:
CacheList::iterator _cache_iterator;
unsigned _state : 8;
+ unsigned _propagate_state : 8;
unsigned _child_type : 3; // see ChildType enum
+ unsigned _background_new : 1; ///< Whether enable-background: new is set for this element
+ unsigned _background_accumulate : 1; ///< Whether this element accumulates background
+ /// (has any ancestor with enable-background: new)
unsigned _visible : 1;
unsigned _sensitive : 1; ///< Whether this item responds to events
unsigned _cached : 1; ///< Whether the rendering is stored for reuse
diff --git a/src/display/drawing-shape.cpp b/src/display/drawing-shape.cpp
index b333b50f8..1b201927f 100644
--- a/src/display/drawing-shape.cpp
+++ b/src/display/drawing-shape.cpp
@@ -244,8 +244,9 @@ DrawingShape::_pickItem(Geom::Point const &p, double delta, unsigned flags)
if (!_style) return NULL;
bool outline = _drawing.outline();
+ bool pick_as_clip = flags & PICK_AS_CLIP;
- if (SP_SCALE24_TO_FLOAT(_style->opacity.value) == 0 && !outline)
+ if (SP_SCALE24_TO_FLOAT(_style->opacity.value) == 0 && !outline && !pick_as_clip)
// fully transparent, no pick unless outline mode
return NULL;
@@ -253,9 +254,14 @@ DrawingShape::_pickItem(Geom::Point const &p, double delta, unsigned flags)
g_get_current_time (&tstart);
double width;
- if (outline) {
- width = 0.5;
+ if (pick_as_clip) {
+ width = 0; // no width should be applied to clip picking
+ // this overrides display mode and stroke style considerations
+ } else if (outline) {
+ width = 0.5; // in outline mode, everything is stroked with the same 0.5px line width
} else if (_nrstyle.stroke.type != NRStyle::PAINT_NONE && _nrstyle.stroke.opacity > 1e-3) {
+ // for normal picking calculate the distance corresponding top the stroke width
+ // FIXME BUG: this is incorrect for transformed strokes
float const scale = _ctm.descrim();
width = std::max(0.125f, _nrstyle.stroke_width * scale) / 2;
} else {
@@ -264,10 +270,12 @@ DrawingShape::_pickItem(Geom::Point const &p, double delta, unsigned flags)
double dist = Geom::infinity();
int wind = 0;
- bool needfill = (flags & PICK_AS_CLIP) || (_nrstyle.fill.type != NRStyle::PAINT_NONE
- && _nrstyle.fill.opacity > 1e-3 && !outline);
- bool wind_evenodd = (flags & PICK_AS_CLIP) ? (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) : (_style->fill_rule.computed == SP_WIND_RULE_EVENODD);
+ bool needfill = pick_as_clip || (_nrstyle.fill.type != NRStyle::PAINT_NONE &&
+ _nrstyle.fill.opacity > 1e-3 && !outline);
+ bool wind_evenodd = pick_as_clip ? (_style->clip_rule.computed == SP_WIND_RULE_EVENODD) :
+ (_style->fill_rule.computed == SP_WIND_RULE_EVENODD);
+ // actual shape picking
if (_drawing.arena()) {
Geom::Rect viewbox = _drawing.arena()->item.canvas->getViewbox();
viewbox.expandBy (width);
diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp
index 99a142b44..267883b4b 100644
--- a/src/display/nr-filter-blend.cpp
+++ b/src/display/nr-filter-blend.cpp
@@ -201,6 +201,17 @@ double FilterBlend::complexity(Geom::Affine const &)
return 1.1;
}
+bool FilterBlend::uses_background()
+{
+ if (_input == NR_FILTER_BACKGROUNDIMAGE || _input == NR_FILTER_BACKGROUNDALPHA ||
+ _input2 == NR_FILTER_BACKGROUNDIMAGE || _input2 == NR_FILTER_BACKGROUNDALPHA)
+ {
+ return true;
+ } else {
+ return false;
+ }
+}
+
void FilterBlend::set_input(int slot) {
_input = slot;
}
diff --git a/src/display/nr-filter-blend.h b/src/display/nr-filter-blend.h
index 5f71d468d..957d3cfc8 100644
--- a/src/display/nr-filter-blend.h
+++ b/src/display/nr-filter-blend.h
@@ -40,6 +40,7 @@ public:
virtual void render_cairo(FilterSlot &slot);
virtual bool can_handle_affine(Geom::Affine const &);
virtual double complexity(Geom::Affine const &ctm);
+ virtual bool uses_background();
virtual void set_input(int slot);
virtual void set_input(int input, int slot);
diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h
index c87bf6d8f..f744e9f48 100644
--- a/src/display/nr-filter-flood.h
+++ b/src/display/nr-filter-flood.h
@@ -29,6 +29,7 @@ public:
virtual bool can_handle_affine(Geom::Affine const &);
virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans);
virtual double complexity(Geom::Affine const &ctm);
+ virtual bool uses_background() { return false; }
virtual void set_opacity(double o);
virtual void set_color(guint32 c);
diff --git a/src/display/nr-filter-merge.cpp b/src/display/nr-filter-merge.cpp
index 6042da018..28ac19a19 100644
--- a/src/display/nr-filter-merge.cpp
+++ b/src/display/nr-filter-merge.cpp
@@ -72,6 +72,17 @@ double FilterMerge::complexity(Geom::Affine const &)
return 1.02;
}
+bool FilterMerge::uses_background()
+{
+ for (int i = 0; i < _input_image.size(); ++i) {
+ int input = _input_image[i];
+ if (input == NR_FILTER_BACKGROUNDIMAGE || input == NR_FILTER_BACKGROUNDALPHA) {
+ return true;
+ }
+ }
+ return false;
+}
+
void FilterMerge::set_input(int slot) {
_input_image[0] = slot;
}
diff --git a/src/display/nr-filter-merge.h b/src/display/nr-filter-merge.h
index cedab9086..238f9a3e7 100644
--- a/src/display/nr-filter-merge.h
+++ b/src/display/nr-filter-merge.h
@@ -27,6 +27,7 @@ public:
virtual void render_cairo(FilterSlot &);
virtual bool can_handle_affine(Geom::Affine const &);
virtual double complexity(Geom::Affine const &ctm);
+ virtual bool uses_background();
virtual void set_input(int input);
virtual void set_input(int input, int slot);
diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h
index 259a25e7e..501d76447 100644
--- a/src/display/nr-filter-primitive.h
+++ b/src/display/nr-filter-primitive.h
@@ -12,6 +12,7 @@
#define SEEN_NR_FILTER_PRIMITIVE_H
#include <2geom/forward.h>
+#include "display/nr-filter-types.h"
#include "svg/svg-length.h"
struct NRRectL;
@@ -66,6 +67,14 @@ public:
// returns cache score factor, reflecting the cost of rendering this filter
// this should return how many times slower this primitive is that normal rendering
virtual double complexity(Geom::Affine const &/*ctm*/) { return 1.0; }
+
+ virtual bool uses_background() {
+ if (_input == NR_FILTER_BACKGROUNDIMAGE || _input == NR_FILTER_BACKGROUNDALPHA) {
+ return true;
+ } else {
+ return false;
+ }
+ }
/**
* Sets the filter primitive subregion. Passing an unset length
diff --git a/src/display/nr-filter-turbulence.h b/src/display/nr-filter-turbulence.h
index 9f824ef48..0b451d355 100644
--- a/src/display/nr-filter-turbulence.h
+++ b/src/display/nr-filter-turbulence.h
@@ -46,6 +46,7 @@ public:
virtual void render_cairo(FilterSlot &slot);
virtual double complexity(Geom::Affine const &ctm);
+ virtual bool uses_background() { return false; }
void set_baseFrequency(int axis, double freq);
void set_numOctaves(int num);
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index ef9ac5be6..ae50e641b 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -294,6 +294,16 @@ double Filter::complexity(Geom::Affine const &ctm)
return factor;
}
+bool Filter::uses_background()
+{
+ for (unsigned i = 0 ; i < _primitive.size() ; i++) {
+ if (_primitive[i] && _primitive[i]->uses_background()) {
+ return true;
+ }
+ }
+ return false;
+}
+
/* Constructor table holds pointers to static methods returning filter
* primitives. This table is indexed with FilterPrimitiveType, so that
* for example method in _constructor[NR_FILTER_GAUSSIANBLUR]
diff --git a/src/display/nr-filter.h b/src/display/nr-filter.h
index 7d31e10ce..87a0fae94 100644
--- a/src/display/nr-filter.h
+++ b/src/display/nr-filter.h
@@ -167,6 +167,9 @@ public:
// returns cache score factor
double complexity(Geom::Affine const &ctm);
+ // says whether the filter accesses any of the background images
+ bool uses_background();
+
/** Creates a new filter with space for one filter element */
Filter();
/**