1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** \file
3  * SVG <feComposite> implementation.
4  *
5  */
6 /*
7  * Authors:
8  *   hugo Rodrigues <haa.rodrigues@gmail.com>
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 2006 Hugo Rodrigues
12  *
13  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14  */
15 
16 #include "composite.h"
17 
18 #include "attributes.h"
19 #include "helper-fns.h"
20 
21 #include "display/nr-filter.h"
22 #include "display/nr-filter-composite.h"
23 
24 #include "object/sp-filter.h"
25 
26 #include "svg/svg.h"
27 
28 #include "xml/repr.h"
29 
SPFeComposite()30 SPFeComposite::SPFeComposite()
31     : SPFilterPrimitive(), composite_operator(COMPOSITE_DEFAULT),
32       k1(0), k2(0), k3(0), k4(0), in2(Inkscape::Filters::NR_FILTER_SLOT_NOT_SET)
33 {
34 }
35 
36 SPFeComposite::~SPFeComposite() = default;
37 
38 /**
39  * Reads the Inkscape::XML::Node, and initializes SPFeComposite variables.  For this to get called,
40  * our name must be associated with a repr via "sp_object_type_register".  Best done through
41  * sp-object-repr.cpp's repr_name_entries array.
42  */
build(SPDocument * document,Inkscape::XML::Node * repr)43 void SPFeComposite::build(SPDocument *document, Inkscape::XML::Node *repr) {
44 	SPFilterPrimitive::build(document, repr);
45 
46 	this->readAttr(SPAttr::OPERATOR);
47 
48 	if (this->composite_operator == COMPOSITE_ARITHMETIC) {
49 		this->readAttr(SPAttr::K1);
50 		this->readAttr(SPAttr::K2);
51 		this->readAttr(SPAttr::K3);
52 		this->readAttr(SPAttr::K4);
53 	}
54 
55 	this->readAttr(SPAttr::IN2);
56 
57 	/* Unlike normal in, in2 is required attribute. Make sure, we can call
58 	 * it by some name. */
59 	if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET ||
60 		this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT)
61 	{
62 		SPFilter *parent = SP_FILTER(this->parent);
63 		this->in2 = this->name_previous_out();
64 		repr->setAttribute("in2", parent->name_for_image(this->in2));
65 	}
66 }
67 
68 /**
69  * Drops any allocated memory.
70  */
release()71 void SPFeComposite::release() {
72 	SPFilterPrimitive::release();
73 }
74 
75 static FeCompositeOperator
sp_feComposite_read_operator(gchar const * value)76 sp_feComposite_read_operator(gchar const *value) {
77     if (!value) {
78     	return COMPOSITE_DEFAULT;
79     }
80 
81     if (strcmp(value, "over") == 0) {
82     	return COMPOSITE_OVER;
83     } else if (strcmp(value, "in") == 0) {
84     	return COMPOSITE_IN;
85     } else if (strcmp(value, "out") == 0) {
86     	return COMPOSITE_OUT;
87     } else if (strcmp(value, "atop") == 0) {
88     	return COMPOSITE_ATOP;
89     } else if (strcmp(value, "xor") == 0) {
90     	return COMPOSITE_XOR;
91     } else if (strcmp(value, "arithmetic") == 0) {
92     	return COMPOSITE_ARITHMETIC;
93     }
94 #ifdef WITH_CSSCOMPOSITE
95       else if (strcmp(value, "clear") == 0) {
96     	return COMPOSITE_CLEAR;
97     } else if (strcmp(value, "copy") == 0) {
98     	return COMPOSITE_COPY;
99     } else if (strcmp(value, "destination") == 0) {
100     	return COMPOSITE_DESTINATION;
101     } else if (strcmp(value, "destination-over") == 0) {
102     	return COMPOSITE_DESTINATION_OVER;
103     } else if (strcmp(value, "destination-in") == 0) {
104     	return COMPOSITE_DESTINATION_IN;
105     } else if (strcmp(value, "destination-out") == 0) {
106     	return COMPOSITE_DESTINATION_OUT;
107     } else if (strcmp(value, "destination-atop") == 0) {
108     	return COMPOSITE_DESTINATION_ATOP;
109     } else if (strcmp(value, "lighter") == 0) {
110     	return COMPOSITE_LIGHTER;
111     }
112 #endif
113     std::cout << "Inkscape::Filters::FilterCompositeOperator: Unimplemented operator: " << value << std::endl;
114 
115     return COMPOSITE_DEFAULT;
116 }
117 
118 /**
119  * Sets a specific value in the SPFeComposite.
120  */
set(SPAttr key,gchar const * value)121 void SPFeComposite::set(SPAttr key, gchar const *value) {
122     int input;
123     FeCompositeOperator op;
124     double k_n;
125 
126     switch(key) {
127 	/*DEAL WITH SETTING ATTRIBUTES HERE*/
128         case SPAttr::OPERATOR:
129             op = sp_feComposite_read_operator(value);
130             if (op != this->composite_operator) {
131                 this->composite_operator = op;
132                 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
133             }
134             break;
135 
136         case SPAttr::K1:
137             k_n = value ? helperfns_read_number(value) : 0;
138             if (k_n != this->k1) {
139                 this->k1 = k_n;
140                 if (this->composite_operator == COMPOSITE_ARITHMETIC)
141                     this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
142             }
143             break;
144 
145         case SPAttr::K2:
146             k_n = value ? helperfns_read_number(value) : 0;
147             if (k_n != this->k2) {
148                 this->k2 = k_n;
149                 if (this->composite_operator == COMPOSITE_ARITHMETIC)
150                     this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
151             }
152             break;
153 
154         case SPAttr::K3:
155             k_n = value ? helperfns_read_number(value) : 0;
156             if (k_n != this->k3) {
157                 this->k3 = k_n;
158                 if (this->composite_operator == COMPOSITE_ARITHMETIC)
159                     this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
160             }
161             break;
162 
163         case SPAttr::K4:
164             k_n = value ? helperfns_read_number(value) : 0;
165             if (k_n != this->k4) {
166                 this->k4 = k_n;
167                 if (this->composite_operator == COMPOSITE_ARITHMETIC)
168                     this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
169             }
170             break;
171 
172         case SPAttr::IN2:
173             input = this->read_in(value);
174             if (input != this->in2) {
175                 this->in2 = input;
176                 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
177             }
178             break;
179 
180         default:
181         	SPFilterPrimitive::set(key, value);
182             break;
183     }
184 }
185 
186 /**
187  * Receives update notifications.
188  */
update(SPCtx * ctx,guint flags)189 void SPFeComposite::update(SPCtx *ctx, guint flags) {
190     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
191                  SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
192 
193         /* do something to trigger redisplay, updates? */
194 
195     }
196 
197     /* Unlike normal in, in2 is required attribute. Make sure, we can call
198      * it by some name. */
199     /* This may not be true.... see issue at
200      * http://www.w3.org/TR/filter-effects/#feBlendElement (but it doesn't hurt). */
201     if (this->in2 == Inkscape::Filters::NR_FILTER_SLOT_NOT_SET ||
202         this->in2 == Inkscape::Filters::NR_FILTER_UNNAMED_SLOT)
203     {
204         SPFilter *parent = SP_FILTER(this->parent);
205         this->in2 = this->name_previous_out();
206 
207 		//XML Tree being used directly here while it shouldn't be.
208         this->setAttribute("in2", parent->name_for_image(this->in2));
209     }
210 
211     SPFilterPrimitive::update(ctx, flags);
212 }
213 
214 /**
215  * Writes its settings to an incoming repr object, if any.
216  */
write(Inkscape::XML::Document * doc,Inkscape::XML::Node * repr,guint flags)217 Inkscape::XML::Node* SPFeComposite::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
218     SPFilter *parent = SP_FILTER(this->parent);
219 
220     if (!repr) {
221         repr = doc->createElement("svg:feComposite");
222     }
223 
224     gchar const *in2_name = parent->name_for_image(this->in2);
225 
226     if( !in2_name ) {
227 
228         // This code is very similar to name_previous_out()
229         SPObject *i = parent->firstChild();
230 
231         // Find previous filter primitive
232         while (i && i->getNext() != this) {
233         	i = i->getNext();
234         }
235 
236         if( i ) {
237             SPFilterPrimitive *i_prim = SP_FILTER_PRIMITIVE(i);
238             in2_name = parent->name_for_image(i_prim->image_out);
239         }
240     }
241 
242     if (in2_name) {
243         repr->setAttribute("in2", in2_name);
244     } else {
245         g_warning("Unable to set in2 for feComposite");
246     }
247 
248     char const *comp_op;
249 
250     switch (this->composite_operator) {
251         case COMPOSITE_OVER:
252             comp_op = "over"; break;
253         case COMPOSITE_IN:
254             comp_op = "in"; break;
255         case COMPOSITE_OUT:
256             comp_op = "out"; break;
257         case COMPOSITE_ATOP:
258             comp_op = "atop"; break;
259         case COMPOSITE_XOR:
260             comp_op = "xor"; break;
261         case COMPOSITE_ARITHMETIC:
262             comp_op = "arithmetic"; break;
263 #ifdef WITH_CSSCOMPOSITE
264         // New CSS operators
265         case COMPOSITE_CLEAR:
266             comp_op = "clear"; break;
267         case COMPOSITE_COPY:
268             comp_op = "copy"; break;
269         case COMPOSITE_DESTINATION:
270             comp_op = "destination"; break;
271         case COMPOSITE_DESTINATION_OVER:
272             comp_op = "destination-over"; break;
273         case COMPOSITE_DESTINATION_IN:
274             comp_op = "destination-in"; break;
275         case COMPOSITE_DESTINATION_OUT:
276             comp_op = "destination-out"; break;
277         case COMPOSITE_DESTINATION_ATOP:
278             comp_op = "destination-atop"; break;
279         case COMPOSITE_LIGHTER:
280             comp_op = "lighter"; break;
281 #endif
282         default:
283             comp_op = nullptr;
284     }
285 
286     repr->setAttribute("operator", comp_op);
287 
288     if (this->composite_operator == COMPOSITE_ARITHMETIC) {
289         sp_repr_set_svg_double(repr, "k1", this->k1);
290         sp_repr_set_svg_double(repr, "k2", this->k2);
291         sp_repr_set_svg_double(repr, "k3", this->k3);
292         sp_repr_set_svg_double(repr, "k4", this->k4);
293     } else {
294         repr->removeAttribute("k1");
295         repr->removeAttribute("k2");
296         repr->removeAttribute("k3");
297         repr->removeAttribute("k4");
298     }
299 
300     SPFilterPrimitive::write(doc, repr, flags);
301 
302     return repr;
303 }
304 
build_renderer(Inkscape::Filters::Filter * filter)305 void SPFeComposite::build_renderer(Inkscape::Filters::Filter* filter) {
306     g_assert(filter != nullptr);
307 
308     int primitive_n = filter->add_primitive(Inkscape::Filters::NR_FILTER_COMPOSITE);
309     Inkscape::Filters::FilterPrimitive *nr_primitive = filter->get_primitive(primitive_n);
310     Inkscape::Filters::FilterComposite *nr_composite = dynamic_cast<Inkscape::Filters::FilterComposite*>(nr_primitive);
311     g_assert(nr_composite != nullptr);
312 
313     this->renderer_common(nr_primitive);
314 
315     nr_composite->set_operator(this->composite_operator);
316     nr_composite->set_input(1, this->in2);
317 
318     if (this->composite_operator == COMPOSITE_ARITHMETIC) {
319         nr_composite->set_arithmetic(this->k1, this->k2,
320                                      this->k3, this->k4);
321     }
322 }
323 
324 /*
325   Local Variables:
326   mode:c++
327   c-file-style:"stroustrup"
328   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
329   indent-tabs-mode:nil
330   fill-column:99
331   End:
332 */
333 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
334