Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-filter.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Hugo Rodrigues <haa.rodrigues@gmail.com>
8 * Niko Kiirala <niko@kiirala.com>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 2006,2007 Authors
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "sp-filter.h"
18
19#include <cstring>
20#include <utility>
21#include <vector>
22#include <unordered_map>
23
24#include <2geom/transforms.h>
25#include <glibmm.h>
26
27#include "attributes.h"
28#include "bad-uri-exception.h"
30#include "display/nr-filter.h"
31#include "document.h"
33#include "sp-filter-reference.h"
34#include "uri.h"
37
40 , filterUnits_set(false)
41 , primitiveUnits(SP_FILTER_UNITS_USERSPACEONUSE)
42 , primitiveUnits_set(false)
43{
44 href = std::make_unique<SPFilterReference>(this);
45
46 // Gets called when the filter is (re)attached to another filter.
47 href->changedSignal().connect([this] (SPObject *old_ref, SPObject *ref) {
48 if (old_ref) {
49 modified_connection.disconnect();
50 }
51
52 if (is<SPFilter>(ref) && ref != this) {
53 modified_connection = ref->connectModified([this] (SPObject*, unsigned) {
55 });
56 }
57
59 });
60
61 x = 0;
62 y = 0;
63 width = 0;
64 height = 0;
65 auto_region = true;
66}
67
68SPFilter::~SPFilter() = default;
69
71{
72 // Read values of key attributes from XML nodes into object.
73 readAttr(SPAttr::STYLE); // struct not derived from SPItem, we need to do this ourselves.
83 _refcount = 0;
84
86
87 document->addResource("filter", this);
88}
89
91{
92 document->removeResource("filter", this);
93
94 if (href) {
95 modified_connection.disconnect();
96 href->detach();
97 href.reset();
98 }
99
101}
102
103void SPFilter::set(SPAttr key, char const *value)
104{
105 switch (key) {
107 if (value) {
108 if (!std::strcmp(value, "userSpaceOnUse")) {
110 } else {
112 }
113 filterUnits_set = true;
114 } else {
116 filterUnits_set = false;
117 }
119 break;
121 if (value) {
122 if (!std::strcmp(value, "objectBoundingBox")) {
124 } else {
126 }
127 primitiveUnits_set = true;
128 } else {
130 primitiveUnits_set = false;
131 }
133 break;
134 case SPAttr::X:
135 x.readOrUnset(value);
137 break;
138 case SPAttr::Y:
139 y.readOrUnset(value);
141 break;
142 case SPAttr::WIDTH:
143 width.readOrUnset(value);
145 break;
146 case SPAttr::HEIGHT:
147 height.readOrUnset(value);
149 break;
151 auto_region = !value || std::strcmp(value, "false");
153 break;
155 filterRes.set(value);
157 break;
159 if (value) {
160 try {
161 href->attach(Inkscape::URI(value));
162 } catch (Inkscape::BadURIException const &e) {
163 g_warning("%s", e.what());
164 href->detach();
165 }
166 } else {
167 href->detach();
168 }
169 break;
170 default:
171 // See if any parents need this value.
172 SPObject::set(key, value);
173 break;
174 }
175}
176
181{
182 // NOTE: this is currently updated by sp_style_filter_ref_changed() in style.cpp
183 return _refcount;
184}
185
186void SPFilter::update(SPCtx *ctx, unsigned flags)
187{
188 auto const cflags = cascade_flags(flags);
189
190 ensure_slots();
191
193 auto ictx = static_cast<SPItemCtx*>(ctx);
194
195 // Do here since we know viewport (Bounding box case handled during rendering)
196 // Note: This only works for root viewport since this routine is not called after
197 // setting a new viewport. A true fix requires a strategy like SPItemView or SPMarkerView.
200 }
201 }
202
203 // Update filter primitives in order to update filter primitive area
204 for (auto &c : children) {
206 c.updateDisplay(ctx, cflags);
207 }
208 }
209
210 SPObject::update(ctx, flags);
211}
212
213void SPFilter::modified(unsigned flags)
214{
215 auto const cflags = cascade_flags(flags);
216
217 // We are not an LPE, do not update filter regions on load.
220 }
221
222 for (auto &c : children) {
224 c.emitModified(cflags);
225 }
226 }
227
228 for (auto item : views) {
229 item->setFilterRenderer(build_renderer(item));
230 }
231}
232
234{
235 // Original from sp-item-group.cpp
236 if (flags & SP_OBJECT_WRITE_BUILD) {
237 if (!repr) {
238 repr = doc->createElement("svg:filter");
239 }
240
241 std::vector<Inkscape::XML::Node *> l;
242 for (auto &child : children) {
243 auto crepr = child.updateRepr(doc, nullptr, flags);
244 if (crepr) {
245 l.push_back(crepr);
246 }
247 }
248
249 for (auto i = l.rbegin(); i != l.rend(); ++i) {
250 repr->addChild(*i, nullptr);
252 }
253 } else {
254 for (auto &child : children) {
255 child.updateRepr(flags);
256 }
257 }
258
259 if ((flags & SP_OBJECT_WRITE_ALL) || filterUnits_set) {
260 switch (filterUnits) {
262 repr->setAttribute("filterUnits", "userSpaceOnUse");
263 break;
264 default:
265 repr->setAttribute("filterUnits", "objectBoundingBox");
266 break;
267 }
268 }
269
270 if ((flags & SP_OBJECT_WRITE_ALL) || primitiveUnits_set) {
271 switch (primitiveUnits) {
273 repr->setAttribute("primitiveUnits", "objectBoundingBox");
274 break;
275 default:
276 repr->setAttribute("primitiveUnits", "userSpaceOnUse");
277 break;
278 }
279 }
280
281 if (x._set) {
283 } else {
284 repr->removeAttribute("x");
285 }
286
287 if (y._set) {
289 } else {
290 repr->removeAttribute("y");
291 }
292
293 if (width._set) {
295 } else {
296 repr->removeAttribute("width");
297 }
298
299 if (height._set) {
301 } else {
302 repr->removeAttribute("height");
303 }
304
305 if (filterRes.getNumber() >= 0) {
307 repr->setAttribute("filterRes", tmp);
308 } else {
309 repr->removeAttribute("filterRes");
310 }
311
312 if (href->getURI()) {
313 auto uri_string = href->getURI()->str();
314 auto href_key = Inkscape::getHrefAttribute(*repr).first;
316 }
317
318 SPObject::write(doc, repr, flags);
319
320 return repr;
321}
322
330{
332 return;
333 }
334
335 // Combine all items into one region for updating.
337 for (auto &obj : hrefList) {
338 auto item = cast<SPItem>(obj);
340 }
341 if (opt_r) {
342 Geom::Rect region = *opt_r;
343 set_filter_region(region.left(), region.top(), region.width(), region.height());
344 }
345}
346
353{
355 return; // No adjustment for dead box
356 }
357
358 auto region = get_automatic_filter_region(item);
359
360 // Set the filter region into this filter object
361 set_filter_region(region.left(), region.top(), region.width(), region.height());
362}
363
370{
371 // Calling bbox instead of visualBound() avoids re-requesting filter regions
374 if (!v_box || !g_box) {
375 return Geom::Rect(); // No adjustment for dead box
376 }
377
378 // Because the filter box is in geometric bounding box units, it must ALSO
379 // take account of the visualBox, so even if the filter does NOTHING to the
380 // size of an object, we must add the difference between the geometric and
381 // visual boxes ourselves or find them cut off by renderers of all kinds.
384 for (auto &primitive_obj : children) {
385 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
386 if (primitive) {
387 // Update the region with the primitive's options
388 outbox = primitive->calculate_region(outbox);
389 }
390 }
391
392 // Include the original visual bounding-box in the result
393 outbox.unionWith(v_box);
394 // Scale outbox to width/height scale of input, this scales the geometric
395 // into the visual bounding box requiring any changes to it to re-run this.
396 outbox *= Geom::Translate(-inbox.left(), -inbox.top());
397 outbox *= Geom::Scale(1.0 / inbox.width(), 1.0 / inbox.height());
398 return outbox;
399}
400
404void SPFilter::set_filter_region(double x, double y, double width, double height)
405{
406 if (width != 0 && height != 0) {
407 // TODO: set it in UserSpaceOnUse instead?
408 auto repr = getRepr();
413 }
414}
415
419bool SPFilter::valid_for(SPObject const *obj) const
420{
421 for (auto &primitive_obj : children) {
422 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
423 if (primitive && !primitive->valid_for(obj)) {
424 return false;
425 }
426 }
427 return true;
428}
429
431{
433
435 for (auto &v : views) {
436 f->show(v);
437 }
438 }
439
441}
442
444{
446 for (auto &v : views) {
447 f->hide(v);
448 }
449 }
450
452
454}
455
461
468
470{
471 if (slots_valid) return;
472 slots_valid = true;
473
475
476 for (auto &c : children) {
477 if (auto prim = cast<SPFilterPrimitive>(&c)) {
478 prim->resolve_slots(resolver);
479 }
480 }
481}
482
483std::unique_ptr<Inkscape::Filters::Filter> SPFilter::build_renderer(Inkscape::DrawingItem *item)
484{
485 auto nr_filter = std::make_unique<Inkscape::Filters::Filter>(primitive_count());
486
487 ensure_slots();
488
489 nr_filter->set_filter_units(filterUnits);
490 nr_filter->set_primitive_units(primitiveUnits);
491 nr_filter->set_x(x);
492 nr_filter->set_y(y);
493 nr_filter->set_width(width);
494 nr_filter->set_height(height);
495
496 if (filterRes.getNumber() >= 0) {
497 if (filterRes.getOptNumber() >= 0) {
498 nr_filter->set_resolution(filterRes.getNumber(), filterRes.getOptNumber());
499 } else {
500 nr_filter->set_resolution(filterRes.getNumber());
501 }
502 }
503
504 nr_filter->clear_primitives();
505 for (auto &primitive_obj : children) {
506 if (auto primitive = cast<SPFilterPrimitive>(&primitive_obj)) {
507 nr_filter->add_primitive(primitive->build_renderer(item));
508 }
509 }
510
511 return nr_filter;
512}
513
515{
516 int count = 0;
517
518 for (auto const &primitive_obj : children) {
520 count++;
521 }
522 }
523
524 return count;
525}
526
527Glib::ustring SPFilter::get_new_result_name() const
528{
529 int largest = 0;
530
531 for (auto const &primitive_obj : children) {
533 auto repr = primitive_obj.getRepr();
534 auto result = repr->attribute("result");
535 if (result) {
536 int index;
537 if (std::sscanf(result, "result%5d", &index) == 1) {
538 if (index > largest) {
539 largest = index;
540 }
541 }
542 }
543 }
544 }
545
546 return "result" + Glib::Ascii::dtostr(largest + 1);
547}
548
550{
551 views.emplace_back(item);
552
553 for (auto &c : children) {
554 if (auto f = cast<SPFilterPrimitive>(&c)) {
555 f->show(item);
556 }
557 }
558
559 item->setFilterRenderer(build_renderer(item));
560}
561
563{
564 auto it = std::find(views.begin(), views.end(), item);
565 assert(it != views.end());
566 views.erase(it);
567
568 for (auto &c : children) {
569 if (auto f = cast<SPFilterPrimitive>(&c)) {
570 f->hide(item);
571 }
572 }
573
574 item->setFilterRenderer(nullptr);
575}
576
577/*
578 Local Variables:
579 mode:c++
580 c-file-style:"stroustrup"
581 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
582 indent-tabs-mode:nil
583 fill-column:99
584 End:
585*/
586// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ PRIMITIVEUNITS
@ FILTERUNITS
@ XLINK_HREF
@ AUTO_REGION
TODO: insert short description here.
C top() const
Return top coordinate of the rectangle (+Y is downwards).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Axis aligned, non-empty rectangle.
Definition rect.h:92
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
SVG drawing item for display.
Represents an URI as per RFC 2396.
Definition uri.h:36
Interface for refcounted XML nodes.
Definition node.h:80
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
float getOptNumber(bool or_num=false) const
float getNumber() const
void set(char const *str)
std::string getValueString() const
void calcDimsFromParentViewport(const SPItemCtx *ictx, bool assign_to_set=false, SPDimensions const *use=nullptr)
Update computed x/y/width/height for "percent" units and/or from its referencing clone parent.
SVGLength y
SVGLength height
SVGLength width
SVGLength x
Typed SVG document implementation.
Definition document.h:101
bool removeResource(char const *key, SPObject *object)
bool addResource(char const *key, SPObject *object)
Glib::ustring get_new_result_name() const
Returns a result image name that is not in use inside this filter.
void hide(Inkscape::DrawingItem *item)
void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_repr, Inkscape::XML::Node *new_repr) override
unsigned getRefCount()
Returns the number of references to the filter.
Geom::Rect get_automatic_filter_region(SPItem const *item) const
Generate a filter region based on the item and return it.
void modified(unsigned flags) override
void update_filter_region(SPItem *item)
Update the filter region based on the object's bounding box.
int primitive_count() const
Returns the number of filter primitives in this SPFilter object.
void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) override
bool primitiveUnits_set
Definition sp-filter.h:67
void release() override
Definition sp-filter.cpp:90
unsigned _refcount
Definition sp-filter.h:75
std::vector< Inkscape::DrawingItem * > views
Definition sp-filter.h:95
Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned flags) override
void set_filter_region(double x, double y, double width, double height)
Set the filter region attributes from a bounding box.
~SPFilter() override
void remove_child(Inkscape::XML::Node *child) override
std::unique_ptr< SPFilterReference > href
Definition sp-filter.h:69
void update_filter_all_regions()
Update the filter's region based on its detectable href links.
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-filter.cpp:70
bool valid_for(SPObject const *obj) const
Checks each filter primitive to make sure the object won't cause issues.
void update(SPCtx *ctx, unsigned flags) override
bool filterUnits_set
Definition sp-filter.h:65
void show(Inkscape::DrawingItem *item)
bool auto_region
Definition sp-filter.h:70
NumberOptNumber filterRes
Definition sp-filter.h:68
SPFilterUnits primitiveUnits
Definition sp-filter.h:66
sigc::scoped_connection modified_connection
Definition sp-filter.h:72
bool slots_valid
Definition sp-filter.h:93
void set(SPAttr key, char const *value) override
void ensure_slots()
SPFilterUnits filterUnits
Definition sp-filter.h:64
void invalidate_slots()
std::unique_ptr< Inkscape::Filters::Filter > build_renderer(Inkscape::DrawingItem *item)
Returns a renderer for this filter, for use by the DrawingItem item.
Base class for visual SVG elements.
Definition sp-item.h:109
virtual Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const
Definition sp-item.cpp:922
@ VISUAL_BBOX
Definition sp-item.h:118
@ GEOMETRIC_BBOX
Definition sp-item.h:116
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * repr
Definition sp-object.h:193
void requestModified(unsigned int flags)
Requests that a modification notification signal be emitted later (e.g.
SPObject * get_child_by_repr(Inkscape::XML::Node *repr)
Return object's child whose node pointer equals repr.
SPDocument * document
Definition sp-object.h:188
virtual void set(SPAttr key, const char *value)
virtual void remove_child(Inkscape::XML::Node *child)
virtual Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
virtual void update(SPCtx *ctx, unsigned int flags)
virtual void release()
void readAttr(char const *key)
Read value of key attribute from XML node into object.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
virtual void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
std::list< SPObject * > hrefList
Definition sp-object.h:197
virtual void build(SPDocument *doc, Inkscape::XML::Node *repr)
virtual void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_repr, Inkscape::XML::Node *new_repr)
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
void readOrUnset(char const *str, Unit u=NONE, float v=0, float c=0)
bool _set
Definition svg-length.h:41
float computed
Definition svg-length.h:50
Css & result
double c[8][4]
Canvas item belonging to an SVG drawing element.
SPItem * item
Affine identity()
Create an identity matrix.
Definition affine.h:210
static R & release(R &r)
Decrements the reference count of a anchored object.
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
static cairo_user_data_key_t key
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
Document level base class for all SVG filter primitives.
TODO: insert short description here.
@ SP_FILTER_UNITS_USERSPACEONUSE
@ SP_FILTER_UNITS_OBJECTBOUNDINGBOX
SVG <filter> element.
unsigned cascade_flags(unsigned flags)
Definition sp-object.h:60
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Unused.
Definition sp-object.h:94
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
int index
double height
double width
Affine transformation classes.