trace.cpp revision 23c2596db8256f153092ae54cdad0dedb5b2ac94
/*
* A generic interface for plugging different
* autotracers into Inkscape.
*
* Authors:
* Bob Jamison <rjamison@earthlink.net>
*
* 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-handles.h>
#include <document.h>
#include "message-stack.h"
#include <glibmm/i18n.h>
#include <selection.h>
#include <xml/repr.h>
#include "sp-item.h"
#include "sp-image.h"
#include "siox.h"
#include "imagemap-gdk.h"
namespace Inkscape {
namespace Trace {
/**
*
*/
SPImage *
Tracer::getSelectedSPImage()
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
if (!desktop)
{
g_warning("Trace: No active desktop\n");
return NULL;
}
Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
if (!sel)
{
char *msg = _("Select an <b>image</b> to trace");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
return NULL;
}
if (sioxEnabled)
{
SPImage *img = NULL;
GSList const *list = sel->itemList();
sioxItems.clear();
std::vector<SPItem *> items;
/*
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");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
return NULL;
}
img = SP_IMAGE(item);
}
else if (img) //# items -after- the image in tree (above it in Z)
{
sioxItems.push_back(item);
}
}
if (!img || sioxItems.size() < 1)
{
char *msg = _("Select one image and one or more items above it");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
return NULL;
}
return img;
}
else
//### No SIOX. We want exactly one image selected
{
SPItem *item = sel->singleItem();
if (!item)
{
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;
}
if (!SP_IS_IMAGE(item))
{
char *msg = _("Select an <b>image</b> to trace");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
return NULL;
}
SPImage *img = SP_IMAGE(item);
return img;
}
}
/**
*
*/
GdkPixbuf *
Tracer::getSelectedImage()
{
SPImage *img = getSelectedSPImage();
if (!img)
return NULL;
GdkPixbuf *pixbuf = img->pixbuf;
return pixbuf;
}
GdkPixbuf *
Tracer::sioxProcessImage(SPImage *img, GdkPixbuf *origPixbuf)
{
RgbMap *rgbMap = gdkPixbufToRgbMap(origPixbuf);
//We need to create two things:
// 1. An array of long pixel values of ARGB
// 2. A matching array of per-pixel float 'confidence' values
long *imgBuf = new long[rgbMap->width * rgbMap->height];
float *confidenceMatrix = new float[rgbMap->width * rgbMap->height];
long idx = 0;
for (int j=0 ; j<rgbMap->height ; j++)
for (int i=0 ; i<rgbMap->width ; i++)
{
RGB rgb = rgbMap->getPixel(rgbMap, i, j);
long pix = (((long)rgb.r) << 16 & 0xFF0000L) |
(((long)rgb.g) << 8 & 0x00FF00L) |
(((long)rgb.b) & 0x0000FFL);
imgBuf[idx++] = pix;
}
//## ok we have our pixel buf
org::siox::SioxSegmentator ss(rgbMap->width, rgbMap->height, NULL, 0);
ss.segmentate(imgBuf, rgbMap->width * rgbMap->height,
confidenceMatrix, rgbMap->width * rgbMap->height,
0, 0.0);
idx = 0;
for (int j=0 ; j<rgbMap->height ; j++)
for (int i=0 ; i<rgbMap->width ; i++)
{
long pix = imgBuf[idx++];
RGB rgb;
rgb.r = (pix>>16) & 0xff;
rgb.g = (pix>> 8) & 0xff;
rgb.b = (pix ) & 0xff;
rgbMap->setPixelRGB(rgbMap, i, j, rgb);
}
GdkPixbuf *newPixbuf = rgbMapToGdkPixbuf(rgbMap);
rgbMap->destroy(rgbMap);
delete imgBuf;
delete confidenceMatrix;
return newPixbuf;
}
//#########################################################################
//# T R A C E
//#########################################################################
/**
* Whether we want to enable SIOX subimage selection
*/
void Tracer::enableSiox(bool enable)
{
sioxEnabled = enable;
}
/**
* Threaded method that does single bitmap--->path conversion
*/
void Tracer::traceThread()
{
//## Remember. NEVER leave this method without setting
//## engine back to NULL
//## Prepare our kill flag. We will watch this later to
//## see if the main thread wants us to stop
keepGoing = true;
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
if (!desktop)
{
g_warning("Trace: No active desktop\n");
return;
}
Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
if (!SP_ACTIVE_DOCUMENT)
{
char *msg = _("Trace: No active document");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
engine = NULL;
return;
}
SPDocument *doc = SP_ACTIVE_DOCUMENT;
sp_document_ensure_up_to_date(doc);
SPImage *img = getSelectedSPImage();
if (!img)
{
engine = NULL;
return;
}
GdkPixbuf *pixbuf = img->pixbuf;
if (!pixbuf)
{
char *msg = _("Trace: Image has no bitmap data");
SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
//g_warning(msg);
engine = NULL;
return;
}
//## SIOX pre-processing to get a smart subimage of the pixbuf.
//## This is done before any other filters
if (sioxEnabled)
{
/*
Ok, we have requested siox, and getSelectedSPImage() has found a single
bitmap and one or more SPItems above it. Now what we need to do is create
a siox-segmented subimage pixbuf. We not need alter 'img' at all, since this
pixbuf will be the same dimensions and at the same location.
Remember to free this new pixbuf later.
*/
pixbuf = sioxProcessImage(img, pixbuf);
}
int nrPaths;
TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
//printf("nrPaths:%d\n", nrPaths);
//### Check if we should stop
if (!keepGoing || !results || nrPaths<1)
{
engine = NULL;
return;
}
//### Get pointers to the <image> and its parent
Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
Inkscape::XML::Node *par = sp_repr_parent(imgRepr);
//### Get some information for the new transform()
double x = 0.0;
double y = 0.0;
double width = 0.0;
double height = 0.0;
double dval = 0.0;
if (sp_repr_get_double(imgRepr, "x", &dval))
x = dval;
if (sp_repr_get_double(imgRepr, "y", &dval))
y = dval;
if (sp_repr_get_double(imgRepr, "width", &dval))
width = dval;
if (sp_repr_get_double(imgRepr, "height", &dval))
height = dval;
NR::Matrix trans(NR::translate(x, y));
double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
double iheight = (double)gdk_pixbuf_get_height(pixbuf);
double iwscale = width / iwidth;
double ihscale = height / iheight;
NR::Matrix scal(NR::scale(iwscale, ihscale));
//# Convolve scale, translation, and the original transform
NR::Matrix tf(scal);
tf *= trans;
tf *= img->transform;
//#OK. Now let's start making new nodes
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");
par->addChild(groupRepr, imgRepr);
}
long totalNodeCount = 0L;
for (TracingEngineResult *result=results ;
result ; result=result->next)
{
totalNodeCount += result->getNodeCount();
Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
pathRepr->setAttribute("style", result->getStyle());
pathRepr->setAttribute("d", result->getPathData());
if (nrPaths > 1)
groupRepr->addChild(pathRepr, NULL);
else
par->addChild(pathRepr, imgRepr);
//### Apply the transform from the image to the new shape
SPObject *reprobj = doc->getObjectByRepr(pathRepr);
if (reprobj)
{
SPItem *newItem = SP_ITEM(reprobj);
sp_item_write_transform(newItem, pathRepr, tf, NULL);
}
if (nrPaths == 1)
{
selection->clear();
selection->add(pathRepr);
}
Inkscape::GC::release(pathRepr);
}
//did we allocate a pixbuf copy?
if (sioxEnabled)
{
g_free(pixbuf);
}
delete results;
// If we have a group, then focus on, then forget it
if (nrPaths > 1)
{
selection->clear();
selection->add(groupRepr);
Inkscape::GC::release(groupRepr);
}
//## inform the document, so we can undo
sp_document_done(doc);
engine = NULL;
char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
g_free(msg);
}
/**
* Main tracing method
*/
void Tracer::trace(TracingEngine *theEngine)
{
//Check if we are already running
if (engine)
return;
engine = theEngine;
#if HAVE_THREADS
//Ensure that thread support is running
if (!Glib::thread_supported())
Glib::thread_init();
//Create our thread and run it
Glib::Thread::create(
sigc::mem_fun(*this, &Tracer::traceThread), false);
#else
traceThread();
#endif
}
/**
* Abort the thread that is executing trace()
*/
void Tracer::abort()
{
//## Inform Trace's working thread
keepGoing = false;
if (engine)
{
engine->abort();
}
}
} // namespace Trace
} // namespace Inkscape
//#########################################################################
//# E N D O F F I L E
//#########################################################################