Code

Refactoring work for filter effects renderer initialization
[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  *   Niko Kiirala <niko@kiirala.com>
10  *
11  * Copyright (C) 2006,2007 Authors
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include "attributes.h"
21 #include "document.h"
22 #include "sp-filter.h"
23 #include "sp-filter-reference.h"
24 #include "uri.h"
25 #include "xml/repr.h"
27 #define SP_MACROS_SILENT
28 #include "macros.h"
30 #include "display/nr-filter.cpp"
32 /* Filter base class */
34 static void sp_filter_class_init(SPFilterClass *klass);
35 static void sp_filter_init(SPFilter *filter);
37 static void sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
38 static void sp_filter_release(SPObject *object);
39 static void sp_filter_set(SPObject *object, unsigned int key, gchar const *value);
40 static void sp_filter_update(SPObject *object, SPCtx *ctx, guint flags);
41 static void sp_filter_child_added(SPObject *object,
42                                     Inkscape::XML::Node *child,
43                                     Inkscape::XML::Node *ref);
44 static void sp_filter_remove_child(SPObject *object, Inkscape::XML::Node *child);
45 static Inkscape::XML::Node *sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
47 static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter);
48 static void filter_ref_modified(SPObject *href, guint flags, SPFilter *filter);
50 static SPObjectClass *filter_parent_class;
52 GType
53 sp_filter_get_type()
54 {
55     static GType filter_type = 0;
57     if (!filter_type) {
58         GTypeInfo filter_info = {
59             sizeof(SPFilterClass),
60             NULL, NULL,
61             (GClassInitFunc) sp_filter_class_init,
62             NULL, NULL,
63             sizeof(SPFilter),
64             16,
65             (GInstanceInitFunc) sp_filter_init,
66             NULL,    /* value_table */
67         };
68         filter_type = g_type_register_static(SP_TYPE_OBJECT, "SPFilter", &filter_info, (GTypeFlags)0);
69     }
70     return filter_type;
71 }
73 static void
74 sp_filter_class_init(SPFilterClass *klass)
75 {
77     SPObjectClass *sp_object_class = (SPObjectClass *)klass;
79     filter_parent_class = (SPObjectClass*)g_type_class_peek_parent(klass);
81     sp_object_class->build = sp_filter_build;
82     sp_object_class->release = sp_filter_release;
83     sp_object_class->write = sp_filter_write;
84     sp_object_class->set = sp_filter_set;
85     sp_object_class->update = sp_filter_update;
86     sp_object_class->child_added = sp_filter_child_added;
87     sp_object_class->remove_child = sp_filter_remove_child;
88 }
90 static void
91 sp_filter_init(SPFilter *filter)
92 {
93     filter->href = new SPFilterReference(SP_OBJECT(filter));
94     filter->href->changedSignal().connect(sigc::bind(sigc::ptr_fun(filter_ref_changed), filter));
96     filter->x = 0;
97     filter->y = 0;
98     filter->width = 0;
99     filter->height = 0;
101     filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
102     filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
103     filter->filterUnits_set = FALSE;
104     filter->primitiveUnits_set = FALSE;
105     filter->_primitive_count=0;
106     
107     filter->_primitive_table_size = 1;
108     filter->_primitives = new SPFilterPrimitive*[1];
109     filter->_primitives[0] = NULL;
111     filter->_renderer = NULL;
114 /**
115  * Reads the Inkscape::XML::Node, and initializes SPFilter variables.  For this to get called,
116  * our name must be associated with a repr via "sp_object_type_register".  Best done through
117  * sp-object-repr.cpp's repr_name_entries array.
118  */
119 static void
120 sp_filter_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
122     if (((SPObjectClass *) filter_parent_class)->build) {
123         ((SPObjectClass *) filter_parent_class)->build(object, document, repr);
124     }
126     //Read values of key attributes from XML nodes into object.
127     sp_object_read_attr(object, "filterUnits");
128     sp_object_read_attr(object, "primitiveUnits");
129     sp_object_read_attr(object, "x");
130     sp_object_read_attr(object, "y");
131     sp_object_read_attr(object, "width");
132     sp_object_read_attr(object, "height");
133     sp_object_read_attr(object, "filterRes");
134     sp_object_read_attr(object, "xlink:href");
136 //is this necessary?
137     sp_document_add_resource(document, "filter", object);
140 /**
141  * Drops any allocated memory.
142  */
143 static void
144 sp_filter_release(SPObject *object)
146     SPFilter *filter = SP_FILTER(object);
148     if (SP_OBJECT_DOCUMENT(object)) {
149         /* Unregister ourselves */
150         sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "filter", SP_OBJECT(object));
151     }
153 //TODO: release resources here
155     //release href
156     if (filter->href) {
157         if (filter->href->getObject()) {
158             sp_signal_disconnect_by_data(filter->href->getObject(), filter);
159         }
160         filter->href->detach();
161         delete filter->href;
162         filter->href = NULL;
163     }
165     if (((SPObjectClass *) filter_parent_class)->release)
166         ((SPObjectClass *) filter_parent_class)->release(object);
169 /**
170  * Sets a specific value in the SPFilter.
171  */
172 static void
173 sp_filter_set(SPObject *object, unsigned int key, gchar const *value)
175     SPFilter *filter = SP_FILTER(object);
177     switch (key) {
178         case SP_ATTR_FILTERUNITS:
179             if (value) {
180                 if (!strcmp(value, "userSpaceOnUse")) {
181                     filter->filterUnits = SP_FILTER_UNITS_USERSPACEONUSE;
182                 } else {
183                     filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
184                 }
185                 filter->filterUnits_set = TRUE;
186             } else {
187                 filter->filterUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
188                 filter->filterUnits_set = FALSE;
189             }
190             object->requestModified(SP_OBJECT_MODIFIED_FLAG);
191             break;
192         case SP_ATTR_PRIMITIVEUNITS:
193             if (value) {
194                 if (!strcmp(value, "userSpaceOnUse")) {
195                     filter->primitiveUnits = SP_FILTER_UNITS_USERSPACEONUSE;
196                 } else {
197                     filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
198                 }
199                 filter->primitiveUnits_set = TRUE;
200             } else {
201                 filter->primitiveUnits = SP_FILTER_UNITS_OBJECTBOUNDINGBOX;
202                 filter->primitiveUnits_set = FALSE;
203             }
204             object->requestModified(SP_OBJECT_MODIFIED_FLAG);
205             break;
206     case SP_ATTR_X:
207             filter->x.readOrUnset(value);
208         object->requestModified(SP_OBJECT_MODIFIED_FLAG);
209             break;
210     case SP_ATTR_Y:
211         filter->y.readOrUnset(value);
212         object->requestModified(SP_OBJECT_MODIFIED_FLAG);
213             break;
214     case SP_ATTR_WIDTH:
215         filter->width.readOrUnset(value);
216         object->requestModified(SP_OBJECT_MODIFIED_FLAG);
217             break;
218     case SP_ATTR_HEIGHT:
219         filter->height.readOrUnset(value);
220         object->requestModified(SP_OBJECT_MODIFIED_FLAG);
221             break;
222     case SP_ATTR_FILTERRES:
223         filter->filterRes.set(value);
224             break;
225         case SP_ATTR_XLINK_HREF:
226             if (value) {
227                 try {
228                     filter->href->attach(Inkscape::URI(value));
229                 } catch (Inkscape::BadURIException &e) {
230                     g_warning("%s", e.what());
231                     filter->href->detach();
232                 }
233             } else {
234                 filter->href->detach();
235             }
236             break;
237         default:
238             // See if any parents need this value. 
239             if (((SPObjectClass *) filter_parent_class)->set) {
240                 ((SPObjectClass *) filter_parent_class)->set(object, key, value);
241             }
242             break;
243     }
246 /**
247  * Receives update notifications.
248  */
249 static void
250 sp_filter_update(SPObject *object, SPCtx *ctx, guint flags)
252     //SPFilter *filter = SP_FILTER(object);
254     if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG |
255                  SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
257         /* do something to trigger redisplay, updates? */
259     }
261     if (((SPObjectClass *) filter_parent_class)->update) {
262         ((SPObjectClass *) filter_parent_class)->update(object, ctx, flags);
263     }
266 /**
267  * Writes its settings to an incoming repr object, if any.
268  */
269 static Inkscape::XML::Node *
270 sp_filter_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
272     SPFilter *filter = SP_FILTER(object);
274     if (!repr) {
275         repr = SP_OBJECT_REPR(object)->duplicate(NULL); // FIXME
276     }
278     if ((flags & SP_OBJECT_WRITE_ALL) || filter->filterUnits_set) {
279         switch (filter->filterUnits) {
280             case SP_FILTER_UNITS_USERSPACEONUSE:
281                 repr->setAttribute("filterUnits", "userSpaceOnUse");
282                 break;
283             default:
284                 repr->setAttribute("filterUnits", "objectBoundingBox");
285                 break;
286         }
287     }
289     if ((flags & SP_OBJECT_WRITE_ALL) || filter->primitiveUnits_set) {
290         switch (filter->primitiveUnits) {
291             case SP_FILTER_UNITS_USERSPACEONUSE:
292                 repr->setAttribute("primitiveUnits", "userSpaceOnUse");
293                 break;
294             default:
295                 repr->setAttribute("primitiveUnits", "objectBoundingBox");
296                 break;
297         }
298     }
300     if (filter->x._set) {
301         sp_repr_set_svg_double(repr, "x", filter->x.computed);
302     } else {
303         repr->setAttribute("x", NULL);
304     }
306     if (filter->y._set) {
307         sp_repr_set_svg_double(repr, "y", filter->y.computed);
308     } else {
309         repr->setAttribute("y", NULL);
310     }
312     if (filter->width._set) {
313         sp_repr_set_svg_double(repr, "width", filter->width.computed);
314     } else {
315         repr->setAttribute("width", NULL);
316     }
318     if (filter->height._set) {
319         sp_repr_set_svg_double(repr, "height", filter->height.computed);
320     } else {
321         repr->setAttribute("height", NULL);
322     }
324     if (filter->filterRes.getNumber()>=0) {
325         gchar *tmp = filter->filterRes.getValueString();
326         repr->setAttribute("filterRes", tmp);
327         g_free(tmp);
328     } else {
329         repr->setAttribute("filterRes", NULL);
330     }
332     if (filter->href->getURI()) {
333         gchar *uri_string = filter->href->getURI()->toString();
334         repr->setAttribute("xlink:href", uri_string);
335         g_free(uri_string);
336     }
338     if (((SPObjectClass *) filter_parent_class)->write) {
339         ((SPObjectClass *) filter_parent_class)->write(object, repr, flags);
340     }
342     return repr;
346 /**
347  * Gets called when the filter is (re)attached to another filter.
348  */
349 static void
350 filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter)
352     if (old_ref) {
353         sp_signal_disconnect_by_data(old_ref, filter);
354     }
355     if ( SP_IS_FILTER(ref)
356          && ref != filter )
357     {
358         ref->connectModified(sigc::bind(sigc::ptr_fun(&filter_ref_modified), filter));
359         //g_signal_connect(G_OBJECT(ref), "modified", G_CALLBACK(filter_ref_modified), filter);
360     }
362     filter_ref_modified(ref, 0, filter);
365 static void
366 filter_ref_modified(SPObject *href, guint flags, SPFilter *filter)
368     SP_OBJECT(filter)->requestModified(SP_OBJECT_MODIFIED_FLAG);
372 void _enlarge_primitive_table(SPFilter * filter) {
373     SPFilterPrimitive **new_tbl = new SPFilterPrimitive*[filter->_primitive_table_size * 2];
374     for (int i = 0 ; i < filter->_primitive_count ; i++) {
375         new_tbl[i] = filter->_primitives[i];
376     }
377     filter->_primitive_table_size *= 2;
378     for (int i = filter->_primitive_count ; i < filter->_primitive_table_size ; i++) {
379         new_tbl[i] = NULL;
380     }
381     delete[] filter->_primitives;
382     filter->_primitives = new_tbl;
385 SPFilterPrimitive *add_primitive(SPFilter *filter, SPFilterPrimitive *primitive)
387     if (filter->_primitive_count >= filter->_primitive_table_size) {
388         _enlarge_primitive_table(filter);
389     }
390     filter->_primitives[filter->_primitive_count] = primitive;
391     filter->_primitive_count++;
392     return primitive;
395 /**
396  * Callback for child_added event.
397  */
398 static void
399 sp_filter_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
400 {/*
401     SPFilter *f = SP_FILTER(object);
403     if (((SPObjectClass *) filter_parent_class)->child_added)
404         (* ((SPObjectClass *) filter_parent_class)->child_added)(object, child, ref);
406     object->requestModified(SP_OBJECT_MODIFIED_FLAG);
407         */
410 /**
411  * Callback for remove_child event.
412  */
413 static void
414 sp_filter_remove_child(SPObject *object, Inkscape::XML::Node *child)
415 {/*
416     SPFilter *f = SP_FILTER(object);
418     if (((SPObjectClass *) filter_parent_class)->remove_child)
419         (* ((SPObjectClass *) filter_parent_class)->remove_child)(object, child);
421     SPObject *ochild;
423     
424     object->requestModified(SP_OBJECT_MODIFIED_FLAG);
425         */
428 void sp_filter_build_renderer(SPFilter *sp_filter, NR::Filter *nr_filter)
430     g_assert(sp_filter != NULL);
431     g_assert(nr_filter != NULL);
433     sp_filter->_renderer = nr_filter;
435     nr_filter->set_x(sp_filter->x);
436     nr_filter->set_y(sp_filter->y);
437     nr_filter->set_width(sp_filter->width);
438     nr_filter->set_height(sp_filter->height);
440     nr_filter->clear_primitives();
441     for (int i = 0 ; i < sp_filter->_primitive_count ; i++) {
442         SPFilterPrimitive *primitive = sp_filter->_primitives[i];
443         g_assert(primitive != NULL);
444         if (((SPFilterPrimitiveClass*) G_OBJECT_GET_CLASS(primitive))->build_renderer) {
445             ((SPFilterPrimitiveClass *) G_OBJECT_GET_CLASS(primitive))->build_renderer(primitive, nr_filter);
446         } else {
447             g_warning("Cannot build filter renderer: missing builder");
448         }
449     }
452 int sp_filter_primitive_count(SPFilter *filter) {
453     g_assert(filter != NULL);
454     return filter->_primitive_count;
457 /*
458   Local Variables:
459   mode:c++
460   c-file-style:"stroustrup"
461   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
462   indent-tabs-mode:nil
463   fill-column:99
464   End:
465 */
466 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :