6182954f6e090c783b05e171dc8728b5e6a62580
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::CheckButton sioxButton;
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;
140 Gtk::HBox potraceMultiScanHBox3;
141 Gtk::RadioButton potraceMultiScanMonoRadioButton;
142 Gtk::Label potraceMultiScanNrColorLabel;
144 Gtk::CheckButton potraceMultiScanStackButton;
145 Gtk::CheckButton potraceMultiScanSmoothButton;
146 Gtk::CheckButton potraceMultiScanBackgroundButton;
149 //preview
150 Gtk::Frame potracePreviewFrame;
151 Gtk::HBox potracePreviewBox;
152 Gtk::Button potracePreviewButton;
153 Gtk::Image potracePreviewImage;
155 //credits
156 Gtk::Frame potraceCreditsFrame;
157 Gtk::VBox potraceCreditsVBox;
158 Gtk::Label potraceCreditsLabel;
160 //########## Other items
161 Gtk::VBox otherBox;
165 };
170 //#########################################################################
171 //## E V E N T S
172 //#########################################################################
174 /**
175 * This does potrace processing
176 * Only preview if do_i_trace is false
177 */
178 void TraceDialogImpl::potraceProcess(bool do_i_trace)
179 {
180 //##### Get the tracer and engine
181 Inkscape::Trace::Potrace::PotraceTracingEngine pte;
183 /* inversion */
184 bool invert = potraceInvertButton.get_active();
185 pte.setInvert(invert);
187 //##### Get the preprocessor settings
188 /* siox -- performed by Tracer, and before any of the others */
189 if (sioxButton.get_active())
190 tracer.enableSiox(true);
191 else
192 tracer.enableSiox(false);
194 /* one of the following */
195 if (potraceBrightnessRadioButton.get_active())
196 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS);
197 else if (potraceMultiScanBrightnessRadioButton.get_active())
198 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS_MULTI);
199 else if (potraceCannyRadioButton.get_active())
200 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_CANNY);
201 else if (potraceQuantRadioButton.get_active())
202 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT);
203 else if (potraceMultiScanColorRadioButton.get_active())
204 {
205 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_COLOR);
206 pte.setInvert(false);
207 }
208 else if (potraceMultiScanMonoRadioButton.get_active())
209 {
210 pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_MONO);
211 pte.setInvert(false);
212 }
214 //##### Get the single-scan settings
215 /* brightness */
216 double brightnessThreshold = potraceBrightnessSpinner.get_value();
217 pte.setBrightnessThreshold(brightnessThreshold);
219 /* canny */
220 double cannyHighThreshold = potraceCannyHiSpinner.get_value();
221 pte.setCannyHighThreshold(cannyHighThreshold);
223 /* quantization */
224 int quantNrColors = potraceQuantNrColorSpinner.get_value_as_int();
225 pte.setQuantizationNrColors(quantNrColors);
227 //##### Get multiple-scan settings
228 int multiScanNrColors = potraceMultiScanNrColorSpinner.get_value_as_int();
229 pte.setMultiScanNrColors(multiScanNrColors);
230 bool do_i_stack = potraceMultiScanStackButton.get_active();
231 pte.setMultiScanStack(do_i_stack);
232 bool do_i_smooth = potraceMultiScanSmoothButton.get_active();
233 pte.setMultiScanSmooth(do_i_smooth);
234 bool do_i_remove_background = potraceMultiScanBackgroundButton.get_active();
235 pte.setMultiScanRemoveBackground(do_i_remove_background);
237 //##### Get intermediate bitmap image
238 Glib::RefPtr<Gdk::Pixbuf> pixbuf = tracer.getSelectedImage();
239 if (pixbuf)
240 {
241 Glib::RefPtr<Gdk::Pixbuf> preview = pte.preview(pixbuf);
242 if (preview)
243 {
244 int width = preview->get_width();
245 int height = preview->get_height();
246 double scaleFactor = 100.0 / (double)height;
247 int newWidth = (int) (((double)width) * scaleFactor);
248 int newHeight = (int) (((double)height) * scaleFactor);
249 Glib::RefPtr<Gdk::Pixbuf> scaledPreview =
250 preview->scale_simple(newWidth, newHeight,
251 Gdk::INTERP_NEAREST);
252 //g_object_unref(preview);
253 potracePreviewImage.set(scaledPreview);
254 }
255 }
257 //##### Convert
258 if (do_i_trace)
259 {
260 if (potraceCancelButton)
261 potraceCancelButton->set_sensitive(true);
262 if (potraceOkButton)
263 potraceOkButton->set_sensitive(false);
264 tracer.trace(&pte);
265 if (potraceCancelButton)
266 potraceCancelButton->set_sensitive(false);
267 if (potraceOkButton)
268 potraceOkButton->set_sensitive(true);
269 }
271 }
274 /**
275 * Abort processing
276 */
277 void TraceDialogImpl::abort()
278 {
279 //### Do some GUI thing
281 //### Make the abort() call to the tracer
282 tracer.abort();
284 }
288 //#########################################################################
289 //## E V E N T S
290 //#########################################################################
292 /**
293 * Callback from the Preview button. Can be called from elsewhere.
294 */
295 void TraceDialogImpl::potracePreviewCallback()
296 {
297 potraceProcess(false);
298 }
300 /**
301 * Default response from the dialog. Let's intercept it
302 */
303 void TraceDialogImpl::responseCallback(int response_id)
304 {
306 if (response_id == GTK_RESPONSE_OK)
307 {
308 int panelNr = notebook.get_current_page();
309 //g_message("selected panel:%d\n", panelNr);
311 if (panelNr == 0)
312 {
313 potraceProcess(true);
314 }
315 }
316 else if (response_id == GTK_RESPONSE_CANCEL)
317 {
318 abort();
319 }
320 else
321 {
322 hide();
323 return;
324 }
328 }
333 //#########################################################################
334 //## C O N S T R U C T O R / D E S T R U C T O R
335 //#########################################################################
336 /**
337 * Constructor
338 */
339 TraceDialogImpl::TraceDialogImpl()
340 {
342 Gtk::VBox *mainVBox = get_vbox();
344 #define MARGIN 4
346 /*#### SIOX ####*/
347 //# for now, put at the top of the potrace box. something better later
348 sioxButton.set_label(_("SIOX foreground selection"));
349 sioxBox.pack_start(sioxButton, false, false, MARGIN);
350 tips.set_tip(sioxButton,
351 _("Cover the area you want to select as the foreground"));
352 sioxVBox.pack_start(sioxBox, false, false, MARGIN);
353 sioxFrame.set_label(_("SIOX"));
354 sioxFrame.add(sioxVBox);
355 potraceBox.pack_start(sioxFrame, false, false, 0);
358 //##Set up the Potrace panel
360 /*#### brightness ####*/
361 potraceBrightnessRadioButton.set_label(_("Brightness"));
362 potraceGroup = potraceBrightnessRadioButton.get_group();
363 potraceBrightnessBox.pack_start(potraceBrightnessRadioButton, false, false, MARGIN);
364 tips.set_tip(potraceBrightnessRadioButton, _("Trace by a given brightness level"));
366 potraceBrightnessSpinner.set_digits(3);
367 potraceBrightnessSpinner.set_increments(0.01, 0.1);
368 potraceBrightnessSpinner.set_range(0.0, 1.0);
369 potraceBrightnessSpinner.set_value(0.45);
370 potraceBrightnessBox.pack_end(potraceBrightnessSpinner, false, false, MARGIN);
371 tips.set_tip(potraceBrightnessSpinner, _("Brightness cutoff for black/white"));
373 potraceBrightnessSpinnerLabel.set_label(_("Threshold:"));
374 potraceBrightnessBox.pack_end(potraceBrightnessSpinnerLabel, false, false, MARGIN);
376 potraceBrightnessVBox.pack_start(potraceBrightnessBox, false, false, MARGIN);
378 potraceBrightnessFrame.set_label(_("Image Brightness"));
379 //potraceBrightnessFrame.set_shadow_type(Gtk::SHADOW_NONE);
380 potraceBrightnessFrame.add(potraceBrightnessVBox);
381 potraceBox.pack_start(potraceBrightnessFrame, false, false, 0);
383 /*#### canny edge detection ####*/
384 // TRANSLATORS: "Canny" is the name of the inventor of this edge detection method
385 potraceCannyRadioButton.set_label(_("Optimal Edge Detection (Canny)"));
386 potraceCannyRadioButton.set_group(potraceGroup);
387 potraceCannyBox.pack_start(potraceCannyRadioButton, false, false, MARGIN);
388 tips.set_tip(potraceCannyRadioButton, _("Trace with edge detection by J. Canny's algorithm"));
389 /*
390 potraceCannyBox.pack_start(potraceCannySeparator);
391 potraceCannyLoSpinnerLabel.set_label(_("Low"));
392 potraceCannyBox.pack_start(potraceCannyLoSpinnerLabel);
393 potraceCannyLoSpinner.set_digits(5);
394 potraceCannyLoSpinner.set_increments(0.01, 0.1);
395 potraceCannyLoSpinner.set_range(0.0, 1.0);
396 potraceCannyLoSpinner.set_value(0.1);
397 potraceCannyBox.pack_start(potraceCannyLoSpinner);
398 */
399 potraceCannyHiSpinner.set_digits(3);
400 potraceCannyHiSpinner.set_increments(0.01, 0.1);
401 potraceCannyHiSpinner.set_range(0.0, 1.0);
402 potraceCannyHiSpinner.set_value(0.65);
403 potraceCannyBox.pack_end(potraceCannyHiSpinner, false, false, MARGIN);
404 tips.set_tip(potraceCannyHiSpinner, _("Brightness cutoff for adjacent pixels (determines edge thickness)"));
406 potraceCannyHiSpinnerLabel.set_label(_("Threshold:"));
407 potraceCannyBox.pack_end(potraceCannyHiSpinnerLabel, false, false, MARGIN);
409 potraceCannyVBox.pack_start(potraceCannyBox, false, false, MARGIN);
411 potraceCannyFrame.set_label(_("Edge Detection"));
412 //potraceCannyFrame.set_shadow_type(Gtk::SHADOW_NONE);
413 potraceCannyFrame.add(potraceCannyVBox);
414 potraceBox.pack_start(potraceCannyFrame, false, false, 0);
416 /*#### quantization ####*/
417 // TRANSLATORS: Color Quantization: the process of reducing the number of colors
418 // in an image by selecting an optimized set of representative colors and then
419 // re-applying this reduced set to the original image.
420 potraceQuantRadioButton.set_label(_("Color Quantization"));
421 potraceQuantRadioButton.set_group(potraceGroup);
422 potraceQuantBox.pack_start(potraceQuantRadioButton, false, false, MARGIN);
423 tips.set_tip(potraceQuantRadioButton, _("Trace along the boundaries of reduced colors"));
425 potraceQuantNrColorSpinner.set_digits(2);
426 potraceQuantNrColorSpinner.set_increments(1.0, 4.0);
427 potraceQuantNrColorSpinner.set_range(2.0, 64.0);
428 potraceQuantNrColorSpinner.set_value(8.0);
429 potraceQuantBox.pack_end(potraceQuantNrColorSpinner, false, false, MARGIN);
430 tips.set_tip(potraceQuantNrColorSpinner, _("The number of reduced colors"));
432 potraceQuantNrColorLabel.set_label(_("Colors:"));
433 potraceQuantBox.pack_end(potraceQuantNrColorLabel, false, false, MARGIN);
435 potraceQuantVBox.pack_start(potraceQuantBox, false, false, MARGIN);
437 potraceQuantFrame.set_label(_("Quantization / Reduction"));
438 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
439 potraceQuantFrame.add(potraceQuantVBox);
440 potraceBox.pack_start(potraceQuantFrame, false, false, 0);
442 /*#### Multiple scanning####*/
443 //----Hbox1
444 potraceMultiScanBrightnessRadioButton.set_label(_("Brightness"));
445 potraceMultiScanBrightnessRadioButton.set_group(potraceGroup);
446 potraceMultiScanHBox1.pack_start(potraceMultiScanBrightnessRadioButton, false, false, MARGIN);
447 tips.set_tip(potraceMultiScanBrightnessRadioButton, _("Trace the given number of brightness levels"));
449 potraceMultiScanNrColorSpinner.set_digits(0);
450 potraceMultiScanNrColorSpinner.set_increments(1.0, 4.0);
451 potraceMultiScanNrColorSpinner.set_range(2.0, 256.0);
452 potraceMultiScanNrColorSpinner.set_value(8.0);
453 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorSpinner, false, false, MARGIN);
454 potraceMultiScanNrColorLabel.set_label(_("Scans:"));
455 potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorLabel, false, false, MARGIN);
456 tips.set_tip(potraceMultiScanNrColorSpinner, _("The desired number of scans"));
458 potraceMultiScanVBox.pack_start(potraceMultiScanHBox1, false, false, MARGIN);
460 //----Hbox2
461 potraceMultiScanColorRadioButton.set_label(_("Color"));
462 potraceMultiScanColorRadioButton.set_group(potraceGroup);
463 potraceMultiScanHBox2.pack_start(potraceMultiScanColorRadioButton, false, false, MARGIN);
464 tips.set_tip(potraceMultiScanColorRadioButton, _("Trace the given number of reduced colors"));
467 // TRANSLATORS: "Layer" refers to one of the stacked paths in the multiscan
468 potraceMultiScanBackgroundButton.set_label(_("Remove background"));
469 potraceMultiScanBackgroundButton.set_active(false);
470 potraceMultiScanHBox2.pack_end(potraceMultiScanBackgroundButton, false, false, MARGIN);
471 tips.set_tip(potraceMultiScanBackgroundButton, _("Remove bottom (background) layer when done"));
473 potraceMultiScanVBox.pack_start(potraceMultiScanHBox2, false, false, MARGIN);
475 //---Hbox3
476 potraceMultiScanMonoRadioButton.set_label(_("Monochrome"));
477 potraceMultiScanMonoRadioButton.set_group(potraceGroup);
478 potraceMultiScanHBox3.pack_start(potraceMultiScanMonoRadioButton, false, false, MARGIN);
479 tips.set_tip(potraceMultiScanMonoRadioButton, _("Same as Color, but convert result to grayscale"));
481 // TRANSLATORS: "Stack" is a verb here
482 potraceMultiScanStackButton.set_label(_("Stack"));
483 potraceMultiScanStackButton.set_active(true);
484 potraceMultiScanHBox3.pack_end(potraceMultiScanStackButton, false, false, MARGIN);
485 tips.set_tip(potraceMultiScanStackButton, _("Stack scans vertically (no gaps) or tile horizontally (usually with gaps)"));
487 // TRANSLATORS: "Smooth" is a verb here
488 potraceMultiScanSmoothButton.set_label(_("Smooth"));
489 potraceMultiScanSmoothButton.set_active(true);
490 potraceMultiScanHBox3.pack_end(potraceMultiScanSmoothButton, false, false, MARGIN);
491 tips.set_tip(potraceMultiScanSmoothButton, _("Apply Gaussian blur to the bitmap before tracing"));
494 potraceMultiScanVBox.pack_start(potraceMultiScanHBox3, false, false, MARGIN);
496 potraceMultiScanFrame.set_label(_("Multiple Scanning"));
497 //potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
498 potraceMultiScanFrame.add(potraceMultiScanVBox);
499 potraceBox.pack_start(potraceMultiScanFrame, false, false, 0);
501 /*#### Preview ####*/
502 potracePreviewButton.set_label(_("Preview"));
503 potracePreviewButton.signal_clicked().connect(
504 sigc::mem_fun(*this, &TraceDialogImpl::potracePreviewCallback) );
505 potracePreviewBox.pack_end(potracePreviewButton, false, false, 0);//do not expand
506 tips.set_tip(potracePreviewButton, _("Preview the result without actual tracing"));
509 potracePreviewImage.set_size_request(100,100);
510 //potracePreviewImage.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
511 potracePreviewBox.pack_start(potracePreviewImage, true, true, 0);
512 potracePreviewFrame.set_label(_("Preview")); // I guess it's correct to call the "intermediate bitmap" a preview of the trace
513 //potracePreviewFrame.set_shadow_type(Gtk::SHADOW_NONE);
514 potracePreviewFrame.add(potracePreviewBox);
515 potraceBox.pack_start(potracePreviewFrame, true, true, 0);
517 /*#### swap black and white ####*/
518 potraceInvertButton.set_label(_("Invert"));
519 potraceInvertButton.set_active(false);
520 potraceInvertBox.pack_end(potraceInvertButton, false, false, MARGIN);
521 potraceBox.pack_start(potraceInvertBox, false, false, MARGIN);
522 tips.set_tip(potraceInvertButton, _("Invert black and white regions for single traces"));
524 /*#### Credits ####*/
525 potraceCreditsLabel.set_text(
526 _("Thanks to Peter Selinger, http://potrace.sourceforge.net")
527 );
528 potraceCreditsVBox.pack_start(potraceCreditsLabel, false, false, MARGIN);
529 potraceCreditsFrame.set_label(_("Credits"));
530 potraceCreditsFrame.set_shadow_type(Gtk::SHADOW_NONE);
531 potraceCreditsFrame.add(potraceCreditsVBox);
532 potraceBox.pack_start(potraceCreditsFrame, false, false, 0);
534 /*done */
535 // TRANSLATORS: Potrace is an application for transforming bitmaps into
536 // vector graphics (http://potrace.sourceforge.net/)
537 notebook.append_page(potraceBox, _("Potrace"));
539 //##Set up the Other panel
540 // This may be reenabled when we have another tracer; now an empty tab is confusing so I'm disabling it
541 // notebook.append_page(otherBox, _("Other"));
543 //##Put the notebook on the dialog
544 mainVBox->pack_start(notebook);
546 //## The OK button
547 potraceCancelButton = add_button(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
548 if (potraceCancelButton)
549 {
550 tips.set_tip((*potraceCancelButton), _("Abort a trace in progress"));
551 potraceCancelButton->set_sensitive(false);
552 }
553 potraceOkButton = add_button(Gtk::Stock::OK, GTK_RESPONSE_OK);
554 tips.set_tip((*potraceOkButton), _("Execute the trace"));
556 show_all_children();
558 //## Connect the signal
559 signal_response().connect(
560 sigc::mem_fun(*this, &TraceDialogImpl::responseCallback) );
561 }
563 /**
564 * Factory method. Use this to create a new TraceDialog
565 */
566 TraceDialog *TraceDialog::create()
567 {
568 TraceDialog *dialog = new TraceDialogImpl();
569 return dialog;
570 }
573 /**
574 * Constructor
575 */
576 TraceDialogImpl::~TraceDialogImpl()
577 {
580 }
587 } //namespace Dialog
588 } //namespace UI
589 } //namespace Inkscape
591 //#########################################################################
592 //## E N D O F F I L E
593 //#########################################################################