Code

Updated cases for attributes added in <color-profile> support
[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     Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
198     NRArenaItem *aImg = sp_item_get_arenaitem(img, desktop->dkey);
199     //g_message("img: %d %d %d %d\n", aImg->bbox.x0, aImg->bbox.y0,
200     //                                aImg->bbox.x1, aImg->bbox.y1);
202     double width  = (double)(aImg->bbox.x1 - aImg->bbox.x0);
203     double height = (double)(aImg->bbox.y1 - aImg->bbox.y0);
205     double iwidth  = (double)ppMap->width;
206     double iheight = (double)ppMap->height;
208     double iwscale = width  / iwidth;
209     double ihscale = height / iheight;
211     unsigned long cmIndex = 0;
214     std::vector<NRArenaItem *> arenaItems;
215     std::vector<SPShape *>::iterator iter;
216     for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++)
217         {
218         SPItem *item = *iter;
219         //### Create ArenaItems and set transform
220         NRArenaItem *aItem = sp_item_get_arenaitem(item, desktop->dkey);
222         //nr_arena_item_set_transform(aItem, item->transform);
223         //g_message("%d %d %d %d\n", aItem->bbox.x0, aItem->bbox.y0,
224         //                           aItem->bbox.x1, aItem->bbox.y1);
225         arenaItems.push_back(aItem);
226         }
227     //g_message("%d arena items\n", arenaItems.size());
229     PackedPixelMap *dumpMap = PackedPixelMapCreate(ppMap->width, ppMap->height);
231     for (int row=0 ; row<ppMap->height ; row++)
232         {
233         double ypos = ((double)aImg->bbox.y0) + ihscale * (double) row;
234         for (int col=0 ; col<ppMap->width ; col++)
235             {
236             //Get absolute X,Y position
237             double xpos = ((double)aImg->bbox.x0) + iwscale * (double)col;
238             NR::Point point(xpos, ypos);
239             point *= aImg->transform;
240             //point *= imgMat;
241             //point = desktop->doc2dt(point);
242             std::vector<SPShape *>::iterator iter;
243             //g_message("x:%f    y:%f\n", point[0], point[1]);
244             bool weHaveAHit = false;
245             std::vector<NRArenaItem *>::iterator aIter;
246             for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
247                 {
248                 NRArenaItem *arenaItem = *aIter;
249                 NRArenaItemClass *arenaClass =
250                     (NRArenaItemClass *) NR_OBJECT_GET_CLASS (arenaItem);
251                 if (arenaClass->pick(arenaItem, point, 1.0f, 1))
252                     {
253                     weHaveAHit = true;
254                     break;
255                     }
256                 }
258             if (weHaveAHit)
259                 {
260                 //g_message("hit!\n");
261                 dumpMap->setPixelLong(dumpMap, col, row, 0L);
262                 confidenceMatrix[cmIndex] =
263                         org::siox::SioxSegmentator::CERTAIN_FOREGROUND_CONFIDENCE;
264                 }
265             else
266                 {
267                 dumpMap->setPixelLong(dumpMap, col, row,
268                         ppMap->getPixel(ppMap, col, row));
269                 confidenceMatrix[cmIndex] =
270                         org::siox::SioxSegmentator::CERTAIN_BACKGROUND_CONFIDENCE;
271                 }
272             cmIndex++;
273             }
274         }
276     //## ok we have our pixel buf
277     org::siox::SioxSegmentator ss(ppMap->width, ppMap->height, NULL, 0);
278     ss.segmentate(imgBuf, confidenceMatrix, 0, 0.0);
280     dumpMap->writePPM(dumpMap, "siox.ppm");
281     dumpMap->destroy(dumpMap);
283     /* Free Arena and ArenaItem */
284     /*
285     std::vector<NRArenaItem *>::iterator aIter;
286     for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
287        {
288        NRArenaItem *arenaItem = *aIter;
289        nr_arena_item_unref(arenaItem);
290        }
291     nr_object_unref((NRObject *) arena);
292     */
294     GdkPixbuf *newPixbuf = packedPixelMapToGdkPixbuf(ppMap);
295     ppMap->destroy(ppMap);
296     delete confidenceMatrix;
298     return newPixbuf;
304 //#########################################################################
305 //#  T R A C E
306 //#########################################################################
308 /**
309  *  Whether we want to enable SIOX subimage selection
310  */
311 void Tracer::enableSiox(bool enable)
313     sioxEnabled = enable;
317 /**
318  *  Threaded method that does single bitmap--->path conversion
319  */
320 void Tracer::traceThread()
322     //## Remember. NEVER leave this method without setting
323     //## engine back to NULL
325     //## Prepare our kill flag.  We will watch this later to
326     //## see if the main thread wants us to stop
327     keepGoing = true;
329     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
330     if (!desktop)
331         {
332         g_warning("Trace: No active desktop\n");
333         return;
334         }
336     Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
338     if (!SP_ACTIVE_DOCUMENT)
339         {
340         char *msg = _("Trace: No active document");
341         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
342         //g_warning(msg);
343         engine = NULL;
344         return;
345         }
346     SPDocument *doc = SP_ACTIVE_DOCUMENT;
347     sp_document_ensure_up_to_date(doc);
350     SPImage *img = getSelectedSPImage();
351     if (!img)
352         {
353         engine = NULL;
354         return;
355         }
357     GdkPixbuf *pixbuf = img->pixbuf;
359     if (!pixbuf)
360         {
361         char *msg = _("Trace: Image has no bitmap data");
362         SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
363         //g_warning(msg);
364         engine = NULL;
365         return;
366         }
368     //## SIOX pre-processing to get a smart subimage of the pixbuf.
369     //## This is done before any other filters
370     if (sioxEnabled)
371         {
372         /*
373            Ok, we have requested siox, and getSelectedSPImage() has found a single
374            bitmap and one or more SPItems above it.  Now what we need to do is create
375            a siox-segmented subimage pixbuf.  We not need alter 'img' at all, since this
376            pixbuf will be the same dimensions and at the same location.
377            Remember to free this new pixbuf later.
378         */
379         pixbuf = sioxProcessImage(img, pixbuf);
380         }
382     int nrPaths;
383     TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
384     //printf("nrPaths:%d\n", nrPaths);
386     //### Check if we should stop
387     if (!keepGoing || !results || nrPaths<1)
388         {
389         engine = NULL;
390         return;
391         }
393     //### Get pointers to the <image> and its parent
394     Inkscape::XML::Node *imgRepr   = SP_OBJECT(img)->repr;
395     Inkscape::XML::Node *par       = sp_repr_parent(imgRepr);
397     //### Get some information for the new transform()
398     double x      = 0.0;
399     double y      = 0.0;
400     double width  = 0.0;
401     double height = 0.0;
402     double dval   = 0.0;
404     if (sp_repr_get_double(imgRepr, "x", &dval))
405         x = dval;
406     if (sp_repr_get_double(imgRepr, "y", &dval))
407         y = dval;
409     if (sp_repr_get_double(imgRepr, "width", &dval))
410         width = dval;
411     if (sp_repr_get_double(imgRepr, "height", &dval))
412         height = dval;
414     NR::Matrix trans(NR::translate(x, y));
416     double iwidth  = (double)gdk_pixbuf_get_width(pixbuf);
417     double iheight = (double)gdk_pixbuf_get_height(pixbuf);
419     double iwscale = width  / iwidth;
420     double ihscale = height / iheight;
422     NR::Matrix scal(NR::scale(iwscale, ihscale));
424     //# Convolve scale, translation, and the original transform
425     NR::Matrix tf(scal);
426     tf *= trans;
427     tf *= img->transform;
430     //#OK.  Now let's start making new nodes
432     Inkscape::XML::Node *groupRepr = NULL;
434     //# if more than 1, make a <g>roup of <path>s
435     if (nrPaths > 1)
436         {
437         groupRepr = sp_repr_new("svg:g");
438         par->addChild(groupRepr, imgRepr);
439         }
441     long totalNodeCount = 0L;
443     for (TracingEngineResult *result=results ;
444                   result ; result=result->next)
445         {
446         totalNodeCount += result->getNodeCount();
448         Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
449         pathRepr->setAttribute("style", result->getStyle());
450         pathRepr->setAttribute("d",     result->getPathData());
452         if (nrPaths > 1)
453             groupRepr->addChild(pathRepr, NULL);
454         else
455             par->addChild(pathRepr, imgRepr);
457         //### Apply the transform from the image to the new shape
458         SPObject *reprobj = doc->getObjectByRepr(pathRepr);
459         if (reprobj)
460             {
461             SPItem *newItem = SP_ITEM(reprobj);
462             sp_item_write_transform(newItem, pathRepr, tf, NULL);
463             }
464         if (nrPaths == 1)
465             {
466             selection->clear();
467             selection->add(pathRepr);
468             }
469         Inkscape::GC::release(pathRepr);
470         }
472     //did we allocate a pixbuf copy?
473     if (sioxEnabled)
474         {
475         g_free(pixbuf);
476         }
478     delete results;
480     // If we have a group, then focus on, then forget it
481     if (nrPaths > 1)
482         {
483         selection->clear();
484         selection->add(groupRepr);
485         Inkscape::GC::release(groupRepr);
486         }
488     //## inform the document, so we can undo
489     sp_document_done(doc);
491     engine = NULL;
493     char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
494     SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
495     g_free(msg);
503 /**
504  *  Main tracing method
505  */
506 void Tracer::trace(TracingEngine *theEngine)
508     //Check if we are already running
509     if (engine)
510         return;
512     engine = theEngine;
514 #if HAVE_THREADS
515     //Ensure that thread support is running
516     if (!Glib::thread_supported())
517         Glib::thread_init();
519     //Create our thread and run it
520     Glib::Thread::create(
521         sigc::mem_fun(*this, &Tracer::traceThread), false);
522 #else
523     traceThread();
524 #endif
532 /**
533  *  Abort the thread that is executing trace()
534  */
535 void Tracer::abort()
538     //## Inform Trace's working thread
539     keepGoing = false;
541     if (engine)
542         {
543         engine->abort();
544         }
550 } // namespace Trace
552 } // namespace Inkscape
555 //#########################################################################
556 //# E N D   O F   F I L E
557 //#########################################################################