Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
filter-chemistry.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Various utility methods for filters
4 *
5 * Authors:
6 * Hugo Rodrigues
7 * bulia byak
8 * Niko Kiirala
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 2006-2008 authors
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "filter-chemistry.h"
18
19#include <cstring>
20#include <glibmm.h>
21
22#include "desktop-style.h"
23#include "document.h"
24#include "filter-enums.h"
25#include "style.h"
26
27#include "object/sp-defs.h"
28#include "object/sp-item.h"
29
32
37static guint count_filter_hrefs(SPObject *o, SPFilter *filter)
38{
39 if (!o)
40 return 1;
41
42 guint i = 0;
43
44 SPStyle *style = o->style;
45 if (style
46 && style->filter.set
47 && style->getFilter() == filter)
48 {
49 i ++;
50 }
51
52 for (auto& child: o->children) {
53 i += count_filter_hrefs(&child, filter);
54 }
55
56 return i;
57}
58
60{
61 g_return_val_if_fail(document != nullptr, NULL);
62
63 SPDefs *defs = document->getDefs();
64
65 Inkscape::XML::Document *xml_doc = document->getReprDoc();
66
67 // create a new filter
69 repr = xml_doc->createElement("svg:filter");
70
71 // Inkscape now supports both sRGB and linear color-interpolation-filters.
72 // But, for the moment, keep sRGB as default value for new filters
73 // (historically set to sRGB and doesn't require conversion between
74 // filter cairo surfaces and other types of cairo surfaces).
76 sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
77 sp_repr_css_change(repr, css, "style");
79
80 // Append the new filter node to defs
81 defs->appendChild(repr);
83
84 // get corresponding object
85 auto f = cast<SPFilter>( document->getObjectByRepr(repr) );
86
87
88 g_assert(f != nullptr);
89
90 return f;
91}
92
95{
96 Inkscape::XML::Document *xml_doc = filter->document->getReprDoc();
97
98 //create filter primitive node
100 repr = xml_doc->createElement(FPConverter.get_key(type).c_str());
101
102 // set default values
103 switch(type) {
105 repr->setAttribute("mode", "normal");
106 break;
108 break;
110 break;
112 break;
114 repr->setAttribute("order", "3 3");
115 repr->setAttribute("kernelMatrix", "0 0 0 0 0 0 0 0 0");
116 break;
118 break;
120 break;
122 break;
124 repr->setAttribute("stdDeviation", "1");
125 break;
127 break;
129 break;
131 repr->setAttribute("radius", "1");
132 break;
134 repr->setAttribute("dx", "0");
135 repr->setAttribute("dy", "0");
136 break;
138 break;
140 break;
142 break;
143 default:
144 break;
145 }
146
147 //set primitive as child of filter node
148 // XML tree being used directly while/where it shouldn't be...
149 filter->appendChild(repr);
151
152 // get corresponding object
153 auto prim = cast<SPFilterPrimitive>( filter->document->getObjectByRepr(repr) );
154
155 g_assert(prim != nullptr);
156
157 return prim;
158}
159
163SPFilter *
164new_filter_gaussian_blur (SPDocument *document, gdouble radius, double expansion)
165{
166 g_return_val_if_fail(document != nullptr, NULL);
167
168 SPDefs *defs = document->getDefs();
169
170 Inkscape::XML::Document *xml_doc = document->getReprDoc();
171
172 // create a new filter
174 repr = xml_doc->createElement("svg:filter");
175 //repr->setAttribute("inkscape:collect", "always");
176
177
178 /* Inkscape now supports both sRGB and linear color-interpolation-filters.
179 * But, for the moment, keep sRGB as default value for new filters.
180 * historically set to sRGB and doesn't require conversion between
181 * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
183 sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
184 sp_repr_css_change(repr, css, "style");
186
187 //create feGaussianBlur node
188 Inkscape::XML::Node *b_repr;
189 b_repr = xml_doc->createElement("svg:feGaussianBlur");
190 //b_repr->setAttribute("inkscape:collect", "always");
191
192 double stdDeviation = radius;
193 if (expansion != 0)
194 stdDeviation /= expansion;
195
196 //set stdDeviation attribute
197 b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
198
199 //set feGaussianBlur as child of filter node
200 repr->appendChild(b_repr);
201 Inkscape::GC::release(b_repr);
202
203 // Append the new filter node to defs
204 defs->appendChild(repr);
206
207 // get corresponding object
208 auto f = cast<SPFilter>( document->getObjectByRepr(repr) );
209 auto b = cast<SPGaussianBlur>( document->getObjectByRepr(b_repr) );
210
211 g_assert(f != nullptr);
212 g_assert(b != nullptr);
213
214 return f;
215}
216
217
222static SPFilter *
223new_filter_blend_gaussian_blur (SPDocument *document, const char *blendmode, gdouble radius, double expansion)
224{
225 g_return_val_if_fail(document != nullptr, NULL);
226
227 SPDefs *defs = document->getDefs();
228
229 Inkscape::XML::Document *xml_doc = document->getReprDoc();
230
231 // create a new filter
233 repr = xml_doc->createElement("svg:filter");
234 repr->setAttribute("inkscape:collect", "always");
235
236 /* Inkscape now supports both sRGB and linear color-interpolation-filters.
237 * But, for the moment, keep sRGB as default value for new filters.
238 * historically set to sRGB and doesn't require conversion between
239 * filter cairo surfaces and other types of cairo surfaces. lp:1127103 */
241 sp_repr_css_set_property(css, "color-interpolation-filters", "sRGB");
242 sp_repr_css_change(repr, css, "style");
244
245 // Append the new filter node to defs
246 defs->appendChild(repr);
248
249 // get corresponding object
250 auto f = cast<SPFilter>( document->getObjectByRepr(repr) );
251 // Gaussian blur primitive
252 if(radius != 0) {
253 //create feGaussianBlur node
254 Inkscape::XML::Node *b_repr;
255 b_repr = xml_doc->createElement("svg:feGaussianBlur");
256 b_repr->setAttribute("inkscape:collect", "always");
257
258 double stdDeviation = radius;
259 if (expansion != 0)
260 stdDeviation /= expansion;
261
262 //set stdDeviation attribute
263 b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
264
265 //set feGaussianBlur as child of filter node
266 repr->appendChild(b_repr);
267 Inkscape::GC::release(b_repr);
268
269 auto b = cast<SPGaussianBlur>( document->getObjectByRepr(b_repr) );
270 g_assert(b != nullptr);
271 }
272 // Blend primitive
273 if(strcmp(blendmode, "normal")) {
274 Inkscape::XML::Node *b_repr;
275 b_repr = xml_doc->createElement("svg:feBlend");
276 b_repr->setAttribute("inkscape:collect", "always");
277 b_repr->setAttribute("mode", blendmode);
278 b_repr->setAttribute("in2", "BackgroundImage");
279
280 // set feBlend as child of filter node
281 repr->appendChild(b_repr);
282 Inkscape::GC::release(b_repr);
283
284 // Enable background image buffer for document
285 Inkscape::XML::Node *root = b_repr->root();
286 if (!root->attribute("enable-background")) {
287 root->setAttribute("enable-background", "new");
288 }
289
290 auto b = cast<SPFeBlend>(document->getObjectByRepr(b_repr));
291 g_assert(b != nullptr);
292 }
293
294 g_assert(f != nullptr);
295
296 return f;
297}
298
303SPFilter *
304new_filter_simple_from_item (SPDocument *document, SPItem *item, const char *mode, gdouble radius)
305{
306 return new_filter_blend_gaussian_blur(document, mode, radius, item->i2dt_affine().descrim());
307}
308
317/* TODO: this should be made more generic, not just for blurs */
319 gdouble radius)
320{
321 if (!item->style || !item->style->filter.set) {
322 return new_filter_simple_from_item(document, item, "normal", radius);
323 }
324
325 SPFilter *filter = item->style->getFilter();
326 if (!filter) {
327 // We reach here when filter.set is true, but the href is not found in the document
328 return new_filter_simple_from_item(document, item, "normal", radius);
329 }
330
331 Inkscape::XML::Document *xml_doc = document->getReprDoc();
332
333 // If there are more users for this filter, duplicate it
334 if (filter->hrefcount > count_filter_hrefs(item, filter)) {
335 Inkscape::XML::Node *repr = item->style->getFilter()->getRepr()->duplicate(xml_doc);
336 SPDefs *defs = document->getDefs();
337 defs->appendChild(repr);
338
339 filter = cast<SPFilter>( document->getObjectByRepr(repr) );
341 }
342
343 // Determine the required standard deviation value
344 Geom::Affine i2d (item->i2dt_affine ());
345 double expansion = i2d.descrim();
346 double stdDeviation = radius;
347 if (expansion != 0)
348 stdDeviation /= expansion;
349
350 // Set the filter effects area
351 SPFilter *f = item->style->getFilter();
352
353 Inkscape::XML::Node *repr = f->getRepr();
354 // Search for gaussian blur primitives. If found, set the stdDeviation
355 // of the first one and return.
356 Inkscape::XML::Node *primitive = repr->firstChild();
357 while (primitive) {
358 if (strcmp("svg:feGaussianBlur", primitive->name()) == 0) {
359 primitive->setAttributeSvgDouble("stdDeviation", stdDeviation);
360 return filter;
361 }
362 primitive = primitive->next();
363 }
364
365 // If there were no gaussian blur primitives, create a new one
366
367 //create feGaussianBlur node
368 Inkscape::XML::Node *b_repr;
369 b_repr = xml_doc->createElement("svg:feGaussianBlur");
370 //b_repr->setAttribute("inkscape:collect", "always");
371
372 //set stdDeviation attribute
373 b_repr->setAttributeSvgDouble("stdDeviation", stdDeviation);
374
375 //set feGaussianBlur as child of filter node
376 filter->getRepr()->appendChild(b_repr);
377 Inkscape::GC::release(b_repr);
378
379 return filter;
380}
381
382void remove_filter (SPObject *item, bool recursive)
383{
386 if (recursive) {
388 } else {
389 sp_repr_css_change(item->getRepr(), css, "style");
390 }
392}
393
395{
396 SPFilter *filt = item->style->getFilter();
397 if (filt && filt->getId()) {
398 Glib::ustring filter = filt->getId();
399 if (!filter.rfind("selectable_hidder_filter", 0)) {
400 remove_filter(item, false);
401 }
402 }
403}
404
406{
407 SPFilter *filt = item->style->getFilter();
408 if (filt && filt->getId()) {
409 Glib::ustring filter = filt->getId();
410 if (!filter.rfind("selectable_hidder_filter", 0)) {
411 return true;
412 }
413 }
414 return false;
415}
416
421/* TODO: the removed filter primitive may had had a named result image, so
422 * after removing, the filter may be in erroneous state, this situation should
423 * be handled gracefully */
425{
426 if (item->style && item->style->filter.set && item->style->getFilter()) {
427 // Search for the first blur primitive and remove it. (if found)
429 Inkscape::XML::Node *primitive = repr->firstChild();
430 while (primitive) {
431 if (strcmp("svg:feGaussianBlur", primitive->name()) == 0) {
432 sp_repr_unparent(primitive);
433 break;
434 }
435 primitive = primitive->next();
436 }
437
438 // If there are no more primitives left in this filter, discard it.
439 if (repr->childCount() == 0) {
440 remove_filter(item, false);
441 }
442 }
443}
444
450/* TODO: the removed filter primitive may had had a named result image, so
451 * after removing, the filter may be in erroneous state, this situation should
452 * be handled gracefully */
454{
455 if (!item) {
456 return;
457 }
458 if (item->style && item->style->filter.set && item->style->getFilter()) {
459 // Search for the first blur primitive and remove it. (if found)
460 size_t blurcount = 0;
461 size_t blendcount = 0;
462 size_t total = 0;
463 // determine whether filter is simple (blend and/or blur) or complex
464 SPFeBlend *blend = nullptr;
465 for (auto &primitive_obj:item->style->getFilter()->children) {
466 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
467 if (primitive) {
468 if (is<SPFeBlend>(primitive)) {
469 blend = cast<SPFeBlend>(primitive);
470 ++blendcount;
471 }
472 if (is<SPGaussianBlur>(primitive)) {
473 ++blurcount;
474 }
475 ++total;
476 }
477 }
478 if (blend && total == 2 && blurcount == 1) {
479 blend->deleteObject(true);
480 } else if (total == 1 && blurcount != 1) {
481 remove_filter(item, false);
482 }
483 }
484}
485
491{
492 auto blend = SP_CSS_BLEND_NORMAL;
493 if (!item) {
494 return blend;
495 }
496 if (item->style && item->style->filter.set && item->style->getFilter()) {
497 // Search for the first blur primitive and remove it. (if found)
498 size_t blurcount = 0;
499 size_t blendcount = 0;
500 size_t total = 0;
501 // determine whether filter is simple (blend and/or blur) or complex
502 for (auto &primitive_obj:item->style->getFilter()->children) {
503 auto primitive = cast<SPFilterPrimitive>(&primitive_obj);
504 if (primitive) {
505 auto spblend = cast<SPFeBlend>(primitive);
506 if (spblend) {
507 ++blendcount;
508 blend = spblend->get_blend_mode();
509 }
510 if (is<SPGaussianBlur>(primitive)) {
511 ++blurcount;
512 }
513 ++total;
514 }
515 }
516 if (!((blend && total == 2 && blurcount == 1) || total == 1)) {
517 blend = SP_CSS_BLEND_NORMAL;
518 }
519 }
520 return blend;
521}
522
524{
525 return (filter->children.size() == 1 &&
526 is<SPGaussianBlur>(&filter->children.front()));
527}
528
530{
531 if (filter->children.size() == 1 &&
532 is<SPGaussianBlur>(&filter->children.front())) {
533
534 auto gb = cast<SPGaussianBlur>(filter->firstChild());
535 double x = gb->get_std_deviation().getNumber();
536 double y = gb->get_std_deviation().getOptNumber();
537 if (x > 0 && y > 0) {
538 return MAX(x, y);
539 }
540 return x;
541 }
542 return 0.0;
543}
544
546 if (!item || !item->style) {
547 return false;
548 }
549
550 bool change_blend = (item->style->mix_blend_mode.set ? item->style->mix_blend_mode.value : SP_CSS_BLEND_NORMAL) != blend_mode;
551 // < 1.0 filter based blend removal
552 if (!item->style->mix_blend_mode.set && item->style->filter.set && item->style->getFilter()) {
554 }
555 item->style->mix_blend_mode.set = TRUE;
558 } else {
559 item->style->mix_blend_mode.value = blend_mode;
560 }
561
562 if (change_blend) { // we do blend so we need to update display style
563 item->updateRepr(SP_OBJECT_WRITE_NO_CHILDREN | SP_OBJECT_WRITE_EXT);
564 }
565
566 return change_blend;
567}
568
569
570/*
571 Local Variables:
572 mode:c++
573 c-file-style:"stroustrup"
574 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
575 indent-tabs-mode:nil
576 fill-column:99
577 End:
578*/
579// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
SVG blend filter effect.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
const Glib::ustring & get_key(const E id) const
Definition enums.h:99
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * next()=0
Get the next sibling of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual unsigned childCount() const =0
Get the number of children of this node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
virtual Node * root()=0
Get the root node of this node's document.
Typed SVG document implementation.
Definition document.h:103
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:245
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void appendChild(Inkscape::XML::Node *child)
SPDocument * document
Definition sp-object.h:188
SPObject * firstChild()
Definition sp-object.h:315
char const * getId() const
Returns the objects current ID string.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
ChildrenList children
Definition sp-object.h:907
unsigned int hrefcount
Definition sp-object.h:186
An SVG style object.
Definition style.h:45
SPFilter * getFilter()
Definition style.h:335
T< SPAttr::ISOLATION, SPIEnum< SPIsolation > > isolation
mix-blend-mode: CSS Compositing and Blending Level 1
Definition style.h:219
T< SPAttr::MIX_BLEND_MODE, SPIEnum< SPBlendMode > > mix_blend_mode
Definition style.h:220
T< SPAttr::FILTER, SPIFilter > filter
Filter effect.
Definition style.h:275
RootCluster root
std::shared_ptr< Css const > css
bool filter_is_single_gaussian_blur(SPFilter *filter)
static guint count_filter_hrefs(SPObject *o, SPFilter *filter)
Count how many times the filter is used by the styles of o and its descendants.
static SPFilter * new_filter_blend_gaussian_blur(SPDocument *document, const char *blendmode, gdouble radius, double expansion)
Creates a simple filter with a blend primitive and a blur primitive of specified radius for an item w...
SPBlendMode filter_get_legacy_blend(SPObject *item)
Get if the filter have a < 1.0 blending filter @params: the item to get filtered blend.
SPFilter * new_filter_gaussian_blur(SPDocument *document, gdouble radius, double expansion)
Creates a filter with blur primitive of specified radius for an item with the given matrix expansion,...
void remove_filter_legacy_blend(SPObject *item)
Removes blend primitive from the filter attached to given item.
void remove_filter_gaussian_blur(SPObject *item)
Removes the first feGaussianBlur from the filter attached to given item.
bool has_hidder_filter(SPObject const *item)
double get_single_gaussian_blur_radius(SPFilter *filter)
void remove_filter(SPObject *item, bool recursive)
bool set_blend_mode(SPItem *item, SPBlendMode blend_mode)
SPFilterPrimitive * filter_add_primitive(SPFilter *filter, const Inkscape::Filters::FilterPrimitiveType type)
SPFilter * new_filter(SPDocument *document)
SPFilter * new_filter_simple_from_item(SPDocument *document, SPItem *item, const char *mode, gdouble radius)
Creates a simple filter for the given item with blend and blur primitives, using the specified mode a...
void remove_hidder_filter(SPObject *item)
SPFilter * modify_filter_gaussian_blur_from_item(SPDocument *document, SPItem *item, gdouble radius)
Modifies the gaussian blur applied to the item.
const EnumDataConverter< Inkscape::Filters::FilterPrimitiveType > FPConverter(FPData, Inkscape::Filters::NR_FILTER_ENDPRIMITIVETYPE)
SVG Gaussian blur filter effect.
SPItem * item
static R & release(R &r)
Decrements the reference count of a anchored object.
int mode
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition repr-css.cpp:358
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
void sp_repr_css_change_recursive(Node *repr, SPCSSAttr *css, gchar const *attr)
Definition repr-css.cpp:371
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:191
void sp_repr_unparent(Inkscape::XML::Node *repr)
Remove repr from children of its parent node.
Definition repr.h:107
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
SPBlendMode
@ SP_CSS_BLEND_NORMAL
@ SP_CSS_ISOLATION_ISOLATE
SPStyle - a style object for SPItem objects.