inkscape-potrace.cpp revision 6129af7cc5b723223e9617614c931936e5190421
359a38ce40498397028473d956691915ed3e849atavmjong-free/*
359a38ce40498397028473d956691915ed3e849atavmjong-free * This is the C++ glue between Inkscape and Potrace
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Authors:
359a38ce40498397028473d956691915ed3e849atavmjong-free * Bob Jamison <rjamison@titan.com>
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Copyright (C) 2004 Bob Jamison
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Released under GNU GPL, read the file 'COPYING' for more information
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free * Potrace, the wonderful tracer located at http://potrace.sourceforge.net,
359a38ce40498397028473d956691915ed3e849atavmjong-free * is provided by the generosity of Peter Selinger, to whom we are grateful.
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free */
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <glibmm/i18n.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <gtkmm/main.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "trace/filterset.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "trace/imagemap-gdk.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
3fad3df12ae2d320c12871d471eb0faf5f187cbdAlex Valavanis#include <inkscape.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <desktop-handles.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "message-stack.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <sp-path.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include <svg/stringstream.h>
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "curve.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "bitmap.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free#include "inkscape-potrace.h"
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic void updateGui()
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free //## Allow the GUI to update
359a38ce40498397028473d956691915ed3e849atavmjong-free Gtk::Main::iteration(false); //at least once, non-blocking
359a38ce40498397028473d956691915ed3e849atavmjong-free while( Gtk::Main::events_pending() )
359a38ce40498397028473d956691915ed3e849atavmjong-free Gtk::Main::iteration();
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free}
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic void potraceStatusCallback(double progress, void *userData) /* callback fn */
d1561c248f49dc3508ae9e6557fc0d371928e394Markus Engel{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White updateGui();
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!userData)
359a38ce40498397028473d956691915ed3e849atavmjong-free return;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free //g_message("progress: %f\n", progress);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free //Inkscape::Trace::Potrace::PotraceTracingEngine *engine =
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White // (Inkscape::Trace::Potrace::PotraceTracingEngine *)userData;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free//required by potrace
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Inkscape {
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Trace {
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freenamespace Potrace {
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free/**
359a38ce40498397028473d956691915ed3e849atavmjong-free *
359a38ce40498397028473d956691915ed3e849atavmjong-free */
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhitePotraceTracingEngine::PotraceTracingEngine()
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free //##### Our defaults
359a38ce40498397028473d956691915ed3e849atavmjong-free invert = false;
359a38ce40498397028473d956691915ed3e849atavmjong-free traceType = TRACE_BRIGHTNESS;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free quantizationNrColors = 8;
359a38ce40498397028473d956691915ed3e849atavmjong-free
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz brightnessThreshold = 0.45;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free cannyHighThreshold = 0.65;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free}
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freetypedef struct
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free double x;
359a38ce40498397028473d956691915ed3e849atavmjong-free double y;
359a38ce40498397028473d956691915ed3e849atavmjong-free} Point;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free/**
5f19756f48574526dda8abedebf811c9d1456e80Markus Engel * Check a point against a list of points to see if it
359a38ce40498397028473d956691915ed3e849atavmjong-free * has already occurred.
359a38ce40498397028473d956691915ed3e849atavmjong-free */
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic bool
359a38ce40498397028473d956691915ed3e849atavmjong-freehasPoint(std::vector<Point> &points, double x, double y)
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free for (unsigned int i=0; i<points.size() ; i++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free Point p = points[i];
359a38ce40498397028473d956691915ed3e849atavmjong-free if (p.x == x && p.y == y)
359a38ce40498397028473d956691915ed3e849atavmjong-free return true;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free return false;
359a38ce40498397028473d956691915ed3e849atavmjong-free}
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free/**
359a38ce40498397028473d956691915ed3e849atavmjong-free * Recursively descend the path_t node tree, writing paths in SVG
359a38ce40498397028473d956691915ed3e849atavmjong-free * format into the output stream. The Point vector is used to prevent
359a38ce40498397028473d956691915ed3e849atavmjong-free * redundant paths. Returns number of paths processed.
359a38ce40498397028473d956691915ed3e849atavmjong-free */
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic long
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhitewritePaths(PotraceTracingEngine *engine, potrace_path_t *plist,
359a38ce40498397028473d956691915ed3e849atavmjong-free Inkscape::SVGOStringStream& data, std::vector<Point> &points)
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz{
359a38ce40498397028473d956691915ed3e849atavmjong-free long nodeCount = 0L;
359a38ce40498397028473d956691915ed3e849atavmjong-free
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White potrace_path_t *node;
359a38ce40498397028473d956691915ed3e849atavmjong-free for (node=plist; node ; node=node->sibling)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White {
359a38ce40498397028473d956691915ed3e849atavmjong-free potrace_curve_t *curve = &(node->curve);
359a38ce40498397028473d956691915ed3e849atavmjong-free //g_message("node->fm:%d\n", node->fm);
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!curve->n)
359a38ce40498397028473d956691915ed3e849atavmjong-free continue;
359a38ce40498397028473d956691915ed3e849atavmjong-free dpoint_t *pt = curve->c[curve->n - 1];
359a38ce40498397028473d956691915ed3e849atavmjong-free double x0 = 0.0;
359a38ce40498397028473d956691915ed3e849atavmjong-free double y0 = 0.0;
359a38ce40498397028473d956691915ed3e849atavmjong-free double x1 = 0.0;
359a38ce40498397028473d956691915ed3e849atavmjong-free double y1 = 0.0;
359a38ce40498397028473d956691915ed3e849atavmjong-free double x2 = pt[2].x;
359a38ce40498397028473d956691915ed3e849atavmjong-free double y2 = pt[2].y;
359a38ce40498397028473d956691915ed3e849atavmjong-free //Have we been here already?
359a38ce40498397028473d956691915ed3e849atavmjong-free if (hasPoint(points, x2, y2))
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free //g_message("duplicate point: (%f,%f)\n", x2, y2);
359a38ce40498397028473d956691915ed3e849atavmjong-free continue;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free else
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free Point p;
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz p.x = x2; p.y = y2;
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz points.push_back(p);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free data << "M " << x2 << " " << y2 << " ";
359a38ce40498397028473d956691915ed3e849atavmjong-free nodeCount++;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int i=0 ; i<curve->n ; i++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!engine->keepGoing)
359a38ce40498397028473d956691915ed3e849atavmjong-free return 0L;
359a38ce40498397028473d956691915ed3e849atavmjong-free pt = curve->c[i];
359a38ce40498397028473d956691915ed3e849atavmjong-free x0 = pt[0].x;
359a38ce40498397028473d956691915ed3e849atavmjong-free y0 = pt[0].y;
359a38ce40498397028473d956691915ed3e849atavmjong-free x1 = pt[1].x;
359a38ce40498397028473d956691915ed3e849atavmjong-free y1 = pt[1].y;
359a38ce40498397028473d956691915ed3e849atavmjong-free x2 = pt[2].x;
359a38ce40498397028473d956691915ed3e849atavmjong-free y2 = pt[2].y;
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz switch (curve->tag[i])
3eb97a45889abb73fa05c413b45785ea682f07c5Jon A. Cruz {
359a38ce40498397028473d956691915ed3e849atavmjong-free case POTRACE_CORNER:
359a38ce40498397028473d956691915ed3e849atavmjong-free data << "L " << x1 << " " << y1 << " " ;
359a38ce40498397028473d956691915ed3e849atavmjong-free data << "L " << x2 << " " << y2 << " " ;
359a38ce40498397028473d956691915ed3e849atavmjong-free break;
359a38ce40498397028473d956691915ed3e849atavmjong-free case POTRACE_CURVETO:
359a38ce40498397028473d956691915ed3e849atavmjong-free data << "C " << x0 << " " << y0 << " "
359a38ce40498397028473d956691915ed3e849atavmjong-free << x1 << " " << y1 << " "
359a38ce40498397028473d956691915ed3e849atavmjong-free << x2 << " " << y2 << " ";
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free break;
359a38ce40498397028473d956691915ed3e849atavmjong-free default:
359a38ce40498397028473d956691915ed3e849atavmjong-free break;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free nodeCount++;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White data << "z";
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free for (path_t *child=node->childlist; child ; child=child->sibling)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free nodeCount += writePaths(engine, child, data, points);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free return nodeCount;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free}
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic GrayMap *
359a38ce40498397028473d956691915ed3e849atavmjong-freefilter(PotraceTracingEngine &engine, GdkPixbuf * pixbuf)
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!pixbuf)
359a38ce40498397028473d956691915ed3e849atavmjong-free return NULL;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free GrayMap *newGm = NULL;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /*### Color quantization -- banding ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free if (engine.getTraceType() == TRACE_QUANT)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RgbMap *rgbmap = gdkPixbufToRgbMap(pixbuf);
359a38ce40498397028473d956691915ed3e849atavmjong-free //rgbMap->writePPM(rgbMap, "rgb.ppm");
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = quantizeBand(rgbmap,
359a38ce40498397028473d956691915ed3e849atavmjong-free engine.getQuantizationNrColors());
359a38ce40498397028473d956691915ed3e849atavmjong-free rgbmap->destroy(rgbmap);
359a38ce40498397028473d956691915ed3e849atavmjong-free //return newGm;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /*### Brightness threshold ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free else if ( engine.getTraceType() == TRACE_BRIGHTNESS ||
359a38ce40498397028473d956691915ed3e849atavmjong-free engine.getTraceType() == TRACE_BRIGHTNESS_MULTI )
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free GrayMap *gm = gdkPixbufToGrayMap(pixbuf);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = GrayMapCreate(gm->width, gm->height);
359a38ce40498397028473d956691915ed3e849atavmjong-free double floor = 3.0 *
359a38ce40498397028473d956691915ed3e849atavmjong-free ( engine.getBrightnessFloor() * 256.0 );
359a38ce40498397028473d956691915ed3e849atavmjong-free double cutoff = 3.0 *
359a38ce40498397028473d956691915ed3e849atavmjong-free ( engine.getBrightnessThreshold() * 256.0 );
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int y=0 ; y<gm->height ; y++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int x=0 ; x<gm->width ; x++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free double brightness = (double)gm->getPixel(gm, x, y);
359a38ce40498397028473d956691915ed3e849atavmjong-free if (brightness >= floor && brightness < cutoff)
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm->setPixel(newGm, x, y, GRAYMAP_BLACK); //black pixel
359a38ce40498397028473d956691915ed3e849atavmjong-free else
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm->setPixel(newGm, x, y, GRAYMAP_WHITE); //white pixel
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free gm->destroy(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free //newGm->writePPM(newGm, "brightness.ppm");
359a38ce40498397028473d956691915ed3e849atavmjong-free //return newGm;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /*### Canny edge detection ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free else if (engine.getTraceType() == TRACE_CANNY)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free GrayMap *gm = gdkPixbufToGrayMap(pixbuf);
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = grayMapCanny(gm, 0.1, engine.getCannyHighThreshold());
359a38ce40498397028473d956691915ed3e849atavmjong-free gm->destroy(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free //newGm->writePPM(newGm, "canny.ppm");
359a38ce40498397028473d956691915ed3e849atavmjong-free //return newGm;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /*### Do I invert the image? ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free if (newGm && engine.getInvert())
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int y=0 ; y<newGm->height ; y++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int x=0 ; x<newGm->width ; x++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
bf9ec3e969ba6b11cbbc613545aedc63cc886973Matthew Petroff unsigned long brightness = newGm->getPixel(newGm, x, y);
359a38ce40498397028473d956691915ed3e849atavmjong-free brightness = 765 - brightness;
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm->setPixel(newGm, x, y, brightness);
add38d633bbf8ef881bdb908735ea27385c554b8Matthew Petroff }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free return newGm;//none of the above
359a38ce40498397028473d956691915ed3e849atavmjong-free}
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-freestatic IndexedMap *
359a38ce40498397028473d956691915ed3e849atavmjong-freefilterIndexed(PotraceTracingEngine &engine, GdkPixbuf * pixbuf)
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free if (!pixbuf)
359a38ce40498397028473d956691915ed3e849atavmjong-free return NULL;
bf9ec3e969ba6b11cbbc613545aedc63cc886973Matthew Petroff
359a38ce40498397028473d956691915ed3e849atavmjong-free IndexedMap *newGm = NULL;
359a38ce40498397028473d956691915ed3e849atavmjong-free
add38d633bbf8ef881bdb908735ea27385c554b8Matthew Petroff /*### Color quant multiscan ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free if (engine.getTraceType() == TRACE_QUANT_COLOR)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
359a38ce40498397028473d956691915ed3e849atavmjong-free if (engine.getMultiScanSmooth())
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RgbMap *gaussMap = rgbMapGaussian(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = rgbMapQuantize(gaussMap, 8, engine.getMultiScanNrColors());
359a38ce40498397028473d956691915ed3e849atavmjong-free gaussMap->destroy(gaussMap);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free else
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = rgbMapQuantize(gm, 8, engine.getMultiScanNrColors());
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free gm->destroy(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /*### Quant multiscan ###*/
359a38ce40498397028473d956691915ed3e849atavmjong-free else if (engine.getTraceType() == TRACE_QUANT_MONO)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
359a38ce40498397028473d956691915ed3e849atavmjong-free if (engine.getMultiScanSmooth())
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RgbMap *gaussMap = rgbMapGaussian(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = rgbMapQuantize(gaussMap, 8, engine.getMultiScanNrColors());
359a38ce40498397028473d956691915ed3e849atavmjong-free gaussMap->destroy(gaussMap);
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free else
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm = rgbMapQuantize(gm, 8, engine.getMultiScanNrColors());
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free gm->destroy(gm);
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free //Turn to grays
359a38ce40498397028473d956691915ed3e849atavmjong-free for (int i=0 ; i<newGm->nrColors ; i++)
359a38ce40498397028473d956691915ed3e849atavmjong-free {
359a38ce40498397028473d956691915ed3e849atavmjong-free RGB rgb = newGm->clut[i];
359a38ce40498397028473d956691915ed3e849atavmjong-free int grayVal = (rgb.r + rgb.g + rgb.b) / 3;
359a38ce40498397028473d956691915ed3e849atavmjong-free rgb.r = rgb.g = rgb.b = grayVal;
359a38ce40498397028473d956691915ed3e849atavmjong-free newGm->clut[i] = rgb;
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free }
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free return newGm;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhiteGdkPixbuf *
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. WhitePotraceTracingEngine::preview(GdkPixbuf * pixbuf)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White{
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White if ( traceType == TRACE_QUANT_COLOR ||
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White traceType == TRACE_QUANT_MONO )
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White {
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White IndexedMap *gm = filterIndexed(*this, pixbuf);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White if (!gm)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White return NULL;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White GdkPixbuf *newBuf = indexedMapToGdkPixbuf(gm);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White gm->destroy(gm);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White return newBuf;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White }
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White else
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White {
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White GrayMap *gm = filter(*this, pixbuf);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White if (!gm)
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White return NULL;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White GdkPixbuf *newBuf = grayMapToGdkPixbuf(gm);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White gm->destroy(gm);
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White return newBuf;
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White }
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White}
652485ad88d2a42f827c7e355220efeb3b2e37afLiam P. White
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free//*This is the core inkscape-to-potrace binding
359a38ce40498397028473d956691915ed3e849atavmjong-freechar *PotraceTracingEngine::grayMapToPath(GrayMap *grayMap, long *nodeCount)
359a38ce40498397028473d956691915ed3e849atavmjong-free{
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free /* get default parameters */
359a38ce40498397028473d956691915ed3e849atavmjong-free potrace_param_t *potraceParams = potrace_param_default();
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free potraceParams->progress.callback = potraceStatusCallback;
359a38ce40498397028473d956691915ed3e849atavmjong-free potraceParams->progress.data = (void *)this;
359a38ce40498397028473d956691915ed3e849atavmjong-free
359a38ce40498397028473d956691915ed3e849atavmjong-free potrace_bitmap_t *potraceBitmap = bm_new(grayMap->width, grayMap->height);
bm_clear(potraceBitmap, 0);
//##Read the data out of the GrayMap
for (int y=0 ; y<grayMap->height ; y++)
{
for (int x=0 ; x<grayMap->width ; x++)
{
BM_UPUT(potraceBitmap, x, y,
grayMap->getPixel(grayMap, x, y) ? 0 : 1);
}
}
//##Debug
/*
FILE *f = fopen("poimage.pbm", "wb");
bm_writepbm(f, bm);
fclose(f);
*/
if (!keepGoing)
{
g_warning("aborted");
return NULL;
}
/* trace a bitmap*/
potrace_state_t *potraceState = potrace_trace(potraceParams,
potraceBitmap);
//## Free the Potrace bitmap
bm_free(potraceBitmap);
if (!keepGoing)
{
g_warning("aborted");
potrace_state_free(potraceState);
potrace_param_free(potraceParams);
return NULL;
}
Inkscape::SVGOStringStream data;
data << "";
//## copy the path information into our d="" attribute string
std::vector<Point> points;
long thisNodeCount = writePaths(this, potraceState->plist, data, points);
/* free a potrace items */
potrace_state_free(potraceState);
potrace_param_free(potraceParams);
if (!keepGoing)
return NULL;
char *d = strdup((char *)data.str().c_str());
if ( nodeCount)
*nodeCount = thisNodeCount;
return d;
}
/**
* This is called for a single scan
*/
TracingEngineResult *
PotraceTracingEngine::traceSingle(GdkPixbuf * thePixbuf, int *nrPaths)
{
if (!thePixbuf)
return NULL;
brightnessFloor = 0.0; //important to set this
GrayMap *grayMap = filter(*this, thePixbuf);
if (!grayMap)
return NULL;
long nodeCount;
char *d = grayMapToPath(grayMap, &nodeCount);
grayMap->destroy(grayMap);
if (!d)
{
*nrPaths = 0;
return NULL;
}
char *style = "fill:#000000";
//g_message("### GOT '%s' \n", d);
TracingEngineResult *result = new TracingEngineResult(style, d, nodeCount);
free(d);
*nrPaths = 1;
return result;
}
/**
* Called for multiple-scanning algorithms
*/
TracingEngineResult *
PotraceTracingEngine::traceBrightnessMulti(GdkPixbuf * thePixbuf, int *nrPaths)
{
if (!thePixbuf)
return NULL;
double low = 0.2; //bottom of range
double high = 0.9; //top of range
double delta = (high - low ) / ((double)multiScanNrColors);
brightnessFloor = 0.0; //Set bottom to black
int traceCount = 0;
TracingEngineResult *results = NULL;
for ( brightnessThreshold = low ;
brightnessThreshold <= high ;
brightnessThreshold += delta)
{
GrayMap *grayMap = filter(*this, thePixbuf);
if (!grayMap)
return NULL;
long nodeCount;
char *d = grayMapToPath(grayMap, &nodeCount);
grayMap->destroy(grayMap);
if (!d)
{
*nrPaths = 0;
return NULL;
}
int grayVal = (int)(256.0 * brightnessThreshold);
char style[31];
sprintf(style, "fill-opacity:1.0;fill:#%02x%02x%02x",
grayVal, grayVal, grayVal);
//g_message("### GOT '%s' \n", d);
TracingEngineResult *result = new TracingEngineResult(style, d, nodeCount);
free(d);
if (!results)
{
results = result; //first one
}
else
{
//walk to end of list
TracingEngineResult *r;
for (r=results ; r->next ; r=r->next)
{}
r->next = result;
}
if (!multiScanStack)
brightnessFloor = brightnessThreshold;
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
if (desktop)
{
gchar *msg = g_strdup_printf(_("Trace: %d. %ld nodes"), traceCount++, nodeCount);
sp_desktop_message_stack(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
g_free(msg);
}
}
//report the count of paths processed
*nrPaths = multiScanNrColors;
return results;
}
/**
* Quantization
*/
TracingEngineResult *
PotraceTracingEngine::traceQuant(GdkPixbuf * thePixbuf, int *nrPaths)
{
if (!thePixbuf)
return NULL;
IndexedMap *iMap = filterIndexed(*this, thePixbuf);
if (!iMap)
return NULL;
TracingEngineResult *results = NULL;
//Create and clear a gray map
GrayMap *gm = GrayMapCreate(iMap->width, iMap->height);
for (int row=0 ; row<gm->height ; row++)
for (int col=0 ; col<gm->width ; col++)
gm->setPixel(gm, col, row, GRAYMAP_WHITE);
for (int colorIndex=0 ; colorIndex<iMap->nrColors ; colorIndex++)
{
/*Make a gray map for each color index */
for (int row=0 ; row<iMap->height ; row++)
{
for (int col=0 ; col<iMap->width ; col++)
{
int indx = (int) iMap->getPixel(iMap, col, row);
if (indx == colorIndex)
{
gm->setPixel(gm, col, row, GRAYMAP_BLACK); //black
}
else
{
if (!multiScanStack)
gm->setPixel(gm, col, row, GRAYMAP_WHITE);//white
}
}
}
//## Now we have a traceable graymap
long nodeCount;
char *d = grayMapToPath(gm, &nodeCount);
if (!d)
{
*nrPaths = 0;
return NULL;
}
//### get style info
char style[13];
RGB rgb = iMap->clut[colorIndex];
sprintf(style, "fill:#%02x%02x%02x", rgb.r, rgb.g, rgb.b);
//g_message("### GOT '%s' \n", d);
TracingEngineResult *result = new TracingEngineResult(style, d, nodeCount);
free(d);
if (!results)
{
results = result; //first one
}
else
{
//prepend
/*
result->next = results;
results = result;
*/
//append
TracingEngineResult *r;
for (r=results ; r->next ; r=r->next)
{}
r->next = result;
}
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
if (desktop)
{
gchar *msg = g_strdup_printf(_("Trace: %d. %ld nodes"), colorIndex, nodeCount);
sp_desktop_message_stack(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
g_free(msg);
}
}
//report the count of paths processed
*nrPaths = iMap->nrColors;
gm->destroy(gm);
iMap->destroy(iMap);
return results;
}
/**
* This is the working method of this interface, and all
* implementing classes. Take a GdkPixbuf, trace it, and
* return the path data that is compatible with the d="" attribute
* of an SVG <path> element.
*/
TracingEngineResult *
PotraceTracingEngine::trace(GdkPixbuf * thePixbuf, int *nrPaths)
{
//Set up for messages
keepGoing = 1;
if ( traceType == TRACE_QUANT_COLOR ||
traceType == TRACE_QUANT_MONO )
{
return traceQuant(thePixbuf, nrPaths);
}
else if ( traceType == TRACE_BRIGHTNESS_MULTI )
{
return traceBrightnessMulti(thePixbuf, nrPaths);
}
else
{
return traceSingle(thePixbuf, nrPaths);
}
}
/**
* Abort the thread that is executing getPathDataFromPixbuf()
*/
void
PotraceTracingEngine::abort()
{
//g_message("PotraceTracingEngine::abort()\n");
keepGoing = 0;
}
} // namespace Potrace
} // namespace Trace
} // namespace Inkscape