[calligra] krita: Implement Split Alpha functionality

Dmitry Kazakov dimula73 at gmail.com
Wed Nov 26 10:45:49 UTC 2014


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 at 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&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:
 
     


More information about the kimageshop mailing list