1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** \file
3 * SVG <feConvolveMatrix> implementation.
4 *
5 */
6 /*
7 * Authors:
8 * Felipe Corrêa da Silva Sanches <juca@members.fsf.org>
9 * hugo Rodrigues <haa.rodrigues@gmail.com>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 2006 Hugo Rodrigues
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17 #include <cstring>
18 #include <cmath>
19 #include <vector>
20
21 #include "convolvematrix.h"
22
23 #include "attributes.h"
24 #include "helper-fns.h"
25
26 #include "display/nr-filter.h"
27
28 #include "xml/repr.h"
29
SPFeConvolveMatrix()30 SPFeConvolveMatrix::SPFeConvolveMatrix() : SPFilterPrimitive() {
31 this->bias = 0;
32 this->divisorIsSet = false;
33 this->divisor = 0;
34
35 //Setting default values:
36 this->order.set("3 3");
37 this->targetX = 1;
38 this->targetY = 1;
39 this->edgeMode = Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_DUPLICATE;
40 this->preserveAlpha = false;
41
42 //some helper variables:
43 this->targetXIsSet = false;
44 this->targetYIsSet = false;
45 this->kernelMatrixIsSet = false;
46 }
47
48 SPFeConvolveMatrix::~SPFeConvolveMatrix() = default;
49
50 /**
51 * Reads the Inkscape::XML::Node, and initializes SPFeConvolveMatrix variables. For this to get called,
52 * our name must be associated with a repr via "sp_object_type_register". Best done through
53 * sp-object-repr.cpp's repr_name_entries array.
54 */
build(SPDocument * document,Inkscape::XML::Node * repr)55 void SPFeConvolveMatrix::build(SPDocument *document, Inkscape::XML::Node *repr) {
56 SPFilterPrimitive::build(document, repr);
57
58 /*LOAD ATTRIBUTES FROM REPR HERE*/
59 this->readAttr(SPAttr::ORDER);
60 this->readAttr(SPAttr::KERNELMATRIX);
61 this->readAttr(SPAttr::DIVISOR);
62 this->readAttr(SPAttr::BIAS);
63 this->readAttr(SPAttr::TARGETX);
64 this->readAttr(SPAttr::TARGETY);
65 this->readAttr(SPAttr::EDGEMODE);
66 this->readAttr(SPAttr::KERNELUNITLENGTH);
67 this->readAttr(SPAttr::PRESERVEALPHA);
68 }
69
70 /**
71 * Drops any allocated memory.
72 */
release()73 void SPFeConvolveMatrix::release() {
74 SPFilterPrimitive::release();
75 }
76
sp_feConvolveMatrix_read_edgeMode(gchar const * value)77 static Inkscape::Filters::FilterConvolveMatrixEdgeMode sp_feConvolveMatrix_read_edgeMode(gchar const *value){
78 if (!value) {
79 return Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_DUPLICATE; //duplicate is default
80 }
81
82 switch (value[0]) {
83 case 'd':
84 if (strncmp(value, "duplicate", 9) == 0) {
85 return Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_DUPLICATE;
86 }
87 break;
88 case 'w':
89 if (strncmp(value, "wrap", 4) == 0) {
90 return Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_WRAP;
91 }
92 break;
93 case 'n':
94 if (strncmp(value, "none", 4) == 0) {
95 return Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_NONE;
96 }
97 break;
98 }
99
100 return Inkscape::Filters::CONVOLVEMATRIX_EDGEMODE_DUPLICATE; //duplicate is default
101 }
102
103 /**
104 * Sets a specific value in the SPFeConvolveMatrix.
105 */
set(SPAttr key,gchar const * value)106 void SPFeConvolveMatrix::set(SPAttr key, gchar const *value) {
107 double read_num;
108 int read_int;
109 bool read_bool;
110 Inkscape::Filters::FilterConvolveMatrixEdgeMode read_mode;
111
112 switch(key) {
113 /*DEAL WITH SETTING ATTRIBUTES HERE*/
114 case SPAttr::ORDER:
115 this->order.set(value);
116
117 //From SVG spec: If <orderY> is not provided, it defaults to <orderX>.
118 if (this->order.optNumIsSet() == false) {
119 this->order.setOptNumber(this->order.getNumber());
120 }
121
122 if (this->targetXIsSet == false) {
123 this->targetX = (int) floor(this->order.getNumber()/2);
124 }
125
126 if (this->targetYIsSet == false) {
127 this->targetY = (int) floor(this->order.getOptNumber()/2);
128 }
129
130 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
131 break;
132 case SPAttr::KERNELMATRIX:
133 if (value){
134 this->kernelMatrixIsSet = true;
135 this->kernelMatrix = helperfns_read_vector(value);
136
137 if (! this->divisorIsSet) {
138 this->divisor = 0;
139
140 for (double i : this->kernelMatrix) {
141 this->divisor += i;
142 }
143
144 if (this->divisor == 0) {
145 this->divisor = 1;
146 }
147 }
148
149 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
150 } else {
151 g_warning("For feConvolveMatrix you MUST pass a kernelMatrix parameter!");
152 }
153 break;
154 case SPAttr::DIVISOR:
155 if (value) {
156 read_num = helperfns_read_number(value);
157
158 if (read_num == 0) {
159 // This should actually be an error, but given our UI it is more useful to simply set divisor to the default.
160 if (this->kernelMatrixIsSet) {
161 for (double i : this->kernelMatrix) {
162 read_num += i;
163 }
164 }
165
166 if (read_num == 0) {
167 read_num = 1;
168 }
169
170 if (this->divisorIsSet || this->divisor!=read_num) {
171 this->divisorIsSet = false;
172 this->divisor = read_num;
173 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
174 }
175 } else if (!this->divisorIsSet || this->divisor!=read_num) {
176 this->divisorIsSet = true;
177 this->divisor = read_num;
178 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
179 }
180 }
181 break;
182 case SPAttr::BIAS:
183 read_num = 0;
184 if (value) {
185 read_num = helperfns_read_number(value);
186 }
187
188 if (read_num != this->bias){
189 this->bias = read_num;
190 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
191 }
192 break;
193 case SPAttr::TARGETX:
194 if (value) {
195 read_int = (int) helperfns_read_number(value);
196
197 if (read_int < 0 || read_int > this->order.getNumber()){
198 g_warning("targetX must be a value between 0 and orderX! Assuming floor(orderX/2) as default value.");
199 read_int = (int) floor(this->order.getNumber()/2.0);
200 }
201
202 this->targetXIsSet = true;
203
204 if (read_int != this->targetX){
205 this->targetX = read_int;
206 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
207 }
208 }
209 break;
210 case SPAttr::TARGETY:
211 if (value) {
212 read_int = (int) helperfns_read_number(value);
213
214 if (read_int < 0 || read_int > this->order.getOptNumber()){
215 g_warning("targetY must be a value between 0 and orderY! Assuming floor(orderY/2) as default value.");
216 read_int = (int) floor(this->order.getOptNumber()/2.0);
217 }
218
219 this->targetYIsSet = true;
220
221 if (read_int != this->targetY){
222 this->targetY = read_int;
223 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
224 }
225 }
226 break;
227 case SPAttr::EDGEMODE:
228 read_mode = sp_feConvolveMatrix_read_edgeMode(value);
229
230 if (read_mode != this->edgeMode){
231 this->edgeMode = read_mode;
232 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
233 }
234 break;
235 case SPAttr::KERNELUNITLENGTH:
236 this->kernelUnitLength.set(value);
237
238 //From SVG spec: If the <dy> value is not specified, it defaults to the same value as <dx>.
239 if (this->kernelUnitLength.optNumIsSet() == false) {
240 this->kernelUnitLength.setOptNumber(this->kernelUnitLength.getNumber());
241 }
242
243 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
244 break;
245 case SPAttr::PRESERVEALPHA:
246 read_bool = helperfns_read_bool(value, false);
247
248 if (read_bool != this->preserveAlpha){
249 this->preserveAlpha = read_bool;
250 this->parent->requestModified(SP_OBJECT_MODIFIED_FLAG);
251 }
252 break;
253 default:
254 SPFilterPrimitive::set(key, value);
255 break;
256 }
257
258 }
259
260 /**
261 * Receives update notifications.
262 */
update(SPCtx * ctx,guint flags)263 void SPFeConvolveMatrix::update(SPCtx *ctx, guint flags) {
264 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
265 SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
266
267 /* do something to trigger redisplay, updates? */
268
269 }
270
271 SPFilterPrimitive::update(ctx, flags);
272 }
273
274 /**
275 * Writes its settings to an incoming repr object, if any.
276 */
write(Inkscape::XML::Document * doc,Inkscape::XML::Node * repr,guint flags)277 Inkscape::XML::Node* SPFeConvolveMatrix::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
278 /* TODO: Don't just clone, but create a new repr node and write all
279 * relevant values into it */
280 if (!repr) {
281 repr = this->getRepr()->duplicate(doc);
282 }
283
284
285 SPFilterPrimitive::write(doc, repr, flags);
286
287 return repr;
288 }
289
build_renderer(Inkscape::Filters::Filter * filter)290 void SPFeConvolveMatrix::build_renderer(Inkscape::Filters::Filter* filter) {
291 g_assert(filter != nullptr);
292
293 int primitive_n = filter->add_primitive(Inkscape::Filters::NR_FILTER_CONVOLVEMATRIX);
294 Inkscape::Filters::FilterPrimitive *nr_primitive = filter->get_primitive(primitive_n);
295 Inkscape::Filters::FilterConvolveMatrix *nr_convolve = dynamic_cast<Inkscape::Filters::FilterConvolveMatrix*>(nr_primitive);
296 g_assert(nr_convolve != nullptr);
297
298 this->renderer_common(nr_primitive);
299
300 nr_convolve->set_targetX(this->targetX);
301 nr_convolve->set_targetY(this->targetY);
302 nr_convolve->set_orderX( (int)this->order.getNumber() );
303 nr_convolve->set_orderY( (int)this->order.getOptNumber() );
304 nr_convolve->set_kernelMatrix(this->kernelMatrix);
305 nr_convolve->set_divisor(this->divisor);
306 nr_convolve->set_bias(this->bias);
307 nr_convolve->set_preserveAlpha(this->preserveAlpha);
308 }
309 /*
310 Local Variables:
311 mode:c++
312 c-file-style:"stroustrup"
313 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
314 indent-tabs-mode:nil
315 fill-column:99
316 End:
317 */
318 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
319