1 /*
2 * A simple dialog for setting the parameters for autotracing a
3 * bitmap <image> into an svg <path>
4 *
5 * Authors:
6 * Bob Jamison
7 * Other dudes from The Inkscape Organization
8 *
9 * Copyright (C) 2004, 2005 Authors
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include <gtkmm/notebook.h>
18 #include <gtkmm/frame.h>
19 #include <gtkmm/spinbutton.h>
20 #include <gtkmm/stock.h>
22 #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
23 #include <glibmm/i18n.h>
27 #include "tracedialog.h"
28 #include "trace/potrace/inkscape-potrace.h"
31 namespace Inkscape {
32 namespace UI {
33 namespace Dialog {
36 //#########################################################################
37 //## I M P L E M E N T A T I O N
38 //#########################################################################
40 /**
41 * A dialog for adjusting bitmap->vector tracing parameters
42 */
43 class TraceDialogImpl : public TraceDialog
44 {
46 public:
49 /**
50 * Constructor
51 */
52 TraceDialogImpl();
54 /**
55 * Destructor
56 */
57 ~TraceDialogImpl();
59 /**
60 * Callback from OK or Cancel
61 */
62 void responseCallback(int response_id);
64 private:
66 /**
67 * This is the big almighty McGuffin
68 */
69 Inkscape::Trace::Tracer tracer;
71 /**
72 * This does potrace processing
73 * Only preview if do_i_trace is false
74 */
75 void potraceProcess(bool do_i_trace);
77 /**
78 * Abort processing
79 */
80 void abort();
82 void potracePreviewCallback();
84 Gtk::Notebook notebook;
85 Gtk::Tooltips tips;
87 //########## Potrace items
88 Gtk::VBox potraceBox;
89 Gtk::RadioButtonGroup potraceGroup;
90 Gtk::CheckButton potraceInvertButton;
91 Gtk::HBox potraceInvertBox;
92 Gtk::Button *potraceOkButton;
93 Gtk::Button *potraceCancelButton;
95 //brightness
96 Gtk::Frame potraceBrightnessFrame;
97 Gtk::VBox potraceBrightnessVBox;
98 Gtk::HBox potraceBrightnessBox;
99 Gtk::RadioButton potraceBrightnessRadioButton;
100 Gtk::Label potraceBrightnessSpinnerLabel;
101 Gtk::SpinButton potraceBrightnessSpinner;
104 //edge detection
105 Gtk::Frame potraceCannyFrame;
106 Gtk::HBox potraceCannyBox;
107 Gtk::VBox potraceCannyVBox;
108 Gtk::RadioButton potraceCannyRadioButton;
109 //Gtk::HSeparator potraceCannySeparator;
110 //Gtk::Label potraceCannyLoSpinnerLabel;
111 //Gtk::SpinButton potraceCannyLoSpinner;
112 Gtk::Label potraceCannyHiSpinnerLabel;
113 Gtk::SpinButton potraceCannyHiSpinner;
115 //quantization
116 Gtk::Frame potraceQuantFrame;
117 Gtk::HBox potraceQuantBox;
118 Gtk::VBox potraceQuantVBox;
119 Gtk::RadioButton potraceQuantRadioButton;
120 Gtk::Label potraceQuantNrColorLabel;
121 Gtk::SpinButton potraceQuantNrColorSpinner;
123 //multiple path scanning
124 Gtk::Frame potraceMultiScanFrame;
125 Gtk::VBox potraceMultiScanVBox;
127 Gtk::HBox potraceMultiScanHBox1;
128 Gtk::RadioButton potraceMultiScanBrightnessRadioButton;
129 Gtk::SpinButton potraceMultiScanNrColorSpinner;
131 Gtk::HBox potraceMultiScanHBox2;
132 Gtk::RadioButton potraceMultiScanColorRadioButton;
133 Gtk::CheckButton potraceMultiScanStackButton;
135 Gtk::HBox potraceMultiScanHBox3;
136 Gtk::RadioButton potraceMultiScanMonoRadioButton;
137 Gtk::Label potraceMultiScanNrColorLabel;
139 Gtk::CheckButton potraceMultiScanSmoothButton;
142 //preview
143 Gtk::Frame potracePreviewFrame;
144 Gtk::HBox potracePreviewBox;
145 Gtk::Button potracePreviewButton;
146 Gtk::Image potracePreviewImage;
148 //credits
149 Gtk::Frame potraceCreditsFrame;
150 Gtk::VBox potraceCreditsVBox;
151 Gtk::Label potraceCreditsLabel;
153 //########## Other items
154 Gtk::VBox otherBox;
158 };
163 //#########################################################################
164 //## E V E N T S
165 //#########################################################################
167 /**
168 * This does potrace processing
169 * Only preview if do_i_trace is false
170 */
171 void TraceDialogImpl::potraceProcess(bool do_i_trace)
172 {
173 //##### Get the tracer and engine
174 Inkscape::Trace::Potrace::PotraceTracingEngine pte;
176 /* inversion */
177 bool invert = potraceInvertButton.get_active();
178 pte.setInvert(invert);
180 //##### Get the single-scan settings
181 /* which one? */
182 if (potraceBrightnessRadioButton.get_active())
183 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS);
184 else if (potraceMultiScanBrightnessRadioButton.get_active())
185 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS_MULTI);
186 else if (potraceCannyRadioButton.get_active())
187 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_CANNY);
188 else if (potraceQuantRadioButton.get_active())
189 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT);
190 else if (potraceMultiScanColorRadioButton.get_active())
191 {
192 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_COLOR);
193 pte.setInvert(false);
194 }
195 else if (potraceMultiScanMonoRadioButton.get_active())
196 {
197 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_MONO);
198 pte.setInvert(false);
199 }
201 /* brightness */
202 double brightnessThreshold = potraceBrightnessSpinner.get_value();
203 pte.setBrightnessThreshold(brightnessThreshold);
205 /* canny */
206 double cannyHighThreshold = potraceCannyHiSpinner.get_value();
207 pte.setCannyHighThreshold(cannyHighThreshold);
209 /* quantization */
210 int quantNrColors = potraceQuantNrColorSpinner.get_value_as_int();
211 pte.setQuantizationNrColors(quantNrColors);
213 //##### Get multiple-scan settings
214 int multiScanNrColors = potraceMultiScanNrColorSpinner.get_value_as_int();
215 pte.setMultiScanNrColors(multiScanNrColors);
216 bool do_i_stack = potraceMultiScanStackButton.get_active();
217 pte.setMultiScanStack(do_i_stack);
218 bool do_i_smooth = potraceMultiScanSmoothButton.get_active();
219 pte.setMultiScanSmooth(do_i_smooth);
221 //##### Get intermediate bitmap image
222 GdkPixbuf *pixbuf = tracer.getSelectedImage();
223 if (pixbuf)
224 {
225 GdkPixbuf *preview = pte.preview(pixbuf);
226 if (preview)
227 {
228 Glib::RefPtr<Gdk::Pixbuf> thePreview = Glib::wrap(preview);
229 int width = thePreview->get_width();
230 int height = thePreview->get_height();
231 double scaleFactor = 100.0 / (double)height;
232 int newWidth = (int) (((double)width) * scaleFactor);
233 int newHeight = (int) (((double)height) * scaleFactor);
234 Glib::RefPtr<Gdk::Pixbuf> scaledPreview =
235 thePreview->scale_simple(newWidth, newHeight,
236 Gdk::INTERP_NEAREST);
237 //g_object_unref(preview);
238 potracePreviewImage.set(scaledPreview);
239 }
240 }
242 //##### Convert
243 if (do_i_trace)
244 {
245 if (potraceCancelButton)
246 potraceCancelButton->set_sensitive(true);
247 if (potraceOkButton)
248 potraceOkButton->set_sensitive(false);
249 tracer.trace(&pte);
250 if (potraceCancelButton)
251 potraceCancelButton->set_sensitive(false);
252 if (potraceOkButton)
253 potraceOkButton->set_sensitive(true);
254 }
256 }
259 /**
260 * Abort processing
261 */
262 void TraceDialogImpl::abort()
263 {
264 //### Do some GUI thing
266 //### Make the abort() call to the tracer
267 tracer.abort();
269 }
273 //#########################################################################
274 //## E V E N T S
275 //#########################################################################
277 /**
278 * Callback from the Preview button. Can be called from elsewhere.
279 */
280 void TraceDialogImpl::potracePreviewCallback()
281 {
282 potraceProcess(false);
283 }
285 /**
286 * Default response from the dialog. Let's intercept it
287 */
288 void TraceDialogImpl::responseCallback(int response_id)
289 {
291 if (response_id == GTK_RESPONSE_OK)
292 {
293 int panelNr = notebook.get_current_page();
294 //g_message("selected panel:%d\n", panelNr);
296 if (panelNr == 0)
297 {
298 potraceProcess(true);
299 }
300 }
301 else if (response_id == GTK_RESPONSE_CANCEL)
302 {
303 abort();
304 }
305 else
306 {
307 hide();
308 return;
309 }
313 }
318 //#########################################################################
319 //## C O N S T R U C T O R / D E S T R U C T O R
320 //#########################################################################
321 /**
322 * Constructor
323 */
324 TraceDialogImpl::TraceDialogImpl()
325 {
327 Gtk::VBox *mainVBox = get_vbox();
329 #define MARGIN 4
331 //##Set up the Potrace panel
333 /*#### brightness ####*/
334 potraceBrightnessRadioButton.set_label(_("Brightness"));
335 potraceGroup = potraceBrightnessRadioButton.get_group();
336 potraceBrightnessBox.pack_start(potraceBrightnessRadioButton, false, false, MARGIN);
337 tips.set_tip(potraceBrightnessRadioButton, _("Trace by a given brightness level"));
339 potraceBrightnessSpinner.set_digits(3);
340 potraceBrightnessSpinner.set_increments(0.01, 0.1);
341 potraceBrightnessSpinner.set_range(0.0, 1.0);
342 potraceBrightnessSpinner.set_value(0.45);
343 potraceBrightnessBox.pack_end(potraceBrightnessSpinner, false, false, MARGIN);
344 tips.set_tip(potraceBrightnessSpinner, _("Brightness cutoff for black/white"));
346 potraceBrightnessSpinnerLabel.set_label(_("Threshold:"));
347 potraceBrightnessBox.pack_end(potraceBrightnessSpinnerLabel, false, false, MARGIN);
349 potraceBrightnessVBox.pack_start(potraceBrightnessBox, false, false, MARGIN);
351 potraceBrightnessFrame.set_label(_("Image Brightness"));
352 //potraceBrightnessFrame.set_shadow_type(Gtk::SHADOW_NONE);
353 potraceBrightnessFrame.add(potraceBrightnessVBox);
354 potraceBox.pack_start(potraceBrightnessFrame, false, false, 0);
356 /*#### canny edge detection ####*/
357 // TRANSLATORS: "Canny" is the name of the inventor of this edge detection method
358 potraceCannyRadioButton.set_label(_("Optimal Edge Detection (Canny)"));
359 potraceCannyRadioButton.set_group(potraceGroup);
360 potraceCannyBox.pack_start(potraceCannyRadioButton, false, false, MARGIN);
361 tips.set_tip(potraceCannyRadioButton, _("Trace with edge detection by J. Canny's algorithm"));
362 /*
363 potraceCannyBox.pack_start(potraceCannySeparator);
364 potraceCannyLoSpinnerLabel.set_label(_("Low"));
365 potraceCannyBox.pack_start(potraceCannyLoSpinnerLabel);
366 potraceCannyLoSpinner.set_digits(5);
367 potraceCannyLoSpinner.set_increments(0.01, 0.1);
368 potraceCannyLoSpinner.set_range(0.0, 1.0);
369 potraceCannyLoSpinner.set_value(0.1);
370 potraceCannyBox.pack_start(potraceCannyLoSpinner);
371 */
372 potraceCannyHiSpinner.set_digits(3);
373 potraceCannyHiSpinner.set_increments(0.01, 0.1);
374 potraceCannyHiSpinner.set_range(0.0, 1.0);
375 potraceCannyHiSpinner.set_value(0.65);
376 potraceCannyBox.pack_end(potraceCannyHiSpinner, false, false, MARGIN);
377 tips.set_tip(potraceCannyHiSpinner, _("Brightness cutoff for adjacent pixels (determines edge thickness)"));
379 potraceCannyHiSpinnerLabel.set_label(_("Threshold:"));
380 potraceCannyBox.pack_end(potraceCannyHiSpinnerLabel, false, false, MARGIN);
382 potraceCannyVBox.pack_start(potraceCannyBox, false, false, MARGIN);
384 potraceCannyFrame.set_label(_("Edge Detection"));
385 //potraceCannyFrame.set_shadow_type(Gtk::SHADOW_NONE);
386 potraceCannyFrame.add(potraceCannyVBox);
387 potraceBox.pack_start(potraceCannyFrame, false, false, 0);
389 /*#### quantization ####*/
390 // TRANSLATORS: Color Quantization: the process of reducing the number of colors
391 // in an image by selecting an optimized set of representative colors and then
392 // re-applying this reduced set to the original image.
393 potraceQuantRadioButton.set_label(_("Color Quantization"));
394 potraceQuantRadioButton.set_group(potraceGroup);
395 potraceQuantBox.pack_start(potraceQuantRadioButton, false, false, MARGIN);
396 tips.set_tip(potraceQuantRadioButton, _("Trace along the boundaries of reduced colors"));
398 potraceQuantNrColorSpinner.set_digits(2);
399 potraceQuantNrColorSpinner.set_increments(1.0, 4.0);
400 potraceQuantNrColorSpinner.set_range(2.0, 64.0);
401 potraceQuantNrColorSpinner.set_value(8.0);
402 potraceQuantBox.pack_end(potraceQuantNrColorSpinner, false, false, MARGIN);
403 tips.set_tip(potraceQuantNrColorSpinner, _("The number of reduced colors"));
405 potraceQuantNrColorLabel.set_label(_("Colors:"));
406 potraceQuantBox.pack_end(potraceQuantNrColorLabel, false, false, MARGIN);
408 potraceQuantVBox.pack_start(potraceQuantBox, false, false, MARGIN);
410 potraceQuantFrame.set_label(_("Quantization / Reduction"));
411 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
412 potraceQuantFrame.add(potraceQuantVBox);
413 potraceBox.pack_start(potraceQuantFrame, false, false, 0);
415 /*#### Multiple scanning####*/
416 //----Hbox1
417 potraceMultiScanBrightnessRadioButton.set_label(_("Brightness"));
418 potraceMultiScanBrightnessRadioButton.set_group(potraceGroup);
419 potraceMultiScanHBox1.pack_start(potraceMultiScanBrightnessRadioButton, false, false, MARGIN);
420 tips.set_tip(potraceMultiScanBrightnessRadioButton, _("Trace the given number of brightness levels"));
422 potraceMultiScanNrColorSpinner.set_digits(2);
423 potraceMultiScanNrColorSpinner.set_increments(1.0, 4.0);
424 potraceMultiScanNrColorSpinner.set_range(2.0, 256.0);
425 potraceMultiScanNrColorSpinner.set_value(8.0);
426 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorSpinner, false, false, MARGIN);
427 potraceMultiScanNrColorLabel.set_label(_("Scans:"));
428 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorLabel, false, false, MARGIN);
429 tips.set_tip(potraceMultiScanNrColorSpinner, _("The desired number of scans"));
431 potraceMultiScanVBox.pack_start(potraceMultiScanHBox1, false, false, MARGIN);
433 //----Hbox2
434 potraceMultiScanColorRadioButton.set_label(_("Color"));
435 potraceMultiScanColorRadioButton.set_group(potraceGroup);
436 potraceMultiScanHBox2.pack_start(potraceMultiScanColorRadioButton, false, false, MARGIN);
437 tips.set_tip(potraceMultiScanColorRadioButton, _("Trace the given number of reduced colors"));
440 potraceMultiScanVBox.pack_start(potraceMultiScanHBox2, false, false, MARGIN);
442 //---Hbox3
443 potraceMultiScanMonoRadioButton.set_label(_("Monochrome"));
444 potraceMultiScanMonoRadioButton.set_group(potraceGroup);
445 potraceMultiScanHBox3.pack_start(potraceMultiScanMonoRadioButton, false, false, MARGIN);
446 tips.set_tip(potraceMultiScanMonoRadioButton, _("Same as Color, but convert result to grayscale"));
448 // TRANSLATORS: "Stack" is a verb here
449 potraceMultiScanStackButton.set_label(_("Stack"));
450 potraceMultiScanStackButton.set_active(true);
451 potraceMultiScanHBox3.pack_end(potraceMultiScanStackButton, false, false, MARGIN);
452 tips.set_tip(potraceMultiScanStackButton, _("Stack scans vertically (no gaps) or tile horizontally (usually with gaps)"));
454 // TRANSLATORS: "Smooth" is a verb here
455 potraceMultiScanSmoothButton.set_label(_("Smooth"));
456 potraceMultiScanSmoothButton.set_active(true);
457 potraceMultiScanHBox3.pack_end(potraceMultiScanSmoothButton, false, false, MARGIN);
458 tips.set_tip(potraceMultiScanSmoothButton, _("Apply Gaussian blur to the bitmap before tracing"));
460 potraceMultiScanVBox.pack_start(potraceMultiScanHBox3, false, false, MARGIN);
462 potraceMultiScanFrame.set_label(_("Multiple Scanning"));
463 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
464 potraceMultiScanFrame.add(potraceMultiScanVBox);
465 potraceBox.pack_start(potraceMultiScanFrame, false, false, 0);
467 /*#### Preview ####*/
468 potracePreviewButton.set_label(_("Preview"));
469 potracePreviewButton.signal_clicked().connect(
470 sigc::mem_fun(*this, &TraceDialogImpl::potracePreviewCallback) );
471 potracePreviewBox.pack_end(potracePreviewButton, false, false, 0);//do not expand
472 tips.set_tip(potracePreviewButton, _("Preview the result without actual tracing"));
475 potracePreviewImage.set_size_request(100,100);
476 //potracePreviewImage.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
477 potracePreviewBox.pack_start(potracePreviewImage, true, true, 0);
478 potracePreviewFrame.set_label(_("Preview")); // I guess it's correct to call the "intermediate bitmap" a preview of the trace
479 //potracePreviewFrame.set_shadow_type(Gtk::SHADOW_NONE);
480 potracePreviewFrame.add(potracePreviewBox);
481 potraceBox.pack_start(potracePreviewFrame, true, true, 0);
483 /*#### swap black and white ####*/
484 potraceInvertButton.set_label(_("Invert"));
485 potraceInvertButton.set_active(false);
486 potraceInvertBox.pack_end(potraceInvertButton, false, false, MARGIN);
487 potraceBox.pack_start(potraceInvertBox, false, false, MARGIN);
488 tips.set_tip(potraceInvertButton, _("Invert black and white regions for single traces"));
490 /*#### Credits ####*/
491 potraceCreditsLabel.set_text(
492 _("Thanks to Peter Selinger, http://potrace.sourceforge.net")
493 );
494 potraceCreditsVBox.pack_start(potraceCreditsLabel, false, false, MARGIN);
495 potraceCreditsFrame.set_label(_("Credits"));
496 potraceCreditsFrame.set_shadow_type(Gtk::SHADOW_NONE);
497 potraceCreditsFrame.add(potraceCreditsVBox);
498 potraceBox.pack_start(potraceCreditsFrame, false, false, 0);
500 /*done */
501 // TRANSLATORS: Potrace is an application for transforming bitmaps into
502 // vector graphics (http://potrace.sourceforge.net/)
503 notebook.append_page(potraceBox, _("Potrace"));
505 //##Set up the Other panel
506 // This may be reenabled when we have another tracer; now an empty tab is confusing so I'm disabling it
507 // notebook.append_page(otherBox, _("Other"));
509 //##Put the notebook on the dialog
510 mainVBox->pack_start(notebook);
512 //## The OK button
513 potraceCancelButton = add_button(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
514 if (potraceCancelButton)
515 {
516 tips.set_tip((*potraceCancelButton), _("Abort a trace in progress"));
517 potraceCancelButton->set_sensitive(false);
518 }
519 potraceOkButton = add_button(Gtk::Stock::OK, GTK_RESPONSE_OK);
520 tips.set_tip((*potraceOkButton), _("Execute the trace"));
522 show_all_children();
524 //## Connect the signal
525 signal_response().connect(
526 sigc::mem_fun(*this, &TraceDialogImpl::responseCallback) );
527 }
529 /**
530 * Factory method. Use this to create a new TraceDialog
531 */
532 TraceDialog *TraceDialog::create()
533 {
534 TraceDialog *dialog = new TraceDialogImpl();
535 return dialog;
536 }
539 /**
540 * Constructor
541 */
542 TraceDialogImpl::~TraceDialogImpl()
543 {
546 }
553 } //namespace Dialog
554 } //namespace UI
555 } //namespace Inkscape
557 //#########################################################################
558 //## E N D O F F I L E
559 //#########################################################################