summaryrefslogtreecommitdiffstats
path: root/src/trace/autotrace
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
commitc853ffb5b2f75f5a889ed2e3ef89b818a736e87a (patch)
tree7d13a0883bb7936b84d6ecdd7bc332b41ed04bee /src/trace/autotrace
parentInitial commit. (diff)
downloadinkscape-upstream.tar.xz
inkscape-upstream.zip
Adding upstream version 1.3+ds.upstream/1.3+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/trace/autotrace')
-rw-r--r--src/trace/autotrace/inkscape-autotrace.cpp226
-rw-r--r--src/trace/autotrace/inkscape-autotrace.h61
2 files changed, 287 insertions, 0 deletions
diff --git a/src/trace/autotrace/inkscape-autotrace.cpp b/src/trace/autotrace/inkscape-autotrace.cpp
new file mode 100644
index 0000000..9a8f6eb
--- /dev/null
+++ b/src/trace/autotrace/inkscape-autotrace.cpp
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * This is the C++ glue between Inkscape and Autotrace
+ *//*
+ *
+ * Authors:
+ * Marc Jeanmougin
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ */
+#include <iomanip>
+#include <2geom/path-sink.h>
+#include <glibmm/i18n.h>
+
+#include "inkscape-autotrace.h"
+#include "async/progress.h"
+
+extern "C" {
+#include "3rdparty/autotrace/autotrace.h"
+#include "3rdparty/autotrace/output.h"
+#include "3rdparty/autotrace/spline.h"
+}
+
+namespace Inkscape {
+namespace Trace {
+namespace Autotrace {
+namespace {
+
+struct at_splines_deleter { void operator()(at_splines_type *p) { at_splines_free(p); }; };
+using at_splines_uniqptr = std::unique_ptr<at_splines_type, at_splines_deleter>;
+
+/**
+ * Eliminate the alpha channel by overlaying on top of white, and ensure the result is in packed RGB8 format.
+ * If nothing needs to be done, the original pixbuf is returned, otherwise a new pixbuf is returned.
+ */
+Glib::RefPtr<Gdk::Pixbuf> to_rgb8_packed(Glib::RefPtr<Gdk::Pixbuf> const &pixbuf)
+{
+ int width = pixbuf->get_width();
+ int height = pixbuf->get_height();
+ int rowstride = pixbuf->get_rowstride();
+ int nchannels = pixbuf->get_n_channels();
+ auto data = pixbuf->get_pixels();
+
+ if (nchannels == 3 && rowstride == width * 3) {
+ return pixbuf;
+ }
+
+ int imgsize = width * height;
+ auto out = new unsigned char[3 * imgsize];
+ auto q = out;
+
+ for (int y = 0; y < height; y++) {
+ auto p = data + rowstride * y;
+ for (int x = 0; x < width; x++) {
+ unsigned char alpha = nchannels == 3 ? 255 : p[3];
+ unsigned char white = 255 - alpha;
+ for (int c = 0; c < 3; c++) {
+ *(q++) = (int)p[c] * alpha / 256 + white;
+ }
+ p += nchannels;
+ }
+ }
+
+ return Gdk::Pixbuf::create_from_data(out, Gdk::COLORSPACE_RGB, false, 8, width, height, width * 3, [out] (auto) { delete [] out; });
+}
+
+} // namespace
+
+AutotraceTracingEngine::AutotraceTracingEngine()
+{
+ // Create options struct, automatically filled with defaults.
+ opts = at_fitting_opts_new();
+ opts->background_color = at_color_new(255, 255, 255);
+ autotrace_init();
+}
+
+AutotraceTracingEngine::~AutotraceTracingEngine()
+{
+ at_fitting_opts_free(opts);
+}
+
+Glib::RefPtr<Gdk::Pixbuf> AutotraceTracingEngine::preview(Glib::RefPtr<Gdk::Pixbuf> const &pixbuf)
+{
+ // Todo: Actually generate a meaningful preview.
+ return to_rgb8_packed(pixbuf);
+}
+
+TraceResult AutotraceTracingEngine::trace(Glib::RefPtr<Gdk::Pixbuf> const &pixbuf, Async::Progress<double> &progress)
+{
+ auto pb = to_rgb8_packed(pixbuf);
+
+ at_bitmap bitmap;
+ bitmap.height = pb->get_height();
+ bitmap.width = pb->get_width();
+ bitmap.bitmap = pb->get_pixels();
+ bitmap.np = 3;
+
+ auto throttled = Async::ProgressStepThrottler(progress, 0.02);
+ auto sub_trace = Async::SubProgress(throttled, 0.0, 0.8);
+
+ auto splines = at_splines_uniqptr(at_splines_new_full(
+ &bitmap, opts,
+ nullptr, nullptr,
+ [] (gfloat frac, gpointer data) { reinterpret_cast<decltype(sub_trace)*>(data)->report(frac); }, &sub_trace,
+ [] (gpointer data) -> gboolean { return !reinterpret_cast<decltype(sub_trace)*>(data)->keepgoing(); }, &sub_trace
+ ));
+ // at_output_write_func wfunc = at_output_get_handler_by_suffix("svg");
+ // at_spline_writer *wfunc = at_output_get_handler_by_suffix("svg");
+ // at_splines_write(wfunc, stdout, "", NULL, splines, NULL, NULL);
+
+ sub_trace.report_or_throw(1.0);
+ auto sub_convert = Async::SubProgress(throttled, 0.8, 0.2);
+
+ int height = splines->height;
+ at_spline_list_type list;
+ at_color last_color = { 0, 0, 0 };
+
+ std::string style;
+ Geom::PathBuilder pathbuilder;
+ TraceResult res;
+
+ auto get_style = [&] {
+ char color[10];
+ std::sprintf(color, "#%02x%02x%02x;", list.color.r, list.color.g, list.color.b);
+
+ std::stringstream ss;
+ ss << (splines->centerline || list.open ? "stroke:" : "fill:") << color
+ << (splines->centerline || list.open ? "fill:" : "stroke:") << "none";
+
+ return ss.str();
+ };
+
+ auto to_geom = [=] (at_real_coord const &c) {
+ return Geom::Point(c.x, height - c.y);
+ };
+
+ int const num_splines = SPLINE_LIST_ARRAY_LENGTH(*splines);
+ for (int list_i = 0; list_i < num_splines; list_i++) {
+ sub_convert.report_or_throw((double)list_i / num_splines);
+
+ list = SPLINE_LIST_ARRAY_ELT(*splines, list_i);
+
+ if (list_i == 0 || !at_color_equal(&list.color, &last_color)) {
+ if (list_i > 0) {
+ if (!(splines->centerline || list.open)) {
+ pathbuilder.closePath();
+ } else {
+ pathbuilder.flush();
+ }
+ res.emplace_back(std::move(style), pathbuilder.peek());
+ pathbuilder.clear();
+ }
+
+ style = get_style();
+ }
+
+ auto const first = SPLINE_LIST_ELT(list, 0);
+ pathbuilder.moveTo(to_geom(START_POINT(first)));
+
+ for (int spline_i = 0; spline_i < SPLINE_LIST_LENGTH(list); spline_i++) {
+ auto const spline = SPLINE_LIST_ELT(list, spline_i);
+
+ if (SPLINE_DEGREE(spline) == AT_LINEARTYPE) {
+ pathbuilder.lineTo(to_geom(END_POINT(spline)));
+ } else {
+ pathbuilder.curveTo(to_geom(CONTROL1(spline)), to_geom(CONTROL2(spline)), to_geom(END_POINT(spline)));
+ }
+
+ last_color = list.color;
+ }
+ }
+
+ if (SPLINE_LIST_ARRAY_LENGTH(*splines) > 0) {
+ if (!(splines->centerline || list.open)) {
+ pathbuilder.closePath();
+ } else {
+ pathbuilder.flush();
+ }
+ res.emplace_back(std::move(style), pathbuilder.peek());
+ }
+
+ return res;
+}
+
+void AutotraceTracingEngine::setColorCount(unsigned color_count)
+{
+ opts->color_count = color_count;
+}
+
+void AutotraceTracingEngine::setCenterLine(bool centerline)
+{
+ opts->centerline = centerline;
+}
+
+void AutotraceTracingEngine::setPreserveWidth(bool preserve_width)
+{
+ opts->preserve_width = preserve_width;
+}
+
+void AutotraceTracingEngine::setFilterIterations(unsigned filter_iterations)
+{
+ opts->filter_iterations = filter_iterations;
+}
+
+void AutotraceTracingEngine::setErrorThreshold(float error_threshold)
+{
+ opts->error_threshold = error_threshold;
+}
+
+} // namespace Autotrace
+} // 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/autotrace/inkscape-autotrace.h b/src/trace/autotrace/inkscape-autotrace.h
new file mode 100644
index 0000000..a117592
--- /dev/null
+++ b/src/trace/autotrace/inkscape-autotrace.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * This is the C++ glue between Inkscape and Autotrace
+ *//*
+ *
+ * Authors:
+ * Marc Jeanmougin
+ *
+ * Copyright (C) 2018 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ *
+ * Autotrace is available at http://github.com/autotrace/autotrace.
+ *
+ */
+#ifndef INKSCAPE_TRACE_AUTOTRACE_H
+#define INKSCAPE_TRACE_AUTOTRACE_H
+
+#include "trace/trace.h"
+using at_fitting_opts_type = struct _at_fitting_opts_type;
+
+namespace Inkscape {
+namespace Trace {
+namespace Autotrace {
+
+class AutotraceTracingEngine final
+ : public TracingEngine
+{
+public:
+ AutotraceTracingEngine();
+ ~AutotraceTracingEngine() override;
+
+ TraceResult trace(Glib::RefPtr<Gdk::Pixbuf> const &pixbuf, Async::Progress<double> &progress) override;
+ Glib::RefPtr<Gdk::Pixbuf> preview(Glib::RefPtr<Gdk::Pixbuf> const &pixbuf) override;
+
+ void setColorCount(unsigned);
+ void setCenterLine(bool);
+ void setPreserveWidth(bool);
+ void setFilterIterations(unsigned);
+ void setErrorThreshold(float);
+
+private:
+ at_fitting_opts_type *opts;
+};
+
+} // namespace Autotrace
+} // namespace Trace
+} // namespace Inkscape
+
+#endif // INKSCAPE_TRACE_AUTOTRACE_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 :