summaryrefslogtreecommitdiffstats
path: root/src/trace/potrace
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/trace/potrace
parentInitial commit. (diff)
downloadinkscape-upstream.tar.xz
inkscape-upstream.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/trace/potrace')
-rw-r--r--src/trace/potrace/bitmap.h118
-rw-r--r--src/trace/potrace/inkscape-potrace.cpp679
-rw-r--r--src/trace/potrace/inkscape-potrace.h145
3 files changed, 942 insertions, 0 deletions
diff --git a/src/trace/potrace/bitmap.h b/src/trace/potrace/bitmap.h
new file mode 100644
index 0000000..2fb07e8
--- /dev/null
+++ b/src/trace/potrace/bitmap.h
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * inline macros for accessing bitmaps
+ */
+/* Copyright (C) 2001-2015 Peter Selinger.
+ This file is part of Potrace. It is free software and it is covered
+ by the GNU General Public License. See the file COPYING for details. */
+
+#ifndef BITMAP_H
+#define BITMAP_H
+
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+#include <cstddef>
+
+/* The bitmap type is defined in potracelib.h */
+#include "potracelib.h"
+
+/* The present file defines some convenient macros and static inline
+ functions for accessing bitmaps. Since they only produce inline
+ code, they can be conveniently shared by the library and frontends,
+ if desired */
+
+/* ---------------------------------------------------------------------- */
+/* some measurements */
+
+#define BM_WORDSIZE ((int)sizeof(potrace_word))
+#define BM_WORDBITS (8*BM_WORDSIZE)
+#define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1))
+#define BM_ALLBITS (~(potrace_word)0)
+
+/* macros for accessing pixel at index (x,y). U* macros omit the
+ bounds check. */
+
+#define bm_scanline(bm, y) ((bm)->map + (ptrdiff_t)(y)*(ptrdiff_t)(bm)->dy)
+#define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS])
+#define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1)))
+#define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a))
+#define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h))
+#define BM_UGET(bm, x, y) ((*bm_index(bm, x, y) & bm_mask(x)) != 0)
+#define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x))
+#define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x))
+#define BM_UINV(bm, x, y) (*bm_index(bm, x, y) ^= bm_mask(x))
+#define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y))
+#define BM_GET(bm, x, y) (bm_safe(bm, x, y) ? BM_UGET(bm, x, y) : 0)
+#define BM_SET(bm, x, y) (bm_safe(bm, x, y) ? BM_USET(bm, x, y) : 0)
+#define BM_CLR(bm, x, y) (bm_safe(bm, x, y) ? BM_UCLR(bm, x, y) : 0)
+#define BM_INV(bm, x, y) (bm_safe(bm, x, y) ? BM_UINV(bm, x, y) : 0)
+#define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0)
+
+/* free the given bitmap. Leaves errno untouched. */
+static inline void bm_free(potrace_bitmap_t *bm) {
+ if (bm) {
+ free(bm->map);
+ }
+ free(bm);
+}
+
+/* return new un-initialized bitmap. NULL with errno on error.
+ Assumes w, h >= 0. */
+static inline potrace_bitmap_t *bm_new(int w, int h) {
+ potrace_bitmap_t *bm;
+ int dy = w == 0 ? 0 : (w - 1) / BM_WORDBITS + 1;
+ ptrdiff_t size = (ptrdiff_t)dy * (ptrdiff_t)h * (ptrdiff_t)BM_WORDSIZE;
+
+ /* check for overflow error */
+ if (size < 0 || (h != 0 && dy != 0 && size / h / dy != BM_WORDSIZE)) {
+ errno = ENOMEM;
+ return nullptr;
+ }
+
+ bm = (potrace_bitmap_t *) malloc(sizeof(potrace_bitmap_t));
+ if (!bm) {
+ return nullptr;
+ }
+ bm->w = w;
+ bm->h = h;
+ bm->dy = dy;
+ bm->map = (potrace_word *) malloc(size);
+ if (!bm->map) {
+ g_warning("bm_new: can not allocate memory for bitmap (%td).", size);
+ free(bm);
+ return nullptr;
+ }
+ return bm;
+}
+
+/* clear the given bitmap. Set all bits to c. */
+static inline void bm_clear(potrace_bitmap_t *bm, int c) {
+ /* Note: if the bitmap was created with bm_new, then it is
+ guaranteed that size will fit into the ptrdiff_t type. */
+ ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h * (ptrdiff_t)BM_WORDSIZE;
+ memset(bm->map, c ? -1 : 0, size);
+}
+
+/* duplicate the given bitmap. Return NULL on error with errno set. */
+static inline potrace_bitmap_t *bm_dup(const potrace_bitmap_t *bm) {
+ potrace_bitmap_t *bm1 = bm_new(bm->w, bm->h);
+ ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h * (ptrdiff_t)BM_WORDSIZE;
+ if (!bm1) {
+ return nullptr;
+ }
+ memcpy(bm1->map, bm->map, size);
+ return bm1;
+}
+
+/* invert the given bitmap. */
+static inline void bm_invert(potrace_bitmap_t *bm) {
+ ptrdiff_t i;
+ ptrdiff_t size = (ptrdiff_t)bm->dy * (ptrdiff_t)bm->h;
+
+ for (i = 0; i < size; i++) {
+ bm->map[i] ^= BM_ALLBITS;
+ }
+}
+
+#endif /* BITMAP_H */
diff --git a/src/trace/potrace/inkscape-potrace.cpp b/src/trace/potrace/inkscape-potrace.cpp
new file mode 100644
index 0000000..d6e9b8f
--- /dev/null
+++ b/src/trace/potrace/inkscape-potrace.cpp
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This is the C++ glue between Inkscape and Potrace
+ *
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ * Stéphane Gimenez <dev@gim.name>
+ *
+ * Copyright (C) 2004-2006 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ * Potrace, the wonderful tracer located at http://potrace.sourceforge.net,
+ * is provided by the generosity of Peter Selinger, to whom we are grateful.
+ *
+ */
+
+#include "inkscape-potrace.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm/main.h>
+#include <iomanip>
+
+#include "trace/filterset.h"
+#include "trace/quantize.h"
+#include "trace/imagemap-gdk.h"
+
+#include <inkscape.h>
+#include "desktop.h"
+#include "message-stack.h"
+
+#include "object/sp-path.h"
+
+#include <svg/path-string.h>
+#include "bitmap.h"
+
+using Glib::ustring;
+
+static void updateGui()
+{
+ //## Allow the GUI to update
+ Gtk::Main::iteration(false); //at least once, non-blocking
+ while( Gtk::Main::events_pending() )
+ Gtk::Main::iteration();
+
+}
+
+
+static void potraceStatusCallback(double /*progress*/, void *userData) /* callback fn */
+{
+ updateGui();
+
+ if (!userData)
+ return;
+
+ //g_message("progress: %f\n", progress);
+
+ //Inkscape::Trace::Potrace::PotraceTracingEngine *engine =
+ // (Inkscape::Trace::Potrace::PotraceTracingEngine *)userData;
+}
+
+
+namespace {
+ustring twohex( int value )
+{
+ return ustring::format(std::hex, std::setfill(L'0'), std::setw(2), value);
+}
+} // namespace
+
+
+//required by potrace
+namespace Inkscape {
+
+namespace Trace {
+
+namespace Potrace {
+
+
+/**
+ *
+ */
+PotraceTracingEngine::PotraceTracingEngine() :
+ keepGoing(1),
+ traceType(TRACE_BRIGHTNESS),
+ invert(false),
+ quantizationNrColors(8),
+ brightnessThreshold(0.45),
+ brightnessFloor(0),
+ cannyHighThreshold(0.65),
+ multiScanNrColors(8),
+ multiScanStack(true),
+ multiScanSmooth(false),
+ multiScanRemoveBackground(false)
+{
+ /* get default parameters */
+ potraceParams = potrace_param_default();
+ potraceParams->progress.callback = potraceStatusCallback;
+ potraceParams->progress.data = (void *)this;
+}
+
+PotraceTracingEngine::PotraceTracingEngine(TraceType traceType, bool invert, int quantizationNrColors, double brightnessThreshold, double brightnessFloor, double cannyHighThreshold, int multiScanNrColors, bool multiScanStack, bool multiScanSmooth, bool multiScanRemoveBackground) :
+ keepGoing(1), traceType(traceType), invert(invert), quantizationNrColors(quantizationNrColors), brightnessThreshold(brightnessThreshold), brightnessFloor(brightnessFloor), cannyHighThreshold(cannyHighThreshold), multiScanNrColors(multiScanNrColors) , multiScanStack(multiScanStack), multiScanSmooth(multiScanSmooth), multiScanRemoveBackground(multiScanRemoveBackground)
+{
+ potraceParams = potrace_param_default();
+ potraceParams->progress.callback = potraceStatusCallback;
+ potraceParams->progress.data = (void *)this;
+}
+
+
+PotraceTracingEngine::~PotraceTracingEngine()
+{
+ potrace_param_free(potraceParams);
+}
+
+
+
+
+struct Point
+{
+ double x;
+ double y;
+};
+
+
+/**
+ * Check a point against a list of points to see if it
+ * has already occurred.
+ */
+static bool hasPoint(std::vector<Point> &points, double x, double y)
+{
+ for (auto p : points)
+ {
+ if (p.x == x && p.y == y)
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Recursively descend the potrace_path_t node tree, writing paths in SVG
+ * format into the output stream. The Point vector is used to prevent
+ * redundant paths. Returns number of paths processed.
+ */
+static long writePaths(PotraceTracingEngine *engine, potrace_path_t *plist,
+ Inkscape::SVG::PathString& data, std::vector<Point> &points)
+{
+ long nodeCount = 0L;
+
+ potrace_path_t *node;
+ for (node=plist; node ; node=node->sibling)
+ {
+ potrace_curve_t *curve = &(node->curve);
+ //g_message("node->fm:%d\n", node->fm);
+ if (!curve->n)
+ continue;
+ const potrace_dpoint_t *pt = curve->c[curve->n - 1];
+ double x0 = 0.0;
+ double y0 = 0.0;
+ double x1 = 0.0;
+ double y1 = 0.0;
+ double x2 = pt[2].x;
+ double y2 = pt[2].y;
+ //Have we been here already?
+ if (hasPoint(points, x2, y2))
+ {
+ //g_message("duplicate point: (%f,%f)\n", x2, y2);
+ continue;
+ }
+ else
+ {
+ Point p;
+ p.x = x2; p.y = y2;
+ points.push_back(p);
+ }
+ data.moveTo(x2, y2);
+ nodeCount++;
+
+ for (int i=0 ; i<curve->n ; i++)
+ {
+ if (!engine->keepGoing)
+ return 0L;
+ pt = curve->c[i];
+ x0 = pt[0].x;
+ y0 = pt[0].y;
+ x1 = pt[1].x;
+ y1 = pt[1].y;
+ x2 = pt[2].x;
+ y2 = pt[2].y;
+ switch (curve->tag[i])
+ {
+ case POTRACE_CORNER:
+ data.lineTo(x1, y1).lineTo(x2, y2);
+ break;
+ case POTRACE_CURVETO:
+ data.curveTo(x0, y0, x1, y1, x2, y2);
+ break;
+ default:
+ break;
+ }
+ nodeCount++;
+ }
+ data.closePath();
+
+ for (potrace_path_t *child=node->childlist; child ; child=child->sibling)
+ {
+ nodeCount += writePaths(engine, child, data, points);
+ }
+ }
+
+ return nodeCount;
+
+}
+
+
+static GrayMap *filter(PotraceTracingEngine &engine, GdkPixbuf * pixbuf)
+{
+ if (!pixbuf)
+ return nullptr;
+
+ GrayMap *newGm = nullptr;
+
+ /*### Color quantization -- banding ###*/
+ if (engine.traceType == TRACE_QUANT)
+ {
+ RgbMap *rgbmap = gdkPixbufToRgbMap(pixbuf);
+ if (!rgbmap)
+ return nullptr;
+ //rgbMap->writePPM(rgbMap, "rgb.ppm");
+ newGm = quantizeBand(rgbmap,
+ engine.quantizationNrColors);
+ rgbmap->destroy(rgbmap);
+ //return newGm;
+ }
+
+ /*### Brightness threshold ###*/
+ else if ( engine.traceType == TRACE_BRIGHTNESS ||
+ engine.traceType == TRACE_BRIGHTNESS_MULTI )
+ {
+ GrayMap *gm = gdkPixbufToGrayMap(pixbuf);
+ if (!gm)
+ return nullptr;
+
+ newGm = GrayMapCreate(gm->width, gm->height);
+ if (!newGm)
+ {
+ gm->destroy(gm);
+ return nullptr;
+ }
+ double floor = 3.0 *
+ ( engine.brightnessFloor * 256.0 );
+ double cutoff = 3.0 *
+ ( engine.brightnessThreshold * 256.0 );
+ for (int y=0 ; y<gm->height ; y++)
+ {
+ for (int x=0 ; x<gm->width ; x++)
+ {
+ double brightness = (double)gm->getPixel(gm, x, y);
+ if (brightness >= floor && brightness < cutoff)
+ newGm->setPixel(newGm, x, y, GRAYMAP_BLACK); //black pixel
+ else
+ newGm->setPixel(newGm, x, y, GRAYMAP_WHITE); //white pixel
+ }
+ }
+
+ gm->destroy(gm);
+ //newGm->writePPM(newGm, "brightness.ppm");
+ //return newGm;
+ }
+
+ /*### Canny edge detection ###*/
+ else if (engine.traceType == TRACE_CANNY)
+ {
+ GrayMap *gm = gdkPixbufToGrayMap(pixbuf);
+ if (!gm)
+ return nullptr;
+ newGm = grayMapCanny(gm, 0.1, engine.cannyHighThreshold);
+ gm->destroy(gm);
+ //newGm->writePPM(newGm, "canny.ppm");
+ //return newGm;
+ }
+
+ /*### Do I invert the image? ###*/
+ if (newGm && engine.invert)
+ {
+ for (int y=0 ; y<newGm->height ; y++)
+ {
+ for (int x=0 ; x<newGm->width ; x++)
+ {
+ unsigned long brightness = newGm->getPixel(newGm, x, y);
+ brightness = 765 - brightness;
+ newGm->setPixel(newGm, x, y, brightness);
+ }
+ }
+ }
+
+ return newGm;//none of the above
+}
+
+
+static IndexedMap *filterIndexed(PotraceTracingEngine &engine, GdkPixbuf * pixbuf)
+{
+ if (!pixbuf)
+ return nullptr;
+
+ IndexedMap *newGm = nullptr;
+
+ RgbMap *gm = gdkPixbufToRgbMap(pixbuf);
+ if (!gm)
+ return nullptr;
+ if (engine.multiScanSmooth)
+ {
+ RgbMap *gaussMap = rgbMapGaussian(gm);
+ newGm = rgbMapQuantize(gaussMap, engine.multiScanNrColors);
+ gaussMap->destroy(gaussMap);
+ }
+ else
+ {
+ newGm = rgbMapQuantize(gm, engine.multiScanNrColors);
+ }
+ gm->destroy(gm);
+
+ if (newGm && (engine.traceType == TRACE_QUANT_MONO || engine.traceType == TRACE_BRIGHTNESS_MULTI))
+ {
+ //Turn to grays
+ for (int i=0 ; i<newGm->nrColors ; i++)
+ {
+ RGB rgb = newGm->clut[i];
+ int grayVal = (rgb.r + rgb.g + rgb.b) / 3;
+ rgb.r = rgb.g = rgb.b = grayVal;
+ newGm->clut[i] = rgb;
+ }
+ }
+
+ return newGm;
+}
+
+
+
+
+Glib::RefPtr<Gdk::Pixbuf>
+PotraceTracingEngine::preview(Glib::RefPtr<Gdk::Pixbuf> thePixbuf)
+{
+ GdkPixbuf *pixbuf = thePixbuf->gobj();
+
+ if ( traceType == TRACE_QUANT_COLOR ||
+ traceType == TRACE_QUANT_MONO ||
+ traceType == TRACE_BRIGHTNESS_MULTI) /* this is a lie: multipass doesn't use filterIndexed, but it's a better preview approx than filter() */
+ {
+ IndexedMap *gm = filterIndexed(*this, pixbuf);
+ if (!gm)
+ return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
+
+ Glib::RefPtr<Gdk::Pixbuf> newBuf =
+ Glib::wrap(indexedMapToGdkPixbuf(gm), false);
+
+ gm->destroy(gm);
+
+ return newBuf;
+ }
+ else
+ {
+ GrayMap *gm = filter(*this, pixbuf);
+ if (!gm)
+ return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
+
+ Glib::RefPtr<Gdk::Pixbuf> newBuf =
+ Glib::wrap(grayMapToGdkPixbuf(gm), false);
+
+ gm->destroy(gm);
+
+ return newBuf;
+ }
+}
+
+
+//*This is the core inkscape-to-potrace binding
+std::string PotraceTracingEngine::grayMapToPath(GrayMap *grayMap, long *nodeCount)
+{
+ if (!keepGoing)
+ {
+ g_warning("aborted");
+ return "";
+ }
+
+ potrace_bitmap_t *potraceBitmap = bm_new(grayMap->width, grayMap->height);
+ if (!potraceBitmap)
+ {
+ return "";
+ }
+ 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);
+ */
+
+ /* 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);
+ return "";
+ }
+
+ Inkscape::SVG::PathString 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);
+
+ if (!keepGoing)
+ return "";
+
+ if ( nodeCount)
+ *nodeCount = thisNodeCount;
+
+ return data.string();
+}
+
+
+
+/**
+ * This is called for a single scan
+ */
+std::vector<TracingEngineResult> PotraceTracingEngine::traceSingle(GdkPixbuf * thePixbuf)
+{
+
+ std::vector<TracingEngineResult> results;
+
+ if (!thePixbuf)
+ return results;
+
+ brightnessFloor = 0.0; //important to set this
+
+ GrayMap *grayMap = filter(*this, thePixbuf);
+ if (!grayMap)
+ return results;
+
+ long nodeCount = 0L;
+ std::string d = grayMapToPath(grayMap, &nodeCount);
+
+ grayMap->destroy(grayMap);
+
+ char const *style = "fill:#000000";
+
+ //g_message("### GOT '%s' \n", d);
+ TracingEngineResult result(style, d, nodeCount);
+ results.push_back(result);
+
+ return results;
+}
+
+
+/**
+ * This allows routines that already generate GrayMaps to skip image filtering,
+ * increasing performance.
+ */
+std::vector<TracingEngineResult> PotraceTracingEngine::traceGrayMap(GrayMap *grayMap)
+{
+
+ std::vector<TracingEngineResult> results;
+
+ brightnessFloor = 0.0; //important to set this
+
+ long nodeCount = 0L;
+ std::string d = grayMapToPath(grayMap, &nodeCount);
+
+ char const *style = "fill:#000000";
+
+ //g_message("### GOT '%s' \n", d);
+ TracingEngineResult result(style, d, nodeCount);
+ results.push_back(result);
+
+ return results;
+}
+
+/**
+ * Called for multiple-scanning algorithms
+ */
+std::vector<TracingEngineResult> PotraceTracingEngine::traceBrightnessMulti(GdkPixbuf * thePixbuf)
+{
+ std::vector<TracingEngineResult> results;
+
+ if ( thePixbuf ) {
+ 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;
+
+ for ( brightnessThreshold = low ;
+ brightnessThreshold <= high ;
+ brightnessThreshold += delta) {
+ GrayMap *grayMap = filter(*this, thePixbuf);
+ if ( grayMap ) {
+ long nodeCount = 0L;
+ std::string d = grayMapToPath(grayMap, &nodeCount);
+
+ grayMap->destroy(grayMap);
+
+ if ( !d.empty() ) {
+ //### get style info
+ int grayVal = (int)(256.0 * brightnessThreshold);
+ ustring style = ustring::compose("fill-opacity:1.0;fill:#%1%2%3", twohex(grayVal), twohex(grayVal), twohex(grayVal) );
+
+ //g_message("### GOT '%s' \n", style.c_str());
+ TracingEngineResult result(style.raw(), d, nodeCount);
+ results.push_back(result);
+
+ if (!multiScanStack) {
+ brightnessFloor = brightnessThreshold;
+ }
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ ustring msg = ustring::compose(_("Trace: %1. %2 nodes"), traceCount++, nodeCount);
+ desktop->getMessageStack()->flash(Inkscape::NORMAL_MESSAGE, msg);
+ }
+ }
+ }
+ }
+
+ //# Remove the bottom-most scan, if requested
+ if (results.size() > 1 && multiScanRemoveBackground) {
+ results.erase(results.end() - 1);
+ }
+ }
+
+ return results;
+}
+
+
+/**
+ * Quantization
+ */
+std::vector<TracingEngineResult> PotraceTracingEngine::traceQuant(GdkPixbuf * thePixbuf)
+{
+ std::vector<TracingEngineResult> results;
+
+ if (thePixbuf) {
+ IndexedMap *iMap = filterIndexed(*this, thePixbuf);
+ if ( iMap ) {
+ //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 = 0L;
+ std::string d = grayMapToPath(gm, &nodeCount);
+
+ if ( !d.empty() ) {
+ //### get style info
+ RGB rgb = iMap->clut[colorIndex];
+ ustring style = ustring::compose("fill:#%1%2%3", twohex(rgb.r), twohex(rgb.g), twohex(rgb.b) );
+
+ //g_message("### GOT '%s' \n", style.c_str());
+ TracingEngineResult result(style.raw(), d, nodeCount);
+ results.push_back(result);
+
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+ ustring msg = ustring::compose(_("Trace: %1. %2 nodes"), colorIndex, nodeCount);
+ desktop->getMessageStack()->flash(Inkscape::NORMAL_MESSAGE, msg);
+ }
+ }
+ }// for colorIndex
+
+ gm->destroy(gm);
+ iMap->destroy(iMap);
+ }
+
+ //# Remove the bottom-most scan, if requested
+ if (results.size() > 1 && multiScanRemoveBackground) {
+ results.erase(results.end() - 1);
+ }
+ }
+
+ 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.
+ */
+std::vector<TracingEngineResult>
+PotraceTracingEngine::trace(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
+{
+
+ GdkPixbuf *thePixbuf = pixbuf->gobj();
+
+ //Set up for messages
+ keepGoing = 1;
+
+ if ( traceType == TRACE_QUANT_COLOR ||
+ traceType == TRACE_QUANT_MONO )
+ {
+ return traceQuant(thePixbuf);
+ }
+ else if ( traceType == TRACE_BRIGHTNESS_MULTI )
+ {
+ return traceBrightnessMulti(thePixbuf);
+ }
+ else
+ {
+ return traceSingle(thePixbuf);
+ }
+}
+
+
+/**
+ * Abort the thread that is executing getPathDataFromPixbuf()
+ */
+void PotraceTracingEngine::abort()
+{
+ //g_message("PotraceTracingEngine::abort()\n");
+ keepGoing = 0;
+}
+
+
+
+
+} // namespace Potrace
+} // namespace Trace
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/trace/potrace/inkscape-potrace.h b/src/trace/potrace/inkscape-potrace.h
new file mode 100644
index 0000000..c4c7bf4
--- /dev/null
+++ b/src/trace/potrace/inkscape-potrace.h
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This is the C++ glue between Inkscape and Potrace
+ *
+ * Authors:
+ * Bob Jamison <rjamison@titan.com>
+ * Stéphane Gimenez <dev@gim.name>
+ *
+ * Copyright (C) 2004-2006 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ * Potrace, the wonderful tracer located at http://potrace.sourceforge.net,
+ * is provided by the generosity of Peter Selinger, to whom we are grateful.
+ *
+ */
+
+#ifndef __INKSCAPE_POTRACE_H__
+#define __INKSCAPE_POTRACE_H__
+
+#include <trace/trace.h>
+#include <potracelib.h>
+
+struct GrayMap_def;
+typedef GrayMap_def GrayMap;
+
+namespace Inkscape {
+
+namespace Trace {
+
+namespace Potrace {
+
+enum TraceType
+ {
+ TRACE_BRIGHTNESS,
+ TRACE_BRIGHTNESS_MULTI,
+ TRACE_CANNY,
+ TRACE_QUANT,
+ TRACE_QUANT_COLOR,
+ TRACE_QUANT_MONO,
+ // Used in tradedialog.cpp
+ AUTOTRACE_SINGLE,
+ AUTOTRACE_MULTI,
+ AUTOTRACE_CENTERLINE
+ };
+
+
+class PotraceTracingEngine : public TracingEngine
+{
+
+ public:
+
+ /**
+ *
+ */
+ PotraceTracingEngine();
+ PotraceTracingEngine(TraceType traceType, bool invert, int quantizationNrColors, double brightnessThreshold, double brightnessFloor, double cannyHighThreshold, int multiScanNrColors, bool multiScanStack, bool multiScanSmooth, bool multiScanRemoveBackground);
+
+ /**
+ *
+ */
+ ~PotraceTracingEngine() override;
+
+ /**
+ * This is the working method of this implementing class, 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.
+ */
+ std::vector<TracingEngineResult> trace(
+ Glib::RefPtr<Gdk::Pixbuf> pixbuf) override;
+
+ /**
+ * Abort the thread that is executing getPathDataFromPixbuf()
+ */
+ void abort() override;
+
+ /**
+ *
+ */
+ Glib::RefPtr<Gdk::Pixbuf> preview(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
+
+ /**
+ *
+ */
+ int keepGoing;
+
+ std::vector<TracingEngineResult>traceGrayMap(GrayMap *grayMap);
+
+ potrace_param_t *potraceParams;
+ TraceType traceType;
+
+ //## do I invert at the end?
+ bool invert;
+
+ //## Color-->b&w quantization
+ int quantizationNrColors;
+
+ //## brightness items
+ double brightnessThreshold;
+ double brightnessFloor; //usually 0.0
+
+ //## canny items
+ double cannyHighThreshold;
+
+ //## Color-->multiscan quantization
+ int multiScanNrColors;
+ bool multiScanStack; //do we tile or stack?
+ bool multiScanSmooth;//do we use gaussian filter?
+ bool multiScanRemoveBackground; //do we remove the bottom trace?
+
+ private:
+ /**
+ * This is the actual wrapper of the call to Potrace. nodeCount
+ * returns the count of nodes created. May be NULL if ignored.
+ */
+ std::string grayMapToPath(GrayMap *gm, long *nodeCount);
+
+ std::vector<TracingEngineResult>traceBrightnessMulti(GdkPixbuf *pixbuf);
+ std::vector<TracingEngineResult>traceQuant(GdkPixbuf *pixbuf);
+ std::vector<TracingEngineResult>traceSingle(GdkPixbuf *pixbuf);
+
+
+};//class PotraceTracingEngine
+
+
+
+} // namespace Potrace
+} // namespace Trace
+} // namespace Inkscape
+
+
+#endif //__INKSCAPE_POTRACE_H__
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :