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