summaryrefslogtreecommitdiffstats
path: root/src/ui
diff options
context:
space:
mode:
authorVinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>2013-09-19 04:26:02 +0000
committerVinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>2013-09-19 04:26:02 +0000
commitbf782f3e9d8f76e3e6f32101bbd38754ed0197fb (patch)
tree10f49896eb4ecbab0076fefdac580c9c97cf608e /src/ui
parentMerge C++ification of the SP tree by Markus Engel (diff)
parentMissing file from libdepixelize (diff)
downloadinkscape-bf782f3e9d8f76e3e6f32101bbd38754ed0197fb.tar.gz
inkscape-bf782f3e9d8f76e3e6f32101bbd38754ed0197fb.zip
Merging libdepixelize branch
(bzr r12533)
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/CMakeLists.txt1
-rw-r--r--src/ui/dialog/Makefile_insert2
-rw-r--r--src/ui/dialog/dialog-manager.cpp3
-rw-r--r--src/ui/dialog/pixelartdialog.cpp490
-rw-r--r--src/ui/dialog/pixelartdialog.h58
5 files changed, 554 insertions, 0 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index c95dd35cc..233e01862 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -71,6 +71,7 @@ set(ui_SRC
dialog/text-edit.cpp
dialog/tile.cpp
dialog/tracedialog.cpp
+ dialog/pixelartdialog.cpp
dialog/transformation.cpp
dialog/undo-history.cpp
# dialog/whiteboard-connect.cpp
diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert
index 09a7ef573..c37767a08 100644
--- a/src/ui/dialog/Makefile_insert
+++ b/src/ui/dialog/Makefile_insert
@@ -101,6 +101,8 @@ ink_common_sources += \
ui/dialog/tile.h \
ui/dialog/tracedialog.cpp \
ui/dialog/tracedialog.h \
+ ui/dialog/pixelartdialog.cpp \
+ ui/dialog/pixelartdialog.h \
ui/dialog/transformation.cpp \
ui/dialog/transformation.h \
ui/dialog/undo-history.cpp \
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index 0ce74f54e..17f6ae74d 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -35,6 +35,7 @@
#include "ui/dialog/symbols.h"
#include "ui/dialog/tile.h"
#include "ui/dialog/tracedialog.h"
+#include "ui/dialog/pixelartdialog.h"
#include "ui/dialog/transformation.h"
#include "ui/dialog/undo-history.h"
#include "ui/dialog/panel-dialog.h"
@@ -118,6 +119,7 @@ DialogManager::DialogManager() {
registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>);
registerFactory("TileDialog", &create<TileDialog, FloatingBehavior>);
registerFactory("Trace", &create<TraceDialog, FloatingBehavior>);
+ registerFactory("PixelArt", &create<PixelArtDialog, FloatingBehavior>);
registerFactory("Transformation", &create<Transformation, FloatingBehavior>);
registerFactory("UndoHistory", &create<UndoHistory, FloatingBehavior>);
registerFactory("InputDevices", &create<InputDialog, FloatingBehavior>);
@@ -151,6 +153,7 @@ DialogManager::DialogManager() {
registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>);
registerFactory("TileDialog", &create<TileDialog, DockBehavior>);
registerFactory("Trace", &create<TraceDialog, DockBehavior>);
+ registerFactory("PixelArt", &create<PixelArtDialog, DockBehavior>);
registerFactory("Transformation", &create<Transformation, DockBehavior>);
registerFactory("UndoHistory", &create<UndoHistory, DockBehavior>);
registerFactory("InputDevices", &create<InputDialog, DockBehavior>);
diff --git a/src/ui/dialog/pixelartdialog.cpp b/src/ui/dialog/pixelartdialog.cpp
new file mode 100644
index 000000000..6f0845ada
--- /dev/null
+++ b/src/ui/dialog/pixelartdialog.cpp
@@ -0,0 +1,490 @@
+/**
+ * @file
+ * Pixel art tracing settings dialog - implementation.
+ */
+/* Authors:
+ * Bob Jamison <rjamison@titan.com>
+ * Stéphane Gimenez <dev@gim.name>
+ * Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+ * Other dudes from The Inkscape Organization
+ *
+ * Copyright (C) 2004-2013 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pixelartdialog.h"
+#include <gtkmm/radiobutton.h>
+#include <gtkmm/stock.h>
+
+#include <gtk/gtk.h> //for GTK_RESPONSE* types
+#include <glibmm/i18n.h>
+
+#include "ui/widget/spinbutton.h"
+#include "ui/widget/frame.h"
+
+#include "desktop.h"
+#include "desktop-tracker.h"
+#include "message-stack.h"
+#include "selection.h"
+#include "preferences.h"
+
+#include "sp-image.h"
+#include "libdepixelize/kopftracer2011.h"
+#include <algorithm>
+#include "document.h"
+#include "xml/repr.h"
+#include "xml/document.h"
+#include "svg/svg.h"
+#include "svg/svg-color.h"
+#include "color.h"
+#include "svg/css-ostringstream.h"
+#include "document-undo.h"
+
+#ifdef HAVE_OPENMP
+#include <omp.h>
+#endif // HAVE_OPENMP
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+template<class T>
+T move(T &obj)
+{
+#ifdef LIBDEPIXELIZE_ENABLE_CPP11
+ return std::move(obj);
+#else
+ T ret;
+ std::swap(obj, ret);
+ return ret;
+#endif // LIBDEPIXELIZE_ENABLE_CPP11
+}
+
+/**
+ * A dialog for adjusting pixel art -> vector tracing parameters
+ */
+class PixelArtDialogImpl : public PixelArtDialog
+{
+public:
+ PixelArtDialogImpl();
+
+ ~PixelArtDialogImpl();
+
+private:
+ void setDesktop(SPDesktop *desktop);
+ void setTargetDesktop(SPDesktop *desktop);
+
+ //############ Events
+
+ void responseCallback(int response_id);
+
+ //############ UI Logic
+
+ Tracer::Kopf2011::Options options();
+
+ void vectorize();
+ void processLibdepixelize(SPImage *img);
+ void setDefaults();
+ void updatePreview();
+
+ bool ignorePreview;
+ bool pendingPreview;
+
+ //############ UI
+
+ Gtk::HBox buttonsHBox;
+
+ Gtk::Button *mainOkButton;
+ Gtk::Button *mainCancelButton;
+ Gtk::Button *mainResetButton;
+
+ Gtk::VBox heuristicsVBox;
+ UI::Widget::Frame heuristicsFrame;
+
+ Gtk::HBox curvesMultiplierHBox;
+ Gtk::Label curvesMultiplierLabel;
+ Inkscape::UI::Widget::SpinButton curvesMultiplierSpinner;
+
+ Gtk::HBox islandsWeightHBox;
+ Gtk::Label islandsWeightLabel;
+ Inkscape::UI::Widget::SpinButton islandsWeightSpinner;
+
+ Gtk::HBox sparsePixelsMultiplierHBox;
+ Gtk::Label sparsePixelsMultiplierLabel;
+ Inkscape::UI::Widget::SpinButton sparsePixelsMultiplierSpinner;
+
+ Gtk::HBox sparsePixelsRadiusHBox;
+ Gtk::Label sparsePixelsRadiusLabel;
+ Inkscape::UI::Widget::SpinButton sparsePixelsRadiusSpinner;
+
+ Gtk::VBox outputVBox;
+ UI::Widget::Frame outputFrame;
+ Gtk::RadioButtonGroup outputGroup;
+
+ Gtk::RadioButton voronoiRadioButton;
+ Gtk::RadioButton noOptimizeRadioButton;
+ Gtk::RadioButton optimizeRadioButton;
+
+ SPDesktop *desktop;
+ DesktopTracker deskTrack;
+ sigc::connection desktopChangeConn;
+};
+
+void PixelArtDialogImpl::setDesktop(SPDesktop *desktop)
+{
+ Panel::setDesktop(desktop);
+ deskTrack.setBase(desktop);
+}
+
+void PixelArtDialogImpl::setTargetDesktop(SPDesktop *desktop)
+{
+ this->desktop = desktop;
+}
+
+PixelArtDialogImpl::PixelArtDialogImpl() :
+ PixelArtDialog(),
+ ignorePreview(false),
+ pendingPreview(false)
+{
+
+ Gtk::Box *contents = _getContents();
+
+ // Heuristics
+ {
+ curvesMultiplierLabel.set_label(_("_Curves (multiplier)"));
+ curvesMultiplierLabel.set_use_underline(true);
+ curvesMultiplierLabel.set_mnemonic_widget(curvesMultiplierSpinner);
+ curvesMultiplierLabel.set_tooltip_text(_("Favors connections that are part of a long curve"));
+ curvesMultiplierSpinner.set_increments(0.125, 0);
+ curvesMultiplierSpinner.set_digits(3);
+ curvesMultiplierSpinner.set_range(-10, 10);
+ curvesMultiplierSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ curvesMultiplierHBox.pack_start(curvesMultiplierLabel, false, false);
+ curvesMultiplierHBox.pack_end(curvesMultiplierSpinner, false, false);
+ heuristicsVBox.pack_start(curvesMultiplierHBox, false, false);
+
+ islandsWeightLabel.set_label(_("_Islands (weight)"));
+ islandsWeightLabel.set_use_underline(true);
+ islandsWeightLabel.set_mnemonic_widget(islandsWeightSpinner);
+ islandsWeightLabel.set_tooltip_text(_("Avoid single disconnected pixels"));
+
+ islandsWeightSpinner.set_tooltip_text(_("A constant vote value"));
+ islandsWeightSpinner.set_increments(1, 0);
+ islandsWeightSpinner.set_range(-20, 20);
+ islandsWeightSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ islandsWeightHBox.pack_start(islandsWeightLabel, false, false);
+ islandsWeightHBox.pack_end(islandsWeightSpinner, false, false);
+ heuristicsVBox.pack_start(islandsWeightHBox, false, false);
+
+ sparsePixelsRadiusLabel.set_label(_("Sparse pixels (window _radius)"));
+ sparsePixelsRadiusLabel.set_use_underline(true);
+ sparsePixelsRadiusLabel.set_mnemonic_widget(sparsePixelsRadiusSpinner);
+
+ sparsePixelsRadiusSpinner.set_increments(1, 0);
+ sparsePixelsRadiusSpinner.set_range(2, 8);
+ sparsePixelsRadiusSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ sparsePixelsRadiusSpinner.set_tooltip_text(_("The radius of the window analyzed"));
+ sparsePixelsMultiplierLabel.set_label(_("Sparse pixels (_multiplier)"));
+ sparsePixelsMultiplierLabel.set_use_underline(true);
+ sparsePixelsMultiplierLabel.set_mnemonic_widget(sparsePixelsMultiplierSpinner);
+
+ sparsePixelsMultiplierSpinner.set_increments(0.125, 0);
+ sparsePixelsMultiplierSpinner.set_digits(3);
+ sparsePixelsMultiplierSpinner.set_range(-10, 10);
+ sparsePixelsMultiplierSpinner.get_adjustment()->signal_value_changed()
+ .connect(sigc::mem_fun(*this, &PixelArtDialogImpl::updatePreview));
+
+ {
+ char const *str = _("Favors connections that are part of foreground color");
+ sparsePixelsRadiusLabel.set_tooltip_text(str);
+ sparsePixelsMultiplierLabel.set_tooltip_text(str);
+ }
+
+ {
+ char const *str = _("The heuristic computed vote will be multiplied by this value");
+ curvesMultiplierSpinner.set_tooltip_text(str);
+ sparsePixelsMultiplierSpinner.set_tooltip_text(str);
+ }
+
+ sparsePixelsRadiusHBox.pack_start(sparsePixelsRadiusLabel, false, false);
+ sparsePixelsRadiusHBox.pack_end(sparsePixelsRadiusSpinner, false, false);
+ heuristicsVBox.pack_start(sparsePixelsRadiusHBox, false, false);
+
+ sparsePixelsMultiplierHBox.pack_start(sparsePixelsMultiplierLabel, false, false);
+ sparsePixelsMultiplierHBox.pack_end(sparsePixelsMultiplierSpinner, false, false);
+ heuristicsVBox.pack_start(sparsePixelsMultiplierHBox, false, false);
+
+ heuristicsFrame.set_label(_("Heuristics"));
+ heuristicsFrame.add(heuristicsVBox);
+ contents->pack_start(heuristicsFrame, false, false);
+ }
+
+ // Output
+ {
+ voronoiRadioButton.set_label(_("_Voronoi diagram"));
+ voronoiRadioButton.set_tooltip_text(_("Output composed of straight lines"));
+ voronoiRadioButton.set_use_underline(true);
+ outputGroup = voronoiRadioButton.get_group();
+
+ outputVBox.pack_start(voronoiRadioButton, false, false);
+
+ noOptimizeRadioButton.set_label(_("Convert to _B-spline curves"));
+ noOptimizeRadioButton.set_tooltip_text(_("Preserve staircasing artifacts"));
+ noOptimizeRadioButton.set_use_underline(true);
+ noOptimizeRadioButton.set_group(outputGroup);
+
+ outputVBox.pack_start(noOptimizeRadioButton, false, false);
+
+ optimizeRadioButton.set_label(_("_Smooth curves"));
+ optimizeRadioButton.set_tooltip_text(_("The Kopf-Lischinski algorithm"));
+ optimizeRadioButton.set_use_underline(true);
+ optimizeRadioButton.set_group(outputGroup);
+
+ outputVBox.pack_start(optimizeRadioButton, false, false);
+
+ outputFrame.set_label(_("Output"));
+ outputFrame.add(outputVBox);
+ contents->pack_start(outputFrame, true, false);
+ }
+
+ // Buttons
+ {
+ mainResetButton = addResponseButton(_("Reset"), GTK_RESPONSE_HELP, true);
+ mainResetButton ->set_tooltip_text(_("Reset all settings to defaults"));
+
+ //## The OK button
+ mainCancelButton = addResponseButton(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL);
+ if (mainCancelButton) {
+ mainCancelButton->set_tooltip_text(_("Abort a trace in progress"));
+ mainCancelButton->set_sensitive(false);
+ }
+ mainOkButton = addResponseButton(Gtk::Stock::OK, GTK_RESPONSE_OK);
+ mainOkButton->set_tooltip_text(_("Execute the trace"));
+
+ contents->pack_start(buttonsHBox);
+ }
+
+ setDefaults();
+
+ show_all_children();
+
+ desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &PixelArtDialogImpl::setTargetDesktop) );
+ deskTrack.connect(GTK_WIDGET(gobj()));
+
+ signalResponse().connect(sigc::mem_fun(*this, &PixelArtDialogImpl::responseCallback));
+}
+
+void PixelArtDialogImpl::responseCallback(int response_id)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ vectorize();
+ } else if (response_id == GTK_RESPONSE_CANCEL) {
+ // TODO
+ } else if (response_id == GTK_RESPONSE_HELP) {
+ setDefaults();
+ } else {
+ hide();
+ return;
+ }
+}
+
+Tracer::Kopf2011::Options PixelArtDialogImpl::options()
+{
+ Tracer::Kopf2011::Options options;
+
+ options.curvesMultiplier = curvesMultiplierSpinner.get_value();
+ options.islandsWeight = islandsWeightSpinner.get_value_as_int();
+ options.sparsePixelsMultiplier = sparsePixelsMultiplierSpinner.get_value();
+ options.sparsePixelsRadius = sparsePixelsRadiusSpinner.get_value_as_int();
+ options.optimize = optimizeRadioButton.get_active();
+
+ options.nthreads = Inkscape::Preferences::get()
+ ->getIntLimited("/options/threading/numthreads",
+#ifdef HAVE_OPENMP
+ omp_get_num_procs(),
+#else
+ 1,
+#endif // HAVE_OPENMP
+ 1, 256);
+
+ return options;
+}
+
+void PixelArtDialogImpl::vectorize()
+{
+ Inkscape::MessageStack *msgStack = desktop->messageStack();
+
+ if ( !desktop->selection ) {
+ char *msg = _("Select an <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return;
+ }
+
+ bool found = false;
+
+ for ( GSList const *list = desktop->selection->itemList() ; list
+ ; list = list->next ) {
+ if ( !SP_IS_IMAGE(list->data) )
+ continue;
+
+ found = true;
+
+ processLibdepixelize(SP_IMAGE(list->data));
+ }
+
+ if ( !found ) {
+ char *msg = _("Select an <b>image</b> to trace");
+ msgStack->flash(Inkscape::ERROR_MESSAGE, msg);
+ return;
+ }
+
+ DocumentUndo::done(desktop->doc(), SP_VERB_SELECTION_PIXEL_ART,
+ _("Trace pixel art"));
+
+ // Flush pending updates
+ desktop->doc()->ensureUpToDate();
+}
+
+void PixelArtDialogImpl::processLibdepixelize(SPImage *img)
+{
+ Tracer::Splines out;
+
+ if ( voronoiRadioButton.get_active() ) {
+ out = Tracer::Kopf2011::to_voronoi(Glib::wrap(img->pixbuf, true),
+ options());
+ } else {
+ out = Tracer::Kopf2011::to_splines(Glib::wrap(img->pixbuf, true),
+ options());
+ }
+
+ Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
+ Inkscape::XML::Node *group = xml_doc->createElement("svg:g");
+
+ for ( Tracer::Splines::iterator it = out.begin(), end = out.end()
+ ; it != end ; ++it ) {
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
+
+ {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+
+ {
+ gchar b[64];
+ sp_svg_write_color(b, sizeof(b),
+ SP_RGBA32_U_COMPOSE(unsigned(it->rgba[2]),
+ unsigned(it->rgba[1]),
+ unsigned(it->rgba[0]),
+ unsigned(it->rgba[3])));
+
+ sp_repr_css_set_property(css, "fill", b);
+ }
+
+ {
+ Inkscape::CSSOStringStream osalpha;
+ osalpha << float(it->rgba[3]) / 255.;
+ sp_repr_css_set_property(css, "fill-opacity",
+ osalpha.str().c_str());
+ }
+
+ sp_repr_css_set(repr, css, "style");
+ sp_repr_css_attr_unref(css);
+ }
+
+ gchar *str = sp_svg_write_path(move(it->pathVector));
+ repr->setAttribute("d", str);
+ g_free(str);
+
+ group->appendChild(repr);
+
+ Inkscape::GC::release(repr);
+ }
+
+ {
+ group->setAttribute("transform",
+ (std::string("translate(")
+ + sp_svg_length_write_with_units(img->x)
+ + ' ' + sp_svg_length_write_with_units(img->y)
+ + ')').c_str());
+ }
+
+ desktop->currentLayer()->appendChildRepr(group);
+
+ Inkscape::GC::release(group);
+}
+
+void PixelArtDialogImpl::setDefaults()
+{
+ ignorePreview = true;
+
+ curvesMultiplierSpinner.set_value(Tracer::Kopf2011::Options
+ ::CURVES_MULTIPLIER);
+
+ islandsWeightSpinner.set_value(Tracer::Kopf2011::Options::ISLANDS_WEIGHT);
+
+ sparsePixelsRadiusSpinner.set_value(Tracer::Kopf2011::Options
+ ::SPARSE_PIXELS_RADIUS);
+
+ sparsePixelsMultiplierSpinner.set_value(Tracer::Kopf2011::Options
+ ::SPARSE_PIXELS_MULTIPLIER);
+
+ optimizeRadioButton.set_active();
+
+ ignorePreview = false;
+
+ if ( pendingPreview )
+ updatePreview();
+}
+
+void PixelArtDialogImpl::updatePreview()
+{
+ if ( ignorePreview ) {
+ pendingPreview = true;
+ return;
+ }
+
+ // TODO: update preview
+ pendingPreview = false;
+}
+
+/**
+ * Factory method. Use this to create a new PixelArtDialog
+ */
+PixelArtDialog &PixelArtDialog::getInstance()
+{
+ PixelArtDialog *dialog = new PixelArtDialogImpl();
+ return *dialog;
+}
+
+PixelArtDialogImpl::~PixelArtDialogImpl()
+{
+ desktopChangeConn.disconnect();
+}
+
+
+} //namespace Dialog
+} //namespace UI
+} //namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/ui/dialog/pixelartdialog.h b/src/ui/dialog/pixelartdialog.h
new file mode 100644
index 000000000..165cb8699
--- /dev/null
+++ b/src/ui/dialog/pixelartdialog.h
@@ -0,0 +1,58 @@
+/** @file
+ * @brief Bitmap tracing settings dialog
+ */
+/* Authors:
+ * Bob Jamison
+ * Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
+ * Other dudes from The Inkscape Organization
+ *
+ * Copyright (C) 2004, 2005 Authors
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __PIXELARTDIALOG_H__
+#define __PIXELARTDIALOG_H__
+
+#include "ui/widget/panel.h"
+#include "verbs.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+
+/**
+ * A dialog that displays log messages
+ */
+class PixelArtDialog : public UI::Widget::Panel
+{
+
+public:
+
+ PixelArtDialog() :
+ UI::Widget::Panel("", "/dialogs/pixelart", SP_VERB_SELECTION_PIXEL_ART)
+ {}
+
+
+ static PixelArtDialog &getInstance();
+
+ virtual ~PixelArtDialog() {};
+};
+
+
+} //namespace Dialog
+} //namespace UI
+} //namespace Inkscape
+
+#endif /* __PIXELARTDIALOGDIALOG_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :