1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3 * This is the C++ glue between Inkscape and Autotrace
4 *//*
5 *
6 * Authors:
7 * Marc Jeanmougin
8 *
9 * Copyright (C) 2018 Authors
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 *
13 */
14
15 #include "inkscape-autotrace.h"
16
17 extern "C" {
18 #include "3rdparty/autotrace/autotrace.h"
19 #include "3rdparty/autotrace/output.h"
20 #include "3rdparty/autotrace/spline.h"
21 }
22
23 #include <glibmm/i18n.h>
24 #include <gtkmm/main.h>
25 #include <iomanip>
26
27 #include "trace/filterset.h"
28 #include "trace/imagemap-gdk.h"
29 #include "trace/quantize.h"
30
31 #include "desktop.h"
32 #include "message-stack.h"
33 #include <inkscape.h>
34
35 #include "object/sp-path.h"
36
37 #include <svg/path-string.h>
38
39 using Glib::ustring;
40
41 // static void updateGui()
42 // {
43 // //## Allow the GUI to update
44 // Gtk::Main::iteration(false); // at least once, non-blocking
45 // while (Gtk::Main::events_pending())
46 // Gtk::Main::iteration();
47 // }
48
49 namespace Inkscape {
50
51 namespace Trace {
52
53 namespace Autotrace {
54
to_3channels(GdkPixbuf * input)55 static guchar* to_3channels(GdkPixbuf* input) {
56 if (!input) {
57 return nullptr;
58 }
59 int imgsize = gdk_pixbuf_get_height(input) * gdk_pixbuf_get_width(input);
60 guchar *out = (guchar*)malloc(3 * imgsize);
61 if (!out) {
62 g_warning("Autotrace::to_3channels: can not allocate memory for %d pixel image.", imgsize);
63 return nullptr;
64 }
65 int x=0;
66 guchar* pix = gdk_pixbuf_get_pixels (input);
67 int rs = gdk_pixbuf_get_rowstride (input);
68 for(int row=0;row<gdk_pixbuf_get_height(input);row++) {
69 for (int col=0;col<gdk_pixbuf_get_width(input);col++) {
70 guchar alpha = *(pix + row * rs + col * 4 + 3);
71 guchar white = 255 - alpha;
72 for(int chan=0;chan<3;chan++) {
73 // guchar *pnew = (pix + row * rs + col * 3 + chan);
74 guchar *pold = (pix + row * rs + col * 4 + chan);
75 out[x++] = (guchar)(((int)(*pold) * (int)alpha / 256) + white);
76 }
77 }
78 }
79 return out;
80 }
81
82
83 /**
84 *
85 */
AutotraceTracingEngine()86 AutotraceTracingEngine::AutotraceTracingEngine()
87 : keepGoing(1)
88 , traceType(TRACE_OUTLINE)
89 , invert(false)
90 {
91 /* get default parameters */
92 opts = at_fitting_opts_new();
93 opts->background_color = at_color_new(255,255,255);
94 autotrace_init();
95 }
96
~AutotraceTracingEngine()97 AutotraceTracingEngine::~AutotraceTracingEngine() { at_fitting_opts_free(opts); }
98
99
100
101 // TODO
preview(Glib::RefPtr<Gdk::Pixbuf> thePixbuf)102 Glib::RefPtr<Gdk::Pixbuf> AutotraceTracingEngine::preview(Glib::RefPtr<Gdk::Pixbuf> thePixbuf) {
103 //auto x = thePixbuf.copy();
104 guchar *pb = to_3channels(thePixbuf->gobj());
105 if (!pb) {
106 return Glib::RefPtr<Gdk::Pixbuf>();
107 }
108 return Gdk::Pixbuf::create_from_data(pb, thePixbuf->get_colorspace(), false, 8, thePixbuf->get_width(),
109 thePixbuf->get_height(), (thePixbuf->get_width() * 3),
110 [](const guint8 *pb) { free(const_cast<guint8 *>(pb)); });
111 }
112
test_cancel(void * keepGoing)113 int test_cancel (void* keepGoing){return !(* ((int*)keepGoing));}
114
115 /**
116 * This is the working method of this interface, and all
117 * implementing classes. Take a GdkPixbuf, trace it, and
118 * return the path data that is compatible with the d="" attribute
119 * of an SVG <path> element.
120 */
trace(Glib::RefPtr<Gdk::Pixbuf> pixbuf)121 std::vector<TracingEngineResult> AutotraceTracingEngine::trace(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
122 {
123 GdkPixbuf *pb1 = pixbuf->gobj();
124 guchar *pb = to_3channels(pb1);
125 if (!pb) {
126 return std::vector<TracingEngineResult>();
127 }
128
129 at_bitmap *bitmap =
130 // at_bitmap_new(gdk_pixbuf_get_width(pb), gdk_pixbuf_get_height(pb), gdk_pixbuf_get_n_channels(pb));
131 // bitmap->bitmap = gdk_pixbuf_get_pixels(pb);
132 at_bitmap_new(gdk_pixbuf_get_width(pb1), gdk_pixbuf_get_height(pb1), 3);
133 free(bitmap->bitmap); // should create at_bitmap with bitmap->bitmap = pb
134 bitmap->bitmap = pb;
135
136 at_splines_type *splines = at_splines_new_full(bitmap, opts, NULL, NULL, NULL, NULL, test_cancel, &keepGoing);
137 // at_output_write_func wfunc = at_output_get_handler_by_suffix("svg");
138 // at_spline_writer *wfunc = at_output_get_handler_by_suffix("svg");
139
140
141 int height = splines->height;
142 // const at_splines_type spline = *splines;
143 at_spline_list_array_type spline = *splines;
144
145 unsigned this_list;
146 at_spline_list_type list;
147 at_color last_color = { 0, 0, 0 };
148
149 std::stringstream theStyle;
150 std::stringstream thePath;
151 char color[10];
152 int nNodes = 0;
153
154 std::vector<TracingEngineResult> res;
155
156 // at_splines_write(wfunc, stdout, "", NULL, splines, NULL, NULL);
157
158 for (this_list = 0; this_list < SPLINE_LIST_ARRAY_LENGTH(spline); this_list++) {
159 unsigned this_spline;
160 at_spline_type first;
161
162 list = SPLINE_LIST_ARRAY_ELT(spline, this_list);
163 first = SPLINE_LIST_ELT(list, 0);
164
165 if (this_list == 0 || !at_color_equal(&list.color, &last_color)) {
166 if (this_list > 0) {
167 if (!(spline.centerline || list.open)) {
168 thePath << "z";
169 nNodes++;
170 }
171 TracingEngineResult ter(theStyle.str(), thePath.str(), nNodes);
172 res.push_back(ter);
173 theStyle.clear();
174 thePath.clear();
175 nNodes = 0;
176 }
177 sprintf(color, "#%02x%02x%02x;", list.color.r, list.color.g, list.color.b);
178
179 theStyle << ((spline.centerline || list.open) ? "stroke:" : "fill:") << color
180 << ((spline.centerline || list.open) ? "fill:" : "stroke:") << "none";
181 }
182 thePath << "M" << START_POINT(first).x << " " << height - START_POINT(first).y;
183 nNodes++;
184 for (this_spline = 0; this_spline < SPLINE_LIST_LENGTH(list); this_spline++) {
185 at_spline_type s = SPLINE_LIST_ELT(list, this_spline);
186
187 if (SPLINE_DEGREE(s) == AT_LINEARTYPE) {
188 thePath << "L" << END_POINT(s).x << " " << height - END_POINT(s).y;
189 nNodes++;
190 }
191 else {
192 thePath << "C" << CONTROL1(s).x << " " << height - CONTROL1(s).y << " " << CONTROL2(s).x << " "
193 << height - CONTROL2(s).y << " " << END_POINT(s).x << " " << height - END_POINT(s).y;
194 nNodes++;
195 }
196 last_color = list.color;
197 }
198 }
199 if (!(spline.centerline || list.open))
200 thePath << "z";
201 nNodes++;
202 if (SPLINE_LIST_ARRAY_LENGTH(spline) > 0) {
203 TracingEngineResult ter(theStyle.str(), thePath.str(), nNodes);
204 res.push_back(ter);
205 theStyle.clear();
206 thePath.clear();
207 nNodes = 0;
208 }
209
210 return res;
211 }
212
213
214 /**
215 * Abort the thread that is executing getPathDataFromPixbuf()
216 */
abort()217 void AutotraceTracingEngine::abort()
218 {
219 // g_message("PotraceTracingEngine::abort()\n");
220 keepGoing = 0;
221 }
222
223
224
225 } // namespace Autotrace
226 } // namespace Trace
227 } // namespace Inkscape
228
229 /*
230 Local Variables:
231 mode:c++
232 c-file-style:"stroustrup"
233 c-file-offsets:((innamespace . 0)(inline-open . 0))
234 indent-tabs-mode:nil
235 fill-column:99
236 End:
237 */
238 // vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
239