1 #define __SP_FILTER_CPP__
3 /** \file
4 * SVG <filter> implementation.
5 */
6 /*
7 * Authors:
8 * Hugo Rodrigues <haa.rodrigues@gmail.com>
9 *
10 * Copyright (C) 2006 Hugo Rodrigues
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #ifdef HAVE_CONFIG_H
16 # include "config.h"
17 #endif
19 #include "attributes.h"
20 #include "document.h"
21 #include "sp-filter.h"
22 #include "sp-filter-reference.h"
23 #include "uri.h"
24 #include "xml/repr.h"
26 #define SP_MACROS_SILENT
27 #include "macros.h"
29 #define DEBUG_FILTER
30 #ifdef DEBUG_FILTER
31 # define debug(f, a...) { g_print("%s(%d) %s:", \
32 __FILE__,__LINE__,__FUNCTION__); \
33 g_print(f, ## a); \
34 g_print("\n"); \
35 }
36 #else
37 # define debug(f, a...) /**/
38 #endif
41 /*
42 * For debugging purposes only
43 */
44 void printfilter(SPFilter *filter)
45 {
46 if(filter->filterUnits==SP_FILTER_UNITS_USERSPACEONUSE)
47 g_print("filterUnits=SP_FILTER_UNITS_USERSPACEONUSE\n");
48 else if(filter->filterUnits==SP_FILTER_UNITS_OBJECTBOUNDINGBOX)
49 g_print("filterUnits=SP_FILTER_UNITS_OBJECTBOUNDINGBOX\n");
50 else
51 g_print("filterUnits=UNKNOWN!!!\n");
53 if(filter->primitiveUnits==SP_FILTER_UNITS_USERSPACEONUSE)
54 g_print("primitiveUnits=SP_FILTER_UNITS_USERSPACEONUSE\n");
55 else if(filter->primitiveUnits==SP_FILTER_UNITS_OBJECTBOUNDINGBOX)
56 g_print("primitiveUnits=SP_FILTER_UNITS_OBJECTBOUNDINGBOX\n");
57 else
58 g_print("primitiveUnits=UNKNOWN!!!\n");
60 //TODO: print X, Y, W and H units
61 g_print("x=%lf\n", filter->x.computed);
62 g_print("y=%lf\n", filter->y.computed);
63 g_print("width=%lf\n", filter->width.computed);
64 g_print("height=%lf\n", filter->height.computed);
65 g_print("filterRes=(%lf %lf)\n", filter->filterRes.getNumber(), filter->filterRes.getOptNumber());
67 }
71 /* Filter base class */
73 static void sp_filter_class_init(SPFilterClass *klass);
74 static void sp_filter_init(SPFilter *filter);
76 static void sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
77 static void sp_filter_release(SPObject *object);
78 static void sp_filter_set(SPObject *object, unsigned int key, gchar const *value);
79 static void sp_filter_update(SPObject *object, SPCtx *ctx, guint flags);
80 static Inkscape::XML::Node *sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
82 static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter);
83 static void filter_ref_modified(SPObject *href, SPFilter *filter);
85 static SPObjectClass *filter_parent_class;
87 GType
88 sp_filter_get_type()
89 {
90 static GType filter_type = 0;
92 if (!filter_type) {
93 GTypeInfo filter_info = {
94 sizeof(SPFilterClass),
95 NULL, NULL,
96 (GClassInitFunc) sp_filter_class_init,
97 NULL, NULL,
98 sizeof(SPFilter),
99 16,
100 (GInstanceInitFunc) sp_filter_init,
101 NULL, /* value_table */
102 };
103 filter_type = g_type_register_static(SP_TYPE_OBJECT, "SPFilter", &filter_info, (GTypeFlags)0);
104 }
105 return filter_type;
106 }
108 static void
109 sp_filter_class_init(SPFilterClass *klass)
110 {
112 SPObjectClass *sp_object_class = (SPObjectClass *)klass;
114 filter_parent_class = (SPObjectClass*)g_type_class_peek_parent(klass);
116 sp_object_class->build = sp_filter_build;
117 sp_object_class->release = sp_filter_release;
118 sp_object_class->write = sp_filter_write;
119 sp_object_class->set = sp_filter_set;
120 sp_object_class->update = sp_filter_update;
121 }
123 static void
124 sp_filter_init(SPFilter *filter)
125 {
126 filter->href = new SPFilterReference(SP_OBJECT(filter));
127 filter->href->changedSignal().connect(sigc::bind(sigc::ptr_fun(filter_ref_changed), filter));
129 filter->x = 0;
130 filter->y = 0;
131 filter->width = 0;
132 filter->height = 0;
134 filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
135 filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
136 filter->filterUnits_set = FALSE;
137 filter->primitiveUnits_set = FALSE;
139 }
141 /**
142 * Reads the Inkscape::XML::Node, and initializes SPFilter variables. For this to get called,
143 * our name must be associated with a repr via "sp_object_type_register". Best done through
144 * sp-object-repr.cpp's repr_name_entries array.
145 */
146 static void
147 sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
148 {
149 debug("0x%p",object);
150 if (((SPObjectClass *) filter_parent_class)->build) {
151 ((SPObjectClass *) filter_parent_class)->build(object, document, repr);
152 }
154 //Read values of key attributes from XML nodes into object.
155 sp_object_read_attr(object, "filterUnits");
156 sp_object_read_attr(object, "primitiveUnits");
157 sp_object_read_attr(object, "x");
158 sp_object_read_attr(object, "y");
159 sp_object_read_attr(object, "width");
160 sp_object_read_attr(object, "height");
161 sp_object_read_attr(object, "filterRes");
162 sp_object_read_attr(object, "xlink:href");
164 //is this necessary?
165 sp_document_add_resource(document, "filter", object);
166 }
168 /**
169 * Drops any allocated memory.
170 */
171 static void
172 sp_filter_release(SPObject *object)
173 {
174 debug("0x%p",object);
175 SPFilter *filter = SP_FILTER(object);
177 if (SP_OBJECT_DOCUMENT(object)) {
178 /* Unregister ourselves */
179 sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "filter", SP_OBJECT(object));
180 }
182 //TODO: release resources here
184 //release href
185 if (filter->href) {
186 if (filter->href->getObject()) {
187 sp_signal_disconnect_by_data(filter->href->getObject(), filter);
188 }
189 filter->href->detach();
190 delete filter->href;
191 filter->href = NULL;
192 }
194 if (((SPObjectClass *) filter_parent_class)->release)
195 ((SPObjectClass *) filter_parent_class)->release(object);
196 }
198 /**
199 * Sets a specific value in the SPFilter.
200 */
201 static void
202 sp_filter_set(SPObject *object, unsigned int key, gchar const *value)
203 {
204 debug("0x%p %s(%u): '%s'",object,
205 sp_attribute_name(key),key,value);
206 SPFilter *filter = SP_FILTER(object);
208 switch (key) {
209 case SP_ATTR_FILTERUNITS:
210 if (value) {
211 if (!strcmp(value, "userSpaceOnUse")) {
212 filter->filterUnits = SP_FILTER_UNITS_USERSPACEONUSE;
213 } else {
214 filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
215 }
216 filter->filterUnits_set = TRUE;
217 } else {
218 filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
219 filter->filterUnits_set = FALSE;
220 }
221 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
222 break;
223 case SP_ATTR_PRIMITIVEUNITS:
224 if (value) {
225 if (!strcmp(value, "userSpaceOnUse")) {
226 filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
227 } else {
228 filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
229 }
230 filter->primitiveUnits_set = TRUE;
231 } else {
232 filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
233 filter->primitiveUnits_set = FALSE;
234 }
235 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
236 break;
237 case SP_ATTR_X:
238 filter->x.readOrUnset(value);
239 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
240 break;
241 case SP_ATTR_Y:
242 filter->y.readOrUnset(value);
243 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
244 break;
245 case SP_ATTR_WIDTH:
246 filter->width.readOrUnset(value);
247 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
248 break;
249 case SP_ATTR_HEIGHT:
250 filter->height.readOrUnset(value);
251 object->requestModified(SP_OBJECT_MODIFIED_FLAG);
252 break;
253 case SP_ATTR_FILTERRES:
254 filter->filterRes.set(value);
255 break;
256 case SP_ATTR_XLINK_HREF:
257 if (value) {
258 try {
259 filter->href->attach(Inkscape::URI(value));
260 } catch (Inkscape::BadURIException &e) {
261 g_warning("%s", e.what());
262 filter->href->detach();
263 }
264 } else {
265 filter->href->detach();
266 }
267 break;
268 default:
269 /* See if any parents need this value. */
270 if (((SPObjectClass *) filter_parent_class)->set) {
271 ((SPObjectClass *) filter_parent_class)->set(object, key, value);
272 }
273 break;
274 }
275 }
277 /**
278 * Receives update notifications.
279 */
280 static void
281 sp_filter_update(SPObject *object, SPCtx *ctx, guint flags)
282 {
283 debug("0x%p",object);
284 //SPFilter *filter = SP_FILTER(object);
286 if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
287 SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
289 /* do something to trigger redisplay, updates? */
291 }
293 if (((SPObjectClass *) filter_parent_class)->update) {
294 ((SPObjectClass *) filter_parent_class)->update(object, ctx, flags);
295 }
296 }
298 /**
299 * Writes its settings to an incoming repr object, if any.
300 */
301 static Inkscape::XML::Node *
302 sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
303 {
304 debug("0x%p",object);
305 SPFilter *filter = SP_FILTER(object);
307 // Inkscape-only object, not copied during an "plain SVG" dump:
308 /* if (flags & SP_OBJECT_WRITE_EXT) {
309 if (repr) {
310 // is this sane?
311 repr->mergeFrom(SP_OBJECT_REPR(object), "id");
312 } else {
313 repr = SP_OBJECT_REPR(object)->duplicate();
314 }
315 }
316 */
319 //FIXME: repr node is null at this point,
320 // preventing plain svg save. Need to fix this.
323 if ((flags & SP_OBJECT_WRITE_ALL) || filter->filterUnits_set) {
324 switch (filter->filterUnits) {
325 case SP_FILTER_UNITS_USERSPACEONUSE:
326 repr->setAttribute("filterUnits", "userSpaceOnUse");
327 break;
328 default:
329 repr->setAttribute("filterUnits", "objectBoundingBox");
330 break;
331 }
332 }
334 if ((flags & SP_OBJECT_WRITE_ALL) || filter->primitiveUnits_set) {
335 switch (filter->primitiveUnits) {
336 case SP_FILTER_UNITS_USERSPACEONUSE:
337 repr->setAttribute("primitiveUnits", "userSpaceOnUse");
338 break;
339 default:
340 repr->setAttribute("primitiveUnits", "objectBoundingBox");
341 break;
342 }
343 }
345 if (filter->x._set) {
346 sp_repr_set_svg_double(repr, "x", filter->x.computed);
347 } else {
348 repr->setAttribute("x", NULL);
349 }
351 if (filter->y._set) {
352 sp_repr_set_svg_double(repr, "y", filter->y.computed);
353 } else {
354 repr->setAttribute("y", NULL);
355 }
357 if (filter->width._set) {
358 sp_repr_set_svg_double(repr, "width", filter->width.computed);
359 } else {
360 repr->setAttribute("width", NULL);
361 }
363 if (filter->height._set) {
364 sp_repr_set_svg_double(repr, "height", filter->height.computed);
365 } else {
366 repr->setAttribute("height", NULL);
367 }
369 if (filter->filterRes._set) {
370 char filterRes[32];
371 repr->setAttribute("filterRes", filter->filterRes.getValueString(filterRes));
372 } else {
373 repr->setAttribute("filterRes", NULL);
374 }
376 if (filter->href->getURI()) {
377 gchar *uri_string = filter->href->getURI()->toString();
378 repr->setAttribute("xlink:href", uri_string);
379 g_free(uri_string);
380 }
382 if (((SPObjectClass *) filter_parent_class)->write) {
383 ((SPObjectClass *) filter_parent_class)->write(object, repr, flags);
384 }
386 return repr;
387 }
390 /**
391 * Gets called when the filter is (re)attached to another filter.
392 */
393 static void
394 filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter)
395 {
396 if (old_ref) {
397 sp_signal_disconnect_by_data(old_ref, filter);
398 }
399 if ( SP_IS_FILTER(ref)
400 && ref != filter )
401 {
402 g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(filter_ref_modified), filter);
403 }
405 filter_ref_modified(ref, filter);
406 }
408 static void
409 filter_ref_modified(SPObject *href, SPFilter *filter)
410 {
411 SP_OBJECT(filter)->requestModified(SP_OBJECT_MODIFIED_FLAG);
412 }
415 /*
416 Local Variables:
417 mode:c++
418 c-file-style:"stroustrup"
419 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
420 indent-tabs-mode:nil
421 fill-column:99
422 End:
423 */
424 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :