Code

Add PackedPixelMap to make pixbuf->pixel array conversion much cleaner. Also make...
[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-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         }
132 /**
133  *
134  */
135 GdkPixbuf *
136 Tracer::getSelectedImage()
139     SPImage *img = getSelectedSPImage();
140     if (!img)
141         return NULL;
143     GdkPixbuf *pixbuf = img->pixbuf;
145     return pixbuf;
150 GdkPixbuf *
151 Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
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;
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)
189     sioxEnabled = enable;
193 /**
194  *  Threaded method that does single bitmap--->path conversion
195  */
196 void Tracer::traceThread()
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);
379 /**
380  *  Main tracing method
381  */
382 void Tracer::trace(TracingEngine *theEngine)
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
408 /**
409  *  Abort the thread that is executing trace()
410  */
411 void Tracer::abort()
414     //## Inform Trace's working thread
415     keepGoing = false;
417     if (engine)
418         {
419         engine->abort();
420         }
426 } // namespace Trace
428 } // namespace Inkscape
431 //#########################################################################
432 //# E N D   O F   F I L E
433 //#########################################################################