1 /*
2 * A generic interface for plugging different
3 * autotracers into Inkscape.
4 *
5 * Authors:
6 * Bob Jamison <rjamison@titan.com>
7 *
8 * Copyright (C) 2004 Bob Jamison
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
15 #include "trace/potrace/inkscape-potrace.h"
17 #include <inkscape.h>
18 #include <desktop-handles.h>
19 #include <document.h>
20 #include "message-stack.h"
21 #include <glibmm/i18n.h>
22 #include <selection.h>
23 #include <xml/repr.h>
24 #include "sp-item.h"
25 #include "sp-image.h"
27 namespace Inkscape {
29 namespace Trace {
31 /**
32 *
33 */
34 SPImage *
35 Tracer::getSelectedSPImage()
36 {
37 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
38 if (!desktop)
39 {
40 g_warning("Trace: No active desktop\n");
41 return NULL;
42 }
44 Inkscape::Selection *sel = SP_DT_SELECTION(desktop);
45 if (!sel)
46 {
47 char *msg = _("Select an <b>image</b> to trace");
48 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
49 //g_warning(msg);
50 return NULL;
51 }
53 SPItem *item = sel->singleItem();
54 if (!item)
55 {
56 char *msg = _("Select an <b>image</b> to trace"); //same as above
57 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
58 //g_warning(msg);
59 return NULL;
60 }
62 if (!SP_IS_IMAGE(item))
63 {
64 char *msg = _("Select an <b>image</b> to trace");
65 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
66 //g_warning(msg);
67 return NULL;
68 }
70 selectedItem = item;
72 SPImage *img = SP_IMAGE(item);
74 return img;
76 }
80 /**
81 *
82 */
83 GdkPixbuf *
84 Tracer::getSelectedImage()
85 {
87 SPImage *img = getSelectedSPImage();
88 if (!img)
89 return NULL;
91 GdkPixbuf *pixbuf = img->pixbuf;
93 return pixbuf;
95 }
99 //#########################################################################
100 //# T R A C E
101 //#########################################################################
103 /**
104 * Whether we want to enable SIOX subimage selection
105 */
106 void Tracer::enableSiox(bool enable)
107 {
108 sioxEnabled = enable;
109 }
112 /**
113 * Threaded method that does single bitmap--->path conversion
114 */
115 void Tracer::traceThread()
116 {
117 //## Remember. NEVER leave this method without setting
118 //## engine back to NULL
120 //## Prepare our kill flag. We will watch this later to
121 //## see if the main thread wants us to stop
122 keepGoing = true;
124 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
125 if (!desktop)
126 {
127 g_warning("Trace: No active desktop\n");
128 return;
129 }
131 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
133 if (!SP_ACTIVE_DOCUMENT)
134 {
135 char *msg = _("Trace: No active document");
136 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
137 //g_warning(msg);
138 engine = NULL;
139 return;
140 }
141 SPDocument *doc = SP_ACTIVE_DOCUMENT;
142 sp_document_ensure_up_to_date(doc);
145 SPImage *img = getSelectedSPImage();
146 if (!img || !selectedItem)
147 {
148 engine = NULL;
149 return;
150 }
152 GdkPixbuf *pixbuf = img->pixbuf;
154 if (!pixbuf)
155 {
156 char *msg = _("Trace: Image has no bitmap data");
157 SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, msg);
158 //g_warning(msg);
159 engine = NULL;
160 return;
161 }
163 int nrPaths;
164 TracingEngineResult *results = engine->trace(pixbuf, &nrPaths);
165 //printf("nrPaths:%d\n", nrPaths);
167 //### Check if we should stop
168 if (!keepGoing || !results || nrPaths<1)
169 {
170 engine = NULL;
171 return;
172 }
174 //### Get pointers to the <image> and its parent
175 Inkscape::XML::Node *imgRepr = SP_OBJECT(img)->repr;
176 Inkscape::XML::Node *par = sp_repr_parent(imgRepr);
178 //### Get some information for the new transform()
179 double x = 0.0;
180 double y = 0.0;
181 double width = 0.0;
182 double height = 0.0;
183 double dval = 0.0;
185 if (sp_repr_get_double(imgRepr, "x", &dval))
186 x = dval;
187 if (sp_repr_get_double(imgRepr, "y", &dval))
188 y = dval;
190 if (sp_repr_get_double(imgRepr, "width", &dval))
191 width = dval;
192 if (sp_repr_get_double(imgRepr, "height", &dval))
193 height = dval;
195 NR::Matrix trans(NR::translate(x, y));
197 double iwidth = (double)gdk_pixbuf_get_width(pixbuf);
198 double iheight = (double)gdk_pixbuf_get_height(pixbuf);
200 double iwscale = width / iwidth;
201 double ihscale = height / iheight;
203 NR::Matrix scal(NR::scale(iwscale, ihscale));
205 //# Convolve scale, translation, and the original transform
206 NR::Matrix tf(scal);
207 tf *= trans;
208 tf *= selectedItem->transform;
211 //#OK. Now let's start making new nodes
213 Inkscape::XML::Node *groupRepr = NULL;
215 //# if more than 1, make a <g>roup of <path>s
216 if (nrPaths > 1)
217 {
218 groupRepr = sp_repr_new("svg:g");
219 par->addChild(groupRepr, imgRepr);
220 }
222 long totalNodeCount = 0L;
224 for (TracingEngineResult *result=results ;
225 result ; result=result->next)
226 {
227 totalNodeCount += result->getNodeCount();
229 Inkscape::XML::Node *pathRepr = sp_repr_new("svg:path");
230 pathRepr->setAttribute("style", result->getStyle());
231 pathRepr->setAttribute("d", result->getPathData());
233 if (nrPaths > 1)
234 groupRepr->addChild(pathRepr, NULL);
235 else
236 par->addChild(pathRepr, imgRepr);
238 //### Apply the transform from the image to the new shape
239 SPObject *reprobj = doc->getObjectByRepr(pathRepr);
240 if (reprobj)
241 {
242 SPItem *newItem = SP_ITEM(reprobj);
243 sp_item_write_transform(newItem, pathRepr, tf, NULL);
244 }
245 if (nrPaths == 1)
246 {
247 selection->clear();
248 selection->add(pathRepr);
249 }
250 Inkscape::GC::release(pathRepr);
251 }
254 delete results;
256 // If we have a group, then focus on, then forget it
257 if (nrPaths > 1)
258 {
259 selection->clear();
260 selection->add(groupRepr);
261 Inkscape::GC::release(groupRepr);
262 }
264 //## inform the document, so we can undo
265 sp_document_done(doc);
267 engine = NULL;
269 char *msg = g_strdup_printf(_("Trace: Done. %ld nodes created"), totalNodeCount);
270 SP_DT_MSGSTACK(desktop)->flash(Inkscape::NORMAL_MESSAGE, msg);
271 g_free(msg);
273 }
275 /**
276 * Main tracing method
277 */
278 void Tracer::trace(TracingEngine *theEngine)
279 {
280 //Check if we are already running
281 if (engine)
282 return;
284 engine = theEngine;
286 #if HAVE_THREADS
287 //Ensure that thread support is running
288 if (!Glib::thread_supported())
289 Glib::thread_init();
291 //Create our thread and run it
292 Glib::Thread::create(
293 sigc::mem_fun(*this, &Tracer::traceThread), false);
294 #else
295 traceThread();
296 #endif
298 }
304 /**
305 * Abort the thread that is executing trace()
306 */
307 void Tracer::abort()
308 {
310 //## Inform Trace's working thread
311 keepGoing = false;
313 if (engine)
314 {
315 engine->abort();
316 }
318 }
322 } // namespace Trace
324 } // namespace Inkscape
327 //#########################################################################
328 //# E N D O F F I L E
329 //#########################################################################