// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * SVG drawing for display. *//* * Authors: * Krzysztof KosiƄski * Johan Engelen * * Copyright (C) 2011-2012 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include "display/drawing.h" #include "nr-filter-gaussian.h" #include "nr-filter-types.h" //grayscale colormode: #include "cairo-templates.h" #include "drawing-context.h" #include "canvas-arena.h" namespace Inkscape { // hardcoded grayscale color matrix values as default static const gdouble grayscale_value_matrix[20] = { 0.21, 0.72, 0.072, 0, 0, 0.21, 0.72, 0.072, 0, 0, 0.21, 0.72, 0.072, 0, 0, 0 , 0 , 0 , 1, 0 }; Drawing::Drawing(SPCanvasArena *arena) : _root(nullptr) , outlinecolor(0x000000ff) , delta(0) , _exact(false) , _outline_sensitive(true) , _rendermode(RENDERMODE_NORMAL) , _colormode(COLORMODE_NORMAL) , _blur_quality(BLUR_QUALITY_BEST) , _filter_quality(Filters::FILTER_QUALITY_BEST) , _cache_score_threshold(50000.0) , _cache_budget(0) , _grayscale_colormatrix(std::vector(grayscale_value_matrix, grayscale_value_matrix + 20)) , _canvasarena(arena) { } Drawing::~Drawing() { delete _root; } void Drawing::setRoot(DrawingItem *item) { delete _root; _root = item; if (item) { assert(item->_child_type == DrawingItem::CHILD_ORPHAN); item->_child_type = DrawingItem::CHILD_ROOT; } } RenderMode Drawing::renderMode() const { return _exact ? RENDERMODE_NORMAL : _rendermode; } ColorMode Drawing::colorMode() const { return (outline() || _exact) ? COLORMODE_NORMAL : _colormode; } bool Drawing::outline() const { return renderMode() == RENDERMODE_OUTLINE; } bool Drawing::visibleHairlines() const { return renderMode() == RENDERMODE_VISIBLE_HAIRLINES; } bool Drawing::renderFilters() const { return renderMode() == RENDERMODE_NORMAL || renderMode() == RENDERMODE_VISIBLE_HAIRLINES; } int Drawing::blurQuality() const { if (renderMode() == RENDERMODE_NORMAL) { return _exact ? BLUR_QUALITY_BEST : _blur_quality; } else { return BLUR_QUALITY_WORST; } } int Drawing::filterQuality() const { if (renderMode() == RENDERMODE_NORMAL) { return _exact ? Filters::FILTER_QUALITY_BEST : _filter_quality; } else { return Filters::FILTER_QUALITY_WORST; } } void Drawing::setRenderMode(RenderMode mode) { _rendermode = mode; } void Drawing::setColorMode(ColorMode mode) { _colormode = mode; } void Drawing::setBlurQuality(int q) { _blur_quality = q; } void Drawing::setFilterQuality(int q) { _filter_quality = q; } void Drawing::setExact(bool e) { _exact = e; } void Drawing::setOutlineSensitive(bool e) { _outline_sensitive = e; }; Geom::OptIntRect const & Drawing::cacheLimit() const { return _cache_limit; } void Drawing::setCacheLimit(Geom::OptIntRect const &r, bool update_cache) { _cache_limit = r; if (update_cache) { for (auto _cached_item : _cached_items) { _cached_item->_markForUpdate(DrawingItem::STATE_CACHE, false); } } } void Drawing::setCacheBudget(size_t bytes) { _cache_budget = bytes; _pickItemsForCaching(); } void Drawing::setGrayscaleMatrix(gdouble value_matrix[20]) { _grayscale_colormatrix = Filters::FilterColorMatrix::ColorMatrixMatrix( std::vector (value_matrix, value_matrix + 20) ); } void Drawing::update(Geom::IntRect const &area, unsigned flags, unsigned reset) { if (_root) { auto ctx = _canvasarena ? _canvasarena->ctx : UpdateContext(); _root->update(area, ctx, flags, reset); } if ((flags & DrawingItem::STATE_CACHE) || (flags & DrawingItem::STATE_ALL)) { // process the updated cache scores _pickItemsForCaching(); } } void Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, int antialiasing) { if (_root) { int prev_a = _root->_antialias; if(antialiasing >= 0) _root->setAntialiasing(antialiasing); _root->render(dc, area, flags); _root->setAntialiasing(prev_a); } if (colorMode() == COLORMODE_GRAYSCALE) { // apply grayscale filter on top of everything cairo_surface_t *input = dc.rawTarget(); cairo_surface_t *out = ink_cairo_surface_create_identical(input); ink_cairo_surface_filter(input, out, _grayscale_colormatrix); Geom::Point origin = dc.targetLogicalBounds().min(); dc.setSource(out, origin[Geom::X], origin[Geom::Y]); dc.setOperator(CAIRO_OPERATOR_SOURCE); dc.paint(); dc.setOperator(CAIRO_OPERATOR_OVER); cairo_surface_destroy(out); } } DrawingItem * Drawing::pick(Geom::Point const &p, double delta, unsigned flags) { if (_root) { return _root->pick(p, delta, flags); } return nullptr; } void Drawing::_pickItemsForCaching() { // we cache the objects with the highest score until the budget is exhausted _candidate_items.sort(std::greater()); 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 to_cache; for (CandidateList::iterator j = _candidate_items.begin(); j != i; ++j) { j->item->setCached(true); to_cache.insert(j->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 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 (auto j : to_uncache) { j->setCached(false); } } } // end namespace Inkscape /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :