diff --git a/src/trace/trace.cpp b/src/trace/trace.cpp
index b928ccba941514a9187ff090813823313c8598b7..925e4e195de41aa2068d7f20fc56645ab0197563 100644 (file)
--- a/src/trace/trace.cpp
+++ b/src/trace/trace.cpp
-/*
+/**
* A generic interface for plugging different
* autotracers into Inkscape.
*
* Authors:
- * Bob Jamison <rjamison@titan.com>
+ * Bob Jamison <rjamison@earthlink.net>
*
- * Copyright (C) 2004 Bob Jamison
+ * Copyright (C) 2004-2006 Bob Jamison
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "trace/potrace/inkscape-potrace.h"
#include <inkscape.h>
+#include <desktop.h>
#include <desktop-handles.h>
#include <document.h>
-#include "message-stack.h"
+#include <message-stack.h>
+#include <gtkmm.h>
#include <glibmm/i18n.h>
#include <selection.h>
#include <xml/repr.h>
-#include "sp-item.h"
-#include "sp-image.h"
+#include <xml/attribute-record.h>
+#include <sp-item.h>
+#include <sp-shape.h>
+#include <sp-image.h>
+
+#include <display/nr-arena.h>
+#include <display/nr-arena-shape.h>
+
+#include "siox.h"
+#include "imagemap-gdk.h"
+
+
+
+namespace Inkscape
+{
+
+namespace Trace
+{
+
+
-namespace Inkscape {
-namespace Trace {
/**
- *
+ * Get the selected image. Also check for any SPItems over it, in
+ * case the user wants SIOX pre-processing.
*/
SPImage *
Tracer::getSelectedSPImage()
{
+
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
if (!desktop)
{
- g_warning("Trace: No active desktop\n");
+ g_warning("Trace: No active desktop");
return NULL;
}
- Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
+ Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
+
+ Inkscape::Selection *sel = sp_desktop_selection(desktop);
if (!sel)
{
char *msg = _("Select an <b>image</b> to trace");
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
return NULL;
}
- SPItem *item = sel->singleItem();
- if (!item)
+ if (sioxEnabled)
{
- char *msg = _("Select an <b>image</b> to trace"); //same as above
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
- //g_warning(msg);
- return NULL;
+ SPImage *img = NULL;
+ GSList const *list = sel->itemList();
+ std::vector<SPItem *> items;
+ sioxShapes.clear();
+
+ /*
+ First, things are selected top-to-bottom, so we need to invert
+ them as bottom-to-top so that we can discover the image and any
+ SPItems above it
+ */
+ for ( ; list ; list=list->next)
+ {
+ if (!SP_IS_ITEM(list->data))
+ {
+ continue;
+ }
+ SPItem *item = SP_ITEM(list->data);
+ items.insert(items.begin(), item);
+ }
+ std::vector<SPItem *>::iterator iter;
+ for (iter = items.begin() ; iter!= items.end() ; iter++)
+ {
+ SPItem *item = *iter;
+ if (SP_IS_IMAGE(item))
+ {
+ if (img) //we want only one
+ {
+ char *msg = _("Select only one <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return NULL;
+ }
+ img = SP_IMAGE(item);
+ }
+ else // if (img) //# items -after- the image in tree (above it in Z)
+ {
+ if (SP_IS_SHAPE(item))
+ {
+ SPShape *shape = SP_SHAPE(item);
+ sioxShapes.push_back(shape);
+ }
+ }
+ }
+
+ if (!img || sioxShapes.size() < 1)
+ {
+ char *msg = _("Select one image and one or more shapes above it");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return NULL;
+ }
+ return img;
}
+ else
+ //### SIOX not enabled. We want exactly one image selected
+ {
+ SPItem *item = sel->singleItem();
+ if (!item)
+ {
+ char *msg = _("Select an <b>image</b> to trace"); //same as above
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ //g_warning(msg);
+ return NULL;
+ }
- if (!SP_IS_IMAGE(item))
+ if (!SP_IS_IMAGE(item))
+ {
+ char *msg = _("Select an <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ //g_warning(msg);
+ return NULL;
+ }
+
+ SPImage *img = SP_IMAGE(item);
+
+ return img;
+ }
+
+}
+
+
+
+typedef org::siox::SioxImage SioxImage;
+typedef org::siox::SioxObserver SioxObserver;
+typedef org::siox::Siox Siox;
+
+
+class TraceSioxObserver : public SioxObserver
+{
+public:
+
+ /**
+ *
+ */
+ TraceSioxObserver (void *contextArg) :
+ SioxObserver(contextArg)
+ {}
+
+ /**
+ *
+ */
+ virtual ~TraceSioxObserver ()
+ { }
+
+ /**
+ * Informs the observer how much has been completed.
+ * Return false if the processing should be aborted.
+ */
+ virtual bool progress(float percentCompleted)
+ {
+ //Tracer *tracer = (Tracer *)context;
+ //## Allow the GUI to update
+ Gtk::Main::iteration(false); //at least once, non-blocking
+ while( Gtk::Main::events_pending() )
+ Gtk::Main::iteration();
+ return true;
+ }
+
+ /**
+ * Send an error string to the Observer. Processing will
+ * be halted.
+ */
+ virtual void error(const std::string &msg)
+ {
+ //Tracer *tracer = (Tracer *)context;
+ }
+
+
+};
+
+
+
+
+
+/**
+ * Process a GdkPixbuf, according to which areas have been
+ * obscured in the GUI.
+ */
+Glib::RefPtr<Gdk::Pixbuf>
+Tracer::sioxProcessImage(SPImage *img,
+ Glib::RefPtr<Gdk::Pixbuf>origPixbuf)
+{
+ if (!sioxEnabled)
+ return origPixbuf;
+
+ if (origPixbuf == lastOrigPixbuf)
+ return lastSioxPixbuf;
+
+ //g_message("siox: start");
+
+ //Convert from gdk, so a format we know. By design, the pixel
+ //format in PackedPixelMap is identical to what is needed by SIOX
+ SioxImage simage(origPixbuf->gobj());
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop)
+ {
+ g_warning(_("Trace: No active desktop"));
+ return Glib::RefPtr<Gdk::Pixbuf>(NULL);
+ }
+
+ Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
+
+ Inkscape::Selection *sel = sp_desktop_selection(desktop);
+ if (!sel)
{
char *msg = _("Select an <b>image</b> to trace");
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
- return NULL;
+ return Glib::RefPtr<Gdk::Pixbuf>(NULL);
}
- selectedItem = item;
+ NRArenaItem *aImg = sp_item_get_arenaitem(img, desktop->dkey);
+ //g_message("img: %d %d %d %d\n", aImg->bbox.x0, aImg->bbox.y0,
+ // aImg->bbox.x1, aImg->bbox.y1);
- SPImage *img = SP_IMAGE(item);
+ double width = (double)(aImg->bbox.x1 - aImg->bbox.x0);
+ double height = (double)(aImg->bbox.y1 - aImg->bbox.y0);
- return img;
+ double iwidth = (double)simage.getWidth();
+ double iheight = (double)simage.getHeight();
-}
+ double iwscale = width / iwidth;
+ double ihscale = height / iheight;
+
+ std::vector<NRArenaItem *> arenaItems;
+ std::vector<SPShape *>::iterator iter;
+ for (iter = sioxShapes.begin() ; iter!=sioxShapes.end() ; iter++)
+ {
+ SPItem *item = *iter;
+ NRArenaItem *aItem = sp_item_get_arenaitem(item, desktop->dkey);
+ arenaItems.push_back(aItem);
+ }
+
+ //g_message("%d arena items\n", arenaItems.size());
+
+ //PackedPixelMap *dumpMap = PackedPixelMapCreate(
+ // simage.getWidth(), simage.getHeight());
+
+ //g_message("siox: start selection");
+
+ for (int row=0 ; row<iheight ; row++)
+ {
+ double ypos = ((double)aImg->bbox.y0) + ihscale * (double) row;
+ for (int col=0 ; col<simage.getWidth() ; col++)
+ {
+ //Get absolute X,Y position
+ double xpos = ((double)aImg->bbox.x0) + iwscale * (double)col;
+ NR::Point point(xpos, ypos);
+ point *= aImg->transform;
+ //point *= imgMat;
+ //point = desktop->doc2dt(point);
+ //g_message("x:%f y:%f\n", point[0], point[1]);
+ bool weHaveAHit = false;
+ std::vector<NRArenaItem *>::iterator aIter;
+ for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
+ {
+ NRArenaItem *arenaItem = *aIter;
+ NRArenaItemClass *arenaClass =
+ (NRArenaItemClass *) NR_OBJECT_GET_CLASS (arenaItem);
+ if (arenaClass->pick(arenaItem, point, 1.0f, 1))
+ {
+ weHaveAHit = true;
+ break;
+ }
+ }
+
+ if (weHaveAHit)
+ {
+ //g_message("hit!\n");
+ //dumpMap->setPixelLong(dumpMap, col, row, 0L);
+ simage.setConfidence(col, row,
+ Siox::UNKNOWN_REGION_CONFIDENCE);
+ }
+ else
+ {
+ //g_message("miss!\n");
+ //dumpMap->setPixelLong(dumpMap, col, row,
+ // simage.getPixel(col, row));
+ simage.setConfidence(col, row,
+ Siox::CERTAIN_BACKGROUND_CONFIDENCE);
+ }
+ }
+ }
+
+ //g_message("siox: selection done");
+
+ //dumpMap->writePPM(dumpMap, "siox1.ppm");
+ //dumpMap->destroy(dumpMap);
+
+ //## ok we have our pixel buf
+ TraceSioxObserver observer(this);
+ Siox sengine(&observer);
+ SioxImage result = sengine.extractForeground(simage, 0xffffff);
+ if (!result.isValid())
+ {
+ g_warning(_("Invalid SIOX result"));
+ return Glib::RefPtr<Gdk::Pixbuf>(NULL);
+ }
+
+ //result.writePPM("siox2.ppm");
+
+ /* Free Arena and ArenaItem */
+ /*
+ std::vector<NRArenaItem *>::iterator aIter;
+ for (aIter = arenaItems.begin() ; aIter!=arenaItems.end() ; aIter++)
+ {
+ NRArenaItem *arenaItem = *aIter;
+ nr_arena_item_unref(arenaItem);
+ }
+ nr_object_unref((NRObject *) arena);
+ */
+
+ Glib::RefPtr<Gdk::Pixbuf> newPixbuf = Glib::wrap(result.getGdkPixbuf());
+ //g_message("siox: done");
+
+ lastSioxPixbuf = newPixbuf;
+
+ return newPixbuf;
+}
/**
*
*/
-GdkPixbuf *
+Glib::RefPtr<Gdk::Pixbuf>
Tracer::getSelectedImage()
{
+
SPImage *img = getSelectedSPImage();
if (!img)
- return NULL;
+ return Glib::RefPtr<Gdk::Pixbuf>(NULL);
- GdkPixbuf *pixbuf = img->pixbuf;
+ if (!img->pixbuf)
+ return Glib::RefPtr<Gdk::Pixbuf>(NULL);
- return pixbuf;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf =
+ Glib::wrap(img->pixbuf, true);
+
+ if (sioxEnabled)
+ {
+ Glib::RefPtr<Gdk::Pixbuf> sioxPixbuf =
+ sioxProcessImage(img, pixbuf);
+ if (!sioxPixbuf)
+ {
+ return pixbuf;
+ }
+ else
+ {
+ return sioxPixbuf;
+ }
+ }
+ else
+ {
+ return pixbuf;
+ }
}
return;
}
- Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
+ Inkscape::MessageStack *msgStack = sp_desktop_message_stack(desktop);
+
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
if (!SP_ACTIVE_DOCUMENT)
{
char *msg = _("Trace: No active document");
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
engine = NULL;
return;
SPImage *img = getSelectedSPImage();
- if (!img || !selectedItem)
+ if (!img)
{
engine = NULL;
return;
}
- GdkPixbuf *pixbuf = img->pixbuf;
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf = Glib::wrap(img->pixbuf, true);
+
+ pixbuf = sioxProcessImage(img, pixbuf);
if (!pixbuf)
{
char *msg = _("Trace: Image has no bitmap data");
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
engine = NULL;
return;
}
- int nrPaths;
- TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
- //printf("nrPaths:%d\n", nrPaths);
+ msgStack->flash(Inkscape::NORMAL_MESSAGE, _("Trace: Starting trace..."));
+ desktop->updateCanvasNow();
+
+ std::vector<TracingEngineResult> results =
+ engine->trace(pixbuf);
+ //printf("nrPaths:%d\n", results.size());
+ int nrPaths = results.size();
//### Check if we should stop
- if (!keepGoing || !results || nrPaths<1)
+ if (!keepGoing || nrPaths<1)
{
engine = NULL;
return;
NR::Matrix trans(NR::translate(x, y));
- double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
- double iheight = (double)gdk_pixbuf_get_height(pixbuf);
+ double iwidth = (double)pixbuf->get_width();
+ double iheight = (double)pixbuf->get_height();
double iwscale = width / iwidth;
double ihscale = height / iheight;
//# Convolve scale, translation, and the original transform
NR::Matrix tf(scal);
tf *= trans;
- tf *= selectedItem->transform;
+ tf *= img->transform;
//#OK. Now let's start making new nodes
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
Inkscape::XML::Node *groupRepr = NULL;
//# if more than 1, make a <g>roup of <path>s
if (nrPaths > 1)
{
- groupRepr = sp_repr_new("svg:g");
+ groupRepr = xml_doc->createElement("svg:g");
par->addChild(groupRepr, imgRepr);
}
long totalNodeCount = 0L;
- for (TracingEngineResult *result=results ;
- result ; result=result->next)
+ for (unsigned int i=0 ; i<results.size() ; i++)
{
- totalNodeCount += result->getNodeCount();
+ TracingEngineResult result = results[i];
+ totalNodeCount += result.getNodeCount();
- Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
- pathRepr->setAttribute("style", result->getStyle());
- pathRepr->setAttribute("d", result->getPathData());
+ Inkscape::XML::Node *pathRepr = xml_doc->createElement("svg:path");
+ pathRepr->setAttribute("style", result.getStyle().c_str());
+ pathRepr->setAttribute("d", result.getPathData().c_str());
if (nrPaths > 1)
groupRepr->addChild(pathRepr, NULL);
Inkscape::GC::release(pathRepr);
}
-
- delete results;
-
// If we have a group, then focus on, then forget it
if (nrPaths > 1)
{
}
//## inform the document, so we can undo
- sp_document_done(doc);
+ sp_document_done(doc, SP_VERB_SELECTION_TRACE, _("Trace bitmap"));
engine = NULL;
char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
- SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
+ msgStack->flash(Inkscape::NORMAL_MESSAGE, msg);
g_free(msg);
}
+
+
+
+
/**
* Main tracing method
*/