1 /*
2 * A generic interface for plugging different
3 * autotracers into Inkscape.
4 *
5 * Authors:
6 * Bob Jamison <rjamison@titan.com>
7 *
8 * Copyright (C) 2004 Bob Jamison
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
15 #include "trace/potrace/inkscape-potrace.h"
17 #include <inkscape.h>
18 #include <desktop-handles.h>
19 #include <document.h>
20 #include "message-stack.h"
21 #include <glibmm/i18n.h>
22 #include <selection.h>
23 #include <xml/repr.h>
24 #include "sp-item.h"
25 #include "sp-image.h"
27 namespace Inkscape {
29 namespace Trace {
31 /**
32 *
33 */
34 SPImage *
35 Tracer::getSelectedSPImage()
36 {
37 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
38 if (!desktop)
39 {
40 g_warning("Trace: No active desktop\n");
41 return NULL;
42 }
44 Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
45 if (!sel)
46 {
47 char *msg = _("Select an <b>image</b> to trace");
48 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
49 //g_warning(msg);
50 return NULL;
51 }
53 SPItem *item = sel->singleItem();
54 if (!item)
55 {
56 char *msg = _("Select an <b>image</b> to trace"); //same as above
57 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
58 //g_warning(msg);
59 return NULL;
60 }
62 if (!SP_IS_IMAGE(item))
63 {
64 char *msg = _("Select an <b>image</b> to trace");
65 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
66 //g_warning(msg);
67 return NULL;
68 }
70 selectedItem = item;
72 SPImage *img = SP_IMAGE(item);
74 return img;
76 }
80 /**
81 *
82 */
83 GdkPixbuf *
84 Tracer::getSelectedImage()
85 {
87 SPImage *img = getSelectedSPImage();
88 if (!img)
89 return NULL;
91 GdkPixbuf *pixbuf = img->pixbuf;
93 return pixbuf;
95 }
99 //#########################################################################
100 //# T R A C E
101 //#########################################################################
104 /**
105 * Threaded method that does single bitmap--->path conversion
106 */
107 void Tracer::traceThread()
108 {
109 //## Remember. NEVER leave this method without setting
110 //## engine back to NULL
112 //## Prepare our kill flag. We will watch this later to
113 //## see if the main thread wants us to stop
114 keepGoing = true;
116 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
117 if (!desktop)
118 {
119 g_warning("Trace: No active desktop\n");
120 return;
121 }
123 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
125 if (!SP_ACTIVE_DOCUMENT)
126 {
127 char *msg = _("Trace: No active document");
128 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
129 //g_warning(msg);
130 engine = NULL;
131 return;
132 }
133 SPDocument *doc = SP_ACTIVE_DOCUMENT;
134 sp_document_ensure_up_to_date(doc);
137 SPImage *img = getSelectedSPImage();
138 if (!img || !selectedItem)
139 {
140 engine = NULL;
141 return;
142 }
144 GdkPixbuf *pixbuf = img->pixbuf;
146 if (!pixbuf)
147 {
148 char *msg = _("Trace: Image has no bitmap data");
149 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
150 //g_warning(msg);
151 engine = NULL;
152 return;
153 }
155 int nrPaths;
156 TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
157 //printf("nrPaths:%d\n", nrPaths);
159 //### Check if we should stop
160 if (!keepGoing || !results || nrPaths<1)
161 {
162 engine = NULL;
163 return;
164 }
166 //### Get pointers to the <image> and its parent
167 Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
168 Inkscape::XML::Node *par = sp_repr_parent(imgRepr);
170 //### Get some information for the new transform()
171 double x = 0.0;
172 double y = 0.0;
173 double width = 0.0;
174 double height = 0.0;
175 double dval = 0.0;
177 if (sp_repr_get_double(imgRepr, "x", &dval))
178 x = dval;
179 if (sp_repr_get_double(imgRepr, "y", &dval))
180 y = dval;
182 if (sp_repr_get_double(imgRepr, "width", &dval))
183 width = dval;
184 if (sp_repr_get_double(imgRepr, "height", &dval))
185 height = dval;
187 NR::Matrix trans(NR::translate(x, y));
189 double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
190 double iheight = (double)gdk_pixbuf_get_height(pixbuf);
192 double iwscale = width / iwidth;
193 double ihscale = height / iheight;
195 NR::Matrix scal(NR::scale(iwscale, ihscale));
197 //# Convolve scale, translation, and the original transform
198 NR::Matrix tf(scal);
199 tf *= trans;
200 tf *= selectedItem->transform;
203 //#OK. Now let's start making new nodes
205 Inkscape::XML::Node *groupRepr = NULL;
207 //# if more than 1, make a <g>roup of <path>s
208 if (nrPaths > 1)
209 {
210 groupRepr = sp_repr_new("svg:g");
211 par->addChild(groupRepr, imgRepr);
212 }
214 long totalNodeCount = 0L;
216 for (TracingEngineResult *result=results ;
217 result ; result=result->next)
218 {
219 totalNodeCount += result->getNodeCount();
221 Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
222 pathRepr->setAttribute("style", result->getStyle());
223 pathRepr->setAttribute("d", result->getPathData());
225 if (nrPaths > 1)
226 groupRepr->addChild(pathRepr, NULL);
227 else
228 par->addChild(pathRepr, imgRepr);
230 //### Apply the transform from the image to the new shape
231 SPObject *reprobj = doc->getObjectByRepr(pathRepr);
232 if (reprobj)
233 {
234 SPItem *newItem = SP_ITEM(reprobj);
235 sp_item_write_transform(newItem, pathRepr, tf, NULL);
236 }
237 if (nrPaths == 1)
238 {
239 selection->clear();
240 selection->add(pathRepr);
241 }
242 Inkscape::GC::release(pathRepr);
243 }
246 delete results;
248 // If we have a group, then focus on, then forget it
249 if (nrPaths > 1)
250 {
251 selection->clear();
252 selection->add(groupRepr);
253 Inkscape::GC::release(groupRepr);
254 }
256 //## inform the document, so we can undo
257 sp_document_done(doc);
259 engine = NULL;
261 char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
262 SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
263 g_free(msg);
265 }
267 /**
268 * Main tracing method
269 */
270 void Tracer::trace(TracingEngine *theEngine)
271 {
272 //Check if we are already running
273 if (engine)
274 return;
276 engine = theEngine;
278 #if HAVE_THREADS
279 //Ensure that thread support is running
280 if (!Glib::thread_supported())
281 Glib::thread_init();
283 //Create our thread and run it
284 Glib::Thread::create(
285 sigc::mem_fun(*this, &Tracer::traceThread), false);
286 #else
287 traceThread();
288 #endif
290 }
296 /**
297 * Abort the thread that is executing trace()
298 */
299 void Tracer::abort()
300 {
302 //## Inform Trace's working thread
303 keepGoing = false;
305 if (engine)
306 {
307 engine->abort();
308 }
310 }
314 } // namespace Trace
316 } // namespace Inkscape
319 //#########################################################################
320 //# E N D O F F I L E
321 //#########################################################################