Code

remove superfluous line, compile warning
[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>
30 #include <display/nr-arena.h>
31 #include <display/nr-arena-shape.h>
33 #include "siox.h"
34 #include "imagemap-gdk.h"
36 namespace Inkscape {
38 namespace Trace {
45 /**
46  *
47  */
48 SPImage *
49 Tracer::getSelectedSPImage()
50 {
51     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
52     if (!desktop)
53         {
54         g_warning("Trace: No active desktop\n");
55         return NULL;
56         }
58     Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
59     if (!sel)
60         {
61         char *msg = _("Select an <b>image</b> to trace");
62         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
63         //g_warning(msg);
64         return NULL;
65         }
67     if (sioxEnabled)
68         {
69         SPImage *img = NULL;
70         GSList const *list = sel->itemList();
71         std::vector<SPItem *> items;
72         sioxShapes.clear();
74         /*
75            First, things are selected top-to-bottom, so we need to invert
76            them as bottom-to-top so that we can discover the image and any
77            SPItems above it
78         */
79         for ( ; list ; list=list->next)
80             {
81             if (!SP_IS_ITEM(list->data))
82                 {
83                 continue;
84                 }
85             SPItem *item = SP_ITEM(list->data);
86             items.insert(items.begin(), item);
87             }
88         std::vector<SPItem *>::iterator iter;
89         for (iter = items.begin() ; iter!= items.end() ; iter++)
90             {
91             SPItem *item = *iter;
92             if (SP_IS_IMAGE(item))
93                 {
94                 if (img) //we want only one
95                     {
96                     char *msg = _("Select only one <b>image</b> to trace");
97                     SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
98                     return NULL;
99                     }
100                 img = SP_IMAGE(item);
101                 }
102             else // if (img) //# items -after- the image in tree (above it in Z)
103                 {
104                 if (SP_IS_SHAPE(item))
105                     {
106                     SPShape *shape = SP_SHAPE(item);
107                     sioxShapes.push_back(shape);
108                     }
109                 }
110             }
112         if (!img || sioxShapes.size() < 1)
113             {
114             char *msg = _("Select one image and one or more shapes above it");
115             SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
116             return NULL;
117             }
118         return img;
119         }
120     else
121         //### SIOX not enabled.  We want exactly one image selected
122         {
123         SPItem *item = sel->singleItem();
124         if (!item)
125             {
126             char *msg = _("Select an <b>image</b> to trace");  //same as above
127             SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
128             //g_warning(msg);
129             return NULL;
130             }
132         if (!SP_IS_IMAGE(item))
133             {
134             char *msg = _("Select an <b>image</b> to trace");
135             SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
136             //g_warning(msg);
137             return NULL;
138             }
140         SPImage *img = SP_IMAGE(item);
142         return img;
143         }
149 /**
150  *
151  */
152 GdkPixbuf *
153 Tracer::getSelectedImage()
156     SPImage *img = getSelectedSPImage();
157     if (!img)
158         return NULL;
160     GdkPixbuf *pixbuf = img->pixbuf;
162     return pixbuf;
167 GdkPixbuf *
168 Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
171     //Convert from gdk, so a format we know.  By design, the pixel
172     //format in PackedPixelMap is identical to what is needed by SIOX
173     PackedPixelMap *ppMap = gdkPixbufToPackedPixelMap(origPixbuf);
174     //We need to create two things:
175     //  1.  An array of long pixel values of ARGB
176     //  2.  A matching array of per-pixel float 'confidence' values
177     unsigned long *imgBuf = ppMap->pixels;
178     float *confidenceMatrix = new float[ppMap->width * ppMap->height];
180     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
181     if (!desktop)
182         {
183         g_warning("Trace: No active desktop\n");
184         return NULL;
185         }
187     Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
188     if (!sel)
189         {
190         char *msg = _("Select an <b>image</b> to trace");
191         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
192         //g_warning(msg);
193         return NULL;
194         }
196     NRArenaItem *aImg = sp_item_get_arenaitem(img, desktop->dkey);
197     //g_message("img: %d %d %d %d\n", aImg->bbox.x0, aImg->bbox.y0,
198     //                                aImg->bbox.x1, aImg->bbox.y1);
200     double width  = (double)(aImg->bbox.x1 - aImg->bbox.x0);
201     double height = (double)(aImg->bbox.y1 - aImg->bbox.y0);
203     double iwidth  = (double)ppMap->width;
204     double iheight = (double)ppMap->height;
206     double iwscale = width  / iwidth;
207     double ihscale = height / iheight;
209     unsigned long cmIndex = 0;
212     std::vector<NRArenaItem *> arenaItems;
213     std::vector<SPShape *>::iterator iter;
214     for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++)
215         {
216         SPItem *item = *iter;
217         //### Create ArenaItems and set transform
218         NRArenaItem *aItem = sp_item_get_arenaitem(item, desktop->dkey);
220         //nr_arena_item_set_transform(aItem, item->transform);
221         //g_message("%d %d %d %d\n", aItem->bbox.x0, aItem->bbox.y0,
222         //                           aItem->bbox.x1, aItem->bbox.y1);
223         arenaItems.push_back(aItem);
224         }
225     //g_message("%d arena items\n", arenaItems.size());
227     PackedPixelMap *dumpMap = PackedPixelMapCreate(ppMap->width, ppMap->height);
229     for (int row=0 ; row<ppMap->height ; row++)
230         {
231         double ypos = ((double)aImg->bbox.y0) + ihscale * (double) row;
232         for (int col=0 ; col<ppMap->width ; col++)
233             {
234             //Get absolute X,Y position
235             double xpos = ((double)aImg->bbox.x0) + iwscale * (double)col;
236             NR::Point point(xpos, ypos);
237             point *= aImg->transform;
238             //point *= imgMat;
239             //point = desktop->doc2dt(point);
240             std::vector<SPShape *>::iterator iter;
241             //g_message("x:%f    y:%f\n", point[0], point[1]);
242             bool weHaveAHit = false;
243             std::vector<NRArenaItem *>::iterator aIter;
244             for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
245                 {
246                 NRArenaItem *arenaItem = *aIter;
247                 NRArenaItemClass *arenaClass =
248                     (NRArenaItemClass *) NR_OBJECT_GET_CLASS (arenaItem);
249                 if (arenaClass->pick(arenaItem, point, 1.0f, 1))
250                     {
251                     weHaveAHit = true;
252                     break;
253                     }
254                 }
256             if (weHaveAHit)
257                 {
258                 //g_message("hit!\n");
259                 dumpMap->setPixelLong(dumpMap, col, row, 0L);
260                 confidenceMatrix[cmIndex] =
261                         org::siox::SioxSegmentator::CERTAIN_FOREGROUND_CONFIDENCE;
262                 }
263             else
264                 {
265                 dumpMap->setPixelLong(dumpMap, col, row,
266                         ppMap->getPixel(ppMap, col, row));
267                 confidenceMatrix[cmIndex] =
268                         org::siox::SioxSegmentator::CERTAIN_BACKGROUND_CONFIDENCE;
269                 }
270             cmIndex++;
271             }
272         }
274     //## ok we have our pixel buf
275     org::siox::SioxSegmentator ss(ppMap->width, ppMap->height, NULL, 0);
276     ss.segmentate(imgBuf, confidenceMatrix, 0, 0.0);
278     dumpMap->writePPM(dumpMap, "siox.ppm");
279     dumpMap->destroy(dumpMap);
281     /* Free Arena and ArenaItem */
282     /*
283     std::vector<NRArenaItem *>::iterator aIter;
284     for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
285        {
286        NRArenaItem *arenaItem = *aIter;
287        nr_arena_item_unref(arenaItem);
288        }
289     nr_object_unref((NRObject *) arena);
290     */
292     GdkPixbuf *newPixbuf = packedPixelMapToGdkPixbuf(ppMap);
293     ppMap->destroy(ppMap);
294     delete confidenceMatrix;
296     return newPixbuf;
302 //#########################################################################
303 //#  T R A C E
304 //#########################################################################
306 /**
307  *  Whether we want to enable SIOX subimage selection
308  */
309 void Tracer::enableSiox(bool enable)
311     sioxEnabled = enable;
315 /**
316  *  Threaded method that does single bitmap--->path conversion
317  */
318 void Tracer::traceThread()
320     //## Remember. NEVER leave this method without setting
321     //## engine back to NULL
323     //## Prepare our kill flag.  We will watch this later to
324     //## see if the main thread wants us to stop
325     keepGoing = true;
327     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
328     if (!desktop)
329         {
330         g_warning("Trace: No active desktop\n");
331         return;
332         }
334     Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
336     if (!SP_ACTIVE_DOCUMENT)
337         {
338         char *msg = _("Trace: No active document");
339         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
340         //g_warning(msg);
341         engine = NULL;
342         return;
343         }
344     SPDocument *doc = SP_ACTIVE_DOCUMENT;
345     sp_document_ensure_up_to_date(doc);
348     SPImage *img = getSelectedSPImage();
349     if (!img)
350         {
351         engine = NULL;
352         return;
353         }
355     GdkPixbuf *pixbuf = img->pixbuf;
357     if (!pixbuf)
358         {
359         char *msg = _("Trace: Image has no bitmap data");
360         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
361         //g_warning(msg);
362         engine = NULL;
363         return;
364         }
366     //## SIOX pre-processing to get a smart subimage of the pixbuf.
367     //## This is done before any other filters
368     if (sioxEnabled)
369         {
370         /*
371            Ok, we have requested siox, and getSelectedSPImage() has found a single
372            bitmap and one or more SPItems above it.  Now what we need to do is create
373            a siox-segmented subimage pixbuf.  We not need alter 'img' at all, since this
374            pixbuf will be the same dimensions and at the same location.
375            Remember to free this new pixbuf later.
376         */
377         pixbuf = sioxProcessImage(img, pixbuf);
378         }
380     int nrPaths;
381     TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
382     //printf("nrPaths:%d\n", nrPaths);
384     //### Check if we should stop
385     if (!keepGoing || !results || nrPaths<1)
386         {
387         engine = NULL;
388         return;
389         }
391     //### Get pointers to the <image> and its parent
392     Inkscape::XML::Node *imgRepr   = SP_OBJECT(img)->repr;
393     Inkscape::XML::Node *par       = sp_repr_parent(imgRepr);
395     //### Get some information for the new transform()
396     double x      = 0.0;
397     double y      = 0.0;
398     double width  = 0.0;
399     double height = 0.0;
400     double dval   = 0.0;
402     if (sp_repr_get_double(imgRepr, "x", &dval))
403         x = dval;
404     if (sp_repr_get_double(imgRepr, "y", &dval))
405         y = dval;
407     if (sp_repr_get_double(imgRepr, "width", &dval))
408         width = dval;
409     if (sp_repr_get_double(imgRepr, "height", &dval))
410         height = dval;
412     NR::Matrix trans(NR::translate(x, y));
414     double iwidth  = (double)gdk_pixbuf_get_width(pixbuf);
415     double iheight = (double)gdk_pixbuf_get_height(pixbuf);
417     double iwscale = width  / iwidth;
418     double ihscale = height / iheight;
420     NR::Matrix scal(NR::scale(iwscale, ihscale));
422     //# Convolve scale, translation, and the original transform
423     NR::Matrix tf(scal);
424     tf *= trans;
425     tf *= img->transform;
428     //#OK.  Now let's start making new nodes
430     Inkscape::XML::Node *groupRepr = NULL;
432     //# if more than 1, make a <g>roup of <path>s
433     if (nrPaths > 1)
434         {
435         groupRepr = sp_repr_new("svg:g");
436         par->addChild(groupRepr, imgRepr);
437         }
439     long totalNodeCount = 0L;
441     for (TracingEngineResult *result=results ;
442                   result ; result=result->next)
443         {
444         totalNodeCount += result->getNodeCount();
446         Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
447         pathRepr->setAttribute("style", result->getStyle());
448         pathRepr->setAttribute("d",     result->getPathData());
450         if (nrPaths > 1)
451             groupRepr->addChild(pathRepr, NULL);
452         else
453             par->addChild(pathRepr, imgRepr);
455         //### Apply the transform from the image to the new shape
456         SPObject *reprobj = doc->getObjectByRepr(pathRepr);
457         if (reprobj)
458             {
459             SPItem *newItem = SP_ITEM(reprobj);
460             sp_item_write_transform(newItem, pathRepr, tf, NULL);
461             }
462         if (nrPaths == 1)
463             {
464             selection->clear();
465             selection->add(pathRepr);
466             }
467         Inkscape::GC::release(pathRepr);
468         }
470     //did we allocate a pixbuf copy?
471     if (sioxEnabled)
472         {
473         g_free(pixbuf);
474         }
476     delete results;
478     // If we have a group, then focus on, then forget it
479     if (nrPaths > 1)
480         {
481         selection->clear();
482         selection->add(groupRepr);
483         Inkscape::GC::release(groupRepr);
484         }
486     //## inform the document, so we can undo
487     sp_document_done(doc);
489     engine = NULL;
491     char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
492     SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
493     g_free(msg);
501 /**
502  *  Main tracing method
503  */
504 void Tracer::trace(TracingEngine *theEngine)
506     //Check if we are already running
507     if (engine)
508         return;
510     engine = theEngine;
512 #if HAVE_THREADS
513     //Ensure that thread support is running
514     if (!Glib::thread_supported())
515         Glib::thread_init();
517     //Create our thread and run it
518     Glib::Thread::create(
519         sigc::mem_fun(*this, &Tracer::traceThread), false);
520 #else
521     traceThread();
522 #endif
530 /**
531  *  Abort the thread that is executing trace()
532  */
533 void Tracer::abort()
536     //## Inform Trace's working thread
537     keepGoing = false;
539     if (engine)
540         {
541         engine->abort();
542         }
548 } // namespace Trace
550 } // namespace Inkscape
553 //#########################################################################
554 //# E N D   O F   F I L E
555 //#########################################################################