Code

fix compositing for premultiplication and non-alpha cases
[inkscape.git] / src / live_effects / effect.cpp
1 #define INKSCAPE_LIVEPATHEFFECT_CPP\r
2 \r
3 /*\r
4  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>\r
5  *\r
6  * Released under GNU GPL, read the file 'COPYING' for more information\r
7  */\r
8 \r
9 #include "display/display-forward.h"\r
10 #include "xml/node-event-vector.h"\r
11 #include "sp-object.h"\r
12 #include "attributes.h"\r
13 \r
14 #include "desktop.h"\r
15 \r
16 #include "document.h"\r
17 #include <glibmm/i18n.h>\r
18 \r
19 #include "live_effects/effect.h"\r
20 #include "live_effects/lpeobject.h"\r
21 #include "live_effects/parameter/parameter.h"\r
22 #include <glibmm/ustring.h>\r
23 #include "live_effects/n-art-bpath-2geom.h"\r
24 #include "display/curve.h"\r
25 #include <2geom/sbasis-to-bezier.h>\r
26 #include <gtkmm.h>\r
27 \r
28 // include effects:\r
29 #include "live_effects/lpe-skeletalstrokes.h"\r
30 #include "live_effects/lpe-slant.h"\r
31 #include "live_effects/lpe-test-doEffect-stack.h"\r
32 #include "live_effects/lpe-gears.h"\r
33 #include "live_effects/lpe-curvestitch.h"\r
34 \r
35 namespace Inkscape {\r
36 \r
37 namespace LivePathEffect {\r
38 \r
39 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {\r
40     // {constant defined in effect.h, _("name of your effect"), "name of your effect in SVG"}\r
41     {SKELETAL_STROKES,      _("Path along path"),      "skeletal"},\r
42 #ifdef LPE_ENABLE_TEST_EFFECTS\r
43     {SLANT,                 _("Slant"),                 "slant"},\r
44     {DOEFFECTSTACK_TEST,    _("doEffect stack test"),   "doeffectstacktest"},\r
45 #endif\r
46     {GEARS,                 _("Gears"),                 "gears"},\r
47     {CURVE_STITCH,          _("Curve stitching"),       "curvestitching"},\r
48 };\r
49 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);\r
50 \r
51 Effect*\r
52 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)\r
53 {\r
54     Effect* neweffect = NULL;\r
55     switch (lpenr) {\r
56         case SKELETAL_STROKES:\r
57             neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);\r
58             break;\r
59 #ifdef LPE_ENABLE_TEST_EFFECTS\r
60             case SLANT:\r
61             neweffect = (Effect*) new LPESlant(lpeobj);\r
62             break;\r
63         case DOEFFECTSTACK_TEST:\r
64             neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);\r
65             break;\r
66 #endif\r
67         case GEARS:\r
68             neweffect = (Effect*) new LPEGears(lpeobj);\r
69             break;\r
70         case CURVE_STITCH:\r
71             neweffect = (Effect*) new LPECurveStitch(lpeobj);\r
72             break;\r
73         default:\r
74             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);\r
75             neweffect = NULL;\r
76             break;\r
77     }\r
78 \r
79     if (neweffect) {\r
80         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));\r
81     }\r
82 \r
83     return neweffect;\r
84 }\r
85 \r
86 Effect::Effect(LivePathEffectObject *lpeobject)\r
87 {\r
88     vbox = NULL;\r
89     tooltips = NULL;\r
90     lpeobj = lpeobject;\r
91 }\r
92 \r
93 Effect::~Effect()\r
94 {\r
95     if (tooltips) {\r
96         delete tooltips;\r
97     }\r
98 }\r
99 \r
100 Glib::ustring \r
101 Effect::getName()\r
102 {\r
103     if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)\r
104         return Glib::ustring( LPETypeConverter.get_label(lpeobj->effecttype) );\r
105     else\r
106         return Glib::ustring( _("No effect") );\r
107 }\r
108 \r
109 /*\r
110  *  Here be the doEffect function chain:\r
111  */\r
112 void\r
113 Effect::doEffect (SPCurve * curve)\r
114 {\r
115     NArtBpath *new_bpath = doEffect(SP_CURVE_BPATH(curve));\r
116 \r
117     if (new_bpath) {        // FIXME, add function to SPCurve to change bpath? or a copy function?\r
118         if (curve->_bpath) {\r
119             g_free(curve->_bpath); //delete old bpath\r
120         }\r
121         curve->_bpath = new_bpath;\r
122     }\r
123 }\r
124 \r
125 NArtBpath *\r
126 Effect::doEffect (NArtBpath * path_in)\r
127 {\r
128     std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);\r
129 \r
130     std::vector<Geom::Path> result_pathv = doEffect(orig_pathv);\r
131 \r
132     NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);\r
133 \r
134     return new_bpath;\r
135 }\r
136 \r
137 std::vector<Geom::Path>\r
138 Effect::doEffect (std::vector<Geom::Path> & path_in)\r
139 {\r
140     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;\r
141 \r
142     for (unsigned int i=0; i < path_in.size(); i++) {\r
143         pwd2_in.concat( path_in[i].toPwSb() );\r
144     }\r
145 \r
146     Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect(pwd2_in);\r
147 \r
148     std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);\r
149 \r
150     return path_out;\r
151 }\r
152 \r
153 Geom::Piecewise<Geom::D2<Geom::SBasis> >\r
154 Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)\r
155 {\r
156     g_warning("Effect has no doEffect implementation");\r
157     return pwd2_in;\r
158 }\r
159 \r
160 void\r
161 Effect::readallParameters(Inkscape::XML::Node * repr)\r
162 {\r
163     param_map_type::iterator it = param_map.begin();\r
164     while (it != param_map.end()) {\r
165         const gchar * key = (*it).first.c_str();\r
166         const gchar * value = repr->attribute(key);\r
167         if(value) {\r
168             setParameter(repr, key, NULL, value);\r
169         }\r
170         it++;\r
171     }\r
172 }\r
173 \r
174 void\r
175 Effect::setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value)\r
176 {\r
177     Glib::ustring stringkey(key);\r
178 \r
179     param_map_type::iterator it = param_map.find(stringkey);\r
180     if (it != param_map.end()) {\r
181         if (new_value) {\r
182             bool accepted = it->second->param_readSVGValue(new_value);\r
183             if (!accepted) { \r
184                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);\r
185                 // change was not accepted, so change it back.\r
186                 // think: can this backfire and create infinite loop when started with unacceptable old_value?\r
187                 // repr->setAttribute(key, old_value);\r
188             }\r
189         } else {\r
190             // set default value\r
191             it->second->param_set_default();\r
192         }\r
193     }\r
194 }\r
195 \r
196 void\r
197 Effect::registerParameter(Parameter * param)\r
198 {\r
199     param_map[param->param_key] = param; // inserts or updates\r
200 }\r
201 \r
202 Gtk::Widget *\r
203 Effect::getWidget()\r
204 {\r
205     if (!vbox) {\r
206         vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.\r
207         //if (!tooltips)\r
208             tooltips = new Gtk::Tooltips();\r
209 \r
210         vbox->set_border_width(5);\r
211 \r
212         param_map_type::iterator it = param_map.begin();\r
213         while (it != param_map.end()) {\r
214             Parameter * param = it->second;\r
215             Gtk::Widget * widg = param->param_getWidget();\r
216             Glib::ustring * tip = param->param_getTooltip();\r
217             if (widg) {\r
218                vbox->pack_start(*widg, true, true, 2);\r
219                 if (tip != NULL) {\r
220                     tooltips->set_tip(*widg, *tip);\r
221                 }\r
222             }\r
223 \r
224             it++;\r
225         }\r
226     }\r
227 \r
228     return dynamic_cast<Gtk::Widget *>(vbox);\r
229 }\r
230 \r
231 \r
232 Inkscape::XML::Node * \r
233 Effect::getRepr()\r
234 {\r
235     return SP_OBJECT_REPR(lpeobj);\r
236 }\r
237 \r
238 SPDocument * \r
239 Effect::getSPDoc()\r
240 {\r
241     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");\r
242     return SP_OBJECT_DOCUMENT(lpeobj);\r
243 }\r
244 \r
245 \r
246 } /* namespace LivePathEffect */\r
247 \r
248 } /* namespace Inkscape */\r
249 \r
250 /*\r
251   Local Variables:\r
252   mode:c++\r
253   c-file-style:"stroustrup"\r
254   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
255   indent-tabs-mode:nil\r
256   fill-column:99\r
257   End:\r
258 */\r
259 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r