diff options
Diffstat (limited to 'src/trace/potrace')
-rw-r--r-- | src/trace/potrace/bitmap.h | 118 | ||||
-rw-r--r-- | src/trace/potrace/inkscape-potrace.cpp | 679 | ||||
-rw-r--r-- | src/trace/potrace/inkscape-potrace.h | 145 |
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 : |