Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / extension / internal / filter / filter.cpp
1 /*
2  * Authors:
3  *   Ted Gould <ted@gould.cx>
4  *
5  * Copyright (C) 2008 Authors
6  *
7  * Released under GNU GPL, read the file 'COPYING' for more information
8  */
10 #include "desktop.h"
11 #include "selection.h"
12 #include "document-private.h"
13 #include "sp-item.h"
14 #include "util/glib-list-iterators.h"
15 #include "extension/extension.h"
16 #include "extension/effect.h"
17 #include "extension/system.h"
18 #include "xml/repr.h"
19 #include "xml/simple-node.h"
20 #include "xml/attribute-record.h"
22 #include "filter.h"
24 namespace Inkscape {
25 namespace Extension {
26 namespace Internal {
27 namespace Filter {
29 Filter::Filter() :
30                 Inkscape::Extension::Implementation::Implementation(),
31                 _filter(NULL) {
32         return;
33 }
35 Filter::Filter(gchar const * filter) :
36                 Inkscape::Extension::Implementation::Implementation(),
37                 _filter(filter) {
38         return;
39 }
41 Filter::~Filter (void) {
42         if (_filter != NULL) {
43                 _filter = NULL;
44         }
46         return;
47 }
49 bool
50 Filter::load (Inkscape::Extension::Extension *module)
51 {
52         return true;
53 }
55 Inkscape::Extension::Implementation::ImplementationDocumentCache *
56 Filter::newDocCache (Inkscape::Extension::Extension * ext, Inkscape::UI::View::View * doc)
57 {
58         return NULL;
59 }
61 gchar const *
62 Filter::get_filter_text (Inkscape::Extension::Extension * ext)
63 {
64         return _filter;
65 }
67 Inkscape::XML::Document *
68 Filter::get_filter (Inkscape::Extension::Extension * ext) {
69         gchar const * filter = get_filter_text(ext);
70         return sp_repr_read_mem(filter, strlen(filter), NULL);
71 }
73 void
74 Filter::merge_filters( Inkscape::XML::Node * to, Inkscape::XML::Node * from,
75                        Inkscape::XML::Document * doc,
76                        gchar const * srcGraphic, gchar const * srcGraphicAlpha)
77 {
78         if (from == NULL) return;
80         // copy attributes
81     for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = from->attributeList() ;
82           iter ; ++iter ) {
83                 gchar const * attr = g_quark_to_string(iter->key);
84                 //printf("Attribute List: %s\n", attr);
85                 if (!strcmp(attr, "id")) continue; // nope, don't copy that one!
86                 to->setAttribute(attr, from->attribute(attr));
88                 if (!strcmp(attr, "in") || !strcmp(attr, "in2") || !strcmp(attr, "in3")) {
89                         if (srcGraphic != NULL && !strcmp(from->attribute(attr), "SourceGraphic")) {
90                                 to->setAttribute(attr, srcGraphic);
91                         }
93                         if (srcGraphicAlpha != NULL && !strcmp(from->attribute(attr), "SourceAlpha")) {
94                                 to->setAttribute(attr, srcGraphicAlpha);
95                         }
96                 }
97         }
99         // for each child call recursively
100         for (Inkscape::XML::Node * from_child = from->firstChild();
101                   from_child != NULL ; from_child = from_child->next()) {
102                 Glib::ustring name = "svg:";
103                 name += from_child->name();
105                 Inkscape::XML::Node * to_child = doc->createElement(name.c_str());
106                 to->appendChild(to_child);
107                 merge_filters(to_child, from_child, doc, srcGraphic, srcGraphicAlpha);
109                 if (from_child == from->firstChild() && !strcmp("filter", from->name()) && srcGraphic != NULL && to_child->attribute("in") == NULL) {
110                         to_child->setAttribute("in", srcGraphic);
111                 }
112     Inkscape::GC::release(to_child);
113         }
116 #define FILTER_SRC_GRAPHIC       "fbSourceGraphic"
117 #define FILTER_SRC_GRAPHIC_ALPHA "fbSourceGraphicAlpha"
119 void
120 Filter::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document, Inkscape::Extension::Implementation::ImplementationDocumentCache * docCache)
122         Inkscape::XML::Document *filterdoc = get_filter(module);
123         if (filterdoc == NULL) {
124                 return; // could not parse the XML source of the filter; typically parser will stderr a warning
125         }
127         //printf("Calling filter effect\n");
128     Inkscape::Selection * selection = ((SPDesktop *)document)->selection;
130     using Inkscape::Util::GSListConstIterator;
131     // TODO need to properly refcount the items, at least
132     std::list<SPItem *> items;
133     items.insert<GSListConstIterator<SPItem *> >(items.end(), selection->itemList(), NULL);
135         Inkscape::XML::Document * xmldoc = document->doc()->getReprDoc();
136         Inkscape::XML::Node * defsrepr = SP_OBJECT_REPR(SP_DOCUMENT_DEFS(document->doc()));
138     for(std::list<SPItem *>::iterator item = items.begin();
139             item != items.end(); item++) {
140         SPItem * spitem = *item;
141                 Inkscape::XML::Node * node = SP_OBJECT_REPR(spitem);
143                 SPCSSAttr * css = sp_repr_css_attr(node, "style");
144                 gchar const * filter = sp_repr_css_property(css, "filter", NULL);
146                 if (filter == NULL) {
148                         Inkscape::XML::Node * newfilterroot = xmldoc->createElement("svg:filter");
149                         defsrepr->appendChild(newfilterroot);
151                         Glib::ustring url = "url(#"; url += newfilterroot->attribute("id"); url += ")";
153                         merge_filters(newfilterroot, filterdoc->root(), xmldoc);
155       Inkscape::GC::release(newfilterroot);
157                         sp_repr_css_set_property(css, "filter", url.c_str());
158                         sp_repr_css_set(node, css, "style");
159                 } else {
160                         if (strncmp(filter, "url(#", strlen("url(#")) || filter[strlen(filter) - 1] != ')') {
161                                 // This is not url(#id) -- we can't handle it
162                                 continue;
163                         }
165                         gchar * lfilter = g_strndup(filter + 5, strlen(filter) - 6);
166                         Inkscape::XML::Node * filternode = NULL;
167                         for (Inkscape::XML::Node * child = defsrepr->firstChild(); child != NULL; child = child->next()) {
168                                 if (!strcmp(lfilter, child->attribute("id"))) {
169                                         filternode = child;
170                                         break;
171                                 }
172                         }
173                         g_free(lfilter);
175                         // no filter
176                         if (filternode == NULL) {
177                                 g_warning("no assoziating filter found!");
178                                 continue;
179                         }
181                         if (filternode->lastChild() == NULL) {
182                 // empty filter, we insert
183                 merge_filters(filternode, filterdoc->root(), xmldoc);
184                         } else {
185                 // existing filter, we merge
186                 filternode->lastChild()->setAttribute("result", FILTER_SRC_GRAPHIC);
187                 Inkscape::XML::Node * alpha = xmldoc->createElement("svg:feColorMatrix");
188                 alpha->setAttribute("result", FILTER_SRC_GRAPHIC_ALPHA);
189                 alpha->setAttribute("in", FILTER_SRC_GRAPHIC); // not required, but we're being explicit
190                 alpha->setAttribute("values", "0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0");
192                 filternode->appendChild(alpha);
194                 merge_filters(filternode, filterdoc->root(), xmldoc, FILTER_SRC_GRAPHIC, FILTER_SRC_GRAPHIC_ALPHA);
196                 Inkscape::GC::release(alpha);
197                         }
198                 }
199     }
201     return;
204 #include "extension/internal/clear-n_.h"
206 void
207 Filter::filter_init (gchar const * id, gchar const * name, gchar const * submenu, gchar const * tip, gchar const * filter)
209         gchar * xml_str = g_strdup_printf(
210         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
211             "<name>%s</name>\n"
212             "<id>org.inkscape.effect.filter.%s</id>\n"
213             "<effect>\n"
214                 "<object-type>all</object-type>\n"
215                 "<effects-menu>\n"
216                     "<submenu name=\"" N_("Filters") "\" />\n"
217                                                         "<submenu name=\"%s\"/>\n"
218                 "</effects-menu>\n"
219                 "<menu-tip>%s</menu-tip>\n"
220             "</effect>\n"
221         "</inkscape-extension>\n", name, id, submenu, tip);
222     Inkscape::Extension::build_from_mem(xml_str, new Filter(filter));
223         g_free(xml_str);
224     return;
227 }; /* namespace Filter */
228 }; /* namespace Internal */
229 }; /* namespace Extension */
230 }; /* namespace Inkscape */