[calligra/krita-chili-kazakov] krita: [FEATURE] Implemented Pass-through mode for Group Layers!

Dmitry Kazakov dimula73 at gmail.com
Wed May 13 11:38:24 UTC 2015


Git commit c958035b73f5ddf36789340ca5dc4ed39808352b by Dmitry Kazakov.
Committed on 13/05/2015 at 10:37.
Pushed by dkazakov into branch 'krita-chili-kazakov'.

[FEATURE] Implemented Pass-through mode for Group Layers!

Now just press an icon in the layer box and you'll get photoshop-like
behavior of the group, that is the children layers will be merged as if
they were not in the group.

Still TODO:

1) Create a proper icon for the layer properties item
2) Implement loading/saving layers into Photoshop's PSD
3) Test, test and test! :)

Ref T202
CCBUG:185448
CCMAIL:kimageshop at kde.org

M  +33   -1    krita/image/kis_group_layer.cc
M  +6    -0    krita/image/kis_group_layer.h
M  +2    -1    krita/image/kis_layer_projection_plane.cpp
M  +72   -4    krita/image/kis_projection_leaf.cpp
M  +1    -0    krita/image/kis_projection_leaf.h
M  +92   -0    krita/image/tests/kis_projection_leaf_test.cpp
M  +1    -0    krita/image/tests/kis_projection_leaf_test.h
M  +6    -4    krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
M  +9    -0    krita/ui/kra/kis_kra_loader.cpp
M  +1    -0    krita/ui/kra/kis_kra_savexml_visitor.cpp
M  +1    -0    krita/ui/kra/kis_kra_tags.h

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

diff --git a/krita/image/kis_group_layer.cc b/krita/image/kis_group_layer.cc
index e9eb77e..296a2b2 100644
--- a/krita/image/kis_group_layer.cc
+++ b/krita/image/kis_group_layer.cc
@@ -44,12 +44,15 @@ public:
     Private()
         : paintDevice(0)
         , x(0)
-        , y(0) {
+        , y(0)
+        , passThroughMode(false)
+    {
     }
 
     KisPaintDeviceSP paintDevice;
     qint32 x;
     qint32 y;
+    bool passThroughMode;
 };
 
 KisGroupLayer::KisGroupLayer(KisImageWSP image, const QString &name, quint8 opacity) :
@@ -266,6 +269,35 @@ KoColor KisGroupLayer::defaultProjectionColor() const
     return color;
 }
 
+bool KisGroupLayer::passThroughMode() const
+{
+    return m_d->passThroughMode;
+}
+
+void KisGroupLayer::setPassThroughMode(bool value)
+{
+    m_d->passThroughMode = value;
+}
+
+KisDocumentSectionModel::PropertyList KisGroupLayer::sectionModelProperties() const
+{
+    KisDocumentSectionModel::PropertyList l = KisLayer::sectionModelProperties();
+    // XXX: get right icons
+    l << KisDocumentSectionModel::Property(i18n("Pass Through"), koIcon("transparency-locked"), koIcon("transparency-unlocked"), passThroughMode());
+    return l;
+}
+
+void KisGroupLayer::setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties)
+{
+    foreach (const KisDocumentSectionModel::Property &property, properties) {
+        if (property.name == i18n("Pass Through")) {
+            setPassThroughMode(property.state.toBool());
+        }
+    }
+
+    KisLayer::setSectionModelProperties(properties);
+}
+
 bool KisGroupLayer::accept(KisNodeVisitor &v)
 {
     return v.visit(this);
diff --git a/krita/image/kis_group_layer.h b/krita/image/kis_group_layer.h
index 73bdcad..e08c198 100644
--- a/krita/image/kis_group_layer.h
+++ b/krita/image/kis_group_layer.h
@@ -47,6 +47,9 @@ public:
 
     QIcon icon() const;
 
+    KisDocumentSectionModel::PropertyList sectionModelProperties() const;
+    void setSectionModelProperties(const KisDocumentSectionModel::PropertyList &properties);
+
     virtual void setImage(KisImageWSP image);
 
     virtual KisLayerSP createMergedLayer(KisLayerSP prevLayer);
@@ -92,6 +95,9 @@ public:
      */
     KoColor defaultProjectionColor() const;
 
+    bool passThroughMode() const;
+    void setPassThroughMode(bool value);
+
 protected:
     KisLayer* onlyMeaningfulChild() const;
     KisPaintDeviceSP tryObligeChild() const;
diff --git a/krita/image/kis_layer_projection_plane.cpp b/krita/image/kis_layer_projection_plane.cpp
index 231a9ee..dafa5b0 100644
--- a/krita/image/kis_layer_projection_plane.cpp
+++ b/krita/image/kis_layer_projection_plane.cpp
@@ -23,6 +23,7 @@
 #include <KoChannelInfo.h>
 #include <KoCompositeOpRegistry.h>
 #include "kis_painter.h"
+#include "kis_projection_leaf.h"
 
 
 struct KisLayerProjectionPlane::Private
@@ -91,7 +92,7 @@ void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
 
     painter->setChannelFlags(channelFlags);
     painter->setCompositeOp(m_d->layer->compositeOp());
-    painter->setOpacity(m_d->layer->opacity());
+    painter->setOpacity(m_d->layer->projectionLeaf()->opacity());
     painter->bitBlt(needRect.topLeft(), device, needRect);
 }
 
diff --git a/krita/image/kis_projection_leaf.cpp b/krita/image/kis_projection_leaf.cpp
index 4ca0075..71602ec 100644
--- a/krita/image/kis_projection_leaf.cpp
+++ b/krita/image/kis_projection_leaf.cpp
@@ -18,6 +18,8 @@
 
 #include "kis_projection_leaf.h"
 
+#include <KoColorSpaceRegistry.h>
+
 #include "kis_layer.h"
 #include "kis_mask.h"
 #include "kis_group_layer.h"
@@ -29,6 +31,19 @@ struct KisProjectionLeaf::Private
     Private(KisNode *_node) : node(_node) {}
 
     KisNode* node;
+
+    static bool checkPassThrough(const KisNode *node) {
+        const KisGroupLayer *group = qobject_cast<const KisGroupLayer*>(node);
+        return group && group->passThroughMode();
+    }
+
+    bool checkParentPassThrough() {
+        return node->parent() && checkPassThrough(node->parent());
+    }
+
+    bool checkThisPassThrough() {
+        return checkPassThrough(node);
+    }
 };
 
 KisProjectionLeaf::KisProjectionLeaf(KisNode *node)
@@ -43,31 +58,68 @@ KisProjectionLeaf::~KisProjectionLeaf()
 KisProjectionLeafSP KisProjectionLeaf::parent() const
 {
     KisNodeSP node = m_d->node->parent();
+
+    if (node && Private::checkPassThrough(node)) {
+        node = node->parent();
+    }
+
     return node ? node->projectionLeaf() : KisProjectionLeafSP();
 }
 
 
 KisProjectionLeafSP KisProjectionLeaf::firstChild() const
 {
-    KisNodeSP node = m_d->node->firstChild();
+    KisNodeSP node;
+
+    if (!m_d->checkThisPassThrough()) {
+        node = m_d->node->firstChild();
+    }
+
     return node ? node->projectionLeaf() : KisProjectionLeafSP();
 }
 
 KisProjectionLeafSP KisProjectionLeaf::lastChild() const
 {
-    KisNodeSP node = m_d->node->lastChild();
+    KisNodeSP node;
+
+    if (!m_d->checkThisPassThrough()) {
+        node = m_d->node->lastChild();
+    }
+
     return node ? node->projectionLeaf() : KisProjectionLeafSP();
 }
 
 KisProjectionLeafSP KisProjectionLeaf::prevSibling() const
 {
-    KisNodeSP node = m_d->node->prevSibling();
+    KisNodeSP node;
+
+    if (m_d->checkThisPassThrough()) {
+        node = m_d->node->lastChild();
+    }
+
+    if (!node) {
+        node = m_d->node->prevSibling();
+    }
+
+    if (!node && m_d->checkParentPassThrough()) {
+        node = m_d->node->parent()->prevSibling();
+    }
+
     return node ? node->projectionLeaf() : KisProjectionLeafSP();
 }
 
 KisProjectionLeafSP KisProjectionLeaf::nextSibling() const
 {
     KisNodeSP node = m_d->node->nextSibling();
+
+    if (node && Private::checkPassThrough(node) && node->firstChild()) {
+        node = node->firstChild();
+    }
+
+    if (!node && m_d->checkParentPassThrough()) {
+        node = m_d->node->parent();
+    }
+
     return node ? node->projectionLeaf() : KisProjectionLeafSP();
 }
 
@@ -131,7 +183,23 @@ bool KisProjectionLeaf::dependsOnLowerNodes() const
 bool KisProjectionLeaf::visible() const
 {
     // check opacity as well!
-    return m_d->node->visible();
+    return m_d->node->visible(true);
+}
+
+quint8 KisProjectionLeaf::opacity() const
+{
+    quint8 resultOpacity = m_d->node->opacity();
+    quint8 parentOpacity = 255;
+
+    if (m_d->checkParentPassThrough()) {
+        quint8 parentOpacity = m_d->node->parent()->projectionLeaf()->opacity();
+
+        if (parentOpacity != OPACITY_OPAQUE_U8) {
+            resultOpacity = (int(resultOpacity) * parentOpacity) / OPACITY_OPAQUE_U8;
+        }
+    }
+
+    return resultOpacity;
 }
 
 bool KisProjectionLeaf::isStillInGraph() const
diff --git a/krita/image/kis_projection_leaf.h b/krita/image/kis_projection_leaf.h
index 07b6fec..1a2c46f 100644
--- a/krita/image/kis_projection_leaf.h
+++ b/krita/image/kis_projection_leaf.h
@@ -56,6 +56,7 @@ public:
     bool canHaveChildLayers() const;
     bool dependsOnLowerNodes() const;
     bool visible() const;
+    quint8 opacity() const;
     bool isStillInGraph() const;
 
 private:
diff --git a/krita/image/tests/kis_projection_leaf_test.cpp b/krita/image/tests/kis_projection_leaf_test.cpp
index 4cb101c..592a703 100644
--- a/krita/image/tests/kis_projection_leaf_test.cpp
+++ b/krita/image/tests/kis_projection_leaf_test.cpp
@@ -21,6 +21,7 @@
 #include <qtest_kde.h>
 #include "qimage_based_test.h"
 #include "kis_projection_leaf.h"
+#include "kis_group_layer.h"
 
 
 
@@ -34,6 +35,18 @@ struct TestImage : TestUtil::QImageBasedTest {
 
     KisSurrogateUndoStore *undoStore;
     KisImageSP image;
+
+    KisNodeSP findBlur1() {
+        return findNode(image->root(), "blur1");
+    }
+
+    KisNodeSP findClone1() {
+        return findNode(image->root(), "clone1");
+    }
+
+    KisNodeSP findPaint1() {
+        return findNode(image->root(), "paint1");
+    }
 };
 
 bool safeCompare(KisProjectionLeafSP leaf, KisNodeSP node)
@@ -62,6 +75,50 @@ void checkNode(KisNodeSP node, const QString &prefix)
     }
 }
 
+void printNodes(KisNodeSP node, const QString &prefix = "")
+{
+    qDebug() << prefix << node->name();
+
+    KisNodeSP prevNode = node->lastChild();
+    while(prevNode) {
+        printNodes(prevNode, QString("\"\"%1").arg(prefix));
+        prevNode = prevNode->prevSibling();
+    }
+}
+
+void printLeafsBackward(KisProjectionLeafSP leaf, const QString &prefix = "")
+{
+    qDebug() << prefix << leaf->node()->name();
+
+    KisProjectionLeafSP prevLeaf = leaf->lastChild();
+    while(prevLeaf) {
+        printLeafsBackward(prevLeaf, QString("\"\"%1").arg(prefix));
+        prevLeaf = prevLeaf->prevSibling();
+    }
+}
+
+void printLeafsForward(KisProjectionLeafSP leaf, const QString &prefix = "")
+{
+    qDebug() << prefix << leaf->node()->name();
+
+    KisProjectionLeafSP prevLeaf = leaf->firstChild();
+    while(prevLeaf) {
+        printLeafsForward(prevLeaf, QString("\"\"%1").arg(prefix));
+        prevLeaf = prevLeaf->nextSibling();
+    }
+}
+
+void printParents(KisProjectionLeafSP leaf, const QString &prefix = "")
+{
+    qDebug() << prefix << leaf->node()->name();
+
+
+    leaf = leaf->parent();
+    if (leaf) {
+        printParents(leaf, QString("\"\"%1").arg(prefix));
+    }
+}
+
 void KisProjectionLeafTest::test()
 {
     TestImage t;
@@ -69,4 +126,39 @@ void KisProjectionLeafTest::test()
     checkNode(t.image->root(), "");
 }
 
+void KisProjectionLeafTest::testPassThrough()
+{
+    TestImage t;
+
+    KisGroupLayerSP group1 = new KisGroupLayer(t.image, "group1", OPACITY_OPAQUE_U8);
+    KisPaintLayerSP paint2 = new KisPaintLayer(t.image, "paint2", OPACITY_OPAQUE_U8);
+    KisPaintLayerSP paint3 = new KisPaintLayer(t.image, "paint3", OPACITY_OPAQUE_U8);
+    KisPaintLayerSP paint4 = new KisPaintLayer(t.image, "paint4", OPACITY_OPAQUE_U8);
+
+    group1->setPassThroughMode(true);
+
+    t.image->addNode(group1, t.image->root(), t.findBlur1());
+    t.image->addNode(paint2, group1);
+    t.image->addNode(paint3, group1);
+    t.image->addNode(paint4, group1);
+
+    //checkNode(t.image->root(), "");
+
+    qDebug() << "== Nodes";
+    printNodes(t.image->root());
+    qDebug() << "== Leafs backward";
+    printLeafsBackward(t.image->root()->projectionLeaf());
+    qDebug() << "== Leafs forward";
+    printLeafsForward(t.image->root()->projectionLeaf());
+
+    qDebug() << "== Parents for paint4";
+    printParents(paint4->projectionLeaf());
+
+    qDebug() << "== Parents for paint3";
+    printParents(paint3->projectionLeaf());
+
+    qDebug() << "== Parents for group1";
+    printParents(group1->projectionLeaf());
+}
+
 QTEST_KDEMAIN(KisProjectionLeafTest, GUI)
diff --git a/krita/image/tests/kis_projection_leaf_test.h b/krita/image/tests/kis_projection_leaf_test.h
index 4e9eab4..2244fd4 100644
--- a/krita/image/tests/kis_projection_leaf_test.h
+++ b/krita/image/tests/kis_projection_leaf_test.h
@@ -26,6 +26,7 @@ class KisProjectionLeafTest : public QObject
     Q_OBJECT
 private slots:
     void test();
+    void testPassThrough();
 };
 
 #endif /* __KIS_PROJECTION_LEAF_TEST_H */
diff --git a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
index 61b94c3..08d7c4d 100644
--- a/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
+++ b/krita/plugins/extensions/dockers/defaultdockers/kis_layer_box.cpp
@@ -435,10 +435,7 @@ void KisLayerBox::updateUI()
         if (activeNode->inherits("KisMask")) {
             m_wdgLayerBox->cmbComposite->setEnabled(false);
             m_wdgLayerBox->doubleOpacity->setEnabled(false);
-        }
-
-        if (activeNode->inherits("KisLayer")) {
-            m_wdgLayerBox->cmbComposite->setEnabled(true);
+        } else if (activeNode->inherits("KisLayer")) {
             m_wdgLayerBox->doubleOpacity->setEnabled(true);
 
             KisLayerSP l = qobject_cast<KisLayer*>(activeNode.data());
@@ -450,6 +447,11 @@ void KisLayerBox::updateUI()
             } else {
                 m_wdgLayerBox->cmbComposite->setEnabled(false);
             }
+
+            const KisGroupLayer *group = qobject_cast<const KisGroupLayer*>(activeNode.data());
+            bool compositeSelectionActive = !(group && group->passThroughMode());
+
+            m_wdgLayerBox->cmbComposite->setEnabled(compositeSelectionActive);
         }
     }
 }
diff --git a/krita/ui/kra/kis_kra_loader.cpp b/krita/ui/kra/kis_kra_loader.cpp
index b0122ca..35bd621 100644
--- a/krita/ui/kra/kis_kra_loader.cpp
+++ b/krita/ui/kra/kis_kra_loader.cpp
@@ -580,6 +580,15 @@ KisNodeSP KisKraLoader::loadNode(const KoXmlElement& element, KisImageWSP image,
         }
     }
 
+    if (node->inherits("KisGroupLayer")) {
+        if (element.hasAttribute(PASS_THROUGH_MODE)) {
+            bool value = element.attribute(PASS_THROUGH_MODE, "0") != "0";
+
+            KisGroupLayer *group = qobject_cast<KisGroupLayer*>(node.data());
+            group->setPassThroughMode(value);
+        }
+    }
+
     if (node->inherits("KisPaintLayer")) {
         KisPaintLayer* layer            = qobject_cast<KisPaintLayer*>(node.data());
         QBitArray      channelLockFlags = stringToFlags(element.attribute(CHANNEL_LOCK_FLAGS, ""), colorSpace->channelCount());
diff --git a/krita/ui/kra/kis_kra_savexml_visitor.cpp b/krita/ui/kra/kis_kra_savexml_visitor.cpp
index 9a942b8..8980bcc 100644
--- a/krita/ui/kra/kis_kra_savexml_visitor.cpp
+++ b/krita/ui/kra/kis_kra_savexml_visitor.cpp
@@ -149,6 +149,7 @@ bool KisSaveXmlVisitor::visit(KisGroupLayer *layer)
     else {
         layerElement = m_doc.createElement(LAYER);
         saveLayer(layerElement, GROUP_LAYER, layer);
+        layerElement.setAttribute(PASS_THROUGH_MODE, layer->passThroughMode());
         m_elem.appendChild(layerElement);
     }
     QDomElement elem = m_doc.createElement(LAYERS);
diff --git a/krita/ui/kra/kis_kra_tags.h b/krita/ui/kra/kis_kra_tags.h
index 7eaad6b..1a9294a 100644
--- a/krita/ui/kra/kis_kra_tags.h
+++ b/krita/ui/kra/kis_kra_tags.h
@@ -104,6 +104,7 @@ const QString Y_SHEAR = "y_shear";
 const QString Y_TRANSLATION = "y_translation";
 const QString ACTIVE = "active";
 const QString LAYER_STYLE_UUID = "layerstyle";
+const QString PASS_THROUGH_MODE = "passthrough";
 }
 
 


More information about the kimageshop mailing list