[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    [calligra] krita: Implement Split Alpha functionality
From:       Dmitry Kazakov <dimula73 () gmail ! com>
Date:       2014-11-26 10:45:49
Message-ID: E1Xta6X-0000QI-GT () scm ! kde ! org
[Download RAW message or body]

Git commit b22aa1146899b21114eabe239bcd54c0b1e247f0 by Dmitry Kazakov.
Committed on 26/11/2014 at 10:44.
Pushed by dkazakov into branch 'master'.

Implement Split Alpha functionality

This feature combined with Isolate Layer feature allows user to
edit alpha channel separately and save the resulting layer with
color channels having real color data even though alpha is zero.

http://docs.unity3d.com/Manual/HOWTO-alphamaps.html

CCMAIL:kimageshop@kde.org

M  +5    -1    krita/krita.rc
M  +5    -0    krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M  +14   -12   krita/ui/kis_action.h
M  +13   -1    krita/ui/kis_action_manager.cpp
M  +6    -0    krita/ui/kis_node_commands_adapter.cpp
M  +1    -0    krita/ui/kis_node_commands_adapter.h
M  +188  -28   krita/ui/kis_node_manager.cpp
M  +4    -0    krita/ui/kis_node_manager.h

http://commits.kde.org/calligra/b22aa1146899b21114eabe239bcd54c0b1e247f0

diff --git a/krita/krita.rc b/krita/krita.rc
index e8adff6..3716bb8 100644
--- a/krita/krita.rc
+++ b/krita/krita.rc
@@ -117,7 +117,11 @@
     <Action name="convert_to_selection_mask"/>
   </Menu>
   <Separator/>
-
+  <Menu name="LayerSplitAlpha"><text>S&amp;plit Alpha</text>
+    <Action name="split_alpha_into_mask"/>
+    <Action name="split_alpha_write"/>
+    <Action name="split_alpha_save_merged"/>
+  </Menu>
   <Separator/>
   <Action name="mirrorNodeX"/>
   <Action name="mirrorNodeY"/>
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp \
b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp index \
                7025fc3..8728d54 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -495,6 +495,11 @@ void KisLayerBox::slotContextMenuRequested(const QPoint &pos, \
const QModelIndex  addActionToMenu(convertToMenu, "convert_to_transform_mask");
         addActionToMenu(convertToMenu, "convert_to_selection_mask");
 
+        QMenu *splitAlphaMenu = menu.addMenu(i18n("S&plit Alpha"));
+        addActionToMenu(splitAlphaMenu, "split_alpha_into_mask");
+        addActionToMenu(splitAlphaMenu, "split_alpha_write");
+        addActionToMenu(splitAlphaMenu, "split_alpha_save_merged");
+
         addActionToMenu(&menu, "isolate_layer");
     }
     menu.addSeparator();
diff --git a/krita/ui/kis_action.h b/krita/ui/kis_action.h
index 18cca90..f73b215 100644
--- a/krita/ui/kis_action.h
+++ b/krita/ui/kis_action.h
@@ -31,23 +31,25 @@ class KRITAUI_EXPORT KisAction : public KAction
 public:
     enum ActivationFlag {
         NONE = 0,
-        ACTIVE_NODE = 1,
-        ACTIVE_DEVICE = 2,
-        ACTIVE_LAYER = 4,
-        ACTIVE_SHAPE_LAYER = 8,
-        PIXELS_SELECTED = 16,
-        SHAPES_SELECTED = 32,
-        PIXEL_SELECTION_WITH_PIXELS = 64,
-        PIXELS_IN_CLIPBOARD = 128,
-        SHAPES_IN_CLIPBOARD = 256,
-        NEVER_ACTIVATE = 512
+        ACTIVE_NODE = 0x1,
+        ACTIVE_DEVICE = 0x2,
+        ACTIVE_LAYER = 0x4,
+        ACTIVE_TRANSPARENCY_MASK = 0x8,
+        ACTIVE_SHAPE_LAYER = 0x10,
+        PIXELS_SELECTED = 0x20,
+        SHAPES_SELECTED = 0x40,
+        PIXEL_SELECTION_WITH_PIXELS = 0x80,
+        PIXELS_IN_CLIPBOARD = 0x100,
+        SHAPES_IN_CLIPBOARD = 0x200,
+        NEVER_ACTIVATE = 0x400
     };
     Q_DECLARE_FLAGS(ActivationFlags, ActivationFlag)
 
     enum ActivationCondition {
         NO_CONDITION = 0,
-        ACTIVE_NODE_EDITABLE = 1,
-        SELECTION_EDITABLE = 2
+        ACTIVE_NODE_EDITABLE = 0x1,
+        ACTIVE_NODE_EDITABLE_PAINT_DEVICE = 0x2,
+        SELECTION_EDITABLE = 0x4
     };
     Q_DECLARE_FLAGS(ActivationConditions, ActivationCondition)
     
diff --git a/krita/ui/kis_action_manager.cpp b/krita/ui/kis_action_manager.cpp
index 4562d41..1391d2f 100644
--- a/krita/ui/kis_action_manager.cpp
+++ b/krita/ui/kis_action_manager.cpp
@@ -84,7 +84,7 @@ KisAction *KisActionManager::actionByName(const QString &name) \
const  void KisActionManager::updateGUI()
 {
     KisNodeSP node = d->view->activeNode();
-    KisLayerSP layer = d->view->activeLayer();
+    KisLayerSP layer = dynamic_cast<KisLayer*>(node.data());
 
     //TODO other flags
     KisAction::ActivationFlags flags;
@@ -93,6 +93,9 @@ void KisActionManager::updateGUI()
     }
     if (node) {
         flags |= KisAction::ACTIVE_NODE;
+        if (node->inherits("KisTransparencyMask")) {
+            flags |= KisAction::ACTIVE_TRANSPARENCY_MASK;
+        }
     }
     if (layer) {
         flags |= KisAction::ACTIVE_LAYER;
@@ -121,6 +124,9 @@ void KisActionManager::updateGUI()
     if (node && node->isEditable()) {
         conditions |= KisAction::ACTIVE_NODE_EDITABLE;
     }
+    if (node && node->hasEditablePaintDevice()) {
+        conditions |= KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE;
+    }
     if (d->view->selectionEditable()) {
         conditions |= KisAction::SELECTION_EDITABLE;
     }
@@ -198,6 +204,9 @@ void KisActionManager::dumpActionFlags()
             if (flags & KisAction::ACTIVE_LAYER) {
                 out << "    Active layer\n";
             }
+            if (flags & KisAction::ACTIVE_TRANSPARENCY_MASK) {
+                out << "    Active transparency mask\n";
+            }
             if (flags & KisAction::ACTIVE_NODE) {
                 out << "    Active node\n";
             }
@@ -228,6 +237,9 @@ void KisActionManager::dumpActionFlags()
             if (conditions & KisAction::ACTIVE_NODE_EDITABLE) {
                 out << "    Active Node editable\n";
             }
+            if (conditions & KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE) {
+                out << "    Active Node has editable paint device\n";
+            }
             if (conditions & KisAction::SELECTION_EDITABLE) {
                 out << "    Selection is editable\n";
             }
diff --git a/krita/ui/kis_node_commands_adapter.cpp \
b/krita/ui/kis_node_commands_adapter.cpp index d4e6179..7ed1f58 100644
--- a/krita/ui/kis_node_commands_adapter.cpp
+++ b/krita/ui/kis_node_commands_adapter.cpp
@@ -48,6 +48,12 @@ void KisNodeCommandsAdapter::beginMacro(const KUndo2MagicString& \
macroName)  m_view->image()->undoAdapter()->beginMacro(macroName);
 }
 
+void KisNodeCommandsAdapter::addExtraCommand(KUndo2Command *command)
+{
+    Q_ASSERT(m_view->image()->undoAdapter());
+    m_view->image()->undoAdapter()->addCommand(command);
+}
+
 void KisNodeCommandsAdapter::endMacro()
 {
     Q_ASSERT(m_view->image()->undoAdapter());
diff --git a/krita/ui/kis_node_commands_adapter.h \
b/krita/ui/kis_node_commands_adapter.h index f74b442..ada5878 100644
--- a/krita/ui/kis_node_commands_adapter.h
+++ b/krita/ui/kis_node_commands_adapter.h
@@ -41,6 +41,7 @@ public:
     virtual ~KisNodeCommandsAdapter();
 public:
     void beginMacro(const KUndo2MagicString& macroName);
+    void addExtraCommand(KUndo2Command *command);
     void endMacro();
     void addNode(KisNodeSP node, KisNodeSP parent, KisNodeSP aboveThis);
     void addNode(KisNodeSP node, KisNodeSP parent, quint32 index);
diff --git a/krita/ui/kis_node_manager.cpp b/krita/ui/kis_node_manager.cpp
index 9241f03..b914f74 100644
--- a/krita/ui/kis_node_manager.cpp
+++ b/krita/ui/kis_node_manager.cpp
@@ -22,6 +22,8 @@
 
 #include <kactioncollection.h>
 #include <kmimetype.h>
+#include <kmessagebox.h>
+
 
 #include <KoIcon.h>
 #include <KoProperties.h>
@@ -32,6 +34,10 @@
 #include <KoFilterManager.h>
 #include <KoFileDialog.h>
 
+#include <KoColorSpace.h>
+#include <KoColorSpaceRegistry.h>
+#include <KoColorModelStandardIds.h>
+
 #include <kis_types.h>
 #include <kis_node.h>
 #include <kis_selection.h>
@@ -55,16 +61,22 @@
 #include "kis_action.h"
 #include "kis_action_manager.h"
 #include "kis_processing_applicator.h"
+#include "kis_sequential_iterator.h"
+#include "kis_transaction.h"
+
 #include "processing/kis_mirror_processing_visitor.h"
 
 
 struct KisNodeManager::Private {
 
+    Private(KisNodeManager *_q) : q(_q) {}
+
     ~Private() {
         delete layerManager;
         delete maskManager;
     }
 
+    KisNodeManager *q;
     KisView2 * view;
     KisDoc2 * doc;
     KisLayerManager * layerManager;
@@ -80,6 +92,15 @@ struct KisNodeManager::Private {
 
     QSignalMapper nodeCreationSignalMapper;
     QSignalMapper nodeConversionSignalMapper;
+
+    void saveDeviceAsImage(KisPaintDeviceSP device,
+                           const QString &defaultName,
+                           const QRect &bounds,
+                           qreal xRes,
+                           qreal yRes,
+                           quint8 opacity);
+
+    void mergeTransparencyMaskAsAlpha(bool writeToLayers);
 };
 
 bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node)
@@ -133,7 +154,7 @@ bool KisNodeManager::Private::activateNodeImpl(KisNodeSP node)
 }
 
 KisNodeManager::KisNodeManager(KisView2 * view, KisDoc2 * doc)
-    : m_d(new Private())
+    : m_d(new Private(this))
 {
     m_d->view = view;
     m_d->doc = doc;
@@ -293,6 +314,24 @@ void KisNodeManager::setup(KActionCollection * actionCollection, \
KisActionManage  actionManager->addAction("isolate_layer", action, actionCollection);
     connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleIsolateMode(bool)));
 
+    action  = new KisAction(koIcon("edit-copy"), i18n("Alpha into Mask"), this);
+    action->setActivationFlags(KisAction::ACTIVE_LAYER);
+    action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE_PAINT_DEVICE);
+    actionManager->addAction("split_alpha_into_mask", action, actionCollection);
+    connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaIntoMask()));
+
+    action  = new KisAction(koIcon("transparency-enabled"), i18n("Write as Alpha"), \
this); +    action->setActivationFlags(KisAction::ACTIVE_TRANSPARENCY_MASK);
+    action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
+    actionManager->addAction("split_alpha_write", action, actionCollection);
+    connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaWrite()));
+
+    action  = new KisAction(koIcon("document-save"), i18n("Save Merged..."), this);
+    action->setActivationFlags(KisAction::ACTIVE_TRANSPARENCY_MASK);
+    // HINT: we can save even when the nodes are not editable
+    actionManager->addAction("split_alpha_save_merged", action, actionCollection);
+    connect(action, SIGNAL(triggered()), this, SLOT(slotSplitAlphaSaveMerged()));
+
     connect(m_d->view->image(), SIGNAL(sigIsolatedModeChanged()),
             this, SLOT(slotUpdateIsolateModeAction()));
     connect(this, SIGNAL(sigNodeActivated(KisNodeSP)), \
SLOT(slotUpdateIsolateModeAction())); @@ -944,17 +983,15 @@ void \
KisNodeManager::mirrorNode(KisNodeSP node, const KUndo2MagicString& actionN  \
nodesUpdated();  }
 
-void KisNodeManager::saveNodeAsImage()
+void KisNodeManager::Private::saveDeviceAsImage(KisPaintDeviceSP device,
+                                                const QString &defaultName,
+                                                const QRect &bounds,
+                                                qreal xRes,
+                                                qreal yRes,
+                                                quint8 opacity)
 {
-    KisNodeSP node = activeNode();
-
-    if (!node) {
-        qWarning() << "BUG: Save Node As Image was called without any node \
                selected";
-        return;
-    }
-
-    KoFileDialog dialog(m_d->view, KoFileDialog::SaveFile, "krita/savenodeasimage");
-    dialog.setCaption(i18n("Export \"%1\"", node->name()));
+    KoFileDialog dialog(view, KoFileDialog::SaveFile, "krita/savenodeasimage");
+    dialog.setCaption(i18n("Export \"%1\"", defaultName));
     dialog.setDefaultDir(QDesktopServices::storageLocation(QDesktopServices::PicturesLocation));
                
     dialog.setMimeTypeFilters(KoFilterManager::mimeFilter("application/x-krita", \
KoFilterManager::Export));  QString filename = dialog.url();
@@ -968,29 +1005,18 @@ void KisNodeManager::saveNodeAsImage()
     KMimeType::Ptr mime = KMimeType::findByUrl(url);
     QString mimefilter = mime->name();
 
-    KisImageWSP image = m_d->view->image();
-
-    QRect savedRect = image->bounds() | node->exactBounds();
-
     KisDoc2 d;
-
     d.prepareForImport();
 
-    KisPaintDeviceSP device = node->paintDevice();
-    if (!device) {
-        device = node->projection();
-    }
-
     KisImageSP dst = new KisImage(d.createUndoStore(),
-                                  savedRect.width(),
-                                  savedRect.height(),
+                                  bounds.width(),
+                                  bounds.height(),
                                   device->compositionSourceColorSpace(),
-                                  node->name());
-    dst->setResolution(image->xRes(), image->yRes());
+                                  defaultName);
+    dst->setResolution(xRes, yRes);
     d.setCurrentImage(dst);
-    KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", \
                node->opacity());
-    KisPainter gc(paintLayer->paintDevice());
-    gc.bitBlt(QPoint(0, 0), device, savedRect);
+    KisPaintLayer* paintLayer = new KisPaintLayer(dst, "paint device", opacity);
+    paintLayer->paintDevice()->makeCloneFrom(device, bounds);
     dst->addNode(paintLayer, dst->rootLayer(), KisLayerSP(0));
 
     dst->initialRefreshGraph();
@@ -999,5 +1025,139 @@ void KisNodeManager::saveNodeAsImage()
     d.exportDocument(url);
 }
 
+void KisNodeManager::saveNodeAsImage()
+{
+    KisNodeSP node = activeNode();
+
+    if (!node) {
+        qWarning() << "BUG: Save Node As Image was called without any node \
selected"; +        return;
+    }
+
+    KisImageWSP image = m_d->view->image();
+    QRect saveRect = image->bounds() | node->exactBounds();
+
+    KisPaintDeviceSP device = node->paintDevice();
+    if (!device) {
+        device = node->projection();
+    }
+
+    m_d->saveDeviceAsImage(device, node->name(),
+                           saveRect,
+                           image->xRes(), image->yRes(),
+                           node->opacity());
+}
+
+void KisNodeManager::slotSplitAlphaIntoMask()
+{
+    KisNodeSP node = activeNode();
+
+    // guaranteed by KisActionManager
+    KIS_ASSERT_RECOVER_RETURN(node->hasEditablePaintDevice());
+
+    KisPaintDeviceSP srcDevice = node->paintDevice();
+    const KoColorSpace *srcCS = srcDevice->colorSpace();
+    const QRect processRect = srcDevice->exactBounds();
+
+    KisPaintDeviceSP selectionDevice =
+        new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
+
+    m_d->commandsAdapter->beginMacro(kundo2_i18n("Split Alpha into a Mask"));
+    KisTransaction transaction(kundo2_noi18n("__split_alpha_channel__"), srcDevice);
+
+    KisSequentialIterator srcIt(srcDevice, processRect);
+    KisSequentialIterator dstIt(selectionDevice, processRect);
+
+    do {
+        quint8 *srcPtr = srcIt.rawData();
+        quint8 *alpha8Ptr = dstIt.rawData();
+
+        *alpha8Ptr = srcCS->opacityU8(srcPtr);
+        srcCS->setOpacity(srcPtr, OPACITY_OPAQUE_U8, 1);
+    } while (srcIt.nextPixel() && dstIt.nextPixel());
+
+    m_d->commandsAdapter->addExtraCommand(transaction.endAndTake());
+
+    createNode("KisTransparencyMask", false, selectionDevice);
+    m_d->commandsAdapter->endMacro();
+}
+
+void KisNodeManager::Private::mergeTransparencyMaskAsAlpha(bool writeToLayers)
+{
+    KisNodeSP node = q->activeNode();
+    KisNodeSP parentNode = node->parent();
+
+    // guaranteed by KisActionManager
+    KIS_ASSERT_RECOVER_RETURN(node->inherits("KisTransparencyMask"));
+
+    if (!parentNode->hasEditablePaintDevice()) {
+        KMessageBox::information(view,
+                                 i18n("Cannot write alpha channel of "
+                                      "the parent layer \"%1\".\n"
+                                      "The operation will be \
cancelled.").arg(parentNode->name()), +                                 i18n("Layer \
%1 is not editable").arg(parentNode->name()), +                                 \
"messagebox_splitAlphaIntoLockedLayer"); +        return;
+    }
+
+    KIS_ASSERT_RECOVER_RETURN(parentNode->hasEditablePaintDevice());
+
+    KisPaintDeviceSP dstDevice =
+        writeToLayers ?
+        parentNode->paintDevice() :
+        new KisPaintDevice(*parentNode->paintDevice());
+
+    const KoColorSpace *dstCS = dstDevice->colorSpace();
+
+    KisPaintDeviceSP selectionDevice = node->paintDevice();
+    KIS_ASSERT_RECOVER_RETURN(selectionDevice->colorSpace()->pixelSize() == 1);
+
+    const QRect processRect =
+        selectionDevice->exactBounds() | dstDevice->exactBounds();
+
+    QScopedPointer<KisTransaction> transaction;
+
+    if (writeToLayers) {
+        commandsAdapter->beginMacro(kundo2_i18n("Write Alpha into a Layer"));
+        transaction.reset(new \
KisTransaction(kundo2_noi18n("__write_alpha_channel__"), dstDevice)); +    }
+
+    KisSequentialIterator srcIt(selectionDevice, processRect);
+    KisSequentialIterator dstIt(dstDevice, processRect);
+
+    do {
+        quint8 *alpha8Ptr = srcIt.rawData();
+        quint8 *dstPtr = dstIt.rawData();
+
+        dstCS->setOpacity(dstPtr, *alpha8Ptr, 1);
+    } while (srcIt.nextPixel() && dstIt.nextPixel());
+
+    if (writeToLayers) {
+        commandsAdapter->addExtraCommand(transaction->endAndTake());
+        commandsAdapter->removeNode(node);
+        commandsAdapter->endMacro();
+    } else {
+        KisImageWSP image = view->image();
+        QRect saveRect = image->bounds() | dstDevice->exactBounds();
+
+        saveDeviceAsImage(dstDevice, parentNode->name(),
+                          saveRect,
+                          image->xRes(), image->yRes(),
+                          OPACITY_OPAQUE_U8);
+    }
+}
+
+
+void KisNodeManager::slotSplitAlphaWrite()
+{
+    m_d->mergeTransparencyMaskAsAlpha(true);
+}
+
+void KisNodeManager::slotSplitAlphaSaveMerged()
+{
+    m_d->mergeTransparencyMaskAsAlpha(false);
+}
+
+
 #include "kis_node_manager.moc"
 
diff --git a/krita/ui/kis_node_manager.h b/krita/ui/kis_node_manager.h
index 0e70419..2f57b15 100644
--- a/krita/ui/kis_node_manager.h
+++ b/krita/ui/kis_node_manager.h
@@ -198,6 +198,10 @@ public slots:
     // merges the active layer with the layer below it.
     void mergeLayerDown();
 
+    void slotSplitAlphaIntoMask();
+    void slotSplitAlphaWrite();
+    void slotSplitAlphaSaveMerged();
+
 public:
 
     
_______________________________________________
Krita mailing list
kimageshop@kde.org
https://mail.kde.org/mailman/listinfo/kimageshop


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic