diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2011-08-09 01:14:07 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2011-08-09 01:14:07 +0000 |
| commit | f336c94939e9740501835b2584ad9a3160ac6d51 (patch) | |
| tree | 8ceebf2af7ffbca62e9ada0f4d3f56bf71ba783d /src | |
| parent | Rewrite NRArena -> Inkscape::Drawing. Call render and update (diff) | |
| download | inkscape-f336c94939e9740501835b2584ad9a3160ac6d51.tar.gz inkscape-f336c94939e9740501835b2584ad9a3160ac6d51.zip | |
Initial autocache work
(bzr r10347.1.26)
Diffstat (limited to 'src')
43 files changed, 367 insertions, 225 deletions
diff --git a/src/display/canvas-arena.cpp b/src/display/canvas-arena.cpp index 6026ebd3f..b254a55c8 100644 --- a/src/display/canvas-arena.cpp +++ b/src/display/canvas-arena.cpp @@ -105,7 +105,7 @@ sp_canvas_arena_init (SPCanvasArena *arena) Inkscape::DrawingGroup *root = new DrawingGroup(arena->drawing); root->setPickChildren(true); - root->setCached(true); + root->setCached(true, true); arena->drawing.setRoot(root); arena->drawing.signal_request_update.connect( diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 47f6c55a1..3f409b8ee 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -9,6 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <climits> #include "display/cairo-utils.h" #include "display/cairo-templates.h" #include "display/drawing.h" @@ -29,7 +30,7 @@ namespace Inkscape { * portion of the SVG document. Typically this is created by the SP tree, * in particular the show() virtual function. * - * @section ObjectLifetime Object Lifetime + * @section ObjectLifetime Object lifetime * Deleting a DrawingItem will cause all of its children to be deleted as well. * This can lead to nasty surprises if you hold references to things * which are children of what is being deleted. Therefore, in the SP tree, @@ -38,7 +39,7 @@ namespace Inkscape { * - this will cause dangling pointers inside the SPItem and lead to a crash. * Use the corresponing hide() method. * - * Outside of the SP tree you should not use any references after the root node + * Outside of the SP tree, you should not use any references after the root node * has been deleted. */ @@ -57,6 +58,8 @@ DrawingItem::DrawingItem(Drawing &drawing) , _visible(true) , _sensitive(true) , _cached(0) + , _cached_persistent(0) + , _has_cache_iterator(0) , _propagate(0) // , _renders_opacity(0) , _clip_child(0) @@ -77,6 +80,9 @@ DrawingItem::~DrawingItem() if (_cached) { _drawing._cached_items.erase(this); } + if (_has_cache_iterator) { + _drawing._candidate_items.erase(_cache_iterator); + } // remove this item from parent's children list // due to the effect of clearChildren(), this only happens for the top-level deleted item if (_parent) { @@ -182,17 +188,27 @@ DrawingItem::setSensitive(bool s) _sensitive = s; } -/// Enable / disable storing the rendering in memory. +/** @brief Enable / disable storing the rendering in memory. + * Calling setCached(false, true) will also remove the persistent status + */ void -DrawingItem::setCached(bool c) +DrawingItem::setCached(bool cached, bool persistent) { - _cached = c; - if (c) { + static const char *cache_env = getenv("_INKSCAPE_DISABLE_CACHE"); + if (cache_env) return; + + if (_cached_persistent && !persistent) + return; + + _cached = cached; + _cached_persistent = persistent ? cached : false; + if (cached) { _drawing._cached_items.insert(this); } else { _drawing._cached_items.erase(this); + delete _cache; + _cache = NULL; } - _markForUpdate(STATE_CACHE, false); } void @@ -277,7 +293,7 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne } _state &= ~reset; // reset state of this item - if ((~_state & flags) == 0) return; // nothing to do + if ((~_state & flags) == 0) return; // nothing to do // TODO this might be wrong if (_state & STATE_BBOX) { @@ -323,20 +339,40 @@ DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigne } } - // update cache if enabled - if (_cached) { - Geom::OptIntRect cl = _drawing.cacheLimit(); - cl.intersectWith(_drawbox); - if (cl) { - if (_cache) { - // this takes care of invalidation on transform - _cache->resizeAndTransform(*cl, ctm_change); - } else { - _cache = new Inkscape::DrawingCache(*cl); - // the cache is initially dirty - } + // 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 returnedfrom _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 { - // disable cache for this item - not visible + // 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; } @@ -377,9 +413,10 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag bool outline = _drawing.outline(); bool render_filters = _drawing.renderFilters(); - /* If we are invisible, just return successfully */ + // If we are invisible, return immediately if (!_visible) return; + // TODO convert outline rendering to a separate virtual function if (outline) { _renderOutline(ct, area, flags); return; @@ -389,10 +426,25 @@ DrawingItem::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flag Geom::OptIntRect carea = Geom::intersect(area, _drawbox); if (!carea) return; - // render from cache - if (_cached && _cache) { - if (_cache->paintFromCache(ct, *carea)) - return; + // render from cache if possible + if (_cached) { + if (_cache) { + _cache->prepare(); + if (_cache->paintFromCache(ct, *carea)) + return; + } else { + // There is no cache. This could be because caching of this item + // was just turned on after the last update phase, or because + // we are outside of the canvas. + Geom::OptIntRect cl = _drawing.cacheLimit(); + cl.intersectWith(_drawbox); + if (cl) { + _cache = new DrawingCache(*cl); + } + } + } else { + // if our caching was turned off after the last update, it was already + // deleted in setCached() } // expand carea to contain the dependent area of filters. @@ -695,6 +747,48 @@ DrawingItem::_setStyleCommon(SPStyle *&_style, SPStyle *style) && style->enable_background.value == SP_CSS_BACKGROUND_NEW) { _background_new = true; }*/ + + // TODO: STATE_ALL unsets too much + _markForUpdate(STATE_ALL, false); +} + +double +DrawingItem::_cacheScore() +{ + Geom::OptIntRect cache_rect = _cacheRect(); + if (!cache_rect) return -1.0; + + // a crude first approximation: + // the basic score is the number of pixels in the drawbox + double score = cache_rect->area(); + // this is multiplied by the filter complexity and its expansion + if (_filter &&_drawing.renderFilters()) { + score *= _filter->complexity(_ctm); + Geom::IntRect ref_area = Geom::IntRect::from_xywh(0, 0, 16, 16); + Geom::IntRect test_area = ref_area; + Geom::IntRect limit_area(0, INT_MIN, 16, INT_MAX); + _filter->area_enlarge(test_area, this); + // area_enlarge never shrinks the rect, so the result of intersection below + // must be non-empty + score *= double((test_area & limit_area)->area()) / ref_area.area(); + } + // if the object is clipped, add 1/2 of its bbox pixels + if (_clip && _clip->_bbox) { + score += _clip->_bbox->area() * 0.5; + } + // if masked, add mask score + if (_mask) { + score += _mask->_cacheScore(); + } + g_message("caching score: %f", score); + return score; +} + +Geom::OptIntRect +DrawingItem::_cacheRect() +{ + Geom::OptIntRect r = _drawbox & _drawing.cacheLimit(); + return r; } } // end namespace Inkscape diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index ba0c42695..b934570f2 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -12,7 +12,9 @@ #ifndef SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H #define SEEN_INKSCAPE_DISPLAY_DRAWING_ITEM_H +#include <list> #include <exception> +#include <boost/operators.hpp> #include <boost/utility.hpp> #include <boost/intrusive/list.hpp> #include <2geom/rect.h> @@ -27,6 +29,18 @@ struct UpdateContext { Geom::Affine ctm; }; +struct CacheRecord + : boost::totally_ordered<CacheRecord> +{ + bool operator<(CacheRecord const &other) const { return score < other.score; } + bool operator==(CacheRecord const &other) const { return score == other.score; } + operator DrawingItem *() const { return item; } + double score; + size_t cache_size; + DrawingItem *item; +}; +typedef std::list<CacheRecord> CacheList; + class InvalidItemException : public std::exception { virtual const char *what() const throw() { return "Invalid item in drawing"; @@ -72,7 +86,7 @@ public: bool sensitive() const { return _sensitive; } void setSensitive(bool v); bool cached() const { return _cached; } - void setCached(bool c); + void setCached(bool c, bool persistent = false); void setOpacity(float opacity); void setTransform(Geom::Affine const &trans); @@ -96,6 +110,8 @@ protected: void _markForUpdate(unsigned state, bool propagate); void _markForRendering(); void _setStyleCommon(SPStyle *&_style, SPStyle *style); + double _cacheScore(); + Geom::OptIntRect _cacheRect(); virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) { return 0; } virtual void _renderItem(DrawingContext &ct, Geom::IntRect const &area, unsigned flags) {} @@ -133,10 +149,14 @@ protected: void *_user_data; ///< Used to associate DrawingItems with SPItems that created them DrawingCache *_cache; + CacheList::iterator _cache_iterator; + unsigned _state : 8; unsigned _visible : 1; unsigned _sensitive : 1; ///< Whether this item responds to events unsigned _cached : 1; ///< Whether the rendering is stored for reuse + unsigned _cached_persistent : 1; ///< If set, will always be cached regardless of score + unsigned _has_cache_iterator : 1; ///< If set, _cache_list_pos is valid unsigned _propagate : 1; ///< Whether to call update for all children on next update //unsigned _renders_opacity : 1; ///< Whether object needs temporary surface for opacity unsigned _clip_child : 1; ///< If set, this is not a child of _parent, but a clipping path diff --git a/src/display/drawing-surface.cpp b/src/display/drawing-surface.cpp index 28bdc1f3c..1faa3151e 100644 --- a/src/display/drawing-surface.cpp +++ b/src/display/drawing-surface.cpp @@ -9,6 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <iostream> #include "display/drawing-surface.h" #include "display/drawing-context.h" #include "display/cairo-utils.h" @@ -165,6 +166,7 @@ DrawingSurface::pixelArea() const DrawingCache::DrawingCache(Geom::IntRect const &area) : DrawingSurface(area) , _clean_region(cairo_region_create()) + , _pending_area(area) {} DrawingCache::~DrawingCache() @@ -196,28 +198,41 @@ DrawingCache::isClean(Geom::IntRect const &area) const return false; } } + +/// Call this during the update phase to schedule a transformation of the cache. +void +DrawingCache::scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans) +{ + if (new_area.hasZeroArea() && trans.isIdentity()) return; + _pending_area = new_area; + _pending_transform *= trans; +} + +/// Transforms the cache according to the transform specified during the update phase. +/// Call this during render phase, before painting. void -DrawingCache::resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine const &trans) +DrawingCache::prepare() { Geom::IntRect old_area = pixelArea(); - bool is_identity = false; - bool is_integer_translation = false; - if (trans.isIdentity()) { - is_identity = true; - if (new_area == old_area) return; + bool is_identity = _pending_transform.isIdentity(); + if (is_identity) { + if (_pending_area == old_area) return; } - if (!is_identity && trans.isTranslation()) { - Geom::IntPoint t = trans.translation().round(); - if (Geom::are_near(Geom::Point(t), trans.translation())) { + + bool is_integer_translation = false; + if (!is_identity && _pending_transform.isTranslation()) { + Geom::IntPoint t = _pending_transform.translation().round(); + if (Geom::are_near(Geom::Point(t), _pending_transform.translation())) { // integer translation or identity with change of area is_integer_translation = true; cairo_region_translate(_clean_region, t[X], t[Y]); - if (old_area + t == new_area) { + if (old_area + t == _pending_area) { // if the areas match, the only thing to do // is to ensure that the clean area is not too large - cairo_rectangle_int_t limit = _convertRect(new_area); + cairo_rectangle_int_t limit = _convertRect(_pending_area); cairo_region_intersect_rectangle(_clean_region, &limit); _origin += t; + _pending_transform.setIdentity(); return; } } @@ -226,12 +241,12 @@ DrawingCache::resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine con Geom::IntPoint old_origin = old_area.min(); cairo_surface_t *old_surface = _surface; _surface = NULL; - _pixels = new_area.dimensions(); - _origin = new_area.min(); + _pixels = _pending_area.dimensions(); + _origin = _pending_area.min(); cairo_t *ct = createRawContext(); if (!is_identity) { - ink_cairo_transform(ct, trans); + ink_cairo_transform(ct, _pending_transform); } cairo_set_source_surface(ct, old_surface, old_origin[X], old_origin[Y]); cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE); @@ -245,9 +260,11 @@ DrawingCache::resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine con cairo_region_destroy(_clean_region); _clean_region = cairo_region_create(); } else { - cairo_rectangle_int_t limit = _convertRect(new_area); + cairo_rectangle_int_t limit = _convertRect(_pending_area); cairo_region_intersect_rectangle(_clean_region, &limit); } + std::cout << _pending_transform << old_area << _pending_area << std::endl; + _pending_transform.setIdentity(); } /** @brief Paints the clean area from cache and returns the remaining part */ diff --git a/src/display/drawing-surface.h b/src/display/drawing-surface.h index f279d771b..fd46d66ba 100644 --- a/src/display/drawing-surface.h +++ b/src/display/drawing-surface.h @@ -64,11 +64,14 @@ public: void markDirty(Geom::IntRect const &area = Geom::IntRect::infinite()); void markClean(Geom::IntRect const &area = Geom::IntRect::infinite()); bool isClean(Geom::IntRect const &area) const; - void resizeAndTransform(Geom::IntRect const &new_area, Geom::Affine const &trans); + void scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans); + void prepare(); bool paintFromCache(DrawingContext &ct, Geom::IntRect const &area); protected: cairo_region_t *_clean_region; + Geom::IntRect _pending_area; + Geom::Affine _pending_transform; private: static cairo_rectangle_int_t _convertRect(Geom::IntRect const &r); }; diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index 22bd84587..5881c84ed 100644 --- a/src/display/drawing.cpp +++ b/src/display/drawing.cpp @@ -9,6 +9,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <algorithm> #include "display/drawing.h" #include "nr-filter-gaussian.h" #include "nr-filter-types.h" @@ -24,6 +25,8 @@ Drawing::Drawing(SPCanvasArena *arena) , _colormode(COLORMODE_NORMAL) , _blur_quality(BLUR_QUALITY_BEST) , _filter_quality(Filters::FILTER_QUALITY_BEST) + , _cache_score_threshold(50000.0) + , _cache_budget(128 << 20) // 128 MiB , _canvasarena(arena) { @@ -126,23 +129,51 @@ Drawing::setCacheLimit(Geom::OptIntRect const &r) void Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) { - // TODO add autocache - if (!_root) return; - _root->update(area, ctx, flags, reset); + if (_root) { + _root->update(area, ctx, flags, reset); + } + // process the updated cache scores + // we cache the objects with the highest score until the budget is exhausted + _candidate_items.sort(std::greater<CacheRecord>()); + size_t used = 0; + CandidateList::iterator i; + for (i = _candidate_items.begin(); i != _candidate_items.end(); ++i) { + if (used + i->cache_size > _cache_budget) break; + used += i->cache_size; + } + + std::set<DrawingItem*> to_cache; + for (i = _candidate_items.begin(); i != _candidate_items.end(); ++i) { + i->item->setCached(true); + to_cache.insert(i->item); + } + // Everything which is now in _cached_items but not in to_cache must be uncached + // Note that calling setCached on an item modifies _cached_items + // TODO: find a way to avoid the set copy + std::set<DrawingItem*> to_uncache; + std::set_difference(_cached_items.begin(), _cached_items.end(), + to_cache.begin(), to_cache.end(), + std::inserter(to_uncache, to_uncache.end())); + for (std::set<DrawingItem*>::iterator j = to_uncache.begin(); j != to_uncache.end(); ++j) { + (*j)->setCached(false); + } } void Drawing::render(DrawingContext &ct, Geom::IntRect const &area, unsigned flags) { - if (!_root) return; - _root->render(ct, area, flags); + if (_root) { + _root->render(ct, area, flags); + } } DrawingItem * Drawing::pick(Geom::Point const &p, double delta, bool sticky) { - if (!_root) return NULL; - return _root->pick(p, delta, sticky); + if (_root) { + return _root->pick(p, delta, sticky); + } + return NULL; } } // end namespace Inkscape diff --git a/src/display/drawing.h b/src/display/drawing.h index 4560d277d..a8e70bbe6 100644 --- a/src/display/drawing.h +++ b/src/display/drawing.h @@ -13,6 +13,7 @@ #define SEEN_INKSCAPE_DISPLAY_DRAWING_H #include <set> +#include <boost/operators.hpp> #include <boost/utility.hpp> #include <sigc++/sigc++.h> #include <2geom/rect.h> @@ -22,17 +23,17 @@ namespace Inkscape { -struct OutlineColors { - guint32 paths; - guint32 clippaths; - guint32 masks; - guint32 images; -}; - class Drawing : boost::noncopyable { public: + struct OutlineColors { + guint32 paths; + guint32 clippaths; + guint32 masks; + guint32 images; + }; + Drawing(SPCanvasArena *arena = NULL); ~Drawing(); @@ -66,8 +67,13 @@ public: sigc::signal<void, DrawingItem *> signal_item_deleted; private: + void _reportCacheScore(CacheRecord const &); + + typedef std::list<CacheRecord> CandidateList; + DrawingItem *_root; - std::set<DrawingItem *> _cached_items; + std::set<DrawingItem *> _cached_items; // modified by DrawingItem::setCached() + CacheList _candidate_items; public: // TODO: remove these temporarily public members guint32 outlinecolor; @@ -80,9 +86,12 @@ private: int _filter_quality; Geom::OptIntRect _cache_limit; - OutlineColors _colors; + double _cache_score_threshold; ///< do not consider objects for caching below this score + size_t _cache_budget; ///< maximum allowed size of cache - SPCanvasArena *_canvasarena; // may be NULL is this arena is not the screen but used for export etc. + OutlineColors _colors; + SPCanvasArena *_canvasarena; // may be NULL is this arena is not the screen + // but used for export etc. friend class DrawingItem; }; diff --git a/src/display/nr-filter-blend.cpp b/src/display/nr-filter-blend.cpp index 3cec479fa..99a142b44 100644 --- a/src/display/nr-filter-blend.cpp +++ b/src/display/nr-filter-blend.cpp @@ -196,6 +196,11 @@ bool FilterBlend::can_handle_affine(Geom::Affine const &) return true; } +double FilterBlend::complexity(Geom::Affine const &) +{ + return 1.1; +} + 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 64b3c9284..5f71d468d 100644 --- a/src/display/nr-filter-blend.h +++ b/src/display/nr-filter-blend.h @@ -39,6 +39,7 @@ public: virtual void render_cairo(FilterSlot &slot); virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); virtual void set_input(int slot); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-colormatrix.cpp b/src/display/nr-filter-colormatrix.cpp index 7eb2fa2e9..6fa34bf0b 100644 --- a/src/display/nr-filter-colormatrix.cpp +++ b/src/display/nr-filter-colormatrix.cpp @@ -192,6 +192,11 @@ void FilterColorMatrix::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*tr { } +double FilterColorMatrix::complexity(Geom::Affine const &) +{ + return 2.0; +} + void FilterColorMatrix::set_type(FilterColorMatrixType t){ type = t; } diff --git a/src/display/nr-filter-colormatrix.h b/src/display/nr-filter-colormatrix.h index df851e0aa..5864a010e 100644 --- a/src/display/nr-filter-colormatrix.h +++ b/src/display/nr-filter-colormatrix.h @@ -38,6 +38,7 @@ public: virtual void render_cairo(FilterSlot &slot); 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 void set_type(FilterColorMatrixType type); virtual void set_value(gdouble value); diff --git a/src/display/nr-filter-component-transfer.cpp b/src/display/nr-filter-component-transfer.cpp index 80bc07df8..887352f62 100644 --- a/src/display/nr-filter-component-transfer.cpp +++ b/src/display/nr-filter-component-transfer.cpp @@ -308,6 +308,11 @@ void FilterComponentTransfer::area_enlarge(NRRectL &/*area*/, Geom::Affine const { } +double FilterComponentTransfer::complexity(Geom::Affine const &) +{ + return 2.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-component-transfer.h b/src/display/nr-filter-component-transfer.h index 89bc61403..6d65ae6d1 100644 --- a/src/display/nr-filter-component-transfer.h +++ b/src/display/nr-filter-component-transfer.h @@ -38,6 +38,7 @@ public: virtual void render_cairo(FilterSlot &slot); 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); FilterComponentTransferType type[4]; std::vector<gdouble> tableValues[4]; diff --git a/src/display/nr-filter-composite.cpp b/src/display/nr-filter-composite.cpp index 694ccaec5..b25ecdf2c 100644 --- a/src/display/nr-filter-composite.cpp +++ b/src/display/nr-filter-composite.cpp @@ -139,6 +139,11 @@ void FilterComposite::set_arithmetic(double k1, double k2, double k3, double k4) this->k4 = k4; } +double FilterComposite::complexity(Geom::Affine const &) +{ + return 1.1; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-composite.h b/src/display/nr-filter-composite.h index 930898830..95579cc0e 100644 --- a/src/display/nr-filter-composite.h +++ b/src/display/nr-filter-composite.h @@ -28,6 +28,7 @@ public: virtual void render_cairo(FilterSlot &); virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); virtual void set_input(int input); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-convolve-matrix.cpp b/src/display/nr-filter-convolve-matrix.cpp index 06e28b074..469baf346 100644 --- a/src/display/nr-filter-convolve-matrix.cpp +++ b/src/display/nr-filter-convolve-matrix.cpp @@ -212,6 +212,11 @@ void FilterConvolveMatrix::area_enlarge(NRRectL &area, Geom::Affine const &/*tra area.y1 += orderY - targetY - 1; } +double FilterConvolveMatrix::complexity(Geom::Affine const &) +{ + return kernelMatrix.size(); +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-convolve-matrix.h b/src/display/nr-filter-convolve-matrix.h index d13738260..8b7fc35d1 100644 --- a/src/display/nr-filter-convolve-matrix.h +++ b/src/display/nr-filter-convolve-matrix.h @@ -36,6 +36,7 @@ public: virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); void set_targetY(int coord); void set_targetX(int coord); diff --git a/src/display/nr-filter-diffuselighting.cpp b/src/display/nr-filter-diffuselighting.cpp index 039e56bb0..14144ace5 100644 --- a/src/display/nr-filter-diffuselighting.cpp +++ b/src/display/nr-filter-diffuselighting.cpp @@ -171,6 +171,11 @@ void FilterDiffuseLighting::area_enlarge(NRRectL &area, Geom::Affine const & /*t area.y1 += 1; } +double FilterDiffuseLighting::complexity(Geom::Affine const &) +{ + return 9.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-diffuselighting.h b/src/display/nr-filter-diffuselighting.h index 6e39242f6..bb3ceccb3 100644 --- a/src/display/nr-filter-diffuselighting.h +++ b/src/display/nr-filter-diffuselighting.h @@ -33,6 +33,7 @@ public: virtual ~FilterDiffuseLighting(); virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); union { SPFeDistantLight *distant; diff --git a/src/display/nr-filter-displacement-map.cpp b/src/display/nr-filter-displacement-map.cpp index 15200223b..75e310339 100644 --- a/src/display/nr-filter-displacement-map.cpp +++ b/src/display/nr-filter-displacement-map.cpp @@ -140,6 +140,11 @@ void FilterDisplacementMap::area_enlarge(NRRectL &area, Geom::Affine const &tran area.y1 += (int)(scaley)+2; } +double FilterDisplacementMap::complexity(Geom::Affine const &) +{ + return 3.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-displacement-map.h b/src/display/nr-filter-displacement-map.h index aec4b7eb6..393a904c1 100644 --- a/src/display/nr-filter-displacement-map.h +++ b/src/display/nr-filter-displacement-map.h @@ -27,12 +27,14 @@ public: static FilterPrimitive *create(); virtual ~FilterDisplacementMap(); + virtual void render_cairo(FilterSlot &slot); + virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + virtual void set_input(int slot); virtual void set_input(int input, int slot); virtual void set_scale(double s); virtual void set_channel_selector(int s, FilterDisplacementMapChannelSelector channel); - virtual void render_cairo(FilterSlot &slot); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); private: double scale; diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp index a015d3f1f..5716c1bc5 100644 --- a/src/display/nr-filter-flood.cpp +++ b/src/display/nr-filter-flood.cpp @@ -86,6 +86,13 @@ void FilterFlood::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) { } +double FilterFlood::complexity(Geom::Affine const &) +{ + // flood is actually less expensive than normal rendering, + // but when flood is processed, the object has already been rendered + return 1.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-flood.h b/src/display/nr-filter-flood.h index 6db90d439..c87bf6d8f 100644 --- a/src/display/nr-filter-flood.h +++ b/src/display/nr-filter-flood.h @@ -27,10 +27,13 @@ public: virtual void render_cairo(FilterSlot &slot); 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 void set_opacity(double o); virtual void set_color(guint32 c); virtual void set_icc(SVGICCColor *icc_color); - virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + private: double opacity; guint32 color; diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index a777d76a4..988a8479e 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -691,6 +691,13 @@ bool FilterGaussian::can_handle_affine(Geom::Affine const &) return false; } +double FilterGaussian::complexity(Geom::Affine const &trans) +{ + int area_x = _effect_area_scr(_deviation_x * trans.expansionX()); + int area_y = _effect_area_scr(_deviation_y * trans.expansionY()); + return 2.0 * area_x * area_y; +} + void FilterGaussian::set_deviation(double deviation) { if(IS_FINITE(deviation) && deviation >= 0) { diff --git a/src/display/nr-filter-gaussian.h b/src/display/nr-filter-gaussian.h index 811502016..f52bea01e 100644 --- a/src/display/nr-filter-gaussian.h +++ b/src/display/nr-filter-gaussian.h @@ -37,6 +37,7 @@ public: virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &m); virtual bool can_handle_affine(Geom::Affine const &m); + virtual double complexity(Geom::Affine const &ctm); /** * Set the standard deviation value for gaussian blur. Deviation along diff --git a/src/display/nr-filter-image.cpp b/src/display/nr-filter-image.cpp index b176cdcef..a22d23548 100644 --- a/src/display/nr-filter-image.cpp +++ b/src/display/nr-filter-image.cpp @@ -196,6 +196,12 @@ bool FilterImage::can_handle_affine(Geom::Affine const &) return true; } +double FilterImage::complexity(Geom::Affine const &) +{ + // TODO: right now we cannot actually measure this in any meaningful way. + return 1.1; +} + void FilterImage::set_href(const gchar *href){ if (feImageHref) g_free (feImageHref); feImageHref = (href) ? g_strdup (href) : NULL; diff --git a/src/display/nr-filter-image.h b/src/display/nr-filter-image.h index 0651109ec..5af0b3338 100644 --- a/src/display/nr-filter-image.h +++ b/src/display/nr-filter-image.h @@ -29,6 +29,8 @@ public: virtual void render_cairo(FilterSlot &slot); virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); + void set_document( SPDocument *document ); void set_href(const gchar *href); void set_region(SVGLength x, SVGLength y, SVGLength width, SVGLength height); diff --git a/src/display/nr-filter-merge.cpp b/src/display/nr-filter-merge.cpp index 51d3975cb..6042da018 100644 --- a/src/display/nr-filter-merge.cpp +++ b/src/display/nr-filter-merge.cpp @@ -67,6 +67,11 @@ bool FilterMerge::can_handle_affine(Geom::Affine const &) return true; } +double FilterMerge::complexity(Geom::Affine const &) +{ + return 1.02; +} + 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 263fc8026..cedab9086 100644 --- a/src/display/nr-filter-merge.h +++ b/src/display/nr-filter-merge.h @@ -26,6 +26,7 @@ public: virtual void render_cairo(FilterSlot &); virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); virtual void set_input(int input); virtual void set_input(int input, int slot); diff --git a/src/display/nr-filter-morphology.cpp b/src/display/nr-filter-morphology.cpp index c79667d3e..9e43d01f3 100644 --- a/src/display/nr-filter-morphology.cpp +++ b/src/display/nr-filter-morphology.cpp @@ -158,6 +158,13 @@ void FilterMorphology::area_enlarge(NRRectL &area, Geom::Affine const &trans) area.y1 += enlarge_y; } +double FilterMorphology::complexity(Geom::Affine const &trans) +{ + int enlarge_x = ceil(xradius * trans.expansionX()); + int enlarge_y = ceil(yradius * trans.expansionY()); + return enlarge_x * enlarge_y; +} + void FilterMorphology::set_operator(FilterMorphologyOperator &o){ Operator = o; } diff --git a/src/display/nr-filter-morphology.h b/src/display/nr-filter-morphology.h index 5924085d9..512eca83c 100644 --- a/src/display/nr-filter-morphology.h +++ b/src/display/nr-filter-morphology.h @@ -33,6 +33,8 @@ public: virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); + void set_operator(FilterMorphologyOperator &o); void set_xradius(double x); void set_yradius(double y); diff --git a/src/display/nr-filter-offset.cpp b/src/display/nr-filter-offset.cpp index 3b0f83841..db8b6d92a 100644 --- a/src/display/nr-filter-offset.cpp +++ b/src/display/nr-filter-offset.cpp @@ -85,6 +85,11 @@ void FilterOffset::area_enlarge(NRRectL &area, Geom::Affine const &trans) } } +double FilterOffset::complexity(Geom::Affine const &) +{ + return 1.02; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-offset.h b/src/display/nr-filter-offset.h index 09c57f803..841be6008 100644 --- a/src/display/nr-filter-offset.h +++ b/src/display/nr-filter-offset.h @@ -29,6 +29,7 @@ public: virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); virtual bool can_handle_affine(Geom::Affine const &); + virtual double complexity(Geom::Affine const &ctm); void set_dx(double amount); void set_dy(double amount); diff --git a/src/display/nr-filter-primitive.cpp b/src/display/nr-filter-primitive.cpp index 539e3e952..0a445b9e6 100644 --- a/src/display/nr-filter-primitive.cpp +++ b/src/display/nr-filter-primitive.cpp @@ -161,10 +161,6 @@ Geom::Rect FilterPrimitive::filter_primitive_area(FilterUnits const &units) return area; } -FilterTraits FilterPrimitive::get_input_traits() { - return TRAIT_ANYTHING; -} - } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h index ebecb91ec..259a25e7e 100644 --- a/src/display/nr-filter-primitive.h +++ b/src/display/nr-filter-primitive.h @@ -22,24 +22,6 @@ namespace Filters { class FilterSlot; class FilterUnits; -/* - * Different filter effects need different types of inputs. This is what - * traits are used for: one can specify, what special restrictions - * there are for inputs. - * - * Example: gaussian blur requires that x- and y-axis of input image - * are paraller to blurred object's x- and y-axis, respectively. - * Otherwise blur wouldn't rotate with the object. - * - * Values here should be powers of two, so these can be used as bitfield. - * That is: any combination ef existing traits can be specified. (excluding - * TRAIT_ANYTHING, which is alias for no traits defined) - */ -enum FilterTraits { - TRAIT_ANYTHING = 0, - TRAIT_PARALLER = 1 -}; - class FilterPrimitive { public: FilterPrimitive(); @@ -81,6 +63,10 @@ public: */ virtual void set_output(int slot); + // 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; } + /** * Sets the filter primitive subregion. Passing an unset length * (length._set == false) WILL change the parameter as it is @@ -103,14 +89,6 @@ public: */ Geom::Rect filter_primitive_area(FilterUnits const &units); - /** - * Queries the filter, which traits it needs from its input buffers. - * At the time of writing this, only one trait was needed, having - * user coordinate system and input pixelblock coordinates paraller to - * each other. - */ - virtual FilterTraits get_input_traits(); - /** @brief Indicate whether the filter primitive can handle the given affine. * * Results of some filter primitives depend on the coordinate system used when rendering. @@ -121,7 +99,7 @@ public: * When any filter returns false, filter rendering is performed on an intermediate surface * with edges parallel to the axes of the user coordinate system. This means * the matrices from FilterUnits will contain at most a (possibly non-uniform) scale - * and a translation. When all primitives of the filter return false, the rendering is + * and a translation. When all primitives of the filter return true, the rendering is * performed in display coordinate space and no intermediate surface is used. */ virtual bool can_handle_affine(Geom::Affine const &) { return false; } diff --git a/src/display/nr-filter-specularlighting.cpp b/src/display/nr-filter-specularlighting.cpp index 2e5f69d65..c28fd485a 100644 --- a/src/display/nr-filter-specularlighting.cpp +++ b/src/display/nr-filter-specularlighting.cpp @@ -174,136 +174,6 @@ void FilterSpecularLighting::render_cairo(FilterSlot &slot) cairo_surface_destroy(out); } -/* -int FilterSpecularLighting::render(FilterSlot &slot, FilterUnits const &units) { - NRPixBlock *in = slot.get(_input); - if (!in) { - g_warning("Missing source image for feSpecularLighting (in=%d)", _input); - return 1; - } - - NRPixBlock *out = new NRPixBlock; - - //Fvector *L = NULL; //vector to the light - - int w = in->area.x1 - in->area.x0; - int h = in->area.y1 - in->area.y0; - int x0 = in->area.x0; - int y0 = in->area.y0; - int i, j; - //As long as FilterRes and kernel unit is not supported we hardcode the - //default value - int dx = 1; //TODO setup - int dy = 1; //TODO setup - //surface scale - Geom::Affine trans = units.get_matrix_primitiveunits2pb(); - gdouble ss = surfaceScale * trans[0]; - gdouble ks = specularConstant; //diffuse lighting constant - NR::Fvector L, N, LC, H; - gdouble inter; - - nr_pixblock_setup_fast(out, NR_PIXBLOCK_MODE_R8G8B8A8N, - in->area.x0, in->area.y0, in->area.x1, in->area.y1, - true); - unsigned char *data_i = NR_PIXBLOCK_PX (in); - unsigned char *data_o = NR_PIXBLOCK_PX (out); - //No light, nothing to do - switch (light_type) { - case DISTANT_LIGHT: - //the light vector is constant - { - DistantLight *dl = new DistantLight(light.distant, lighting_color); - dl->light_vector(L); - dl->light_components(LC); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); // CLAMP includes rounding! - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete dl; - } - break; - case POINT_LIGHT: - { - PointLight *pl = new PointLight(light.point, lighting_color, trans); - pl->light_components(LC); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - pl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete pl; - } - break; - case SPOT_LIGHT: - { - SpotLight *sl = new SpotLight(light.spot, lighting_color, trans); - //TODO we need a reference to the filter to determine primitiveUnits - //if objectBoundingBox is used, use a different matrix for light_vector - // UPDATE: trans is now correct matrix from primitiveUnits to - // pixblock coordinates - //finish the work - for (i = 0, j = 0; i < w*h; i++) { - NR::compute_surface_normal(N, ss, in, i / w, i % w, dx, dy); - sl->light_vector(L, - i % w + x0, - i / w + y0, - ss * (double) data_i[4*i+3]/ 255); - sl->light_components(LC, L); - NR::normalized_sum(H, L, NR::EYE_VECTOR); - COMPUTE_INTER(inter, N, H, ks, specularExponent); - - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]); - data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]); - data_o[j] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]); - ++j; - } - out->empty = FALSE; - delete sl; - } - break; - //else unknown light source, doing nothing - case NO_LIGHT: - default: - { - if (light_type != NO_LIGHT) - g_warning("unknown light source %d", light_type); - out->empty = false; - } - } - - //finishing - slot.set(_output, out); - //nr_pixblock_release(in); - //delete in; - return 0; -}*/ - void FilterSpecularLighting::area_enlarge(NRRectL &area, Geom::Affine const & /*trans*/) { // TODO: support kernelUnitLength @@ -314,6 +184,11 @@ void FilterSpecularLighting::area_enlarge(NRRectL &area, Geom::Affine const & /* area.y1 += 1; } +double FilterSpecularLighting::complexity(Geom::Affine const &) +{ + return 9.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-specularlighting.h b/src/display/nr-filter-specularlighting.h index 2fcb02588..8471b70b0 100644 --- a/src/display/nr-filter-specularlighting.h +++ b/src/display/nr-filter-specularlighting.h @@ -31,8 +31,10 @@ public: FilterSpecularLighting(); static FilterPrimitive *create(); virtual ~FilterSpecularLighting(); + virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); union { SPFeDistantLight *distant; diff --git a/src/display/nr-filter-tile.cpp b/src/display/nr-filter-tile.cpp index b88386638..4aadde2aa 100644 --- a/src/display/nr-filter-tile.cpp +++ b/src/display/nr-filter-tile.cpp @@ -45,6 +45,11 @@ void FilterTile::area_enlarge(NRRectL &/*area*/, Geom::Affine const &/*trans*/) { } +double FilterTile::complexity(Geom::Affine const &) +{ + return 1.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-tile.h b/src/display/nr-filter-tile.h index 5c0a3e553..37e257f79 100644 --- a/src/display/nr-filter-tile.h +++ b/src/display/nr-filter-tile.h @@ -27,6 +27,7 @@ public: virtual void render_cairo(FilterSlot &slot); virtual void area_enlarge(NRRectL &area, Geom::Affine const &trans); + virtual double complexity(Geom::Affine const &ctm); }; } /* namespace Filters */ diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index 60d5ce872..f065ded11 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -388,6 +388,11 @@ void FilterTurbulence::render_cairo(FilterSlot &slot) cairo_surface_destroy(out); } +double FilterTurbulence::complexity(Geom::Affine const &) +{ + return 5.0; +} + } /* namespace Filters */ } /* namespace Inkscape */ diff --git a/src/display/nr-filter-turbulence.h b/src/display/nr-filter-turbulence.h index 8d3639543..9f824ef48 100644 --- a/src/display/nr-filter-turbulence.h +++ b/src/display/nr-filter-turbulence.h @@ -45,6 +45,7 @@ public: virtual ~FilterTurbulence(); virtual void render_cairo(FilterSlot &slot); + virtual double complexity(Geom::Affine const &ctm); 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 e84e6f0c2..df6b6222b 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -282,6 +282,18 @@ Geom::Rect Filter::filter_effect_area(Geom::Rect const &bbox) return area; } +double Filter::complexity(Geom::Affine const &ctm) +{ + double factor; + for (unsigned i = 0 ; i < _primitive.size() ; i++) { + if (_primitive[i]) { + double f = _primitive[i]->complexity(ctm); + factor += (f - 1.0); + } + } + return factor; +} + /* 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 31705f53b..7d31e10ce 100644 --- a/src/display/nr-filter.h +++ b/src/display/nr-filter.h @@ -164,6 +164,9 @@ public: */ Geom::Rect filter_effect_area(Geom::Rect const &bbox); + // returns cache score factor + double complexity(Geom::Affine const &ctm); + /** Creates a new filter with space for one filter element */ Filter(); /** |
