Merge lp:~vinipsmaker/inkscape/backgroundpixelart into lp:~inkscape.dev/inkscape/trunk

Proposed by Vinipsmaker
Status: Merged
Merged at revision: 13225
Proposed branch: lp:~vinipsmaker/inkscape/backgroundpixelart
Merge into: lp:~inkscape.dev/inkscape/trunk
Diff against target: 278 lines (+118/-49)
1 file modified
src/ui/dialog/pixelartdialog.cpp (+118/-49)
To merge this branch: bzr merge lp:~vinipsmaker/inkscape/backgroundpixelart
Reviewer Review Type Date Requested Status
Martin Owens functional Approve
Review via email: mp+189521@code.launchpad.net

Description of the change

I'm using Glib::Thread to run the expensive libdepixelize process in a background thread.

Communication with the GUI thread is done through Glib::Dispatcher.

I'm enabling GLIBMM deprecated features to have access to Glib::Thread at the beginning of pixelartdialog.cpp file.

To post a comment you must log in.
12669. By Vinipsmaker

Removing useless comment

Revision history for this message
Martin Owens (doctormo) wrote :

The code looks nice and clean. I see some improvements to the original code.

I'm a little bit confused as to what the branch /does/. Is it so inkscape doesn't block while it's working on the pixelart process?

review: Approve (code)
Revision history for this message
Vinipsmaker (vinipsmaker) wrote :

> I'm a little bit confused as to what the branch /does/. Is it so inkscape
> doesn't block while it's working on the pixelart process?

Yup.

The branch changes will move the libdepixelize call to a different thread, then the main thread (the UI thread) won't block.

But I'm not sure if you're being sarcastic. "The code looks nice and clean"..."[...] what the branch /does/". Something is not right here. I can rewrite it using nice C++11 threading features after Johan Engelen convices Inkscape team to allow C++11 on the release after 0.91.
=P

Strangely I cannot compile this (backgroundpixelart) branch cleanly on my PC anymore. Some tweakings regarding GC and freetype were needed. I think the branch needs to be rebased against a recent revision of main Inkscape branch.

Revision history for this message
Martin Owens (doctormo) wrote :

Oh sorry, no sarcasm. Complete sincerity here. I was just confused between it being something to do with server based inkscape deployments and something that stopped blocking.

Code review doesn't need much functional review. :-)

Would you like to rebase it?

Revision history for this message
Vinipsmaker (vinipsmaker) wrote :

> Would you like to rebase it?

I don't care. I doubt any changes on the pixelart happenend in the main branch and a merge would probably apply clean.

Revision history for this message
Vinipsmaker (vinipsmaker) wrote :

s/pixelart/pixelart dialog/g

Revision history for this message
Martin Owens (doctormo) wrote :

I've merged in the code, although it needed fixing as some of the code had changed. There was a slight issue with the new pixbuf size check and a change in the notion for move -> Dialog::move which didn't apply any more.

Functional tests all pass. I'm considering this done.

review: Approve (functional)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ui/dialog/pixelartdialog.cpp'
2--- src/ui/dialog/pixelartdialog.cpp 2013-09-23 17:55:42 +0000
3+++ src/ui/dialog/pixelartdialog.cpp 2013-10-07 03:31:47 +0000
4@@ -16,6 +16,16 @@
5 # include <config.h>
6 #endif
7
8+#ifdef GLIBMM_DISABLE_DEPRECATED
9+# undef GLIBMM_DISABLE_DEPRECATED
10+# include <glibmm/thread.h>
11+# include <glibmm/dispatcher.h>
12+# define GLIBMM_DISABLE_DEPRECATED 1
13+#else // GLIBMM_DISABLE_DEPRECATED
14+# include <glibmm/thread.h>
15+# include <glibmm/dispatcher.h>
16+#endif // GLIBMM_DISABLE_DEPRECATED
17+
18 #include "pixelartdialog.h"
19 #include <gtkmm/radiobutton.h>
20 #include <gtkmm/stock.h>
21@@ -54,18 +64,6 @@
22 namespace UI {
23 namespace Dialog {
24
25-template<class T>
26-T move(T &obj)
27-{
28-#ifdef LIBDEPIXELIZE_ENABLE_CPP11
29- return std::move(obj);
30-#else
31- T ret;
32- std::swap(obj, ret);
33- return ret;
34-#endif // LIBDEPIXELIZE_ENABLE_CPP11
35-}
36-
37 /**
38 * A dialog for adjusting pixel art -> vector tracing parameters
39 */
40@@ -77,6 +75,23 @@
41 ~PixelArtDialogImpl();
42
43 private:
44+ struct Input
45+ {
46+ Glib::RefPtr<Gdk::Pixbuf> pixbuf;
47+ SVGLength x;
48+ SVGLength y;
49+ };
50+ struct Output
51+ {
52+ Output(Tracer::Splines splines, SVGLength x, SVGLength y) :
53+ splines(splines), x(x), y(y)
54+ {}
55+
56+ Tracer::Splines splines;
57+ SVGLength x;
58+ SVGLength y;
59+ };
60+
61 void setDesktop(SPDesktop *desktop);
62 void setTargetDesktop(SPDesktop *desktop);
63
64@@ -89,7 +104,8 @@
65 Tracer::Kopf2011::Options options();
66
67 void vectorize();
68- void processLibdepixelize(SPImage *img);
69+ void processLibdepixelize(const Input &image);
70+ void importOutput(const Output &out);
71 void setDefaults();
72 void updatePreview();
73
74@@ -133,9 +149,21 @@
75 Gtk::RadioButton optimizeRadioButton;
76 #endif // LIBDEPIXELIZE_INKSCAPE_ENABLE_SMOOTH
77
78+ //############ UI Logic data
79+
80 SPDesktop *desktop;
81 DesktopTracker deskTrack;
82 sigc::connection desktopChangeConn;
83+
84+ //############ Threads
85+ void workerThread();
86+ void onWorkerThreadFinished();
87+ Glib::Thread *thread;
88+ volatile gint abortThread; // C++11's atomic stuff is sooo much nicer
89+ Glib::Dispatcher dispatcher;
90+ std::vector<Input> queue;
91+ std::vector<Output> output;
92+ Tracer::Kopf2011::Options lastOptions;
93 };
94
95 void PixelArtDialogImpl::setDesktop(SPDesktop *desktop)
96@@ -288,6 +316,9 @@
97 deskTrack.connect(GTK_WIDGET(gobj()));
98
99 signalResponse().connect(sigc::mem_fun(*this, &PixelArtDialogImpl::responseCallback));
100+
101+ dispatcher.connect(
102+ sigc::mem_fun(*this, &PixelArtDialogImpl::onWorkerThreadFinished) );
103 }
104
105 void PixelArtDialogImpl::responseCallback(int response_id)
106@@ -295,7 +326,8 @@
107 if (response_id == GTK_RESPONSE_OK) {
108 vectorize();
109 } else if (response_id == GTK_RESPONSE_CANCEL) {
110- // TODO
111+ // libdepixelize's interface need to be extended to allow aborts
112+ g_atomic_int_set(&abortThread, true);
113 } else if (response_id == GTK_RESPONSE_HELP) {
114 setDefaults();
115 } else {
116@@ -340,60 +372,68 @@
117 return;
118 }
119
120- bool found = false;
121-
122 for ( GSList const *list = desktop->selection->itemList() ; list
123 ; list = list->next ) {
124 if ( !SP_IS_IMAGE(list->data) )
125 continue;
126
127- found = true;
128-
129- processLibdepixelize(SP_IMAGE(list->data));
130+ SPImage *img = SP_IMAGE(list->data);
131+ Input input;
132+ input.pixbuf = Glib::wrap(img->pixbuf->getPixbufRaw(), true);
133+ input.x = img->x;
134+ input.y = img->y;
135+
136+ if ( input.pixbuf->get_width() > 256
137+ || input.pixbuf->get_height() > 256 ) {
138+ char *msg = _("Image looks too big. Process may take a while and is"
139+ " wise to save your document before continue."
140+ "\n\nContinue the procedure (without saving)?");
141+ Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_WARNING,
142+ Gtk::BUTTONS_OK_CANCEL, true);
143+
144+ if ( dialog.run() != Gtk::RESPONSE_OK )
145+ continue;
146+ }
147+
148+ queue.push_back(input);
149 }
150
151- if ( !found ) {
152+ if ( !queue.size() ) {
153 char *msg = _("Select an <b>image</b> to trace");
154 msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
155 return;
156 }
157
158- DocumentUndo::done(desktop->doc(), SP_VERB_SELECTION_PIXEL_ART,
159- _("Trace pixel art"));
160-
161- // Flush pending updates
162- desktop->doc()->ensureUpToDate();
163+ mainCancelButton->set_sensitive(true);
164+ mainOkButton->set_sensitive(false);
165+
166+ lastOptions = options();
167+
168+ g_atomic_int_set(&abortThread, false);
169+ thread = Glib::Thread::create(
170+ sigc::mem_fun(*this, &PixelArtDialogImpl::workerThread) );
171 }
172
173-void PixelArtDialogImpl::processLibdepixelize(SPImage *img)
174+void PixelArtDialogImpl::processLibdepixelize(const Input &input)
175 {
176- Tracer::Splines out;
177-
178- Glib::RefPtr<Gdk::Pixbuf> pixbuf
179- = Glib::wrap(img->pixbuf->getPixbufRaw(), true);
180-
181- if ( pixbuf->get_width() > 256 || pixbuf->get_height() > 256 ) {
182- char *msg = _("Image looks too big. Process may take a while and is"
183- " wise to save your document before continue."
184- "\n\nContinue the procedure (without saving)?");
185- Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_WARNING,
186- Gtk::BUTTONS_OK_CANCEL, true);
187-
188- if ( dialog.run() != Gtk::RESPONSE_OK )
189- return;
190- }
191-
192 if ( voronoiRadioButton.get_active() ) {
193- out = Tracer::Kopf2011::to_voronoi(pixbuf, options());
194+ output.push_back(Output(Tracer::Kopf2011::to_voronoi(input.pixbuf,
195+ lastOptions),
196+ input.x, input.y));
197 } else {
198- out = Tracer::Kopf2011::to_splines(pixbuf, options());
199+ output.push_back(Output(Tracer::Kopf2011::to_splines(input.pixbuf,
200+ lastOptions),
201+ input.x, input.y));
202 }
203+}
204
205+void PixelArtDialogImpl::importOutput(const Output &output)
206+{
207 Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
208 Inkscape::XML::Node *group = xml_doc->createElement("svg:g");
209
210- for ( Tracer::Splines::iterator it = out.begin(), end = out.end()
211- ; it != end ; ++it ) {
212+ for ( Tracer::Splines::const_iterator it = output.splines.begin(),
213+ end = output.splines.end() ; it != end ; ++it ) {
214 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
215
216 {
217@@ -421,7 +461,7 @@
218 sp_repr_css_attr_unref(css);
219 }
220
221- gchar *str = sp_svg_write_path(move(it->pathVector));
222+ gchar *str = sp_svg_write_path(it->pathVector);
223 repr->setAttribute("d", str);
224 g_free(str);
225
226@@ -433,14 +473,20 @@
227 {
228 group->setAttribute("transform",
229 (std::string("translate(")
230- + sp_svg_length_write_with_units(img->x)
231- + ' ' + sp_svg_length_write_with_units(img->y)
232+ + sp_svg_length_write_with_units(output.x)
233+ + ' ' + sp_svg_length_write_with_units(output.y)
234 + ')').c_str());
235 }
236
237 desktop->currentLayer()->appendChildRepr(group);
238
239 Inkscape::GC::release(group);
240+
241+ DocumentUndo::done(desktop->doc(), SP_VERB_SELECTION_PIXEL_ART,
242+ _("Trace pixel art"));
243+
244+ // Flush pending updates
245+ desktop->doc()->ensureUpToDate();
246 }
247
248 void PixelArtDialogImpl::setDefaults()
249@@ -481,6 +527,29 @@
250 pendingPreview = false;
251 }
252
253+void PixelArtDialogImpl::workerThread()
254+{
255+ for ( std::vector<Input>::iterator it = queue.begin(), end = queue.end()
256+ ; it != end && !g_atomic_int_get(&abortThread) ; ++it ) {
257+ processLibdepixelize(*it);
258+ }
259+ queue.clear();
260+ dispatcher();
261+}
262+
263+void PixelArtDialogImpl::onWorkerThreadFinished()
264+{
265+ thread->join();
266+ thread = NULL;
267+ for ( std::vector<Output>::const_iterator it = output.begin(),
268+ end = output.end() ; it != end ; ++it ) {
269+ importOutput(*it);
270+ }
271+ output.clear();
272+ mainCancelButton->set_sensitive(false);
273+ mainOkButton->set_sensitive(true);
274+}
275+
276 /**
277 * Factory method. Use this to create a new PixelArtDialog
278 */