tracedialog.cpp revision fdeb14b5acd8ab24c8c8dab6e78c23c967547747
/*
* A simple dialog for setting the parameters for autotracing a
* bitmap <image> into an svg <path>
*
* Authors:
* Bob Jamison
* Other dudes from The Inkscape Organization
*
* Copyright (C) 2004, 2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <gtkmm/notebook.h>
#include <gtkmm/frame.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/stock.h>
#include <gtk/gtkdialog.h> //for GTK_RESPONSE* types
#include <glibmm/i18n.h>
#include "tracedialog.h"
#include "trace/potrace/inkscape-potrace.h"
namespace Inkscape {
namespace UI {
namespace Dialog {
//#########################################################################
//## I M P L E M E N T A T I O N
//#########################################################################
/**
* A dialog for adjusting bitmap->vector tracing parameters
*/
class TraceDialogImpl : public TraceDialog
{
public:
/**
* Constructor
*/
TraceDialogImpl();
/**
* Destructor
*/
~TraceDialogImpl();
/**
* Callback from OK or Cancel
*/
void responseCallback(int response_id);
private:
/**
* This is the big almighty McGuffin
*/
Inkscape::Trace::Tracer tracer;
/**
* This does potrace processing
* Only preview if do_i_trace is false
*/
void potraceProcess(bool do_i_trace);
/**
* Abort processing
*/
void abort();
void potracePreviewCallback();
Gtk::Notebook notebook;
Gtk::Tooltips tips;
//########## General items
Gtk::Frame sioxFrame;
Gtk::VBox sioxVBox;
Gtk::HBox sioxBox;
Gtk::CheckButton sioxButton;
//########## Potrace items
Gtk::VBox potraceBox;
Gtk::RadioButtonGroup potraceGroup;
Gtk::CheckButton potraceInvertButton;
Gtk::HBox potraceInvertBox;
Gtk::Button *potraceOkButton;
Gtk::Button *potraceCancelButton;
//brightness
Gtk::Frame potraceBrightnessFrame;
Gtk::VBox potraceBrightnessVBox;
Gtk::HBox potraceBrightnessBox;
Gtk::RadioButton potraceBrightnessRadioButton;
Gtk::Label potraceBrightnessSpinnerLabel;
Gtk::SpinButton potraceBrightnessSpinner;
//edge detection
Gtk::Frame potraceCannyFrame;
Gtk::HBox potraceCannyBox;
Gtk::VBox potraceCannyVBox;
Gtk::RadioButton potraceCannyRadioButton;
//Gtk::HSeparator potraceCannySeparator;
//Gtk::Label potraceCannyLoSpinnerLabel;
//Gtk::SpinButton potraceCannyLoSpinner;
Gtk::Label potraceCannyHiSpinnerLabel;
Gtk::SpinButton potraceCannyHiSpinner;
//quantization
Gtk::Frame potraceQuantFrame;
Gtk::HBox potraceQuantBox;
Gtk::VBox potraceQuantVBox;
Gtk::RadioButton potraceQuantRadioButton;
Gtk::Label potraceQuantNrColorLabel;
Gtk::SpinButton potraceQuantNrColorSpinner;
//multiple path scanning
Gtk::Frame potraceMultiScanFrame;
Gtk::VBox potraceMultiScanVBox;
Gtk::HBox potraceMultiScanHBox1;
Gtk::RadioButton potraceMultiScanBrightnessRadioButton;
Gtk::SpinButton potraceMultiScanNrColorSpinner;
Gtk::HBox potraceMultiScanHBox2;
Gtk::RadioButton potraceMultiScanColorRadioButton;
Gtk::CheckButton potraceMultiScanStackButton;
Gtk::HBox potraceMultiScanHBox3;
Gtk::RadioButton potraceMultiScanMonoRadioButton;
Gtk::Label potraceMultiScanNrColorLabel;
Gtk::CheckButton potraceMultiScanSmoothButton;
//preview
Gtk::Frame potracePreviewFrame;
Gtk::HBox potracePreviewBox;
Gtk::Button potracePreviewButton;
Gtk::Image potracePreviewImage;
//credits
Gtk::Frame potraceCreditsFrame;
Gtk::VBox potraceCreditsVBox;
Gtk::Label potraceCreditsLabel;
//########## Other items
Gtk::VBox otherBox;
};
//#########################################################################
//## E V E N T S
//#########################################################################
/**
* This does potrace processing
* Only preview if do_i_trace is false
*/
void TraceDialogImpl::potraceProcess(bool do_i_trace)
{
//##### Get the tracer and engine
Inkscape::Trace::Potrace::PotraceTracingEngine pte;
/* inversion */
bool invert = potraceInvertButton.get_active();
pte.setInvert(invert);
//##### Get the preprocessor settings
/* siox -- performed by Tracer, and before any of the others */
if (sioxButton.get_active())
tracer.enableSiox(true);
else
tracer.enableSiox(false);
/* one of the following */
if (potraceBrightnessRadioButton.get_active())
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS);
else if (potraceMultiScanBrightnessRadioButton.get_active())
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_BRIGHTNESS_MULTI);
else if (potraceCannyRadioButton.get_active())
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_CANNY);
else if (potraceQuantRadioButton.get_active())
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT);
else if (potraceMultiScanColorRadioButton.get_active())
{
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_COLOR);
pte.setInvert(false);
}
else if (potraceMultiScanMonoRadioButton.get_active())
{
pte.setTraceType(Inkscape::Trace::Potrace::TRACE_QUANT_MONO);
pte.setInvert(false);
}
//##### Get the single-scan settings
/* brightness */
double brightnessThreshold = potraceBrightnessSpinner.get_value();
pte.setBrightnessThreshold(brightnessThreshold);
/* canny */
double cannyHighThreshold = potraceCannyHiSpinner.get_value();
pte.setCannyHighThreshold(cannyHighThreshold);
/* quantization */
int quantNrColors = potraceQuantNrColorSpinner.get_value_as_int();
pte.setQuantizationNrColors(quantNrColors);
//##### Get multiple-scan settings
int multiScanNrColors = potraceMultiScanNrColorSpinner.get_value_as_int();
pte.setMultiScanNrColors(multiScanNrColors);
bool do_i_stack = potraceMultiScanStackButton.get_active();
pte.setMultiScanStack(do_i_stack);
bool do_i_smooth = potraceMultiScanSmoothButton.get_active();
pte.setMultiScanSmooth(do_i_smooth);
//##### Get intermediate bitmap image
Glib::RefPtr<Gdk::Pixbuf> pixbuf = tracer.getSelectedImage();
if (pixbuf)
{
Glib::RefPtr<Gdk::Pixbuf> preview = pte.preview(pixbuf);
if (preview)
{
int width = preview->get_width();
int height = preview->get_height();
double scaleFactor = 100.0 / (double)height;
int newWidth = (int) (((double)width) * scaleFactor);
int newHeight = (int) (((double)height) * scaleFactor);
Glib::RefPtr<Gdk::Pixbuf> scaledPreview =
preview->scale_simple(newWidth, newHeight,
Gdk::INTERP_NEAREST);
//g_object_unref(preview);
potracePreviewImage.set(scaledPreview);
}
}
//##### Convert
if (do_i_trace)
{
if (potraceCancelButton)
potraceCancelButton->set_sensitive(true);
if (potraceOkButton)
potraceOkButton->set_sensitive(false);
tracer.trace(&pte);
if (potraceCancelButton)
potraceCancelButton->set_sensitive(false);
if (potraceOkButton)
potraceOkButton->set_sensitive(true);
}
}
/**
* Abort processing
*/
void TraceDialogImpl::abort()
{
//### Do some GUI thing
//### Make the abort() call to the tracer
tracer.abort();
}
//#########################################################################
//## E V E N T S
//#########################################################################
/**
* Callback from the Preview button. Can be called from elsewhere.
*/
void TraceDialogImpl::potracePreviewCallback()
{
potraceProcess(false);
}
/**
* Default response from the dialog. Let's intercept it
*/
void TraceDialogImpl::responseCallback(int response_id)
{
if (response_id == GTK_RESPONSE_OK)
{
int panelNr = notebook.get_current_page();
//g_message("selected panel:%d\n", panelNr);
if (panelNr == 0)
{
potraceProcess(true);
}
}
else if (response_id == GTK_RESPONSE_CANCEL)
{
abort();
}
else
{
hide();
return;
}
}
//#########################################################################
//## C O N S T R U C T O R / D E S T R U C T O R
//#########################################################################
/**
* Constructor
*/
TraceDialogImpl::TraceDialogImpl()
{
Gtk::VBox *mainVBox = get_vbox();
#define MARGIN 4
/*#### SIOX ####*/
//# for now, put at the top of the potrace box. something better later
sioxButton.set_label(_("SIOX foreground selection (experimental)"));
sioxBox.pack_start(sioxButton, false, false, MARGIN);
tips.set_tip(sioxButton,
_("Cover the area you want to select as the foreground"));
sioxVBox.pack_start(sioxBox, false, false, MARGIN);
sioxFrame.set_label(_("SIOX"));
sioxFrame.add(sioxVBox);
potraceBox.pack_start(sioxFrame, false, false, 0);
//##Set up the Potrace panel
/*#### brightness ####*/
potraceBrightnessRadioButton.set_label(_("Brightness"));
potraceGroup = potraceBrightnessRadioButton.get_group();
potraceBrightnessBox.pack_start(potraceBrightnessRadioButton, false, false, MARGIN);
tips.set_tip(potraceBrightnessRadioButton, _("Trace by a given brightness level"));
potraceBrightnessSpinner.set_digits(3);
potraceBrightnessSpinner.set_increments(0.01, 0.1);
potraceBrightnessSpinner.set_range(0.0, 1.0);
potraceBrightnessSpinner.set_value(0.45);
potraceBrightnessBox.pack_end(potraceBrightnessSpinner, false, false, MARGIN);
tips.set_tip(potraceBrightnessSpinner, _("Brightness cutoff for black/white"));
potraceBrightnessSpinnerLabel.set_label(_("Threshold:"));
potraceBrightnessBox.pack_end(potraceBrightnessSpinnerLabel, false, false, MARGIN);
potraceBrightnessVBox.pack_start(potraceBrightnessBox, false, false, MARGIN);
potraceBrightnessFrame.set_label(_("Image Brightness"));
//potraceBrightnessFrame.set_shadow_type(Gtk::SHADOW_NONE);
potraceBrightnessFrame.add(potraceBrightnessVBox);
potraceBox.pack_start(potraceBrightnessFrame, false, false, 0);
/*#### canny edge detection ####*/
// TRANSLATORS: "Canny" is the name of the inventor of this edge detection method
potraceCannyRadioButton.set_label(_("Optimal Edge Detection (Canny)"));
potraceCannyRadioButton.set_group(potraceGroup);
potraceCannyBox.pack_start(potraceCannyRadioButton, false, false, MARGIN);
tips.set_tip(potraceCannyRadioButton, _("Trace with edge detection by J. Canny's algorithm"));
/*
potraceCannyBox.pack_start(potraceCannySeparator);
potraceCannyLoSpinnerLabel.set_label(_("Low"));
potraceCannyBox.pack_start(potraceCannyLoSpinnerLabel);
potraceCannyLoSpinner.set_digits(5);
potraceCannyLoSpinner.set_increments(0.01, 0.1);
potraceCannyLoSpinner.set_range(0.0, 1.0);
potraceCannyLoSpinner.set_value(0.1);
potraceCannyBox.pack_start(potraceCannyLoSpinner);
*/
potraceCannyHiSpinner.set_digits(3);
potraceCannyHiSpinner.set_increments(0.01, 0.1);
potraceCannyHiSpinner.set_range(0.0, 1.0);
potraceCannyHiSpinner.set_value(0.65);
potraceCannyBox.pack_end(potraceCannyHiSpinner, false, false, MARGIN);
tips.set_tip(potraceCannyHiSpinner, _("Brightness cutoff for adjacent pixels (determines edge thickness)"));
potraceCannyHiSpinnerLabel.set_label(_("Threshold:"));
potraceCannyBox.pack_end(potraceCannyHiSpinnerLabel, false, false, MARGIN);
potraceCannyVBox.pack_start(potraceCannyBox, false, false, MARGIN);
potraceCannyFrame.set_label(_("Edge Detection"));
//potraceCannyFrame.set_shadow_type(Gtk::SHADOW_NONE);
potraceCannyFrame.add(potraceCannyVBox);
potraceBox.pack_start(potraceCannyFrame, false, false, 0);
/*#### quantization ####*/
// TRANSLATORS: Color Quantization: the process of reducing the number of colors
// in an image by selecting an optimized set of representative colors and then
// re-applying this reduced set to the original image.
potraceQuantRadioButton.set_label(_("Color Quantization"));
potraceQuantRadioButton.set_group(potraceGroup);
potraceQuantBox.pack_start(potraceQuantRadioButton, false, false, MARGIN);
tips.set_tip(potraceQuantRadioButton, _("Trace along the boundaries of reduced colors"));
potraceQuantNrColorSpinner.set_digits(2);
potraceQuantNrColorSpinner.set_increments(1.0, 4.0);
potraceQuantNrColorSpinner.set_range(2.0, 64.0);
potraceQuantNrColorSpinner.set_value(8.0);
potraceQuantBox.pack_end(potraceQuantNrColorSpinner, false, false, MARGIN);
tips.set_tip(potraceQuantNrColorSpinner, _("The number of reduced colors"));
potraceQuantNrColorLabel.set_label(_("Colors:"));
potraceQuantBox.pack_end(potraceQuantNrColorLabel, false, false, MARGIN);
potraceQuantVBox.pack_start(potraceQuantBox, false, false, MARGIN);
potraceQuantFrame.set_label(_("Quantization / Reduction"));
//potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
potraceQuantFrame.add(potraceQuantVBox);
potraceBox.pack_start(potraceQuantFrame, false, false, 0);
/*#### Multiple scanning####*/
//----Hbox1
potraceMultiScanBrightnessRadioButton.set_label(_("Brightness"));
potraceMultiScanBrightnessRadioButton.set_group(potraceGroup);
potraceMultiScanHBox1.pack_start(potraceMultiScanBrightnessRadioButton, false, false, MARGIN);
tips.set_tip(potraceMultiScanBrightnessRadioButton, _("Trace the given number of brightness levels"));
potraceMultiScanNrColorSpinner.set_digits(0);
potraceMultiScanNrColorSpinner.set_increments(1.0, 4.0);
potraceMultiScanNrColorSpinner.set_range(2.0, 256.0);
potraceMultiScanNrColorSpinner.set_value(8.0);
potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorSpinner, false, false, MARGIN);
potraceMultiScanNrColorLabel.set_label(_("Scans:"));
potraceMultiScanHBox1.pack_end(potraceMultiScanNrColorLabel, false, false, MARGIN);
tips.set_tip(potraceMultiScanNrColorSpinner, _("The desired number of scans"));
potraceMultiScanVBox.pack_start(potraceMultiScanHBox1, false, false, MARGIN);
//----Hbox2
potraceMultiScanColorRadioButton.set_label(_("Color"));
potraceMultiScanColorRadioButton.set_group(potraceGroup);
potraceMultiScanHBox2.pack_start(potraceMultiScanColorRadioButton, false, false, MARGIN);
tips.set_tip(potraceMultiScanColorRadioButton, _("Trace the given number of reduced colors"));
potraceMultiScanVBox.pack_start(potraceMultiScanHBox2, false, false, MARGIN);
//---Hbox3
potraceMultiScanMonoRadioButton.set_label(_("Monochrome"));
potraceMultiScanMonoRadioButton.set_group(potraceGroup);
potraceMultiScanHBox3.pack_start(potraceMultiScanMonoRadioButton, false, false, MARGIN);
tips.set_tip(potraceMultiScanMonoRadioButton, _("Same as Color, but convert result to grayscale"));
// TRANSLATORS: "Stack" is a verb here
potraceMultiScanStackButton.set_label(_("Stack"));
potraceMultiScanStackButton.set_active(true);
potraceMultiScanHBox3.pack_end(potraceMultiScanStackButton, false, false, MARGIN);
tips.set_tip(potraceMultiScanStackButton, _("Stack scans vertically (no gaps) or tile horizontally (usually with gaps)"));
// TRANSLATORS: "Smooth" is a verb here
potraceMultiScanSmoothButton.set_label(_("Smooth"));
potraceMultiScanSmoothButton.set_active(true);
potraceMultiScanHBox3.pack_end(potraceMultiScanSmoothButton, false, false, MARGIN);
tips.set_tip(potraceMultiScanSmoothButton, _("Apply Gaussian blur to the bitmap before tracing"));
potraceMultiScanVBox.pack_start(potraceMultiScanHBox3, false, false, MARGIN);
potraceMultiScanFrame.set_label(_("Multiple Scanning"));
//potraceQuantFrame.set_shadow_type(Gtk::SHADOW_NONE);
potraceMultiScanFrame.add(potraceMultiScanVBox);
potraceBox.pack_start(potraceMultiScanFrame, false, false, 0);
/*#### Preview ####*/
potracePreviewButton.set_label(_("Preview"));
potracePreviewButton.signal_clicked().connect(
sigc::mem_fun(*this, &TraceDialogImpl::potracePreviewCallback) );
potracePreviewBox.pack_end(potracePreviewButton, false, false, 0);//do not expand
tips.set_tip(potracePreviewButton, _("Preview the result without actual tracing"));
potracePreviewImage.set_size_request(100,100);
//potracePreviewImage.set_alignment (Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
potracePreviewBox.pack_start(potracePreviewImage, true, true, 0);
potracePreviewFrame.set_label(_("Preview")); // I guess it's correct to call the "intermediate bitmap" a preview of the trace
//potracePreviewFrame.set_shadow_type(Gtk::SHADOW_NONE);
potracePreviewFrame.add(potracePreviewBox);
potraceBox.pack_start(potracePreviewFrame, true, true, 0);
/*#### swap black and white ####*/
potraceInvertButton.set_label(_("Invert"));
potraceInvertButton.set_active(false);
potraceInvertBox.pack_end(potraceInvertButton, false, false, MARGIN);
potraceBox.pack_start(potraceInvertBox, false, false, MARGIN);
tips.set_tip(potraceInvertButton, _("Invert black and white regions for single traces"));
/*#### Credits ####*/
potraceCreditsLabel.set_text(
_("Thanks to Peter Selinger, http://potrace.sourceforge.net")
);
potraceCreditsVBox.pack_start(potraceCreditsLabel, false, false, MARGIN);
potraceCreditsFrame.set_label(_("Credits"));
potraceCreditsFrame.set_shadow_type(Gtk::SHADOW_NONE);
potraceCreditsFrame.add(potraceCreditsVBox);
potraceBox.pack_start(potraceCreditsFrame, false, false, 0);
/*done */
// TRANSLATORS: Potrace is an application for transforming bitmaps into
// vector graphics (http://potrace.sourceforge.net/)
notebook.append_page(potraceBox, _("Potrace"));
//##Set up the Other panel
// This may be reenabled when we have another tracer; now an empty tab is confusing so I'm disabling it
// notebook.append_page(otherBox, _("Other"));
//##Put the notebook on the dialog
mainVBox->pack_start(notebook);
//## The OK button
potraceCancelButton = add_button(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
if (potraceCancelButton)
{
tips.set_tip((*potraceCancelButton), _("Abort a trace in progress"));
potraceCancelButton->set_sensitive(false);
}
potraceOkButton = add_button(Gtk::Stock::OK, GTK_RESPONSE_OK);
tips.set_tip((*potraceOkButton), _("Execute the trace"));
show_all_children();
//## Connect the signal
signal_response().connect(
sigc::mem_fun(*this, &TraceDialogImpl::responseCallback) );
}
/**
* Factory method. Use this to create a new TraceDialog
*/
TraceDialog *TraceDialog::create()
{
TraceDialog *dialog = new TraceDialogImpl();
return dialog;
}
/**
* Constructor
*/
TraceDialogImpl::~TraceDialogImpl()
{
}
} //namespace Dialog
} //namespace UI
} //namespace Inkscape
//#########################################################################
//## E N D O F F I L E
//#########################################################################