Code

svg-filters branch merged back to head
[inkscape.git] / src / sp-filter.cpp
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;
108 static void
109 sp_filter_class_init(SPFilterClass *klass)
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;
123 static void
124 sp_filter_init(SPFilter *filter)
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;
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)
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);
168 /**
169  * Drops any allocated memory.
170  */
171 static void
172 sp_filter_release(SPObject *object)
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);
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)
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     }
277 /**
278  * Receives update notifications.
279  */
280 static void
281 sp_filter_update(SPObject *object, SPCtx *ctx, guint flags)
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     }
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)
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;
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)
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);
408 static void
409 filter_ref_modified(SPObject *href, SPFilter *filter)
411     SP_OBJECT(filter)->requestModified(SP_OBJECT_MODIFIED_FLAG);
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 :