1 /*
2 * A generic interface for plugging different
3 * autotracers into Inkscape.
4 *
5 * Authors:
6 * Bob Jamison <rjamison@earthlink.net>
7 *
8 * Copyright (C) 2004-2006 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 #include "siox.h"
28 #include "imagemap-gdk.h"
30 namespace Inkscape {
32 namespace Trace {
34 /**
35 *
36 */
37 SPImage *
38 Tracer::getSelectedSPImage()
39 {
40 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
41 if (!desktop)
42 {
43 g_warning("Trace: No active desktop\n");
44 return NULL;
45 }
47 Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
48 if (!sel)
49 {
50 char *msg = _("Select an <b>image</b> to trace");
51 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
52 //g_warning(msg);
53 return NULL;
54 }
56 if (sioxEnabled)
57 {
58 SPImage *img = NULL;
59 GSList const *list = sel->itemList();
60 sioxItems.clear();
61 std::vector<SPItem *> items;
62 /*
63 First, things are selected top-to-bottom, so we need to invert
64 them as bottom-to-top so that we can discover the image and any
65 SPItems above it
66 */
67 for ( ; list ; list=list->next)
68 {
69 if (!SP_IS_ITEM(list->data))
70 {
71 continue;
72 }
73 SPItem *item = SP_ITEM(list->data);
74 items.insert(items.begin(), item);
75 }
76 std::vector<SPItem *>::iterator iter;
77 for (iter = items.begin() ; iter!= items.end() ; iter++)
78 {
79 SPItem *item = *iter;
80 if (SP_IS_IMAGE(item))
81 {
82 if (img) //we want only one
83 {
84 char *msg = _("Select only one <b>image</b> to trace");
85 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
86 return NULL;
87 }
88 img = SP_IMAGE(item);
89 }
90 else if (img) //# items -after- the image in tree (above it in Z)
91 {
92 sioxItems.push_back(item);
93 }
94 }
95 if (!img || sioxItems.size() < 1)
96 {
97 char *msg = _("Select one image and one or more items above it");
98 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
99 return NULL;
100 }
101 return img;
102 }
103 else
104 //### No SIOX. We want exactly one image selected
105 {
106 SPItem *item = sel->singleItem();
107 if (!item)
108 {
109 char *msg = _("Select an <b>image</b> to trace"); //same as above
110 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
111 //g_warning(msg);
112 return NULL;
113 }
115 if (!SP_IS_IMAGE(item))
116 {
117 char *msg = _("Select an <b>image</b> to trace");
118 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
119 //g_warning(msg);
120 return NULL;
121 }
123 SPImage *img = SP_IMAGE(item);
125 return img;
126 }
128 }
132 /**
133 *
134 */
135 GdkPixbuf *
136 Tracer::getSelectedImage()
137 {
139 SPImage *img = getSelectedSPImage();
140 if (!img)
141 return NULL;
143 GdkPixbuf *pixbuf = img->pixbuf;
145 return pixbuf;
147 }
150 GdkPixbuf *
151 Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
152 {
154 PackedPixelMap *ppMap = gdkPixbufToPackedPixelMap(origPixbuf);
155 //We need to create two things:
156 // 1. An array of long pixel values of ARGB
157 // 2. A matching array of per-pixel float 'confidence' values
158 unsigned long *imgBuf = ppMap->pixels;
159 float *confidenceMatrix = new float[ppMap->width * ppMap->height];
162 //## ok we have our pixel buf
163 org::siox::SioxSegmentator ss(ppMap->width, ppMap->height, NULL, 0);
164 ss.segmentate(imgBuf, ppMap->width * ppMap->height,
165 confidenceMatrix, ppMap->width * ppMap->height,
166 0, 0.0);
170 GdkPixbuf *newPixbuf = packedPixelMapToGdkPixbuf(ppMap);
171 ppMap->destroy(ppMap);
172 delete confidenceMatrix;
174 return newPixbuf;
175 }
180 //#########################################################################
181 //# T R A C E
182 //#########################################################################
184 /**
185 * Whether we want to enable SIOX subimage selection
186 */
187 void Tracer::enableSiox(bool enable)
188 {
189 sioxEnabled = enable;
190 }
193 /**
194 * Threaded method that does single bitmap--->path conversion
195 */
196 void Tracer::traceThread()
197 {
198 //## Remember. NEVER leave this method without setting
199 //## engine back to NULL
201 //## Prepare our kill flag. We will watch this later to
202 //## see if the main thread wants us to stop
203 keepGoing = true;
205 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
206 if (!desktop)
207 {
208 g_warning("Trace: No active desktop\n");
209 return;
210 }
212 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
214 if (!SP_ACTIVE_DOCUMENT)
215 {
216 char *msg = _("Trace: No active document");
217 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
218 //g_warning(msg);
219 engine = NULL;
220 return;
221 }
222 SPDocument *doc = SP_ACTIVE_DOCUMENT;
223 sp_document_ensure_up_to_date(doc);
226 SPImage *img = getSelectedSPImage();
227 if (!img)
228 {
229 engine = NULL;
230 return;
231 }
233 GdkPixbuf *pixbuf = img->pixbuf;
235 if (!pixbuf)
236 {
237 char *msg = _("Trace: Image has no bitmap data");
238 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
239 //g_warning(msg);
240 engine = NULL;
241 return;
242 }
244 //## SIOX pre-processing to get a smart subimage of the pixbuf.
245 //## This is done before any other filters
246 if (sioxEnabled)
247 {
248 /*
249 Ok, we have requested siox, and getSelectedSPImage() has found a single
250 bitmap and one or more SPItems above it. Now what we need to do is create
251 a siox-segmented subimage pixbuf. We not need alter 'img' at all, since this
252 pixbuf will be the same dimensions and at the same location.
253 Remember to free this new pixbuf later.
254 */
255 pixbuf = sioxProcessImage(img, pixbuf);
256 }
258 int nrPaths;
259 TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
260 //printf("nrPaths:%d\n", nrPaths);
262 //### Check if we should stop
263 if (!keepGoing || !results || nrPaths<1)
264 {
265 engine = NULL;
266 return;
267 }
269 //### Get pointers to the <image> and its parent
270 Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
271 Inkscape::XML::Node *par = sp_repr_parent(imgRepr);
273 //### Get some information for the new transform()
274 double x = 0.0;
275 double y = 0.0;
276 double width = 0.0;
277 double height = 0.0;
278 double dval = 0.0;
280 if (sp_repr_get_double(imgRepr, "x", &dval))
281 x = dval;
282 if (sp_repr_get_double(imgRepr, "y", &dval))
283 y = dval;
285 if (sp_repr_get_double(imgRepr, "width", &dval))
286 width = dval;
287 if (sp_repr_get_double(imgRepr, "height", &dval))
288 height = dval;
290 NR::Matrix trans(NR::translate(x, y));
292 double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
293 double iheight = (double)gdk_pixbuf_get_height(pixbuf);
295 double iwscale = width / iwidth;
296 double ihscale = height / iheight;
298 NR::Matrix scal(NR::scale(iwscale, ihscale));
300 //# Convolve scale, translation, and the original transform
301 NR::Matrix tf(scal);
302 tf *= trans;
303 tf *= img->transform;
306 //#OK. Now let's start making new nodes
308 Inkscape::XML::Node *groupRepr = NULL;
310 //# if more than 1, make a <g>roup of <path>s
311 if (nrPaths > 1)
312 {
313 groupRepr = sp_repr_new("svg:g");
314 par->addChild(groupRepr, imgRepr);
315 }
317 long totalNodeCount = 0L;
319 for (TracingEngineResult *result=results ;
320 result ; result=result->next)
321 {
322 totalNodeCount += result->getNodeCount();
324 Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
325 pathRepr->setAttribute("style", result->getStyle());
326 pathRepr->setAttribute("d", result->getPathData());
328 if (nrPaths > 1)
329 groupRepr->addChild(pathRepr, NULL);
330 else
331 par->addChild(pathRepr, imgRepr);
333 //### Apply the transform from the image to the new shape
334 SPObject *reprobj = doc->getObjectByRepr(pathRepr);
335 if (reprobj)
336 {
337 SPItem *newItem = SP_ITEM(reprobj);
338 sp_item_write_transform(newItem, pathRepr, tf, NULL);
339 }
340 if (nrPaths == 1)
341 {
342 selection->clear();
343 selection->add(pathRepr);
344 }
345 Inkscape::GC::release(pathRepr);
346 }
348 //did we allocate a pixbuf copy?
349 if (sioxEnabled)
350 {
351 g_free(pixbuf);
352 }
354 delete results;
356 // If we have a group, then focus on, then forget it
357 if (nrPaths > 1)
358 {
359 selection->clear();
360 selection->add(groupRepr);
361 Inkscape::GC::release(groupRepr);
362 }
364 //## inform the document, so we can undo
365 sp_document_done(doc);
367 engine = NULL;
369 char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
370 SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
371 g_free(msg);
373 }
379 /**
380 * Main tracing method
381 */
382 void Tracer::trace(TracingEngine *theEngine)
383 {
384 //Check if we are already running
385 if (engine)
386 return;
388 engine = theEngine;
390 #if HAVE_THREADS
391 //Ensure that thread support is running
392 if (!Glib::thread_supported())
393 Glib::thread_init();
395 //Create our thread and run it
396 Glib::Thread::create(
397 sigc::mem_fun(*this, &Tracer::traceThread), false);
398 #else
399 traceThread();
400 #endif
402 }
408 /**
409 * Abort the thread that is executing trace()
410 */
411 void Tracer::abort()
412 {
414 //## Inform Trace's working thread
415 keepGoing = false;
417 if (engine)
418 {
419 engine->abort();
420 }
422 }
426 } // namespace Trace
428 } // namespace Inkscape
431 //#########################################################################
432 //# E N D O F F I L E
433 //#########################################################################