// SPDX-License-Identifier: GPL-2.0-or-later /* * A container class for filter slots. Allows for simple getting and * setting images in filter slots without having to bother with * table indexes and such. * * Author: * Niko Kiirala * * Copyright (C) 2006,2007 Niko Kiirala * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include #include <2geom/transforms.h> #include "display/cairo-utils.h" #include "display/drawing-context.h" #include "display/nr-filter-types.h" #include "display/nr-filter-gaussian.h" #include "display/nr-filter-slot.h" #include "display/nr-filter-units.h" namespace Inkscape { namespace Filters { FilterSlot::FilterSlot(DrawingItem *item, DrawingContext *bgdc, DrawingContext &graphic, FilterUnits const &u) : _item(item) , _source_graphic(graphic.rawTarget()) , _background_ct(bgdc ? bgdc->raw() : nullptr) , _source_graphic_area(graphic.targetLogicalBounds().roundOutwards()) // fixme , _background_area(bgdc ? bgdc->targetLogicalBounds().roundOutwards() : Geom::IntRect()) // fixme , _units(u) , _last_out(NR_FILTER_SOURCEGRAPHIC) , filterquality(FILTER_QUALITY_BEST) , blurquality(BLUR_QUALITY_BEST) { using Geom::X; using Geom::Y; // compute slot bbox Geom::Affine trans = _units.get_matrix_display2pb(); Geom::Rect bbox_trans = graphic.targetLogicalBounds() * trans; Geom::Point min = bbox_trans.min(); _slot_x = min[X]; _slot_y = min[Y]; if (trans.isTranslation()) { _slot_w = _source_graphic_area.width(); _slot_h = _source_graphic_area.height(); } else { _slot_w = ceil(bbox_trans.width()); _slot_h = ceil(bbox_trans.height()); } } FilterSlot::~FilterSlot() { for (auto & _slot : _slots) { cairo_surface_destroy(_slot.second); } } cairo_surface_t *FilterSlot::getcairo(int slot_nr) { if (slot_nr == NR_FILTER_SLOT_NOT_SET) slot_nr = _last_out; SlotMap::iterator s = _slots.find(slot_nr); /* If we didn't have the specified image, but we could create it * from the other information we have, let's do that */ if (s == _slots.end() && (slot_nr == NR_FILTER_SOURCEGRAPHIC || slot_nr == NR_FILTER_SOURCEALPHA || slot_nr == NR_FILTER_BACKGROUNDIMAGE || slot_nr == NR_FILTER_BACKGROUNDALPHA || slot_nr == NR_FILTER_FILLPAINT || slot_nr == NR_FILTER_STROKEPAINT)) { switch (slot_nr) { case NR_FILTER_SOURCEGRAPHIC: { cairo_surface_t *tr = _get_transformed_source_graphic(); // Assume all source graphics are sRGB set_cairo_surface_ci( tr, SP_CSS_COLOR_INTERPOLATION_SRGB ); _set_internal(NR_FILTER_SOURCEGRAPHIC, tr); cairo_surface_destroy(tr); } break; case NR_FILTER_BACKGROUNDIMAGE: { cairo_surface_t *bg = _get_transformed_background(); // Assume all backgrounds are sRGB set_cairo_surface_ci( bg, SP_CSS_COLOR_INTERPOLATION_SRGB ); _set_internal(NR_FILTER_BACKGROUNDIMAGE, bg); cairo_surface_destroy(bg); } break; case NR_FILTER_SOURCEALPHA: { cairo_surface_t *src = getcairo(NR_FILTER_SOURCEGRAPHIC); cairo_surface_t *alpha = ink_cairo_extract_alpha(src); _set_internal(NR_FILTER_SOURCEALPHA, alpha); cairo_surface_destroy(alpha); } break; case NR_FILTER_BACKGROUNDALPHA: { cairo_surface_t *src = getcairo(NR_FILTER_BACKGROUNDIMAGE); cairo_surface_t *ba = ink_cairo_extract_alpha(src); _set_internal(NR_FILTER_BACKGROUNDALPHA, ba); cairo_surface_destroy(ba); } break; case NR_FILTER_FILLPAINT: //TODO case NR_FILTER_STROKEPAINT: //TODO default: break; } s = _slots.find(slot_nr); } if (s == _slots.end()) { // create empty surface cairo_surface_t *empty = cairo_surface_create_similar( _source_graphic, cairo_surface_get_content(_source_graphic), _slot_w, _slot_h); _set_internal(slot_nr, empty); cairo_surface_destroy(empty); s = _slots.find(slot_nr); } return s->second; } cairo_surface_t *FilterSlot::_get_transformed_source_graphic() { Geom::Affine trans = _units.get_matrix_display2pb(); if (trans.isTranslation()) { cairo_surface_reference(_source_graphic); return _source_graphic; } cairo_surface_t *tsg = cairo_surface_create_similar( _source_graphic, cairo_surface_get_content(_source_graphic), _slot_w, _slot_h); cairo_t *tsg_ct = cairo_create(tsg); cairo_translate(tsg_ct, -_slot_x, -_slot_y); ink_cairo_transform(tsg_ct, trans); cairo_translate(tsg_ct, _source_graphic_area.left(), _source_graphic_area.top()); cairo_set_source_surface(tsg_ct, _source_graphic, 0, 0); cairo_set_operator(tsg_ct, CAIRO_OPERATOR_SOURCE); cairo_paint(tsg_ct); cairo_destroy(tsg_ct); return tsg; } cairo_surface_t *FilterSlot::_get_transformed_background() { Geom::Affine trans = _units.get_matrix_display2pb(); cairo_surface_t *tbg; if (_background_ct) { cairo_surface_t *bg = cairo_get_group_target(_background_ct); tbg = cairo_surface_create_similar( bg, cairo_surface_get_content(bg), _slot_w, _slot_h); cairo_t *tbg_ct = cairo_create(tbg); cairo_translate(tbg_ct, -_slot_x, -_slot_y); ink_cairo_transform(tbg_ct, trans); cairo_translate(tbg_ct, _background_area.left(), _background_area.top()); cairo_set_source_surface(tbg_ct, bg, 0, 0); cairo_set_operator(tbg_ct, CAIRO_OPERATOR_SOURCE); cairo_paint(tbg_ct); cairo_destroy(tbg_ct); } else { tbg = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, _slot_w, _slot_h); } return tbg; } cairo_surface_t *FilterSlot::get_result(int res) { cairo_surface_t *result = getcairo(res); Geom::Affine trans = _units.get_matrix_pb2display(); if (trans.isIdentity()) { cairo_surface_reference(result); return result; } cairo_surface_t *r = cairo_surface_create_similar(_source_graphic, cairo_surface_get_content(_source_graphic), _source_graphic_area.width(), _source_graphic_area.height()); copy_cairo_surface_ci( result, r ); cairo_t *r_ct = cairo_create(r); cairo_translate(r_ct, -_source_graphic_area.left(), -_source_graphic_area.top()); ink_cairo_transform(r_ct, trans); cairo_translate(r_ct, _slot_x, _slot_y); cairo_set_source_surface(r_ct, result, 0, 0); cairo_set_operator(r_ct, CAIRO_OPERATOR_SOURCE); cairo_paint(r_ct); cairo_destroy(r_ct); return r; } void FilterSlot::_set_internal(int slot_nr, cairo_surface_t *surface) { // destroy after referencing // this way assigning a surface to a slot it already occupies will not cause errors cairo_surface_reference(surface); SlotMap::iterator s = _slots.find(slot_nr); if (s != _slots.end()) { cairo_surface_destroy(s->second); } _slots[slot_nr] = surface; } void FilterSlot::set(int slot_nr, cairo_surface_t *surface) { g_return_if_fail(surface != nullptr); if (slot_nr == NR_FILTER_SLOT_NOT_SET) slot_nr = NR_FILTER_UNNAMED_SLOT; _set_internal(slot_nr, surface); _last_out = slot_nr; } void FilterSlot::set_primitive_area(int slot_nr, Geom::Rect &area) { if (slot_nr == NR_FILTER_SLOT_NOT_SET) slot_nr = NR_FILTER_UNNAMED_SLOT; _primitiveAreas[slot_nr] = area; } Geom::Rect FilterSlot::get_primitive_area(int slot_nr) { if (slot_nr == NR_FILTER_SLOT_NOT_SET) slot_nr = _last_out; PrimitiveAreaMap::iterator s = _primitiveAreas.find(slot_nr); if (s == _primitiveAreas.end()) { return *(_units.get_filter_area()); } return s->second; } int FilterSlot::get_slot_count() { return _slots.size(); } void FilterSlot::set_quality(FilterQuality const q) { filterquality = q; } void FilterSlot::set_blurquality(int const q) { blurquality = q; } int FilterSlot::get_blurquality() { return blurquality; } void FilterSlot::set_device_scale(int const s) { device_scale = s; } int FilterSlot::get_device_scale() { return device_scale; } Geom::Rect FilterSlot::get_slot_area() const { Geom::Point p(_slot_x, _slot_y); Geom::Point dim(_slot_w, _slot_h); Geom::Rect r(p, p+dim); return r; } } /* namespace Filters */ } /* 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 :