Code

Pot and Dutch translation update
[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 <gtkmm.h>
23 #include <glibmm/i18n.h>
24 #include <selection.h>
25 #include <xml/repr.h>
26 #include <xml/attribute-record.h>
27 #include <sp-item.h>
28 #include <sp-shape.h>
29 #include <sp-image.h>
30 #include <libnr/nr-matrix-ops.h>
31 #include <2geom/transforms.h>
33 #include <display/nr-arena.h>
34 #include <display/nr-arena-shape.h>
36 #include "siox.h"
37 #include "imagemap-gdk.h"
41 namespace Inkscape
42 {
44 namespace Trace
45 {
51 /**
52  * Get the selected image.  Also check for any SPItems over it, in
53  * case the user wants SIOX pre-processing.
54  */
55 SPImage *
56 Tracer::getSelectedSPImage()
57 {
59     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
60     if (!desktop)
61         {
62         g_warning("Trace: No active desktop");
63         return NULL;
64         }
66     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
68     Inkscape::Selection *sel = sp_desktop_selection(desktop);
69     if (!sel)
70         {
71         char *msg = _("Select an <b>image</b> to trace");
72         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
73         //g_warning(msg);
74         return NULL;
75         }
77     if (sioxEnabled)
78         {
79         SPImage *img = NULL;
80         GSList const *list = sel->itemList();
81         std::vector<SPItem *> items;
82         sioxShapes.clear();
84         /*
85            First, things are selected top-to-bottom, so we need to invert
86            them as bottom-to-top so that we can discover the image and any
87            SPItems above it
88         */
89         for ( ; list ; list=list->next)
90             {
91             if (!SP_IS_ITEM(list->data))
92                 {
93                 continue;
94                 }
95             SPItem *item = SP_ITEM(list->data);
96             items.insert(items.begin(), item);
97             }
98         std::vector<SPItem *>::iterator iter;
99         for (iter = items.begin() ; iter!= items.end() ; iter++)
100             {
101             SPItem *item = *iter;
102             if (SP_IS_IMAGE(item))
103                 {
104                 if (img) //we want only one
105                     {
106                     char *msg = _("Select only one <b>image</b> to trace");
107                     msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
108                     return NULL;
109                     }
110                 img = SP_IMAGE(item);
111                 }
112             else // if (img) //# items -after- the image in tree (above it in Z)
113                 {
114                 if (SP_IS_SHAPE(item))
115                     {
116                     SPShape *shape = SP_SHAPE(item);
117                     sioxShapes.push_back(shape);
118                     }
119                 }
120             }
122         if (!img || sioxShapes.size() < 1)
123             {
124             char *msg = _("Select one image and one or more shapes above it");
125             msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
126             return NULL;
127             }
128         return img;
129         }
130     else
131         //### SIOX not enabled.  We want exactly one image selected
132         {
133         SPItem *item = sel->singleItem();
134         if (!item)
135             {
136             char *msg = _("Select an <b>image</b> to trace");  //same as above
137             msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
138             //g_warning(msg);
139             return NULL;
140             }
142         if (!SP_IS_IMAGE(item))
143             {
144             char *msg = _("Select an <b>image</b> to trace");
145             msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
146             //g_warning(msg);
147             return NULL;
148             }
150         SPImage *img = SP_IMAGE(item);
152         return img;
153         }
159 typedef org::siox::SioxImage SioxImage;
160 typedef org::siox::SioxObserver SioxObserver;
161 typedef org::siox::Siox Siox;
164 class TraceSioxObserver : public SioxObserver
166 public:
168     /**
169      *
170      */
171     TraceSioxObserver (void *contextArg) :
172                      SioxObserver(contextArg)
173         {}
175     /**
176      *
177      */
178     virtual ~TraceSioxObserver ()
179         { }
181     /**
182      *  Informs the observer how much has been completed.
183      *  Return false if the processing should be aborted.
184      */
185     virtual bool progress(float /*percentCompleted*/)
186         {
187         //Tracer *tracer = (Tracer *)context;
188         //## Allow the GUI to update
189         Gtk::Main::iteration(false); //at least once, non-blocking
190         while( Gtk::Main::events_pending() )
191             Gtk::Main::iteration();
192         return true;
193         }
195     /**
196      *  Send an error string to the Observer.  Processing will
197      *  be halted.
198      */
199     virtual void error(const std::string &/*msg*/)
200         {
201         //Tracer *tracer = (Tracer *)context;
202         }
205 };
211 /**
212  * Process a GdkPixbuf, according to which areas have been
213  * obscured in the GUI.
214  */
215 Glib::RefPtr<Gdk::Pixbuf>
216 Tracer::sioxProcessImage(SPImage *img,
217              Glib::RefPtr<Gdk::Pixbuf>origPixbuf)
219     if (!sioxEnabled)
220         return origPixbuf;
222     if (origPixbuf == lastOrigPixbuf)
223         return lastSioxPixbuf;
225     //g_message("siox: start");
227     //Convert from gdk, so a format we know.  By design, the pixel
228     //format in PackedPixelMap is identical to what is needed by SIOX
229     SioxImage simage(origPixbuf->gobj());
231     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
232     if (!desktop)
233         {
234         g_warning(_("Trace: No active desktop"));
235         return Glib::RefPtr<Gdk::Pixbuf>(NULL);
236         }
238     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
240     Inkscape::Selection *sel = sp_desktop_selection(desktop);
241     if (!sel)
242         {
243         char *msg = _("Select an <b>image</b> to trace");
244         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
245         //g_warning(msg);
246         return Glib::RefPtr<Gdk::Pixbuf>(NULL);
247         }
249     NRArenaItem *aImg = sp_item_get_arenaitem(img, desktop->dkey);
250     //g_message("img: %d %d %d %d\n", aImg->bbox.x0, aImg->bbox.y0,
251     //                                aImg->bbox.x1, aImg->bbox.y1);
253     double width  = (double)(aImg->bbox.x1 - aImg->bbox.x0);
254     double height = (double)(aImg->bbox.y1 - aImg->bbox.y0);
256     double iwidth  = (double)simage.getWidth();
257     double iheight = (double)simage.getHeight();
259     double iwscale = width  / iwidth;
260     double ihscale = height / iheight;
262     std::vector<NRArenaItem *> arenaItems;
263     std::vector<SPShape *>::iterator iter;
264     for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++)
265         {
266         SPItem *item = *iter;
267         NRArenaItem *aItem = sp_item_get_arenaitem(item, desktop->dkey);
268         arenaItems.push_back(aItem);
269         }
271     //g_message("%d arena items\n", arenaItems.size());
273     //PackedPixelMap *dumpMap = PackedPixelMapCreate(
274     //                simage.getWidth(), simage.getHeight());
276     //g_message("siox: start selection");
278     for (int row=0 ; row<iheight ; row++)
279         {
280         double ypos = ((double)aImg->bbox.y0) + ihscale * (double) row;
281         for (int col=0 ; col<simage.getWidth() ; col++)
282             {
283             //Get absolute X,Y position
284             double xpos = ((double)aImg->bbox.x0) + iwscale * (double)col;
285             Geom::Point point(xpos, ypos);
286             if (aImg->transform)
287                 point *= *aImg->transform;
288             //point *= imgMat;
289             //point = desktop->doc2dt(point);
290             //g_message("x:%f    y:%f\n", point[0], point[1]);
291             bool weHaveAHit = false;
292             std::vector<NRArenaItem *>::iterator aIter;
293             for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
294                 {
295                 NRArenaItem *arenaItem = *aIter;
296                 NRArenaItemClass *arenaClass =
297                     (NRArenaItemClass *) NR_OBJECT_GET_CLASS (arenaItem);
298                 if (arenaClass->pick(arenaItem, point, 1.0f, 1))
299                     {
300                     weHaveAHit = true;
301                     break;
302                     }
303                 }
305             if (weHaveAHit)
306                 {
307                 //g_message("hit!\n");
308                 //dumpMap->setPixelLong(dumpMap, col, row, 0L);
309                 simage.setConfidence(col, row,
310                         Siox::UNKNOWN_REGION_CONFIDENCE);
311                 }
312             else
313                 {
314                 //g_message("miss!\n");
315                 //dumpMap->setPixelLong(dumpMap, col, row,
316                 //        simage.getPixel(col, row));
317                 simage.setConfidence(col, row,
318                         Siox::CERTAIN_BACKGROUND_CONFIDENCE);
319                 }
320             }
321         }
323     //g_message("siox: selection done");
325     //dumpMap->writePPM(dumpMap, "siox1.ppm");
326     //dumpMap->destroy(dumpMap);
328     //## ok we have our pixel buf
329     TraceSioxObserver observer(this);
330     Siox sengine(&observer);
331     SioxImage result = sengine.extractForeground(simage, 0xffffff);
332     if (!result.isValid())
333         {
334         g_warning(_("Invalid SIOX result"));
335         return Glib::RefPtr<Gdk::Pixbuf>(NULL);
336         }
338     //result.writePPM("siox2.ppm");
340     /* Free Arena and ArenaItem */
341     /*
342     std::vector<NRArenaItem *>::iterator aIter;
343     for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
344        {
345        NRArenaItem *arenaItem = *aIter;
346        nr_arena_item_unref(arenaItem);
347        }
348     nr_object_unref((NRObject *) arena);
349     */
351     Glib::RefPtr<Gdk::Pixbuf> newPixbuf = Glib::wrap(result.getGdkPixbuf());
353     //g_message("siox: done");
355     lastSioxPixbuf = newPixbuf;
357     return newPixbuf;
361 /**
362  *
363  */
364 Glib::RefPtr<Gdk::Pixbuf>
365 Tracer::getSelectedImage()
369     SPImage *img = getSelectedSPImage();
370     if (!img)
371         return Glib::RefPtr<Gdk::Pixbuf>(NULL);
373     if (!img->pixbuf)
374         return Glib::RefPtr<Gdk::Pixbuf>(NULL);
376     Glib::RefPtr<Gdk::Pixbuf> pixbuf =
377           Glib::wrap(img->pixbuf, true);
379     if (sioxEnabled)
380         {
381         Glib::RefPtr<Gdk::Pixbuf> sioxPixbuf =
382              sioxProcessImage(img, pixbuf);
383         if (!sioxPixbuf)
384             {
385             return pixbuf;
386             }
387         else
388             {
389             return sioxPixbuf;
390             }
391         }
392     else
393         {
394         return pixbuf;
395         }
401 //#########################################################################
402 //#  T R A C E
403 //#########################################################################
405 /**
406  *  Whether we want to enable SIOX subimage selection
407  */
408 void Tracer::enableSiox(bool enable)
410     sioxEnabled = enable;
414 /**
415  *  Threaded method that does single bitmap--->path conversion
416  */
417 void Tracer::traceThread()
419     //## Remember. NEVER leave this method without setting
420     //## engine back to NULL
422     //## Prepare our kill flag.  We will watch this later to
423     //## see if the main thread wants us to stop
424     keepGoing = true;
426     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
427     if (!desktop)
428         {
429         g_warning("Trace: No active desktop\n");
430         return;
431         }
433     Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
435     Inkscape::Selection *selection = sp_desktop_selection (desktop);
437     if (!SP_ACTIVE_DOCUMENT)
438         {
439         char *msg = _("Trace: No active document");
440         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
441         //g_warning(msg);
442         engine = NULL;
443         return;
444         }
445     SPDocument *doc = SP_ACTIVE_DOCUMENT;
446     sp_document_ensure_up_to_date(doc);
449     SPImage *img = getSelectedSPImage();
450     if (!img)
451         {
452         engine = NULL;
453         return;
454         }
456     Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(img->pixbuf, true);
458     pixbuf = sioxProcessImage(img, pixbuf);
460     if (!pixbuf)
461         {
462         char *msg = _("Trace: Image has no bitmap data");
463         msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
464         //g_warning(msg);
465         engine = NULL;
466         return;
467         }
469     msgStack->flash(Inkscape::NORMAL_MESSAGE, _("Trace: Starting trace..."));
470     desktop->updateCanvasNow();
472     std::vector<TracingEngineResult> results =
473                 engine->trace(pixbuf);
474     //printf("nrPaths:%d\n", results.size());
475     int nrPaths = results.size();
477     //### Check if we should stop
478     if (!keepGoing || nrPaths<1)
479         {
480         engine = NULL;
481         return;
482         }
484     //### Get pointers to the <image> and its parent
485     Inkscape::XML::Node *imgRepr   = SP_OBJECT(img)->repr;
486     Inkscape::XML::Node *par       = sp_repr_parent(imgRepr);
488     //### Get some information for the new transform()
489     double x      = 0.0;
490     double y      = 0.0;
491     double width  = 0.0;
492     double height = 0.0;
493     double dval   = 0.0;
495     if (sp_repr_get_double(imgRepr, "x", &dval))
496         x = dval;
497     if (sp_repr_get_double(imgRepr, "y", &dval))
498         y = dval;
500     if (sp_repr_get_double(imgRepr, "width", &dval))
501         width = dval;
502     if (sp_repr_get_double(imgRepr, "height", &dval))
503         height = dval;
505     double iwidth  = (double)pixbuf->get_width();
506     double iheight = (double)pixbuf->get_height();
508     double iwscale = width  / iwidth;
509     double ihscale = height / iheight;
511     Geom::Translate trans(x, y);
512     Geom::Scale scal(iwscale, ihscale);
514     //# Convolve scale, translation, and the original transform
515     Geom::Matrix tf(scal * trans);
516     tf *= img->transform;
519     //#OK.  Now let's start making new nodes
521     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
522     Inkscape::XML::Node *groupRepr = NULL;
524     //# if more than 1, make a <g>roup of <path>s
525     if (nrPaths > 1)
526         {
527         groupRepr = xml_doc->createElement("svg:g");
528         par->addChild(groupRepr, imgRepr);
529         }
531     long totalNodeCount = 0L;
533     for (unsigned int i=0 ; i<results.size() ; i++)
534         {
535         TracingEngineResult result = results[i];
536         totalNodeCount += result.getNodeCount();
538         Inkscape::XML::Node *pathRepr = xml_doc->createElement("svg:path");
539         pathRepr->setAttribute("style", result.getStyle().c_str());
540         pathRepr->setAttribute("d",     result.getPathData().c_str());
542         if (nrPaths > 1)
543             groupRepr->addChild(pathRepr, NULL);
544         else
545             par->addChild(pathRepr, imgRepr);
547         //### Apply the transform from the image to the new shape
548         SPObject *reprobj = doc->getObjectByRepr(pathRepr);
549         if (reprobj)
550             {
551             SPItem *newItem = SP_ITEM(reprobj);
552             sp_item_write_transform(newItem, pathRepr, tf, NULL);
553             }
554         if (nrPaths == 1)
555             {
556             selection->clear();
557             selection->add(pathRepr);
558             }
559         Inkscape::GC::release(pathRepr);
560         }
562     // If we have a group, then focus on, then forget it
563     if (nrPaths > 1)
564         {
565         selection->clear();
566         selection->add(groupRepr);
567         Inkscape::GC::release(groupRepr);
568         }
570     //## inform the document, so we can undo
571     sp_document_done(doc, SP_VERB_SELECTION_TRACE, _("Trace bitmap"));
573     engine = NULL;
575     char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
576     msgStack->flash(Inkscape::NORMAL_MESSAGE, msg);
577     g_free(msg);
585 /**
586  *  Main tracing method
587  */
588 void Tracer::trace(TracingEngine *theEngine)
590     //Check if we are already running
591     if (engine)
592         return;
594     engine = theEngine;
596 #if HAVE_THREADS
597     //Ensure that thread support is running
598     if (!Glib::thread_supported())
599         Glib::thread_init();
601     //Create our thread and run it
602     Glib::Thread::create(
603         sigc::mem_fun(*this, &Tracer::traceThread), false);
604 #else
605     traceThread();
606 #endif
614 /**
615  *  Abort the thread that is executing trace()
616  */
617 void Tracer::abort()
620     //## Inform Trace's working thread
621     keepGoing = false;
623     if (engine)
624         {
625         engine->abort();
626         }
632 } // namespace Trace
634 } // namespace Inkscape
637 //#########################################################################
638 //# E N D   O F   F I L E
639 //#########################################################################