[calligra] krita/plugins/extensions/gmic: Implement basic preview support for G'MIC for Krita

Lukáš Tvrdý lukast.dev at gmail.com
Sat Nov 22 21:25:34 UTC 2014


Git commit 24eb6339c02e2cab495b183a652588845aa9a349 by Lukáš Tvrdý.
Committed on 22/11/2014 at 21:16.
Pushed by lukast into branch 'master'.

Implement basic preview support for G'MIC for Krita

- implement small preview window support
- implement extra feature: on-canvas preview mode (select Preview Size: On Canvas)
- a lot of small bug-fixes

Preview makes G'MIC for Krita much more usable

List of known issues is in source dir at
[calligra.git]/krita/plugins/extensions/gmic/TODO
Review it before reporting bugs, please

CCMAIL:kimageshop at kde.org

M  +3    -2    krita/plugins/extensions/gmic/CMakeLists.txt
M  +16   -1    krita/plugins/extensions/gmic/Command.cpp
M  +1    -0    krita/plugins/extensions/gmic/Command.h
M  +27   -8    krita/plugins/extensions/gmic/TODO
A  +49   -0    krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp     [License: UNKNOWN]  *
A  +33   -0    krita/plugins/extensions/gmic/kis_filter_preview_widget.h     [License: UNKNOWN]  *
M  +67   -11   krita/plugins/extensions/gmic/kis_gmic_applicator.cpp
M  +9    -3    krita/plugins/extensions/gmic/kis_gmic_applicator.h
M  +33   -3    krita/plugins/extensions/gmic/kis_gmic_filter_settings.cpp
M  +47   -2    krita/plugins/extensions/gmic/kis_gmic_filter_settings.h
M  +44   -19   krita/plugins/extensions/gmic/kis_gmic_input_output_widget.cpp
M  +13   -2    krita/plugins/extensions/gmic/kis_gmic_input_output_widget.h
M  +0    -1    krita/plugins/extensions/gmic/kis_gmic_parser.cpp
M  +210  -54   krita/plugins/extensions/gmic/kis_gmic_plugin.cpp
M  +22   -5    krita/plugins/extensions/gmic/kis_gmic_plugin.h
M  +3    -3    krita/plugins/extensions/gmic/kis_gmic_settings_widget.cpp
M  +23   -12   krita/plugins/extensions/gmic/kis_gmic_synchronize_layers_command.cpp
M  +0    -18   krita/plugins/extensions/gmic/kis_gmic_updater.cpp
M  +221  -91   krita/plugins/extensions/gmic/kis_gmic_widget.cpp
M  +35   -12   krita/plugins/extensions/gmic/kis_gmic_widget.h
M  +7    -1    krita/plugins/extensions/gmic/kis_import_gmic_processing_visitor.cpp
M  +2    -0    krita/plugins/extensions/gmic/kis_input_output_mapper.cpp
M  +2    -2    krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
M  +69   -26   krita/plugins/extensions/gmic/wdg_gmic.ui
A  +128  -0    krita/plugins/extensions/gmic/wdg_gmic_input_output.ui

The files marked with a * at the end have a non valid license. Please read: http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are listed at that page.


http://commits.kde.org/calligra/24eb6339c02e2cab495b183a652588845aa9a349

diff --git a/krita/plugins/extensions/gmic/CMakeLists.txt b/krita/plugins/extensions/gmic/CMakeLists.txt
index 736e103..3452d30 100644
--- a/krita/plugins/extensions/gmic/CMakeLists.txt
+++ b/krita/plugins/extensions/gmic/CMakeLists.txt
@@ -78,8 +78,11 @@ set(kritagmic_shared_PART_SRCS
     kis_gmic_filter_proxy_model.cpp
     kis_gmic_widget.cpp
     kis_gmic_updater.cpp
+    kis_filter_preview_widget.cpp
 )
 
+kde4_add_ui_files(kritagmic_shared_PART_SRCS wdg_gmic.ui wdg_gmic_input_output.ui)
+
 set(kritagmic_PART_SRCS
     kis_gmic_simple_convertor.cpp
     kis_export_gmic_processing_visitor.cpp
@@ -92,8 +95,6 @@ set(kritagmic_PART_SRCS
 
 set(gmicparser_PART_SRCS main.cpp ${kritagmic_shared_PART_SRCS})
 
-
-kde4_add_ui_files(kritagmic_PART_SRCS wdg_gmic.ui )
 kde4_add_plugin(kritagmic ${kritagmic_PART_SRCS})
 
 target_link_libraries(kritagmic kritaui gmic ${QT_QTXML_LIBRARY} ${ZLIB_LIBRARIES})
diff --git a/krita/plugins/extensions/gmic/Command.cpp b/krita/plugins/extensions/gmic/Command.cpp
index a35182f..b514627 100644
--- a/krita/plugins/extensions/gmic/Command.cpp
+++ b/krita/plugins/extensions/gmic/Command.cpp
@@ -59,6 +59,14 @@ void Command::processCommandName(const QString& line)
     m_command = commands.at(0).trimmed();
     m_commandPreview = commands.at(1).trimmed();
 
+    QStringList splitted = m_commandPreview.split("(");
+    if (splitted.size() == 2)
+    {
+        m_commandPreview = splitted.at(0);
+        m_commandPreviewZoom = splitted.at(1);
+        m_commandPreviewZoom.chop(1);
+    }
+
 }
 
 
@@ -369,11 +377,13 @@ void Command::writeConfiguration(KisGmicFilterSetting* setting)
 {
     // example: -gimp_poster_edges 20,60,5,0,10,0,0
     QString command = "-" + m_command + " ";
+    QString commandPreview = "-" + m_commandPreview + " ";
     foreach(Parameter * p, m_parameters)
     {
         if (!p->value().isNull())
         {
             command.append(p->value() +",");
+            commandPreview.append(p->value() +",");
         }
         else
         {
@@ -382,7 +392,6 @@ void Command::writeConfiguration(KisGmicFilterSetting* setting)
                 // implement for given parameter value()!
                 dbgPlugins << "UNHANDLED command parameter: " << p->m_name << p->toString();
             }
-
         }
     }
 
@@ -391,7 +400,13 @@ void Command::writeConfiguration(KisGmicFilterSetting* setting)
         command.chop(1);
     }
 
+    if (commandPreview.endsWith(","))
+    {
+        commandPreview.chop(1);
+    }
+
     setting->setGmicCommand(command);
+    setting->setPreviewGmicCommand(commandPreview);
 }
 
 QString Command::mergeBlockToLine(const QStringList& block)
diff --git a/krita/plugins/extensions/gmic/Command.h b/krita/plugins/extensions/gmic/Command.h
index ad4c10f..021da41 100644
--- a/krita/plugins/extensions/gmic/Command.h
+++ b/krita/plugins/extensions/gmic/Command.h
@@ -40,6 +40,7 @@ public:
 
     QString m_command;
     QString m_commandPreview;
+    QString m_commandPreviewZoom;
     QList<Parameter*> m_parameters;
 
     virtual void add(Component* c);
diff --git a/krita/plugins/extensions/gmic/TODO b/krita/plugins/extensions/gmic/TODO
index 7720fe8..9a01e45 100644
--- a/krita/plugins/extensions/gmic/TODO
+++ b/krita/plugins/extensions/gmic/TODO
@@ -1,10 +1,29 @@
-[DONE] * fix parser, there are still crazy cases where we do not parse notes and separators correctly (multi-line notes)
-[DONE] * add support for missing parameters (int, float, choice, note supported)
-* add support for layer modes: so far current layer is filtered
-    * modes like current layer+above, all visible, etc.
-* UNDO does not work correctly sometimes
-* implement preview and it's modes
-* implement progress
-* figure out on-line updates for filter defintions
+List of known bugs:
+[done] * add ui for various preview sizes
+[done] * thumbnail preview (100%zoom only in first step?)
+[done] * finish full screen preview (ala Krita filters)
+[done] * aspect ratio in preview viewport has to be set correctly
+[done] * preview area has to support opacity 
+[done] * selecting non-filter item in filter tree shows unchanged layer
+[done] * turning off preview: it shows non-filtered active layer
+[done] * preview is computed using gmic command, not using gmic preview command
+* add support for panning in preview viewport
+* implement progress bar for filters and preview
 * separate included gmic code to stand-alone library and link dynamically 
+* if the gmic filter outputs layer smaller than original, it supposed to be centered to canvas size, not bitblt(0,0,image->rect) 
+* what to do with selections for small preview?
+* sometimes preview blocks the UI because it takes too long  => progress bar
+* resizing the preview has to work (Small, Tiny, Large, ...)
+* resizing "input/output area/preview viewport" from filter settings tree
+* support all preview output modes
+* gmic debug output slows down filtering, make it optional
+* support for zooming/panning preview
+* preview area needs border and scrollbars
+* tiny, small, normal, large behaves exactly same for now
+* "Preview mode" only 1st ouput works 
+* missing: Favorite presets group
+* missing: button for expanding all folders
+* missing: Available filters information 
+* scrollbars for filter settings that are huge! currently text is horizontally downscaled and unreadable
+* gmic is located under Layer -> Apply Gmic action, it supposed to be in Filters 
 
diff --git a/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp b/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp
new file mode 100644
index 0000000..5521902
--- /dev/null
+++ b/krita/plugins/extensions/gmic/kis_filter_preview_widget.cpp
@@ -0,0 +1,49 @@
+#include "kis_filter_preview_widget.h"
+#include <kis_canvas_widget_base.h>
+#include <kis_config.h>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QWheelEvent>
+
+#include <QDebug>
+
+KisFilterPreviewWidget::KisFilterPreviewWidget(QWidget* parent): QWidget(parent)
+{
+    KisConfig cfg;
+    // for preview make it smaller than on canvas
+    qint32 checkSize = qMax(1,cfg.checkSize() / 2);
+    QImage checkImage = KisCanvasWidgetBase::createCheckersImage(checkSize);
+    m_checkBrush = QBrush(checkImage);
+    m_pixmap = QPixmap::fromImage(checkImage);
+}
+
+KisFilterPreviewWidget::~KisFilterPreviewWidget()
+{
+
+}
+
+void KisFilterPreviewWidget::paintEvent(QPaintEvent* event)
+{
+    Q_UNUSED(event);
+    QPainter gc(this);
+    gc.fillRect(m_pixmap.rect(), m_checkBrush);
+    gc.drawPixmap(0,0,m_pixmap);
+
+
+    QPen pen(Qt::black, 1, Qt::SolidLine);
+    gc.setPen(pen);
+    gc.drawRect(m_pixmap.rect().adjusted(0,0,-1,-1));
+
+}
+
+QSize KisFilterPreviewWidget::sizeHint() const
+{
+    return QSize(1, 1);
+}
+
+void KisFilterPreviewWidget::setImage(const QImage& img)
+{
+    m_pixmap = QPixmap::fromImage(img);
+    update();
+}
+
diff --git a/krita/plugins/extensions/gmic/kis_filter_preview_widget.h b/krita/plugins/extensions/gmic/kis_filter_preview_widget.h
new file mode 100644
index 0000000..0235036
--- /dev/null
+++ b/krita/plugins/extensions/gmic/kis_filter_preview_widget.h
@@ -0,0 +1,33 @@
+#ifndef KIS_FILTER_PREVIEW_WIDGET
+#define KIS_FILTER_PREVIEW_WIDGET
+
+#include <QWidget>
+
+class QWheelEvent;
+class QPaintEvent;
+
+class KisFilterPreviewWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    KisFilterPreviewWidget(QWidget * parent);
+    ~KisFilterPreviewWidget();
+
+    virtual QSize sizeHint() const;
+    virtual void paintEvent(QPaintEvent* event);
+
+    void setImage(const QImage &img);
+
+protected:
+
+
+
+
+
+private:
+    QPixmap m_pixmap;
+    QBrush m_checkBrush;
+
+};
+
+#endif
diff --git a/krita/plugins/extensions/gmic/kis_gmic_applicator.cpp b/krita/plugins/extensions/gmic/kis_gmic_applicator.cpp
index b5bfc49..6ca0983 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_applicator.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_applicator.cpp
@@ -28,13 +28,15 @@
 #include "kis_image.h"
 #include <kis_selection.h>
 
-KisGmicApplicator::KisGmicApplicator()
+KisGmicApplicator::KisGmicApplicator():m_applicator(0),m_applicatorFinished(false)
 {
 }
 
 
 KisGmicApplicator::~KisGmicApplicator()
 {
+    dbgPlugins << "Destructor: " << m_applicator;
+    delete m_applicator;
 }
 
 void KisGmicApplicator::setProperties(KisImageWSP image, KisNodeSP node, const KUndo2MagicString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand, const QByteArray customCommands)
@@ -47,15 +49,21 @@ void KisGmicApplicator::setProperties(KisImageWSP image, KisNodeSP node, const K
     m_customCommands = customCommands;
 }
 
-void KisGmicApplicator::run()
+
+void KisGmicApplicator::preview()
 {
+    // cancel previous preview if there is one
+    dbgPlugins << "Request for preview, cancelling any previous possible on-canvas preview";
+    cancel();
+
     KisImageSignalVector emitSignals;
     emitSignals << ModifiedSignal;
 
-    KisProcessingApplicator applicator(m_image, m_node,
+    m_applicator = new KisProcessingApplicator(m_image, m_node,
                                        KisProcessingApplicator::RECURSIVE,
                                        emitSignals, m_actionName);
 
+    dbgPlugins << "Creating applicator " << m_applicator;
 
     QSharedPointer< gmic_list<float> > gmicLayers(new gmic_list<float>);
     gmicLayers->assign(m_kritaNodes->size());
@@ -71,21 +79,69 @@ void KisGmicApplicator::run()
     {
         layerSize = QRect(0,0,m_image->width(), m_image->height());
     }
-    KisProcessingVisitorSP visitor;
 
     // convert krita layers to gmic layers
-    visitor = new KisExportGmicProcessingVisitor(m_kritaNodes, gmicLayers, layerSize);
-    applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
+    KisProcessingVisitorSP exportVisitor = new KisExportGmicProcessingVisitor(m_kritaNodes, gmicLayers, layerSize);
+    m_applicator->applyVisitor(exportVisitor, KisStrokeJobData::CONCURRENT);
 
     // apply gmic filters to provided layers
     const char * customCommands = m_customCommands.isNull() ? 0 : m_customCommands.constData();
-    applicator.applyCommand(new KisGmicCommand(m_gmicCommand, gmicLayers, customCommands));
+    m_applicator->applyCommand(new KisGmicCommand(m_gmicCommand, gmicLayers, customCommands));
 
     // synchronize layer count
-    applicator.applyCommand(new KisGmicSynchronizeLayersCommand(m_kritaNodes, gmicLayers, m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
+    m_applicator->applyCommand(new KisGmicSynchronizeLayersCommand(m_kritaNodes, gmicLayers, m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
 
     // would sleep(3) help here?
-    visitor = new KisImportGmicProcessingVisitor(m_kritaNodes, gmicLayers, layerSize, selection);
-    applicator.applyVisitor(visitor, KisStrokeJobData::SEQUENTIAL); // undo information is stored in this visitor
-    applicator.end();
+    KisProcessingVisitorSP  importVisitor = new KisImportGmicProcessingVisitor(m_kritaNodes, gmicLayers, layerSize, selection);
+    m_applicator->applyVisitor(importVisitor, KisStrokeJobData::SEQUENTIAL); // undo information is stored in this visitor
+    m_applicator->explicitlyEmitFinalSignals();
+}
+
+void KisGmicApplicator::cancel()
+{
+
+    if (m_applicator)
+    {
+
+        if (!m_applicatorFinished)
+        {
+            dbgPlugins << "Cancelling applicator: Yes!";
+            m_applicator->cancel();
+        }
+        else
+        {
+            dbgPlugins << "Cancelling applicator: No! Reason: Already finished!";
+        }
+
+
+        dbgPlugins << "deleting applicator: " << m_applicator;
+        delete m_applicator;
+        m_applicator = 0;
+
+
+        m_applicatorFinished = false;
+        dbgPlugins << ppVar(m_applicatorFinished);
+
+    }
+    else
+    {
+        dbgPlugins << "Cancelling applicator: No! Reason: Null applicator!";
+    }
+
+
+}
+
+void KisGmicApplicator::finish()
+{
+    dbgPlugins << "aplicator " << m_applicator << " finished";
+    if (m_applicator)
+    {
+        m_applicator->end();
+        m_applicatorFinished = true;
+        dbgPlugins << ppVar(m_applicatorFinished);
+    }
+    else
+    {
+        dbgPlugins << ppVar(m_applicatorFinished);
+    }
 }
diff --git a/krita/plugins/extensions/gmic/kis_gmic_applicator.h b/krita/plugins/extensions/gmic/kis_gmic_applicator.h
index 389bf6b..bca1637 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_applicator.h
+++ b/krita/plugins/extensions/gmic/kis_gmic_applicator.h
@@ -24,22 +24,28 @@
 #include <kis_types.h>
 #include <QThread>
 
+class KisProcessingApplicator;
 
-class KisGmicApplicator : public QThread
+class KisGmicApplicator
 {
 public:
     KisGmicApplicator();
     ~KisGmicApplicator();
     void setProperties(KisImageWSP image, KisNodeSP node, const KUndo2MagicString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand, const QByteArray customCommands = QByteArray());
-protected:
-    virtual void run();
+
+    void preview();
+    void cancel();
+    void finish();
+
 private:
+    KisProcessingApplicator * m_applicator;
     KisImageWSP m_image;
     KisNodeSP m_node;
     KUndo2MagicString m_actionName;
     KisNodeListSP m_kritaNodes;
     QString m_gmicCommand;
     QByteArray m_customCommands;
+    bool m_applicatorFinished;
 };
 
 #endif
diff --git a/krita/plugins/extensions/gmic/kis_gmic_filter_settings.cpp b/krita/plugins/extensions/gmic/kis_gmic_filter_settings.cpp
index c122e21..d0f5666 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_filter_settings.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_filter_settings.cpp
@@ -20,7 +20,7 @@
 #include <kis_gmic_filter_settings.h>
 
 
-KisGmicFilterSetting::KisGmicFilterSetting():m_gmicCommand(),m_inputLayerMode(ACTIVE_LAYER),m_outputMode(IN_PLACE),m_isBlacklisted(false)
+KisGmicFilterSetting::KisGmicFilterSetting():m_gmicCommand(),m_previewGmicCommand(),m_inputLayerMode(ACTIVE_LAYER),m_outputMode(IN_PLACE),m_isBlacklisted(false)
 {
 
 }
@@ -40,7 +40,17 @@ void KisGmicFilterSetting::setGmicCommand(QString cmd)
     m_gmicCommand = cmd;
 }
 
-InputLayerMode KisGmicFilterSetting::inputLayerMode()
+const QString& KisGmicFilterSetting::previewGmicCommand() const
+{
+    return m_previewGmicCommand;
+}
+
+void KisGmicFilterSetting::setPreviewGmicCommand(QString cmd)
+{
+    m_previewGmicCommand = cmd;
+}
+
+InputLayerMode KisGmicFilterSetting::inputLayerMode() const
 {
      return m_inputLayerMode;
 }
@@ -50,7 +60,7 @@ void KisGmicFilterSetting::setInputLayerMode(InputLayerMode mode)
     m_inputLayerMode = mode;
 }
 
-OutputMode KisGmicFilterSetting::outputMode()
+OutputMode KisGmicFilterSetting::outputMode() const
 {
      return m_outputMode;
 }
@@ -59,3 +69,23 @@ void KisGmicFilterSetting::setOutputMode(OutputMode mode)
 {
     m_outputMode = mode;
 }
+
+OutputPreviewMode KisGmicFilterSetting::previewMode() const
+{
+    return m_previewMode;
+}
+
+void KisGmicFilterSetting::setPreviewMode(OutputPreviewMode mode)
+{
+    m_previewMode = mode;
+}
+
+PreviewSize KisGmicFilterSetting::previewSize() const
+{
+    return m_previewSize;
+}
+
+void KisGmicFilterSetting::setPreviewSize(PreviewSize size)
+{
+    m_previewSize = size;
+}
diff --git a/krita/plugins/extensions/gmic/kis_gmic_filter_settings.h b/krita/plugins/extensions/gmic/kis_gmic_filter_settings.h
index 14ad26a..bdddb32 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_filter_settings.h
+++ b/krita/plugins/extensions/gmic/kis_gmic_filter_settings.h
@@ -33,6 +33,7 @@ static QStringList OUTPUT_MODE_STRINGS = QStringList() << "In place (default)"
     << "New active layer(s)"
     << "New image";
 
+
 // this enum is also index in LAYER_MODE_STRINGS list
 enum InputLayerMode {   NONE = 0,
                         ACTIVE_LAYER,
@@ -57,6 +58,38 @@ static QStringList INPUT_MODE_STRINGS = QStringList() << "None"
     << "All invisibles (decr.)"
     << "All (decr.)";
 
+enum PreviewSize {    TINY = 0,
+                            SMALL,
+                            NORMAL,
+                            LARGE,
+                            ON_CANVAS
+
+
+};
+
+static QStringList PREVIEW_SIZE = QStringList() << "Tiny" << "Small" << "Normal" << "Large" << "On Canvas";
+
+enum OutputPreviewMode {    FIRST = 0,
+                            SECOND,
+                            THIRD,
+                            FOURTH,
+                            FIRST_TO_SECOND,
+                            FIRST_TO_THIRD,
+                            FIRST_TO_FOURTH,
+                            ALL
+
+};
+
+static QStringList PREVIEW_MODE = QStringList() << "1st output"
+    << "2nd output"
+    << "3rd output"
+    << "4th output"
+    << "1st -> 2nd"
+    << "1st -> 3rd"
+    << "1st -> 4th"
+    << "All outputs";
+
+
 class KisGmicFilterSetting
 {
 public:
@@ -66,18 +99,30 @@ public:
     void setGmicCommand(QString cmd);
     const QString& gmicCommand() const;
 
-    InputLayerMode inputLayerMode();
+    void setPreviewGmicCommand(QString cmd);
+    const QString& previewGmicCommand() const;
+
+    InputLayerMode inputLayerMode() const;
     void setInputLayerMode(InputLayerMode mode);
 
-    OutputMode outputMode();
+    OutputMode outputMode() const;
     void setOutputMode(OutputMode mode);
 
+    PreviewSize previewSize() const;
+    void setPreviewSize(PreviewSize size);
+
+    OutputPreviewMode previewMode() const;
+    void setPreviewMode(OutputPreviewMode mode);
+
     void setBlacklisted(bool blacklist){ m_isBlacklisted = blacklist; }
     bool isBlacklisted() const { return m_isBlacklisted; };
 private:
     QString m_gmicCommand;
+    QString m_previewGmicCommand;
     InputLayerMode m_inputLayerMode;
     OutputMode m_outputMode;
+    PreviewSize m_previewSize;
+    OutputPreviewMode m_previewMode;
     bool m_isBlacklisted;
 };
 
diff --git a/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.cpp b/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.cpp
index 6b6644e..b1e7d27 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Lukáš Tvrdý <lukast.dev at gmail.com>
+ * Copyright (c) 2013-2014 Lukáš Tvrdý <lukast.dev at gmail.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -15,6 +15,8 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
 
+#include <KoIcon.h>
+
 #include <kis_gmic_input_output_widget.h>
 
 #include <kis_debug.h>
@@ -25,8 +27,14 @@
 #include <QStringListModel>
 #include <QLabel>
 
-KisGmicInputOutputWidget::KisGmicInputOutputWidget(): QWidget(), m_inputMode(ACTIVE_LAYER), m_outputMode(IN_PLACE)
+KisGmicInputOutputWidget::KisGmicInputOutputWidget(QWidget * parent):
+    QWidget(parent),
+    m_inputMode(ACTIVE_LAYER),
+    m_outputMode(IN_PLACE),
+    m_previewMode(FIRST),
+    m_previewSize(TINY)
 {
+    setupUi(this);
     createMainLayout();
 }
 
@@ -35,44 +43,61 @@ KisGmicInputOutputWidget::~KisGmicInputOutputWidget()
 
 }
 
+KisFilterPreviewWidget* KisGmicInputOutputWidget::previewWidget()
+{
+    return previewViewport;
+}
+
+
+
 void KisGmicInputOutputWidget::createMainLayout()
 {
-    QComboBox * inputCombo = new QComboBox;
+    zoomInButton->setIcon(koIcon("zoom-in"));
+    zoomOutButton->setIcon(koIcon("zoom-out"));
+
     QStringListModel * inputModel = new QStringListModel(INPUT_MODE_STRINGS);
     inputCombo->setModel(inputModel);
     QObject::connect(inputCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setIntputMode(int)));
     inputCombo->setCurrentIndex(static_cast<int>(m_inputMode));
 
-    QComboBox * outputCombo = new QComboBox;
     QStringListModel * outputModel = new QStringListModel(OUTPUT_MODE_STRINGS);
     outputCombo->setModel(outputModel);
     QObject::connect(outputCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setOutputMode(int)));
     outputCombo->setCurrentIndex(static_cast<int>(m_outputMode));
 
-    QGridLayout * gridLayout = new QGridLayout;
-    int row = 0;
-    gridLayout->addWidget(new QLabel("Input"), row, 0);
-    gridLayout->addWidget(inputCombo, row, 1, 1, 2);
-    row++;
-    gridLayout->addWidget(new QLabel("Output"), row, 0);
-    gridLayout->addWidget(outputCombo, row, 1, 1, 2);
+    QStringListModel * previewModeModel = new QStringListModel(PREVIEW_MODE);
+    outputPreviewCombo->setModel(previewModeModel);
+    QObject::connect(outputPreviewCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setPreviewMode(int)));
+    outputPreviewCombo->setCurrentIndex(static_cast<int>(m_previewMode));
 
-    setLayout(gridLayout);
+    QStringListModel * previewSizeModel = new QStringListModel(PREVIEW_SIZE);
+    previewSizeCombo->setModel(previewSizeModel);
+    QObject::connect(previewSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setPreviewSize(int)));
+    previewSizeCombo->setCurrentIndex(static_cast<int>(m_previewSize));
 }
 
 
 void KisGmicInputOutputWidget::setIntputMode(int index)
 {
-        m_inputMode = static_cast<InputLayerMode>(index);
-        dbgPlugins << "Selecting " << INPUT_MODE_STRINGS.at(index);
-        emit sigConfigurationChanged();
-
+    m_inputMode = static_cast<InputLayerMode>(index);
+    emit sigConfigurationChanged();
 }
 
 
 void KisGmicInputOutputWidget::setOutputMode(int index)
 {
-        m_outputMode = static_cast<OutputMode>(index);
-        dbgPlugins << "Selecting " << OUTPUT_MODE_STRINGS.at(index);
-        emit sigConfigurationChanged();
+    m_outputMode = static_cast<OutputMode>(index);
+    emit sigConfigurationChanged();
+}
+
+void KisGmicInputOutputWidget::setPreviewMode(int index)
+{
+    m_previewMode = static_cast<OutputPreviewMode>(index);
+    emit sigConfigurationChanged();
+}
+
+void KisGmicInputOutputWidget::setPreviewSize(int index)
+{
+    m_previewSize = static_cast<PreviewSize>(index);
+    emit sigConfigurationChanged();
 }
diff --git a/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.h b/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.h
index 9fb5964..f9feb6f 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.h
+++ b/krita/plugins/extensions/gmic/kis_gmic_input_output_widget.h
@@ -22,20 +22,27 @@
 #include <QHash>
 #include "kis_gmic_filter_settings.h"
 
+#include "ui_wdg_gmic_input_output.h"
+
 /**
  * creates GUI for Input/Output configuration
  */
-class KisGmicInputOutputWidget : public QWidget
+class KisGmicInputOutputWidget : public QWidget, public Ui::WdgGmicInputOutput
 {
     Q_OBJECT
 
 public:
-    KisGmicInputOutputWidget();
+    KisGmicInputOutputWidget(QWidget * parent);
     ~KisGmicInputOutputWidget();
 
     InputLayerMode inputMode() const { return m_inputMode; };
     OutputMode outputMode() const { return m_outputMode; };
 
+    OutputPreviewMode previewMode() const { return m_previewMode; };
+    PreviewSize previewSize() const { return m_previewSize; };
+
+    KisFilterPreviewWidget * previewWidget();
+
 signals:
     void sigConfigurationChanged();
 
@@ -45,10 +52,14 @@ private:
 private slots:
     void setIntputMode(int index);
     void setOutputMode(int index);
+    void setPreviewMode(int index);
+    void setPreviewSize(int index);
 
 private:
     InputLayerMode m_inputMode;
     OutputMode m_outputMode;
+    OutputPreviewMode m_previewMode;
+    PreviewSize m_previewSize;
 
 };
 
diff --git a/krita/plugins/extensions/gmic/kis_gmic_parser.cpp b/krita/plugins/extensions/gmic/kis_gmic_parser.cpp
index 150673d..7c29ee5 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_parser.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_parser.cpp
@@ -27,7 +27,6 @@
 #include <Parameter.h>
 #include <Command.h>
 #include <Category.h>
-#include "kis_gmic_widget.h"
 
 
 KisGmicParser::KisGmicParser(const QStringList& filePaths):m_filePaths(filePaths)
diff --git a/krita/plugins/extensions/gmic/kis_gmic_plugin.cpp b/krita/plugins/extensions/gmic/kis_gmic_plugin.cpp
index d5c732e..bf9f127 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_plugin.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_plugin.cpp
@@ -1,7 +1,7 @@
 /*
  * This file is part of the KDE project
  *
- * Copyright (c) 2013 Lukáš Tvrdý <lukast.dev at gmail.com>
+ * Copyright (c) 2013-2014 Lukáš Tvrdý <lukast.dev at gmail.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -37,6 +37,7 @@
 #include <kis_image.h>
 #include <kis_paint_device.h>
 #include <kis_layer.h>
+#include <kis_selection.h>
 #include <kis_paint_layer.h>
 #include "kis_statusbar.h"
 #include "widgets/kis_progress_widget.h"
@@ -51,24 +52,53 @@
 #include "kis_input_output_mapper.h"
 #include "kis_gmic_simple_convertor.h"
 #include "kis_gmic_applicator.h"
+#include "kis_export_gmic_processing_visitor.h"
+#include "kis_gmic_command.h"
+#include "kis_import_gmic_processing_visitor.h"
+#include "kis_gmic_synchronize_layers_command.h"
+
+#include "KoColorSpaceConstants.h"
 
 K_PLUGIN_FACTORY(KisGmicPluginFactory, registerPlugin<KisGmicPlugin>();)
 K_EXPORT_PLUGIN(KisGmicPluginFactory("krita"))
 
 const QString STANDARD_GMIC_DEFINITION = "gmic_def.gmic";
 
+class TemporaryFeedback
+{
+public:
+    TemporaryFeedback(QWidget * widget):m_widget(widget)
+    {
+        myTimer.start();
+        QApplication::setOverrideCursor(Qt::WaitCursor);
+    }
+
+    ~TemporaryFeedback()
+    {
+        QApplication::restoreOverrideCursor();
+        double seconds = myTimer.elapsed() * 0.001;
+        m_widget->setWindowTitle(QString("Filtering took ") + QString::number(seconds) + QString(" seconds"));
+    }
+
+private:
+    QWidget * m_widget;
+    QTime myTimer;
+};
+
+
 KisGmicPlugin::KisGmicPlugin(QObject *parent, const QVariantList &)
-        : KisViewPlugin(parent, "kritaplugins/gmic.rc"),m_gmicWidget(0)
+        :   KisViewPlugin(parent, "kritaplugins/gmic.rc"),
+            m_gmicWidget(0),
+            m_previewFilter(true)
 {
     KisAction *action  = new KisAction(i18n("Apply G'Mic Action..."), this);
     action->setActivationFlags(KisAction::ACTIVE_LAYER);
     action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
+    connect(action, SIGNAL(triggered()), this, SLOT(slotShowGmicDialog()));
     addAction("gmic", action);
 
     KGlobal::dirs()->addResourceType("gmic_definitions", "data", "krita/gmic/");
     m_blacklistPath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", STANDARD_GMIC_DEFINITION + ".blacklist");
-
-    connect(action, SIGNAL(triggered()), this, SLOT(slotGmic()));
 }
 
 KisGmicPlugin::~KisGmicPlugin()
@@ -89,7 +119,7 @@ void KisGmicPlugin::setupDefinitionPaths()
         }
     }
 
-    // if we don't have update, prepend gmic_def.gmic
+    // if we don't have updateXXXX.gmic, prepend standard gmic_def.gmic
     int gmicVersion = gmic_version;
     QString updateFileName = "update" + QString::number(gmicVersion) + ".gmic";
     QString updatedGmicDefinitionFilePath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", updateFileName);
@@ -100,9 +130,9 @@ void KisGmicPlugin::setupDefinitionPaths()
     }
 }
 
-
-void KisGmicPlugin::slotGmic()
+void KisGmicPlugin::slotShowGmicDialog()
 {
+    // init everything here
     KisImageWSP image = m_view->image();
     if (!image) return;
 
@@ -112,9 +142,11 @@ void KisGmicPlugin::slotGmic()
     if (m_gmicWidget)
     {
         // restart here?
-        slotClose();
+        slotCloseGmicDialog();
     }
 
+    m_gmicApplicator = new KisGmicApplicator();
+
     setupDefinitionPaths();
     parseGmicCommandDefinitions(m_definitionFilePaths);
 
@@ -128,80 +160,111 @@ void KisGmicPlugin::slotGmic()
     QString updateUrl = "http://gmic.sourceforge.net/" + QString("update") + QString::number(gmic_version) + ".gmic";
     m_gmicWidget = new KisGmicWidget(model, updateUrl);
 
-    m_gmicApplicator = new KisGmicApplicator();
-#ifdef Q_WS_WIN
-    // On windows, gmic needs a humongous stack for its parser
-    m_gmicApplicator->setStackSize(20 * 1024 * 1024);
-#endif
-
-    // apply
-    connect(m_gmicWidget, SIGNAL(sigApplyCommand(KisGmicFilterSetting*)),this, SLOT(slotApplyGmicCommand(KisGmicFilterSetting*)));
+    // preview filter
+    connect(m_gmicWidget, SIGNAL(sigPreviewFilterCommand(KisGmicFilterSetting*)),this, SLOT(slotPreviewGmicCommand(KisGmicFilterSetting*)));
+    // finish filter
+    connect(m_gmicWidget, SIGNAL(sigAcceptOnCanvasPreview()), this, SLOT(slotAcceptOnCanvasPreview()));
+    // cancel filter
+    connect(m_gmicWidget, SIGNAL(sigCancelOnCanvasPreview()), this, SLOT(slotCancelOnCanvasPreview()));
     // cancel
-    connect(m_gmicWidget, SIGNAL(sigClose()),this, SLOT(slotClose()));
-
+    connect(m_gmicWidget, SIGNAL(sigClose()),this, SLOT(slotCloseGmicDialog()));
+    // filter current image with current settings
+    connect(m_gmicWidget, SIGNAL(sigFilterCurrentImage(KisGmicFilterSetting*)), this, SLOT(slotFilterCurrentImage(KisGmicFilterSetting*)));
+    // show active layer in preview viewport
+    connect(m_gmicWidget, SIGNAL(sigPreviewActiveLayer()), this, SLOT(slotPreviewActiveLayer()));
+
+    QString version = QString("%0.%1.%2.%3").arg(gmic_version/1000).arg((gmic_version/100)%10).arg((gmic_version/10)%10).arg(gmic_version%10);
+    QString pluginName = i18n("G'MIC for Krita");
+    m_gmicWidget->setWindowTitle(QString("%0 %1").arg(pluginName).arg(version));
     m_gmicWidget->show();
+    slotPreviewActiveLayer();
 }
 
+void KisGmicPlugin::slotCloseGmicDialog()
+{
+    m_gmicWidget = 0;
+    if (m_gmicApplicator)
+    {
+        m_gmicApplicator->cancel();
+    }
+
+    delete m_gmicApplicator;
+    m_gmicApplicator = 0;
+}
 
-void KisGmicPlugin::slotApplyGmicCommand(KisGmicFilterSetting* setting)
+void KisGmicPlugin::slotPreviewGmicCommand(KisGmicFilterSetting* setting)
 {
-    KUndo2MagicString actionName;
-    KisNodeSP node;
+    dbgPlugins << "Preview Request";
 
-    if (setting->isBlacklisted())
+    KisInputOutputMapper mapper(m_view->image(), m_view->activeNode());
+    KisNodeListSP layers = mapper.inputNodes(setting->inputLayerMode());
+    if (!checkSettingsValidity(layers, setting))
     {
-        KMessageBox::sorry(m_gmicWidget, i18n("Sorry, this filter is crashing Krita and is turned off."), i18n("Krita"));
+        dbgPlugins << "Failed, some feature not implemented";
         return;
     }
 
-    KisInputOutputMapper mapper(m_view->image(), m_view->activeNode());
-    KisNodeListSP kritaNodes = mapper.inputNodes(setting->inputLayerMode());
 
-    if (kritaNodes->isEmpty()) {
-        KMessageBox::sorry(m_gmicWidget, i18n("Sorry, this input mode is not implemented"), i18n("Krita"));
-        return;
+    if (setting->previewSize() !=  ON_CANVAS)
+    {
+        createViewportPreview(layers, setting);
+    }
+    else
+    {
+        startOnCanvasPreview(layers, setting);
     }
+}
 
-    actionName = kundo2_i18n("Gmic filter");
-    node = m_view->image()->root();
 
-    if (setting->outputMode() != IN_PLACE) {
-        KMessageBox::sorry(m_gmicWidget,QString("Sorry, this output mode is not implemented"),"Krita");
-        return;
-    }
+void KisGmicPlugin::slotPreviewActiveLayer()
+{
+    showInPreviewViewport(m_view->activeNode()->paintDevice());
+}
+
+
+void KisGmicPlugin::showInPreviewViewport(KisPaintDeviceSP device)
+{
+        QRect deviceRect = device->exactBounds();
+        qreal aspectRatio = (qreal)deviceRect.width()/deviceRect.height();
+
+        int dstWidth = m_gmicWidget->previewWidget()->size().width();
+        int dstHeight = dstWidth / aspectRatio;
+
+        QImage previewImage = device->createThumbnail(dstWidth, dstHeight, deviceRect);
+        m_gmicWidget->previewWidget()->setImage(previewImage);
+}
 
-    QTime myTimer;
-    myTimer.start();
-    QApplication::setOverrideCursor(Qt::WaitCursor);
 
-    m_gmicApplicator->setProperties(m_view->image(), node, actionName, kritaNodes, setting->gmicCommand(), m_gmicCustomCommands);
-    m_gmicApplicator->start();
-    m_gmicApplicator->wait();
-    m_view->image()->waitForDone();
-    QApplication::restoreOverrideCursor();
+void KisGmicPlugin::slotAcceptOnCanvasPreview()
+{
+    m_gmicApplicator->finish();
+}
 
-    double seconds = myTimer.elapsed() * 0.001;
-    // temporary feedback
-    m_gmicWidget->setWindowTitle(QString("Filtering took ") + QString::number(seconds) + QString(" seconds"));
+void KisGmicPlugin::slotCancelOnCanvasPreview()
+{
+    m_gmicApplicator->cancel();
 }
 
-void KisGmicPlugin::slotClose()
+void KisGmicPlugin::slotFilterCurrentImage(KisGmicFilterSetting* setting)
 {
-    bool result = m_gmicWidget->close();
-    if (!result)
+    dbgPlugins << "Filtering image on canvas!";
+
+    KisInputOutputMapper mapper(m_view->image(), m_view->activeNode());
+    KisNodeListSP layers = mapper.inputNodes(setting->inputLayerMode());
+    if (checkSettingsValidity(layers, setting))
     {
-        dbgPlugins << "Windows was not closed?";
+        TemporaryFeedback feedback(m_gmicWidget);
+        startOnCanvasPreview(layers, setting);
+        slotAcceptOnCanvasPreview();
+        // wait, so that next request to strokes for on-canvas preview is possible
+        m_view->image()->waitForDone();
     }
     else
     {
-        // close event deletes widget
-        m_gmicWidget = 0;
-        delete m_gmicApplicator;
-        m_gmicApplicator = 0;
+        dbgPlugins << "Failed, some feature not implemented";
     }
 }
 
-
 void KisGmicPlugin::parseGmicCommandDefinitions(const QStringList& gmicDefinitionFilePaths)
 {
     foreach (const QString filePath, gmicDefinitionFilePaths)
@@ -211,6 +274,99 @@ void KisGmicPlugin::parseGmicCommandDefinitions(const QStringList& gmicDefinitio
     }
 }
 
+KisNodeListSP KisGmicPlugin::createPreviewThumbnails(KisNodeListSP layers,const QSize &dstSize,const QRect &srcRect)
+{
+        KisNodeListSP previewKritaNodes(new QList< KisNodeSP >());
+        for (int i = 0; i < layers->size(); i++)
+        {
+            KisPaintDeviceSP thumbnail = layers->at(i)->paintDevice()->createThumbnailDevice(dstSize.width(), dstSize.height(), srcRect);
+            KisNodeSP node(new KisPaintLayer(0, "", OPACITY_OPAQUE_U8, thumbnail));
+            previewKritaNodes->append(node);
+        }
+        return previewKritaNodes;
+}
+
+
+void KisGmicPlugin::createViewportPreview(KisNodeListSP layers, KisGmicFilterSetting* setting)
+{
+        QRect canvasRect = m_view->image()->bounds();
+        qreal aspectRatio = (qreal)canvasRect.width() / canvasRect.height();
+
+        int previewWidth = m_gmicWidget->previewWidget()->size().width();
+        int previewHeight = qRound(previewWidth / aspectRatio);
+        QRect previewRect = QRect(QPoint(0,0), QSize(previewWidth, previewHeight));
+
+        KisNodeListSP previewKritaNodes = KisGmicPlugin::createPreviewThumbnails(layers, previewRect.size(), canvasRect);
+
+        QSharedPointer< gmic_list<float> > gmicLayers(new gmic_list<float>);
+        gmicLayers->assign(previewKritaNodes->size());
+
+        KisExportGmicProcessingVisitor exportVisitor(previewKritaNodes, gmicLayers, previewRect);
+        for (int i = 0; i < previewKritaNodes->size(); i++)
+        {
+            exportVisitor.visit( (KisPaintLayer *)(*previewKritaNodes)[i].data(), 0);
+        }
+
+        QString gmicCommand = setting->previewGmicCommand();
+        if (gmicCommand.isEmpty())
+        {
+            gmicCommand = setting->gmicCommand();
+        }
+
+        KisGmicCommand gmicCmd(gmicCommand, gmicLayers, m_gmicCustomCommands);
+        gmicCmd.redo();
+
+        KisGmicSynchronizeLayersCommand syncCmd(previewKritaNodes, gmicLayers, 0);
+        syncCmd.redo();
+
+        KisImportGmicProcessingVisitor importVisitor(previewKritaNodes, gmicLayers, previewRect, 0);
+        for (int i = 0; i < previewKritaNodes->size(); i++)
+        {
+            importVisitor.visit( (KisPaintLayer *)(*previewKritaNodes)[i].data(), 0 );
+        }
+
+        if (previewKritaNodes->size() > 0)
+        {
+            showInPreviewViewport(previewKritaNodes->at(0)->paintDevice());
+        }
+        else
+        {
+            // TODO: show error preview
+        }
+}
+
+
+void KisGmicPlugin::startOnCanvasPreview(KisNodeListSP layers, KisGmicFilterSetting* setting)
+{
+        KUndo2MagicString actionName = kundo2_i18n("Gmic filter");
+        KisNodeSP rootNode = m_view->image()->root();
+        m_gmicApplicator->setProperties(m_view->image(), rootNode, actionName, layers, setting->gmicCommand(), m_gmicCustomCommands);
+        m_gmicApplicator->preview();
+        // do not call KisImage::waitForDone(): strokes are not finished or cancelled, it's just preview!
+}
+
+
+bool KisGmicPlugin::checkSettingsValidity(KisNodeListSP layers, const KisGmicFilterSetting* setting)
+{
+    if (setting->isBlacklisted())
+    {
+        KMessageBox::sorry(m_gmicWidget, i18n("Sorry, this filter is crashing Krita and is turned off."), i18n("Krita"));
+        return false;
+    }
+
+    if (setting->outputMode() != IN_PLACE) {
+        KMessageBox::sorry(m_gmicWidget,QString("Sorry, this output mode is not implemented"),"Krita");
+        return false;
+    }
+
+    if (layers->isEmpty()) {
+        KMessageBox::sorry(m_gmicWidget, i18n("Sorry, this input mode is not implemented"), i18n("Krita"));
+        return false;
+    }
+
+    return true;
+}
 
 
 #include "kis_gmic_plugin.moc"
+
diff --git a/krita/plugins/extensions/gmic/kis_gmic_plugin.h b/krita/plugins/extensions/gmic/kis_gmic_plugin.h
index aeb7e05..d9f62d2 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_plugin.h
+++ b/krita/plugins/extensions/gmic/kis_gmic_plugin.h
@@ -25,6 +25,8 @@
 #include "kis_gmic_parser.h"
 #include <kis_types.h>
 
+class QSize;
+class QRect;
 class KisGmicApplicator;
 class KisGmicWidget;
 
@@ -35,9 +37,28 @@ public:
     KisGmicPlugin(QObject *parent, const QVariantList &);
     virtual ~KisGmicPlugin();
 
+private slots:
+    // life cycle: show -> close
+    void slotShowGmicDialog();
+    void slotCloseGmicDialog();
+
+    void slotPreviewGmicCommand(KisGmicFilterSetting* setting);
+    void slotFilterCurrentImage(KisGmicFilterSetting* setting);
+    void slotCancelOnCanvasPreview();
+    void slotAcceptOnCanvasPreview();
+    void slotPreviewActiveLayer();
+
 private:
     void parseGmicCommandDefinitions(const QStringList &gmicDefinitionFilePaths);
     void setupDefinitionPaths();
+    static KisNodeListSP createPreviewThumbnails(KisNodeListSP layers,const QSize &dstSize,const QRect &srcRect);
+    void createViewportPreview(KisNodeListSP layers, KisGmicFilterSetting* setting);
+    // has to be accepted or cancelled!
+    void startOnCanvasPreview(KisNodeListSP layers, KisGmicFilterSetting* setting);
+    bool checkSettingsValidity(KisNodeListSP layers, const KisGmicFilterSetting * setting);
+
+    // TODO: refactor into responsible classes
+    void showInPreviewViewport(KisPaintDeviceSP device);
 
 private:
     KisGmicWidget * m_gmicWidget;
@@ -45,11 +66,7 @@ private:
     QStringList m_definitionFilePaths;
     QString m_blacklistPath;
     QByteArray m_gmicCustomCommands;
-
-private slots:
-    void slotGmic();
-    void slotApplyGmicCommand(KisGmicFilterSetting* setting);
-    void slotClose();
+    bool m_previewFilter;
 };
 
 #endif
diff --git a/krita/plugins/extensions/gmic/kis_gmic_settings_widget.cpp b/krita/plugins/extensions/gmic/kis_gmic_settings_widget.cpp
index eb343c3..36a9a7e 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_settings_widget.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_settings_widget.cpp
@@ -27,8 +27,9 @@
 
 #include <kfiledialog.h> // For kurlrequester...
 
-KisGmicSettingsWidget::KisGmicSettingsWidget(Command * command):
-    m_commandDefinition(command)
+KisGmicSettingsWidget::KisGmicSettingsWidget(Command * command)
+    :   KisConfigWidget(0, 0, 250),
+        m_commandDefinition(command)
 {
     createSettingsWidget(CreateRole);
 }
@@ -453,7 +454,6 @@ void KisGmicSettingsWidget::createSettingsWidget(ROLE role)
     {
         setLayout(gridLayout);
     }
-    setMinimumSize(sizeHint());
 }
 
 Command* KisGmicSettingsWidget::currentCommandSettings()
diff --git a/krita/plugins/extensions/gmic/kis_gmic_synchronize_layers_command.cpp b/krita/plugins/extensions/gmic/kis_gmic_synchronize_layers_command.cpp
index 5d8323a..33b91fe 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_synchronize_layers_command.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_synchronize_layers_command.cpp
@@ -38,18 +38,30 @@ void KisGmicSynchronizeLayersCommand::redo()
         // if gmic produces more layers
         if (m_nodes->size() < int(m_images->_width))
         {
-            for (unsigned int i = m_nodes->size(); i < m_images->_width; i++)
-            {
-                KisPaintDevice * device = new KisPaintDevice(m_image->colorSpace());
-                KisLayerSP paintLayer = new KisPaintLayer(m_image, "New layer from gmic filter", OPACITY_OPAQUE_U8, device);
-                m_nodes->append(paintLayer);
-                m_image->addNode(paintLayer, m_nodes->at(0)->parent());
 
-                dbgPlugins << "Added new layer";
-            }
+            if (m_image)
+            {
+                for (unsigned int i = m_nodes->size(); i < m_images->_width; i++)
+                {
+                    KisPaintDevice * device = new KisPaintDevice(m_image->colorSpace());
+                    KisLayerSP paintLayer = new KisPaintLayer(m_image, "New layer from gmic filter", OPACITY_OPAQUE_U8, device);
+                    m_nodes->append(paintLayer);
 
-            // would sleep(5) here help?
+                    m_image->addNode(paintLayer, m_nodes->at(0)->parent());
 
+                    dbgPlugins << "Added new layer";
+                }
+            }
+            else
+            {
+                Q_ASSERT(m_nodes->size() > 0);
+                for (unsigned int i = m_nodes->size(); i < m_images->_width; i++)
+                {
+                    KisPaintDevice * device = new KisPaintDevice(m_nodes->at(0)->colorSpace());
+                    KisLayerSP paintLayer = new KisPaintLayer(0, "New layer from gmic filter", OPACITY_OPAQUE_U8, device);
+                    m_nodes->append(paintLayer);
+                }
+            }
         } // if gmic produces less layers, we are going to drop some
         else if (m_nodes->size() > int(m_images->_width))
         {
@@ -58,13 +70,12 @@ void KisGmicSynchronizeLayersCommand::redo()
     }
     else
     {
-        // really?
-        // KUndo2Command::undo();
-        dbgPlugins << "Undo needed?";
+        dbgPlugins << "Redo again needed?";
     }
 }
 
 void KisGmicSynchronizeLayersCommand::undo()
 {
     // do nothing?
+    dbgPlugins << "Not implemented";
 }
diff --git a/krita/plugins/extensions/gmic/kis_gmic_updater.cpp b/krita/plugins/extensions/gmic/kis_gmic_updater.cpp
index 5353899..219295f 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_updater.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_updater.cpp
@@ -22,7 +22,6 @@
 #include <QFile>
 #include <QNetworkRequest>
 #include <qsslerror.h>
-#include <QDebug>
 #include <QTimer>
 #include <kis_debug.h>
 
@@ -77,23 +76,6 @@ void KisGmicUpdater::finishedDownload(QNetworkReply*reply)
     QString path = KGlobal::mainComponent().dirs()->saveLocation("gmic_definitions");
     QString fileName = reply->url().path().split("/").last();;
 
-    /* TODO: optimize based on ETag, do not redownload if not necessery
-     * also try to check Content-Type but so far cimgz report wrongly plain text
-    if (reply->hasRawHeader("Content-Type"))
-    {
-        QString contentType = reply->rawHeader("Content-Type");
-    }
-
-    int i = 0;
-    foreach (const QByteArray &array, reply->rawHeaderList())
-    {
-        ++i;
-        qDebug() << i << " " << QString(array);
-    }
-
-    */
-
-
     QByteArray data = reply->readAll();
 
     QString tmpfilePath = path + fileName + QString(".cimgz");
diff --git a/krita/plugins/extensions/gmic/kis_gmic_widget.cpp b/krita/plugins/extensions/gmic/kis_gmic_widget.cpp
index 3703250..b285e86 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_widget.cpp
+++ b/krita/plugins/extensions/gmic/kis_gmic_widget.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013 Lukáš Tvrdý <lukast.dev at gmail.com>
+ * Copyright (c) 2013-2014 Lukáš Tvrdý <lukast.dev at gmail.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -38,47 +38,49 @@
 #include <kis_gmic_filter_proxy_model.h>
 #include "kis_gmic_updater.h"
 
+
 static const QString maximizeStr = i18n("Maximize");
+static const QString selectFilterStr = i18n("Select a filter...");
 
-KisGmicWidget::KisGmicWidget(KisGmicFilterModel * filters, const QString &updateUrl): QWidget(),m_filterModel(filters),m_updateUrl(updateUrl)
+KisGmicWidget::KisGmicWidget(KisGmicFilterModel * filters, const QString &updateUrl): m_filterModel(filters),m_updateUrl(updateUrl)
 {
+    dbgPlugins << "Constructor:" << this;
+
+    setupUi(this);
     createMainLayout();
     setAttribute(Qt::WA_DeleteOnClose, true);
+
+    m_filterApplied = false;
+    m_onCanvasPreviewActivated = false;
+    m_onCanvasPreviewRequested = false;
 }
 
 KisGmicWidget::~KisGmicWidget()
 {
-    dbgPlugins << "I'm dying...";
+    dbgPlugins << "Destructor:" << this;
     delete m_filterModel;
 }
 
 void KisGmicWidget::createMainLayout()
 {
-    m_filterConfigLayout = new QGridLayout;
 
-    int column = 0;
-    int row = 0;
-    m_inputOutputOptions = new KisGmicInputOutputWidget();
-    m_filterConfigLayout->addWidget(m_inputOutputOptions, row, column);
-    column++;
 
-    m_filterTree = new QTreeView();
+    connect(m_inputOutputOptions->previewCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPreviewChanged(bool)));
+    connect(m_inputOutputOptions->previewSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPreviewSizeChanged()));
+    connect(m_inputOutputOptions->previewSizeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotConfigurationChanged()));
+
+    connect(m_inputOutputOptions->zoomInButton, SIGNAL(clicked(bool)), this, SLOT(slotNotImplemented()));
+    connect(m_inputOutputOptions->zoomOutButton, SIGNAL(clicked(bool)), this, SLOT(slotNotImplemented()));
 
     KisGmicFilterProxyModel *proxyModel = new KisGmicFilterProxyModel(this);
     proxyModel->setSourceModel(m_filterModel);
     proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
-
     m_filterTree->setModel(proxyModel);
     m_filterTree->setItemDelegate(new HtmlDelegate());
 
-    QItemSelectionModel *selectionModel= m_filterTree->selectionModel();
-    connect(selectionModel, SIGNAL(selectionChanged (const QItemSelection &, const QItemSelection &)),
-            this, SLOT(selectionChangedSlot(const QItemSelection &, const QItemSelection &)));
-
-    m_filterConfigLayout->addWidget(m_filterTree, row, column);
+    connect(m_filterTree->selectionModel(), SIGNAL(selectionChanged (const QItemSelection &, const QItemSelection &)),
+            this, SLOT(slotSelectedFilterChanged(const QItemSelection &, const QItemSelection &)));
 
-    QPushButton * updateBtn = new QPushButton(this);
-    updateBtn->setText("Update definitions");
     if (!m_updateUrl.isEmpty())
     {
         updateBtn->setToolTip("Fetching definitions from : " + m_updateUrl);
@@ -87,50 +89,28 @@ void KisGmicWidget::createMainLayout()
     {
         updateBtn->setEnabled(false);
     }
-    connect(updateBtn, SIGNAL(clicked(bool)), this, SLOT(startUpdate()));
-    m_filterConfigLayout->addWidget(updateBtn, row+1, column);
 
-    QLineEdit * searchBox = new QLineEdit(this);
+    connect(updateBtn, SIGNAL(clicked(bool)), this, SLOT(startUpdate()));
     connect(searchBox, SIGNAL(textChanged(QString)), proxyModel, SLOT(setFilterFixedString(QString)));
-    m_filterConfigLayout->addWidget(searchBox, row+2, column);
 
-    column++;
-
-    m_filterOptions = new QWidget();
-    m_filterConfigLayout->addWidget(m_filterOptions,row, column);
-    m_filterConfigLayout->setColumnStretch(column, 1);
-    m_filterOptionsRow = row;
-    m_filterOptionsColumn = column;
-
-    QDialogButtonBox * controlButtonBox = new QDialogButtonBox;
     QPushButton * maximize = new QPushButton(maximizeStr);
-    connect(maximize, SIGNAL(clicked(bool)), this, SLOT(maximizeSlot()));
-
-    controlButtonBox->addButton(maximize, QDialogButtonBox::AcceptRole);
-    controlButtonBox->addButton(QDialogButtonBox::Ok);
-    QAbstractButton *okButton = controlButtonBox->button(QDialogButtonBox::Ok);
-    connect(okButton, SIGNAL(clicked(bool)), this, SLOT(okFilterSlot()));
-
-    controlButtonBox->addButton(QDialogButtonBox::Apply);
-    QAbstractButton *applyButton = controlButtonBox->button(QDialogButtonBox::Apply);
-    connect(applyButton, SIGNAL(clicked(bool)), this, SLOT(applyFilterSlot()));
-
-    controlButtonBox->addButton(QDialogButtonBox::Cancel);
-    QAbstractButton *cancelButton = controlButtonBox->button(QDialogButtonBox::Cancel);
-    connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(cancelFilterSlot()));
-
-    controlButtonBox->addButton(QDialogButtonBox::Reset);
-    QAbstractButton *resetButton = controlButtonBox->button(QDialogButtonBox::Reset);
-    connect(resetButton, SIGNAL(clicked(bool)), this, SLOT(resetFilterSlot()));
-
-    m_filterConfigLayout->addWidget(controlButtonBox,row + 1, column, 1, 2);
-    column++;
-
-    setLayout(m_filterConfigLayout);
+    controlButtonBox->addButton(maximize, QDialogButtonBox::ActionRole);
+    connect(maximize, SIGNAL(clicked(bool)), this, SLOT(slotMaximizeClicked()));
+    connect(controlButtonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked(bool)), this, SLOT(slotOkClicked()));
+    connect(controlButtonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked(bool)), this, SLOT(slotApplyClicked()));
+    connect(controlButtonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked(bool)), this, SLOT(slotCancelClicked()));
+    connect(controlButtonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked(bool)), this, SLOT(slotResetClicked()));
+
+    int indexOfFilterOptions = m_filterConfigLayout->indexOf(m_filterOptions);
+    Q_ASSERT(indexOfFilterOptions != -1);
+    int rowSpan = 0, colSpan = 0;
+    m_filterConfigLayout->getItemPosition(indexOfFilterOptions, &m_filterOptionsRow, &m_filterOptionsColumn, &rowSpan, &colSpan);
+
+    switchOptionsWidgetFor(new QLabel(selectFilterStr));
 }
 
 
-void KisGmicWidget::selectionChangedSlot(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
+void KisGmicWidget::slotSelectedFilterChanged(const QItemSelection & /*newSelection*/, const QItemSelection & /*oldSelection*/)
  {
      //get the text of the selected item
     const QModelIndex index = m_filterTree->selectionModel()->currentIndex();
@@ -150,78 +130,78 @@ void KisGmicWidget::selectionChangedSlot(const QItemSelection & /*newSelection*/
         gmicCommand = var.value<Command *>();
     }
 
-    m_filterConfigLayout->removeWidget(m_filterOptions);
-    delete m_filterOptions;
 
     if (gmicCommand)
     {
-        m_filterOptions = new KisGmicSettingsWidget(gmicCommand);
+        KisGmicSettingsWidget * filterOptions = new KisGmicSettingsWidget(gmicCommand);
+        QObject::connect(filterOptions, SIGNAL(sigConfigurationUpdated()), this, SLOT(slotConfigurationChanged()));
+        switchOptionsWidgetFor(filterOptions);
     }
     else
     {
-        m_filterOptions = new QLabel("Select a filter...");
+        switchOptionsWidgetFor(new QLabel(selectFilterStr));
+        emit sigPreviewActiveLayer();
     }
 
-    m_filterConfigLayout->addWidget(m_filterOptions,m_filterOptionsRow,m_filterOptionsColumn);
-    m_filterConfigLayout->update();
 
+
+#ifdef DEBUG_MODEL
      //find out the hierarchy level of the selected item
-     int hierarchyLevel=1;
+     int hierarchyLevel = 1;
      QModelIndex seekRoot = index;
      while(seekRoot.parent() != QModelIndex())
      {
          seekRoot = seekRoot.parent();
          hierarchyLevel++;
      }
+
      QString showString = QString("%1, Level %2").arg(selectedText)
                           .arg(hierarchyLevel);
      setWindowTitle(showString);
-
-     resize(sizeHint());
+#endif
  }
 
-void KisGmicWidget::applyFilterSlot()
+
+void KisGmicWidget::slotCancelClicked()
 {
-    const QModelIndex index = m_filterTree->selectionModel()->currentIndex();
-    QVariant settings = index.data(FilterSettingsRole);
-    if (settings.isValid())
+    if (m_inputOutputOptions->previewSize() == ON_CANVAS)
     {
-        KisGmicFilterSetting * filterSettings = settings.value<KisGmicFilterSetting * >();
-        filterSettings->setInputLayerMode(m_inputOutputOptions->inputMode());
-        filterSettings->setOutputMode(m_inputOutputOptions->outputMode());
+        emit sigCancelOnCanvasPreview();
+    }
 
+    close();
+}
 
-        dbgPlugins << "Valid settings!";
-        dbgPlugins << "GMIC command : " << filterSettings->gmicCommand();
 
-        emit sigApplyCommand(filterSettings);
+void KisGmicWidget::slotOkClicked()
+{
+    if (m_inputOutputOptions->previewSize() == ON_CANVAS)
+    {
+        emit sigAcceptOnCanvasPreview();
     }
     else
     {
-        dbgPlugins << "Filter is not selected!";
+        if (!m_filterApplied)
+        {
+            KisGmicFilterSetting * filterSettings = currentFilterSettings();
+            if (filterSettings)
+            {
+                emit sigFilterCurrentImage(filterSettings);
+            }
+            m_filterApplied = true;
+        }
     }
 
-}
-
-void KisGmicWidget::cancelFilterSlot()
-{
-    emit sigClose();
-}
-
-
-void KisGmicWidget::okFilterSlot()
-{
-    applyFilterSlot();
-    emit sigClose();
+    close();
 }
 
  void KisGmicWidget::closeEvent(QCloseEvent *event)
  {
-     emit sigClose();
      event->accept();
+     emit sigClose();
  }
 
-void KisGmicWidget::resetFilterSlot()
+void KisGmicWidget::slotResetClicked()
 {
     const QModelIndex index = m_filterTree->selectionModel()->currentIndex();
     QVariant var = index.data(CommandRole);
@@ -243,10 +223,10 @@ void KisGmicWidget::resetFilterSlot()
     {
         currentSettingsWidget->reload();
     }
-    resize(sizeHint());
+
 }
 
-void KisGmicWidget::maximizeSlot()
+void KisGmicWidget::slotMaximizeClicked()
 {
     QPushButton * maximizeButton = qobject_cast<QPushButton *>(sender());
     if (!maximizeButton)
@@ -283,3 +263,153 @@ void KisGmicWidget::finishUpdate()
                         "Restart G'MIC dialog to finish updating! ");
     KMessageBox::information(this, msg, i18nc("@title:window", "Updated"));
 }
+
+void KisGmicWidget::slotPreviewChanged(bool enabling)
+{
+    if (enabling)
+    {
+        requestComputePreview();
+    }
+    else
+    {
+        if (m_inputOutputOptions->previewSize() == ON_CANVAS)
+        {
+            emit sigCancelOnCanvasPreview();
+            m_onCanvasPreviewRequested = false; // cancelled
+        }
+        else
+        {
+            emit sigPreviewActiveLayer();
+        }
+    }
+
+}
+
+void KisGmicWidget::slotPreviewSizeChanged()
+{
+    if (m_inputOutputOptions->previewSize() == ON_CANVAS)
+    {
+        m_onCanvasPreviewActivated = true;
+    }
+    else
+    {
+        if (m_onCanvasPreviewActivated)
+        {
+            emit sigCancelOnCanvasPreview();
+            m_onCanvasPreviewActivated = false;
+            m_onCanvasPreviewRequested = false;
+        }
+    }
+}
+
+
+void KisGmicWidget::slotConfigurationChanged()
+{
+    if (m_inputOutputOptions->previewCheckBox->isChecked())
+    {
+        requestComputePreview();
+    }
+    else
+    {
+        emit sigPreviewActiveLayer();
+    }
+
+}
+
+void KisGmicWidget::slotApplyClicked()
+{
+    if (m_inputOutputOptions->previewSize() == ON_CANVAS)
+    {
+        KisGmicFilterSetting * filterSettings = currentFilterSettings();
+        if (!filterSettings)
+        {
+            return;
+        }
+
+        if (m_inputOutputOptions->previewCheckBox->isChecked())
+        {
+            emit sigAcceptOnCanvasPreview();
+            emit sigPreviewFilterCommand(filterSettings);
+        }
+        else
+        {
+            emit sigFilterCurrentImage(filterSettings);
+            m_filterApplied = true;
+        }
+
+
+    }
+    else // Tiny, Small, Medium, Large preview
+    {
+
+            KisGmicFilterSetting * filterSettings = currentFilterSettings();
+            if (filterSettings)
+            {
+                emit sigFilterCurrentImage(filterSettings);
+                m_filterApplied = true;
+                requestComputePreview();
+            }
+    }
+}
+
+KisGmicFilterSetting* KisGmicWidget::currentFilterSettings()
+{
+    KisGmicFilterSetting * filterSettings = 0;
+    QVariant settings = m_filterTree->selectionModel()->currentIndex().data(FilterSettingsRole);
+    if (settings.isValid())
+    {
+        dbgPlugins << "Valid settings!";
+        filterSettings = settings.value<KisGmicFilterSetting * >();
+        filterSettings->setInputLayerMode(m_inputOutputOptions->inputMode());
+        filterSettings->setOutputMode(m_inputOutputOptions->outputMode());
+        filterSettings->setPreviewMode(m_inputOutputOptions->previewMode());
+        filterSettings->setPreviewSize(m_inputOutputOptions->previewSize());
+        dbgPlugins << "GMIC command : " << filterSettings->gmicCommand();
+        dbgPlugins << "GMIC preview command : " << filterSettings->previewGmicCommand();
+    }
+    else
+    {
+        dbgPlugins << "Filter is not selected!";
+    }
+
+    return filterSettings;
+}
+
+void KisGmicWidget::requestComputePreview()
+{
+    KisGmicFilterSetting * filterSettings = currentFilterSettings();
+    if (filterSettings)
+    {
+        emit sigPreviewFilterCommand(filterSettings);
+        if (m_onCanvasPreviewActivated)
+        {
+            m_onCanvasPreviewRequested = true;
+        }
+    }
+    else
+    {
+        emit sigPreviewActiveLayer();
+    }
+}
+
+void KisGmicWidget::switchOptionsWidgetFor(QWidget* widget)
+{
+    m_filterConfigLayout->removeWidget(m_filterOptions);
+    delete m_filterOptions;
+
+    m_filterOptions = widget;
+
+    m_filterConfigLayout->addWidget(m_filterOptions, m_filterOptionsRow, m_filterOptionsColumn);
+    m_filterConfigLayout->update();
+
+}
+
+KisFilterPreviewWidget * KisGmicWidget::previewWidget()
+{
+    return m_inputOutputOptions->previewWidget();
+}
+
+void KisGmicWidget::slotNotImplemented()
+{
+    KMessageBox::sorry(this, i18n("Sorry, support not implemented yet."), i18n("Krita"));
+}
diff --git a/krita/plugins/extensions/gmic/kis_gmic_widget.h b/krita/plugins/extensions/gmic/kis_gmic_widget.h
index 73ebe42..1a87d69 100644
--- a/krita/plugins/extensions/gmic/kis_gmic_widget.h
+++ b/krita/plugins/extensions/gmic/kis_gmic_widget.h
@@ -20,15 +20,18 @@
 
 #include <QTreeView>
 #include <QGridLayout>
+#include <QCheckBox>
 #include "kis_gmic_filter_model.h"
 #include "kis_gmic_filter_settings.h"
 
+#include "ui_wdg_gmic.h"
+
 class KisGmicUpdater;
 class QCloseEvent;
 class KisGmicInputOutputWidget;
 class QPushButton;
 
-class KisGmicWidget : public QWidget
+class KisGmicWidget : public QWidget, public Ui::WdgGmic
 {
     Q_OBJECT
 
@@ -37,30 +40,46 @@ public:
     KisGmicWidget(KisGmicFilterModel * filters, const QString &updateUrl = QString());
     ~KisGmicWidget();
 
+    KisFilterPreviewWidget * previewWidget();
+
     void createMainLayout();
     virtual void closeEvent(QCloseEvent* );
 
 signals:
-    void sigApplyCommand(KisGmicFilterSetting * setting);
+    void sigFilterCurrentImage(KisGmicFilterSetting * setting); //TODO:const
+    void sigPreviewFilterCommand(KisGmicFilterSetting * setting); //TODO:const
+    void sigAcceptOnCanvasPreview();
+    void sigCancelOnCanvasPreview();
+    void sigPreviewActiveLayer();
     void sigClose();
 
 private slots:
-    void selectionChangedSlot(const QItemSelection & newSelection, const QItemSelection & oldSelection);
-    void applyFilterSlot();
-    void resetFilterSlot();
-    void okFilterSlot();
-    void maximizeSlot();
-    void cancelFilterSlot();
+    void slotSelectedFilterChanged(const QItemSelection & newSelection, const QItemSelection & oldSelection);
+    // buttons
+    void slotApplyClicked();
+    void slotOkClicked();
+    void slotCancelClicked();
+    void slotResetClicked();
+    void slotMaximizeClicked();
+
+    // internet updates slots
     void startUpdate();
     void finishUpdate();
 
+    // preview
+    void slotPreviewChanged(bool enabling);
+    void slotPreviewSizeChanged();
+    void slotConfigurationChanged();
+    void slotNotImplemented();
+
 private:
-    QGridLayout * m_filterConfigLayout;
+    KisGmicFilterSetting * currentFilterSettings();
+    void requestComputePreview();
+    void switchOptionsWidgetFor(QWidget * widget);
 
-    QTreeView * m_filterTree;
-    QWidget * m_filterOptions;
-    KisGmicInputOutputWidget * m_inputOutputOptions;
 
+
+private:
     KisGmicFilterModel * m_filterModel;
     KisGmicUpdater * m_updater;
 
@@ -68,6 +87,10 @@ private:
 
     int m_filterOptionsRow;
     int m_filterOptionsColumn;
+
+    bool m_filterApplied;
+    bool m_onCanvasPreviewActivated;
+    bool m_onCanvasPreviewRequested;
 };
 
 #endif
diff --git a/krita/plugins/extensions/gmic/kis_import_gmic_processing_visitor.cpp b/krita/plugins/extensions/gmic/kis_import_gmic_processing_visitor.cpp
index a55e772..ddd14ed 100644
--- a/krita/plugins/extensions/gmic/kis_import_gmic_processing_visitor.cpp
+++ b/krita/plugins/extensions/gmic/kis_import_gmic_processing_visitor.cpp
@@ -62,7 +62,13 @@ void KisImportGmicProcessingVisitor::visitNodeWithPaintDevice(KisNode *node, Kis
         {
             KisGmicSimpleConvertor::convertFromGmicFast(m_images->_data[index], dst, 255.0f);
         }
-        transaction.commit(undoAdapter);
+
+        if (undoAdapter)
+        {
+            transaction.commit(undoAdapter);
+            node->setDirty(m_dstRect);
+        }
+
     }
 }
 
diff --git a/krita/plugins/extensions/gmic/kis_input_output_mapper.cpp b/krita/plugins/extensions/gmic/kis_input_output_mapper.cpp
index 5e9d9e4..3b9552e 100644
--- a/krita/plugins/extensions/gmic/kis_input_output_mapper.cpp
+++ b/krita/plugins/extensions/gmic/kis_input_output_mapper.cpp
@@ -61,6 +61,7 @@ KisNodeListSP KisInputOutputMapper::inputNodes(InputLayerMode inputMode)
             result->append(m_activeNode->nextSibling());
             break;
         }
+        case NONE:
         case ALL_VISIBLE_LAYERS:
         case ALL_INVISIBLE_LAYERS:
         case ALL_VISIBLE_LAYERS_DECR:
@@ -77,6 +78,7 @@ KisNodeListSP KisInputOutputMapper::inputNodes(InputLayerMode inputMode)
         case ALL_DECR:
         {
             allInversedOrderedLayers(result);
+            break;
         }
         default:
         {
diff --git a/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp b/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
index 393d728..efd2df5 100644
--- a/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
+++ b/krita/plugins/extensions/gmic/tests/kis_gmic_tests.cpp
@@ -590,8 +590,8 @@ void KisGmicTests::testFilterOnlySelection()
 
     KisGmicApplicator applicator;
     applicator.setProperties(image, image->root(), kundo2_noi18n("Gmic filter"), kritaNodes, gmicCommand);
-    applicator.start();
-    applicator.wait();
+    applicator.preview();
+    applicator.finish();
     image->waitForDone();
 
     //image->convertToQImage(image->bounds(), 0).save("filteredSelection.png");
diff --git a/krita/plugins/extensions/gmic/wdg_gmic.ui b/krita/plugins/extensions/gmic/wdg_gmic.ui
index e8018d8..021955b 100644
--- a/krita/plugins/extensions/gmic/wdg_gmic.ui
+++ b/krita/plugins/extensions/gmic/wdg_gmic.ui
@@ -6,40 +6,83 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>503</width>
-    <height>356</height>
+    <width>610</width>
+    <height>478</height>
    </rect>
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
-    <widget class="QLabel" name="label_2">
-     <property name="text">
-      <string>G'Mic string:</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="0" column="1">
-    <widget class="QLineEdit" name="txtGmic"/>
-   </item>
-   <item row="1" column="1">
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>40</height>
-      </size>
-     </property>
-    </spacer>
+    <layout class="QGridLayout" name="m_filterConfigLayout" columnstretch="1,2,2,1">
+     <item row="0" column="0" rowspan="3" alignment="Qt::AlignLeft">
+      <widget class="KisGmicInputOutputWidget" name="m_inputOutputOptions" native="true"/>
+     </item>
+     <item row="0" column="1">
+      <widget class="QTreeView" name="m_filterTree">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QPushButton" name="updateBtn">
+       <property name="text">
+        <string>Update definitions</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QLabel" name="searchLbl">
+         <property name="text">
+          <string>Search:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLineEdit" name="searchBox"/>
+       </item>
+      </layout>
+     </item>
+     <item row="1" column="2" colspan="2">
+      <widget class="QDialogButtonBox" name="controlButtonBox">
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2" colspan="2">
+      <widget class="QWidget" name="m_filterOptions" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>16777215</height>
+        </size>
+       </property>
+      </widget>
+     </item>
+    </layout>
    </item>
   </layout>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
+ <customwidgets>
+  <customwidget>
+   <class>KisGmicInputOutputWidget</class>
+   <extends>QWidget</extends>
+   <header>kis_gmic_input_output_widget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
  <resources/>
  <connections/>
 </ui>
diff --git a/krita/plugins/extensions/gmic/wdg_gmic_input_output.ui b/krita/plugins/extensions/gmic/wdg_gmic_input_output.ui
new file mode 100644
index 0000000..22023fa
--- /dev/null
+++ b/krita/plugins/extensions/gmic/wdg_gmic_input_output.ui
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WdgGmicInputOutput</class>
+ <widget class="QWidget" name="WdgGmicInputOutput">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0" colspan="2">
+    <widget class="KisFilterPreviewWidget" name="previewViewport" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QCheckBox" name="previewCheckBox">
+     <property name="text">
+      <string>Preview</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="zoomInButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Zoom In</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="zoomOutButton">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Zoom Out</string>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="inputLayerLabel">
+     <property name="text">
+      <string>Input</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QComboBox" name="inputCombo"/>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="outputLayerLabel">
+     <property name="text">
+      <string>Output</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QComboBox" name="outputCombo"/>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="outputPreviewLabel">
+     <property name="text">
+      <string>Preview mode</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QComboBox" name="outputPreviewCombo"/>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="previewSizeLabel">
+     <property name="text">
+      <string>Preview size</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1">
+    <widget class="QComboBox" name="previewSizeCombo"/>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KisFilterPreviewWidget</class>
+   <extends>QWidget</extends>
+   <header>kis_filter_preview_widget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>


More information about the kimageshop mailing list