Code

2e46dd854fba8789f826d11a197e4b1635796b5d
[inkscape.git] / src / trace / trace.cpp
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         }
141 /**
142  *
143  */
144 GdkPixbuf *
145 Tracer::getSelectedImage()
148     SPImage *img = getSelectedSPImage();
149     if (!img)
150         return NULL;
152     GdkPixbuf *pixbuf = img->pixbuf;
154     return pixbuf;
159 GdkPixbuf *
160 Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
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;
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)
284     sioxEnabled = enable;
288 /**
289  *  Threaded method that does single bitmap--->path conversion
290  */
291 void Tracer::traceThread()
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);
474 /**
475  *  Main tracing method
476  */
477 void Tracer::trace(TracingEngine *theEngine)
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
503 /**
504  *  Abort the thread that is executing trace()
505  */
506 void Tracer::abort()
509     //## Inform Trace's working thread
510     keepGoing = false;
512     if (engine)
513         {
514         engine->abort();
515         }
521 } // namespace Trace
523 } // namespace Inkscape
526 //#########################################################################
527 //# E N D   O F   F I L E
528 //#########################################################################