2e46dd854fba8789f826d11a197e4b1635796b5d
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.h>
19 #include <desktop-handles.h>
20 #include <document.h>
21 #include <message-stack.h>
22 #include <glibmm/i18n.h>
23 #include <selection.h>
24 #include <xml/repr.h>
25 #include <xml/attribute-record.h>
26 #include <sp-item.h>
27 #include <sp-shape.h>
28 #include <sp-image.h>
29 #include <splivarot.h> //for intersection boolop
31 #include "siox.h"
32 #include "imagemap-gdk.h"
34 namespace Inkscape {
36 namespace Trace {
38 /**
39 *
40 */
41 SPImage *
42 Tracer::getSelectedSPImage()
43 {
44 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
45 if (!desktop)
46 {
47 g_warning("Trace: No active desktop\n");
48 return NULL;
49 }
51 Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
52 if (!sel)
53 {
54 char *msg = _("Select an <b>image</b> to trace");
55 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
56 //g_warning(msg);
57 return NULL;
58 }
60 if (sioxEnabled)
61 {
62 SPImage *img = NULL;
63 GSList const *list = sel->itemList();
64 std::vector<SPItem *> items;
65 sioxShapes.clear();
67 /*
68 First, things are selected top-to-bottom, so we need to invert
69 them as bottom-to-top so that we can discover the image and any
70 SPItems above it
71 */
72 for ( ; list ; list=list->next)
73 {
74 if (!SP_IS_ITEM(list->data))
75 {
76 continue;
77 }
78 SPItem *item = SP_ITEM(list->data);
79 items.insert(items.begin(), item);
80 }
81 std::vector<SPItem *>::iterator iter;
82 for (iter = items.begin() ; iter!= items.end() ; iter++)
83 {
84 SPItem *item = *iter;
85 if (SP_IS_IMAGE(item))
86 {
87 if (img) //we want only one
88 {
89 char *msg = _("Select only one <b>image</b> to trace");
90 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
91 return NULL;
92 }
93 img = SP_IMAGE(item);
94 }
95 else if (img) //# items -after- the image in tree (above it in Z)
96 {
97 if (SP_IS_SHAPE(item))
98 {
99 SPShape *shape = SP_SHAPE(item);
100 sioxShapes.push_back(shape);
101 }
102 }
103 }
104 if (!img || sioxShapes.size() < 1)
105 {
106 char *msg = _("Select one image and one or more shapes above it");
107 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
108 return NULL;
109 }
110 return img;
111 }
112 else
113 //### SIOX not enabled. We want exactly one image selected
114 {
115 SPItem *item = sel->singleItem();
116 if (!item)
117 {
118 char *msg = _("Select an <b>image</b> to trace"); //same as above
119 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
120 //g_warning(msg);
121 return NULL;
122 }
124 if (!SP_IS_IMAGE(item))
125 {
126 char *msg = _("Select an <b>image</b> to trace");
127 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
128 //g_warning(msg);
129 return NULL;
130 }
132 SPImage *img = SP_IMAGE(item);
134 return img;
135 }
137 }
141 /**
142 *
143 */
144 GdkPixbuf *
145 Tracer::getSelectedImage()
146 {
148 SPImage *img = getSelectedSPImage();
149 if (!img)
150 return NULL;
152 GdkPixbuf *pixbuf = img->pixbuf;
154 return pixbuf;
156 }
159 GdkPixbuf *
160 Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
161 {
163 PackedPixelMap *ppMap = gdkPixbufToPackedPixelMap(origPixbuf);
164 //We need to create two things:
165 // 1. An array of long pixel values of ARGB
166 // 2. A matching array of per-pixel float 'confidence' values
167 unsigned long *imgBuf = ppMap->pixels;
168 float *confidenceMatrix = new float[ppMap->width * ppMap->height];
170 Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
171 /*
172 //## Make a Rect overlaying the image
173 Inkscape::XML::Node *parent = imgRepr->parent();
175 Inkscape::XML::Node *rect = sp_repr_new("svg:rect");
176 Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attr =
177 imgRepr->attributeList();
178 for ( ; attr ; attr++)
179 {
180 rect->setAttribute(g_quark_to_string(attr->key), attr->value, false);
181 }
184 //Inkscape::XML::Node *rect = imgRepr->duplicate();
185 parent->appendChild(rect);
186 Inkscape::GC::release(rect);
187 */
189 //### <image> element
190 double x = 0.0;
191 double y = 0.0;
192 double width = 0.0;
193 double height = 0.0;
194 double dval = 0.0;
196 if (sp_repr_get_double(imgRepr, "x", &dval))
197 x = dval;
198 if (sp_repr_get_double(imgRepr, "y", &dval))
199 y = dval;
201 if (sp_repr_get_double(imgRepr, "width", &dval))
202 width = dval;
203 if (sp_repr_get_double(imgRepr, "height", &dval))
204 height = dval;
206 double iwidth = (double)ppMap->width;
207 double iheight = (double)ppMap->height;
209 double iwscale = width / iwidth;
210 double ihscale = height / iheight;
212 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
214 unsigned long cmIndex = 0;
215 for (int row=0 ; row<ppMap->height ; row++)
216 {
217 double ypos = y + ihscale * (double) row;
218 for (int col=0 ; col<ppMap->width ; col++)
219 {
220 //Get absolute X,Y position
221 double xpos = x + iwscale * (double)col;
222 NR::Point point(xpos, ypos);
223 point *= img->transform;
224 point = desktop->doc2dt(point);
225 std::vector<SPShape *>::iterator iter;
226 g_message("x:%f y:%f\n", point[0], point[1]);
227 SPItem *itemOverPoint = desktop->item_at_point(point, false, NULL);
228 int weHaveAHit = false;
229 if (itemOverPoint)
230 {
231 printf("searching\n");
232 for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++)
233 {
234 SPShape *shape = *iter;
235 if (shape == itemOverPoint)
236 {
237 weHaveAHit = true;
238 break;
239 }
240 }
241 }
242 if (weHaveAHit)
243 {
244 g_message("hit!\n");
245 confidenceMatrix[cmIndex] =
246 org::siox::SioxSegmentator::CERTAIN_FOREGROUND_CONFIDENCE;
247 }
248 else
249 {
250 confidenceMatrix[cmIndex] =
251 org::siox::SioxSegmentator::CERTAIN_BACKGROUND_CONFIDENCE;
252 }
253 cmIndex++;
254 }
255 }
257 //## ok we have our pixel buf
258 org::siox::SioxSegmentator ss(ppMap->width, ppMap->height, NULL, 0);
259 ss.segmentate(imgBuf, ppMap->width * ppMap->height,
260 confidenceMatrix, ppMap->width * ppMap->height,
261 0, 0.0);
265 GdkPixbuf *newPixbuf = packedPixelMapToGdkPixbuf(ppMap);
266 ppMap->destroy(ppMap);
267 delete confidenceMatrix;
269 return newPixbuf;
270 }
275 //#########################################################################
276 //# T R A C E
277 //#########################################################################
279 /**
280 * Whether we want to enable SIOX subimage selection
281 */
282 void Tracer::enableSiox(bool enable)
283 {
284 sioxEnabled = enable;
285 }
288 /**
289 * Threaded method that does single bitmap--->path conversion
290 */
291 void Tracer::traceThread()
292 {
293 //## Remember. NEVER leave this method without setting
294 //## engine back to NULL
296 //## Prepare our kill flag. We will watch this later to
297 //## see if the main thread wants us to stop
298 keepGoing = true;
300 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
301 if (!desktop)
302 {
303 g_warning("Trace: No active desktop\n");
304 return;
305 }
307 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
309 if (!SP_ACTIVE_DOCUMENT)
310 {
311 char *msg = _("Trace: No active document");
312 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
313 //g_warning(msg);
314 engine = NULL;
315 return;
316 }
317 SPDocument *doc = SP_ACTIVE_DOCUMENT;
318 sp_document_ensure_up_to_date(doc);
321 SPImage *img = getSelectedSPImage();
322 if (!img)
323 {
324 engine = NULL;
325 return;
326 }
328 GdkPixbuf *pixbuf = img->pixbuf;
330 if (!pixbuf)
331 {
332 char *msg = _("Trace: Image has no bitmap data");
333 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
334 //g_warning(msg);
335 engine = NULL;
336 return;
337 }
339 //## SIOX pre-processing to get a smart subimage of the pixbuf.
340 //## This is done before any other filters
341 if (sioxEnabled)
342 {
343 /*
344 Ok, we have requested siox, and getSelectedSPImage() has found a single
345 bitmap and one or more SPItems above it. Now what we need to do is create
346 a siox-segmented subimage pixbuf. We not need alter 'img' at all, since this
347 pixbuf will be the same dimensions and at the same location.
348 Remember to free this new pixbuf later.
349 */
350 pixbuf = sioxProcessImage(img, pixbuf);
351 }
353 int nrPaths;
354 TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
355 //printf("nrPaths:%d\n", nrPaths);
357 //### Check if we should stop
358 if (!keepGoing || !results || nrPaths<1)
359 {
360 engine = NULL;
361 return;
362 }
364 //### Get pointers to the <image> and its parent
365 Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
366 Inkscape::XML::Node *par = sp_repr_parent(imgRepr);
368 //### Get some information for the new transform()
369 double x = 0.0;
370 double y = 0.0;
371 double width = 0.0;
372 double height = 0.0;
373 double dval = 0.0;
375 if (sp_repr_get_double(imgRepr, "x", &dval))
376 x = dval;
377 if (sp_repr_get_double(imgRepr, "y", &dval))
378 y = dval;
380 if (sp_repr_get_double(imgRepr, "width", &dval))
381 width = dval;
382 if (sp_repr_get_double(imgRepr, "height", &dval))
383 height = dval;
385 NR::Matrix trans(NR::translate(x, y));
387 double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
388 double iheight = (double)gdk_pixbuf_get_height(pixbuf);
390 double iwscale = width / iwidth;
391 double ihscale = height / iheight;
393 NR::Matrix scal(NR::scale(iwscale, ihscale));
395 //# Convolve scale, translation, and the original transform
396 NR::Matrix tf(scal);
397 tf *= trans;
398 tf *= img->transform;
401 //#OK. Now let's start making new nodes
403 Inkscape::XML::Node *groupRepr = NULL;
405 //# if more than 1, make a <g>roup of <path>s
406 if (nrPaths > 1)
407 {
408 groupRepr = sp_repr_new("svg:g");
409 par->addChild(groupRepr, imgRepr);
410 }
412 long totalNodeCount = 0L;
414 for (TracingEngineResult *result=results ;
415 result ; result=result->next)
416 {
417 totalNodeCount += result->getNodeCount();
419 Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
420 pathRepr->setAttribute("style", result->getStyle());
421 pathRepr->setAttribute("d", result->getPathData());
423 if (nrPaths > 1)
424 groupRepr->addChild(pathRepr, NULL);
425 else
426 par->addChild(pathRepr, imgRepr);
428 //### Apply the transform from the image to the new shape
429 SPObject *reprobj = doc->getObjectByRepr(pathRepr);
430 if (reprobj)
431 {
432 SPItem *newItem = SP_ITEM(reprobj);
433 sp_item_write_transform(newItem, pathRepr, tf, NULL);
434 }
435 if (nrPaths == 1)
436 {
437 selection->clear();
438 selection->add(pathRepr);
439 }
440 Inkscape::GC::release(pathRepr);
441 }
443 //did we allocate a pixbuf copy?
444 if (sioxEnabled)
445 {
446 g_free(pixbuf);
447 }
449 delete results;
451 // If we have a group, then focus on, then forget it
452 if (nrPaths > 1)
453 {
454 selection->clear();
455 selection->add(groupRepr);
456 Inkscape::GC::release(groupRepr);
457 }
459 //## inform the document, so we can undo
460 sp_document_done(doc);
462 engine = NULL;
464 char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
465 SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
466 g_free(msg);
468 }
474 /**
475 * Main tracing method
476 */
477 void Tracer::trace(TracingEngine *theEngine)
478 {
479 //Check if we are already running
480 if (engine)
481 return;
483 engine = theEngine;
485 #if HAVE_THREADS
486 //Ensure that thread support is running
487 if (!Glib::thread_supported())
488 Glib::thread_init();
490 //Create our thread and run it
491 Glib::Thread::create(
492 sigc::mem_fun(*this, &Tracer::traceThread), false);
493 #else
494 traceThread();
495 #endif
497 }
503 /**
504 * Abort the thread that is executing trace()
505 */
506 void Tracer::abort()
507 {
509 //## Inform Trace's working thread
510 keepGoing = false;
512 if (engine)
513 {
514 engine->abort();
515 }
517 }
521 } // namespace Trace
523 } // namespace Inkscape
526 //#########################################################################
527 //# E N D O F F I L E
528 //#########################################################################