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 //########## General items
88 Gtk::Frame sioxFrame;
89 Gtk::VBox sioxVBox;
90 Gtk::HBox sioxBox;
91 Gtk::RadioButton sioxRadioButton;
93 //########## Potrace items
94 Gtk::VBox potraceBox;
95 Gtk::RadioButtonGroup potraceGroup;
96 Gtk::CheckButton potraceInvertButton;
97 Gtk::HBox potraceInvertBox;
98 Gtk::Button *potraceOkButton;
99 Gtk::Button *potraceCancelButton;
101 //brightness
102 Gtk::Frame potraceBrightnessFrame;
103 Gtk::VBox potraceBrightnessVBox;
104 Gtk::HBox potraceBrightnessBox;
105 Gtk::RadioButton potraceBrightnessRadioButton;
106 Gtk::Label potraceBrightnessSpinnerLabel;
107 Gtk::SpinButton potraceBrightnessSpinner;
110 //edge detection
111 Gtk::Frame potraceCannyFrame;
112 Gtk::HBox potraceCannyBox;
113 Gtk::VBox potraceCannyVBox;
114 Gtk::RadioButton potraceCannyRadioButton;
115 //Gtk::HSeparator potraceCannySeparator;
116 //Gtk::Label potraceCannyLoSpinnerLabel;
117 //Gtk::SpinButton potraceCannyLoSpinner;
118 Gtk::Label potraceCannyHiSpinnerLabel;
119 Gtk::SpinButton potraceCannyHiSpinner;
121 //quantization
122 Gtk::Frame potraceQuantFrame;
123 Gtk::HBox potraceQuantBox;
124 Gtk::VBox potraceQuantVBox;
125 Gtk::RadioButton potraceQuantRadioButton;
126 Gtk::Label potraceQuantNrColorLabel;
127 Gtk::SpinButton potraceQuantNrColorSpinner;
129 //multiple path scanning
130 Gtk::Frame potraceMultiScanFrame;
131 Gtk::VBox potraceMultiScanVBox;
133 Gtk::HBox potraceMultiScanHBox1;
134 Gtk::RadioButton potraceMultiScanBrightnessRadioButton;
135 Gtk::SpinButton potraceMultiScanNrColorSpinner;
137 Gtk::HBox potraceMultiScanHBox2;
138 Gtk::RadioButton potraceMultiScanColorRadioButton;
139 Gtk::CheckButton potraceMultiScanStackButton;
141 Gtk::HBox potraceMultiScanHBox3;
142 Gtk::RadioButton potraceMultiScanMonoRadioButton;
143 Gtk::Label potraceMultiScanNrColorLabel;
145 Gtk::CheckButton potraceMultiScanSmoothButton;
148 //preview
149 Gtk::Frame potracePreviewFrame;
150 Gtk::HBox potracePreviewBox;
151 Gtk::Button potracePreviewButton;
152 Gtk::Image potracePreviewImage;
154 //credits
155 Gtk::Frame potraceCreditsFrame;
156 Gtk::VBox potraceCreditsVBox;
157 Gtk::Label potraceCreditsLabel;
159 //########## Other items
160 Gtk::VBox otherBox;
164 };
169 //#########################################################################
170 //## E V E N T S
171 //#########################################################################
173 /**
174 * This does potrace processing
175 * Only preview if do_i_trace is false
176 */
177 void TraceDialogImpl::potraceProcess(bool do_i_trace)
178 {
179 //##### Get the tracer and engine
180 Inkscape::Trace::Potrace::PotraceTracingEngine pte;
182 /* inversion */
183 bool invert = potraceInvertButton.get_active();
184 pte.setInvert(invert);
186 //##### Get the preprocessor settings
187 /* siox -- performed by Tracer, and before any of the others */
188 if (sioxRadioButton.get_active())
189 tracer.enableSiox(true);
190 else
191 tracer.enableSiox(false);
193 /* one of the following */
194 if (potraceBrightnessRadioButton.get_active())
195 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS);
196 else if (potraceMultiScanBrightnessRadioButton.get_active())
197 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS_MULTI);
198 else if (potraceCannyRadioButton.get_active())
199 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_CANNY);
200 else if (potraceQuantRadioButton.get_active())
201 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT);
202 else if (potraceMultiScanColorRadioButton.get_active())
203 {
204 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_COLOR);
205 pte.setInvert(false);
206 }
207 else if (potraceMultiScanMonoRadioButton.get_active())
208 {
209 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_MONO);
210 pte.setInvert(false);
211 }
213 //##### Get the single-scan settings
214 /* brightness */
215 double brightnessThreshold = potraceBrightnessSpinner.get_value();
216 pte.setBrightnessThreshold(brightnessThreshold);
218 /* canny */
219 double cannyHighThreshold = potraceCannyHiSpinner.get_value();
220 pte.setCannyHighThreshold(cannyHighThreshold);
222 /* quantization */
223 int quantNrColors = potraceQuantNrColorSpinner.get_value_as_int();
224 pte.setQuantizationNrColors(quantNrColors);
226 //##### Get multiple-scan settings
227 int multiScanNrColors = potraceMultiScanNrColorSpinner.get_value_as_int();
228 pte.setMultiScanNrColors(multiScanNrColors);
229 bool do_i_stack = potraceMultiScanStackButton.get_active();
230 pte.setMultiScanStack(do_i_stack);
231 bool do_i_smooth = potraceMultiScanSmoothButton.get_active();
232 pte.setMultiScanSmooth(do_i_smooth);
234 //##### Get intermediate bitmap image
235 GdkPixbuf *pixbuf = tracer.getSelectedImage();
236 if (pixbuf)
237 {
238 GdkPixbuf *preview = pte.preview(pixbuf);
239 if (preview)
240 {
241 Glib::RefPtr<Gdk::Pixbuf> thePreview = Glib::wrap(preview);
242 int width = thePreview->get_width();
243 int height = thePreview->get_height();
244 double scaleFactor = 100.0 / (double)height;
245 int newWidth = (int) (((double)width) * scaleFactor);
246 int newHeight = (int) (((double)height) * scaleFactor);
247 Glib::RefPtr<Gdk::Pixbuf> scaledPreview =
248 thePreview->scale_simple(newWidth, newHeight,
249 Gdk::INTERP_NEAREST);
250 //g_object_unref(preview);
251 potracePreviewImage.set(scaledPreview);
252 }
253 }
255 //##### Convert
256 if (do_i_trace)
257 {
258 if (potraceCancelButton)
259 potraceCancelButton->set_sensitive(true);
260 if (potraceOkButton)
261 potraceOkButton->set_sensitive(false);
262 tracer.trace(&pte);
263 if (potraceCancelButton)
264 potraceCancelButton->set_sensitive(false);
265 if (potraceOkButton)
266 potraceOkButton->set_sensitive(true);
267 }
269 }
272 /**
273 * Abort processing
274 */
275 void TraceDialogImpl::abort()
276 {
277 //### Do some GUI thing
279 //### Make the abort() call to the tracer
280 tracer.abort();
282 }
286 //#########################################################################
287 //## E V E N T S
288 //#########################################################################
290 /**
291 * Callback from the Preview button. Can be called from elsewhere.
292 */
293 void TraceDialogImpl::potracePreviewCallback()
294 {
295 potraceProcess(false);
296 }
298 /**
299 * Default response from the dialog. Let's intercept it
300 */
301 void TraceDialogImpl::responseCallback(int response_id)
302 {
304 if (response_id == GTK_RESPONSE_OK)
305 {
306 int panelNr = notebook.get_current_page();
307 //g_message("selected panel:%d\n", panelNr);
309 if (panelNr == 0)
310 {
311 potraceProcess(true);
312 }
313 }
314 else if (response_id == GTK_RESPONSE_CANCEL)
315 {
316 abort();
317 }
318 else
319 {
320 hide();
321 return;
322 }
326 }
331 //#########################################################################
332 //## C O N S T R U C T O R / D E S T R U C T O R
333 //#########################################################################
334 /**
335 * Constructor
336 */
337 TraceDialogImpl::TraceDialogImpl()
338 {
340 Gtk::VBox *mainVBox = get_vbox();
342 #define MARGIN 4
344 /*#### SIOX ####*/
345 //# for now, put at the top of the potrace box. something better later
346 sioxRadioButton.set_label(_("SIOX subimage selection"));
347 sioxBox.pack_start(sioxRadioButton, false, false, MARGIN);
348 tips.set_tip(sioxRadioButton, _("Subimage selection with the SIOX algorithm"));
349 sioxVBox.pack_start(sioxBox, false, false, MARGIN);
350 sioxFrame.set_label(_("SIOX (W.I.P.)"));
351 sioxFrame.add(sioxVBox);
352 potraceBox.pack_start(sioxFrame, false, false, 0);
355 //##Set up the Potrace panel
357 /*#### brightness ####*/
358 potraceBrightnessRadioButton.set_label(_("Brightness"));
359 potraceGroup = potraceBrightnessRadioButton.get_group();
360 potraceBrightnessBox.pack_start(potraceBrightnessRadioButton, false, false, MARGIN);
361 tips.set_tip(potraceBrightnessRadioButton, _("Trace by a given brightness level"));
363 potraceBrightnessSpinner.set_digits(3);
364 potraceBrightnessSpinner.set_increments(0.01, 0.1);
365 potraceBrightnessSpinner.set_range(0.0, 1.0);
366 potraceBrightnessSpinner.set_value(0.45);
367 potraceBrightnessBox.pack_end(potraceBrightnessSpinner, false, false, MARGIN);
368 tips.set_tip(potraceBrightnessSpinner, _("Brightness cutoff for black/white"));
370 potraceBrightnessSpinnerLabel.set_label(_("Threshold:"));
371 potraceBrightnessBox.pack_end(potraceBrightnessSpinnerLabel, false, false, MARGIN);
373 potraceBrightnessVBox.pack_start(potraceBrightnessBox, false, false, MARGIN);
375 potraceBrightnessFrame.set_label(_("Image Brightness"));
376 //potraceBrightnessFrame.set_shadow_type(Gtk::SHADOW_NONE);
377 potraceBrightnessFrame.add(potraceBrightnessVBox);
378 potraceBox.pack_start(potraceBrightnessFrame, false, false, 0);
380 /*#### canny edge detection ####*/
381 // TRANSLATORS: "Canny" is the name of the inventor of this edge detection method
382 potraceCannyRadioButton.set_label(_("Optimal Edge Detection (Canny)"));
383 potraceCannyRadioButton.set_group(potraceGroup);
384 potraceCannyBox.pack_start(potraceCannyRadioButton, false, false, MARGIN);
385 tips.set_tip(potraceCannyRadioButton, _("Trace with edge detection by J. Canny's algorithm"));
386 /*
387 potraceCannyBox.pack_start(potraceCannySeparator);
388 potraceCannyLoSpinnerLabel.set_label(_("Low"));
389 potraceCannyBox.pack_start(potraceCannyLoSpinnerLabel);
390 potraceCannyLoSpinner.set_digits(5);
391 potraceCannyLoSpinner.set_increments(0.01, 0.1);
392 potraceCannyLoSpinner.set_range(0.0, 1.0);
393 potraceCannyLoSpinner.set_value(0.1);
394 potraceCannyBox.pack_start(potraceCannyLoSpinner);
395 */
396 potraceCannyHiSpinner.set_digits(3);
397 potraceCannyHiSpinner.set_increments(0.01, 0.1);
398 potraceCannyHiSpinner.set_range(0.0, 1.0);
399 potraceCannyHiSpinner.set_value(0.65);
400 potraceCannyBox.pack_end(potraceCannyHiSpinner, false, false, MARGIN);
401 tips.set_tip(potraceCannyHiSpinner, _("Brightness cutoff for adjacent pixels (determines edge thickness)"));
403 potraceCannyHiSpinnerLabel.set_label(_("Threshold:"));
404 potraceCannyBox.pack_end(potraceCannyHiSpinnerLabel, false, false, MARGIN);
406 potraceCannyVBox.pack_start(potraceCannyBox, false, false, MARGIN);
408 potraceCannyFrame.set_label(_("Edge Detection"));
409 //potraceCannyFrame.set_shadow_type(Gtk::SHADOW_NONE);
410 potraceCannyFrame.add(potraceCannyVBox);
411 potraceBox.pack_start(potraceCannyFrame, false, false, 0);
413 /*#### quantization ####*/
414 // TRANSLATORS: Color Quantization: the process of reducing the number of colors
415 // in an image by selecting an optimized set of representative colors and then
416 // re-applying this reduced set to the original image.
417 potraceQuantRadioButton.set_label(_("Color Quantization"));
418 potraceQuantRadioButton.set_group(potraceGroup);
419 potraceQuantBox.pack_start(potraceQuantRadioButton, false, false, MARGIN);
420 tips.set_tip(potraceQuantRadioButton, _("Trace along the boundaries of reduced colors"));
422 potraceQuantNrColorSpinner.set_digits(2);
423 potraceQuantNrColorSpinner.set_increments(1.0, 4.0);
424 potraceQuantNrColorSpinner.set_range(2.0, 64.0);
425 potraceQuantNrColorSpinner.set_value(8.0);
426 potraceQuantBox.pack_end(potraceQuantNrColorSpinner, false, false, MARGIN);
427 tips.set_tip(potraceQuantNrColorSpinner, _("The number of reduced colors"));
429 potraceQuantNrColorLabel.set_label(_("Colors:"));
430 potraceQuantBox.pack_end(potraceQuantNrColorLabel, false, false, MARGIN);
432 potraceQuantVBox.pack_start(potraceQuantBox, false, false, MARGIN);
434 potraceQuantFrame.set_label(_("Quantization / Reduction"));
435 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
436 potraceQuantFrame.add(potraceQuantVBox);
437 potraceBox.pack_start(potraceQuantFrame, false, false, 0);
439 /*#### Multiple scanning####*/
440 //----Hbox1
441 potraceMultiScanBrightnessRadioButton.set_label(_("Brightness"));
442 potraceMultiScanBrightnessRadioButton.set_group(potraceGroup);
443 potraceMultiScanHBox1.pack_start(potraceMultiScanBrightnessRadioButton, false, false, MARGIN);
444 tips.set_tip(potraceMultiScanBrightnessRadioButton, _("Trace the given number of brightness levels"));
446 potraceMultiScanNrColorSpinner.set_digits(0);
447 potraceMultiScanNrColorSpinner.set_increments(1.0, 4.0);
448 potraceMultiScanNrColorSpinner.set_range(2.0, 256.0);
449 potraceMultiScanNrColorSpinner.set_value(8.0);
450 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorSpinner, false, false, MARGIN);
451 potraceMultiScanNrColorLabel.set_label(_("Scans:"));
452 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorLabel, false, false, MARGIN);
453 tips.set_tip(potraceMultiScanNrColorSpinner, _("The desired number of scans"));
455 potraceMultiScanVBox.pack_start(potraceMultiScanHBox1, false, false, MARGIN);
457 //----Hbox2
458 potraceMultiScanColorRadioButton.set_label(_("Color"));
459 potraceMultiScanColorRadioButton.set_group(potraceGroup);
460 potraceMultiScanHBox2.pack_start(potraceMultiScanColorRadioButton, false, false, MARGIN);
461 tips.set_tip(potraceMultiScanColorRadioButton, _("Trace the given number of reduced colors"));
464 potraceMultiScanVBox.pack_start(potraceMultiScanHBox2, false, false, MARGIN);
466 //---Hbox3
467 potraceMultiScanMonoRadioButton.set_label(_("Monochrome"));
468 potraceMultiScanMonoRadioButton.set_group(potraceGroup);
469 potraceMultiScanHBox3.pack_start(potraceMultiScanMonoRadioButton, false, false, MARGIN);
470 tips.set_tip(potraceMultiScanMonoRadioButton, _("Same as Color, but convert result to grayscale"));
472 // TRANSLATORS: "Stack" is a verb here
473 potraceMultiScanStackButton.set_label(_("Stack"));
474 potraceMultiScanStackButton.set_active(true);
475 potraceMultiScanHBox3.pack_end(potraceMultiScanStackButton, false, false, MARGIN);
476 tips.set_tip(potraceMultiScanStackButton, _("Stack scans vertically (no gaps) or tile horizontally (usually with gaps)"));
478 // TRANSLATORS: "Smooth" is a verb here
479 potraceMultiScanSmoothButton.set_label(_("Smooth"));
480 potraceMultiScanSmoothButton.set_active(true);
481 potraceMultiScanHBox3.pack_end(potraceMultiScanSmoothButton, false, false, MARGIN);
482 tips.set_tip(potraceMultiScanSmoothButton, _("Apply Gaussian blur to the bitmap before tracing"));
484 potraceMultiScanVBox.pack_start(potraceMultiScanHBox3, false, false, MARGIN);
486 potraceMultiScanFrame.set_label(_("Multiple Scanning"));
487 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
488 potraceMultiScanFrame.add(potraceMultiScanVBox);
489 potraceBox.pack_start(potraceMultiScanFrame, false, false, 0);
491 /*#### Preview ####*/
492 potracePreviewButton.set_label(_("Preview"));
493 potracePreviewButton.signal_clicked().connect(
494 sigc::mem_fun(*this, &TraceDialogImpl::potracePreviewCallback) );
495 potracePreviewBox.pack_end(potracePreviewButton, false, false, 0);//do not expand
496 tips.set_tip(potracePreviewButton, _("Preview the result without actual tracing"));
499 potracePreviewImage.set_size_request(100,100);
500 //potracePreviewImage.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
501 potracePreviewBox.pack_start(potracePreviewImage, true, true, 0);
502 potracePreviewFrame.set_label(_("Preview")); // I guess it's correct to call the "intermediate bitmap" a preview of the trace
503 //potracePreviewFrame.set_shadow_type(Gtk::SHADOW_NONE);
504 potracePreviewFrame.add(potracePreviewBox);
505 potraceBox.pack_start(potracePreviewFrame, true, true, 0);
507 /*#### swap black and white ####*/
508 potraceInvertButton.set_label(_("Invert"));
509 potraceInvertButton.set_active(false);
510 potraceInvertBox.pack_end(potraceInvertButton, false, false, MARGIN);
511 potraceBox.pack_start(potraceInvertBox, false, false, MARGIN);
512 tips.set_tip(potraceInvertButton, _("Invert black and white regions for single traces"));
514 /*#### Credits ####*/
515 potraceCreditsLabel.set_text(
516 _("Thanks to Peter Selinger, http://potrace.sourceforge.net")
517 );
518 potraceCreditsVBox.pack_start(potraceCreditsLabel, false, false, MARGIN);
519 potraceCreditsFrame.set_label(_("Credits"));
520 potraceCreditsFrame.set_shadow_type(Gtk::SHADOW_NONE);
521 potraceCreditsFrame.add(potraceCreditsVBox);
522 potraceBox.pack_start(potraceCreditsFrame, false, false, 0);
524 /*done */
525 // TRANSLATORS: Potrace is an application for transforming bitmaps into
526 // vector graphics (http://potrace.sourceforge.net/)
527 notebook.append_page(potraceBox, _("Potrace"));
529 //##Set up the Other panel
530 // This may be reenabled when we have another tracer; now an empty tab is confusing so I'm disabling it
531 // notebook.append_page(otherBox, _("Other"));
533 //##Put the notebook on the dialog
534 mainVBox->pack_start(notebook);
536 //## The OK button
537 potraceCancelButton = add_button(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
538 if (potraceCancelButton)
539 {
540 tips.set_tip((*potraceCancelButton), _("Abort a trace in progress"));
541 potraceCancelButton->set_sensitive(false);
542 }
543 potraceOkButton = add_button(Gtk::Stock::OK, GTK_RESPONSE_OK);
544 tips.set_tip((*potraceOkButton), _("Execute the trace"));
546 show_all_children();
548 //## Connect the signal
549 signal_response().connect(
550 sigc::mem_fun(*this, &TraceDialogImpl::responseCallback) );
551 }
553 /**
554 * Factory method. Use this to create a new TraceDialog
555 */
556 TraceDialog *TraceDialog::create()
557 {
558 TraceDialog *dialog = new TraceDialogImpl();
559 return dialog;
560 }
563 /**
564 * Constructor
565 */
566 TraceDialogImpl::~TraceDialogImpl()
567 {
570 }
577 } //namespace Dialog
578 } //namespace UI
579 } //namespace Inkscape
581 //#########################################################################
582 //## E N D O F F I L E
583 //#########################################################################