[calligra/krita-psd-rempt] krita: Added KisAbstractProjectionPlane interface for Layer Styles and other modes

Dmitry Kazakov dimula73 at gmail.com
Tue Mar 10 06:40:27 UTC 2015


Git commit 3e355203b579ca9be80c2f55827f3f2a30797bd1 by Dmitry Kazakov.
Committed on 10/03/2015 at 06:39.
Pushed by dkazakov into branch 'krita-psd-rempt'.

Added KisAbstractProjectionPlane interface for Layer Styles and other modes

KisAbstarctProjectionPlane makes the abstractions of a merge'able entity.
It regenerate() it when the entity is filthy, fetches need/change/accessRect()
when needed and apply() the entity to the global projection finally.

Now KisAsyncMerger and Kis*Walker use this interface only, instead of
old-fasioned direct methods of KisNode/KisLayer.

The general idea is:

KisLayer uses all its internal framework to generate a projection()
device. The user sets a layer style on a layer by setting a special
projectionPlane() interface, which wraps this generated prejection()
data into Layer Styles.

CCMAIL:kimageshop at kde.org

M  +3    -0    krita/image/CMakeLists.txt
A  +60   -0    krita/image/kis_abstract_projection_plane.cpp     [License: GPL (v2+)]
A  +98   -0    krita/image/kis_abstract_projection_plane.h     [License: GPL (v2+)]
M  +5    -2    krita/image/kis_adjustment_layer.h
M  +7    -46   krita/image/kis_async_merger.cpp
M  +18   -15   krita/image/kis_base_rects_walker.h
M  +8    -6    krita/image/kis_clone_layer.h
M  +3    -1    krita/image/kis_image.cc
M  +10   -0    krita/image/kis_layer.cc
M  +28   -9    krita/image/kis_layer.h
A  +108  -0    krita/image/kis_layer_projection_plane.cpp     [License: GPL (v2+)]
A  +49   -0    krita/image/kis_layer_projection_plane.h     [License: GPL (v2+)]
M  +13   -1    krita/image/kis_mask.cc
M  +5    -0    krita/image/kis_mask.h
A  +76   -0    krita/image/kis_mask_projection_plane.cpp     [License: GPL (v2+)]
A  +52   -0    krita/image/kis_mask_projection_plane.h     [License: GPL (v2+)]
M  +11   -0    krita/image/kis_node.cpp
M  +26   -0    krita/image/kis_node.h
M  +1    -1    krita/image/kis_refresh_subtree_walker.h
M  +9    -6    krita/image/kis_selection_based_layer.h
M  +5    -3    krita/image/tests/kis_paint_layer_test.cpp
M  +2    -1    krita/ui/widgets/kis_scratch_pad.cpp

http://commits.kde.org/calligra/3e355203b579ca9be80c2f55827f3f2a30797bd1

diff --git a/krita/image/CMakeLists.txt b/krita/image/CMakeLists.txt
index 3b1d218..aaa4e1c 100644
--- a/krita/image/CMakeLists.txt
+++ b/krita/image/CMakeLists.txt
@@ -176,6 +176,9 @@ set(kritaimage_LIB_SRCS
    kis_acyclic_signal_connector.cpp
    kis_layer.cc
    kis_indirect_painting_support.cpp
+   kis_abstract_projection_plane.cpp
+   kis_layer_projection_plane.cpp
+   kis_mask_projection_plane.cpp
    kis_mask.cc
    kis_base_mask_generator.cpp
    kis_rect_mask_generator.cpp
diff --git a/krita/image/kis_abstract_projection_plane.cpp b/krita/image/kis_abstract_projection_plane.cpp
new file mode 100644
index 0000000..4bda994
--- /dev/null
+++ b/krita/image/kis_abstract_projection_plane.cpp
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_abstract_projection_plane.h"
+
+
+KisAbstractProjectionPlane::KisAbstractProjectionPlane()
+{
+}
+
+KisAbstractProjectionPlane::~KisAbstractProjectionPlane()
+{
+}
+
+QRect KisDumbProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
+{
+    Q_UNUSED(filthyNode);
+    return rect;
+}
+
+void KisDumbProjectionPlane::apply(KisPainter *painter, const QRect &rect)
+{
+    Q_UNUSED(painter);
+    Q_UNUSED(rect);
+}
+
+
+QRect KisDumbProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    Q_UNUSED(pos);
+    return rect;
+}
+
+QRect KisDumbProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    Q_UNUSED(pos);
+    return rect;
+}
+
+QRect KisDumbProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    Q_UNUSED(pos);
+    return rect;
+}
+
diff --git a/krita/image/kis_abstract_projection_plane.h b/krita/image/kis_abstract_projection_plane.h
new file mode 100644
index 0000000..150bfd1
--- /dev/null
+++ b/krita/image/kis_abstract_projection_plane.h
@@ -0,0 +1,98 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_ABSTRACT_PROJECTION_PLANE_H
+#define __KIS_ABSTRACT_PROJECTION_PLANE_H
+
+#include "kis_types.h"
+#include "kis_layer.h"
+
+class QRect;
+class KisPainter;
+
+
+/**
+ * An interface of the node to the compositioning
+ * system. Compositioning system KisAsyncMerger knows nothing about
+ * the internals of the layer, it just knows that the layer can:
+ *
+ * 1) recalculate() its internal representation if it is filthy
+ *
+ * 2) apply() itself onto a global projection using, writing all its data
+ *            to the projection.
+ *
+ * 3) report parameters of these operations using needRect(),
+ *    changeRect() and accessRect() methods, which mean the same as
+ *    the corresponding methods of KisNode, but include more
+ *    transformations for the layer, e.g. Layer Styles and
+ *    Pass-through mode.
+ */
+class KisAbstractProjectionPlane
+{
+public:
+    KisAbstractProjectionPlane();
+    virtual ~KisAbstractProjectionPlane();
+
+    /**
+     * Is called by the async merger when the node is filthy and
+     * should recalculate its internal representation. For usual
+     * layers it means just calling updateProjection().
+     */
+    virtual QRect recalculate(const QRect& rect, KisNodeSP filthyNode) = 0;
+
+    /**
+     * Writes the data of the projection plane onto a global
+     * projection using \p painter object.
+     */
+    virtual void apply(KisPainter *painter, const QRect &rect) = 0;
+
+    /**
+     * Works like KisNode::needRect(), but includes more
+     * transformations of the layer
+     */
+    virtual QRect needRect(const QRect &rect, KisLayer::PositionToFilthy pos = KisLayer::N_FILTHY) const = 0;
+
+    /**
+     * Works like KisNode::changeRect(), but includes more
+     * transformations of the layer
+     */
+    virtual QRect changeRect(const QRect &rect, KisLayer::PositionToFilthy pos = KisLayer::N_FILTHY) const = 0;
+
+    /**
+     * Works like KisNode::needRect(), but includes more
+     * transformations of the layer
+     */
+    virtual QRect accessRect(const QRect &rect, KisLayer::PositionToFilthy pos = KisLayer::N_FILTHY) const = 0;
+};
+
+/**
+ * A simple implementation of KisAbstractProjectionPlane interface
+ * that does nothing.
+ */
+class KisDumbProjectionPlane : public KisAbstractProjectionPlane
+{
+public:
+    QRect recalculate(const QRect& rect, KisNodeSP filthyNode);
+    void apply(KisPainter *painter, const QRect &rect);
+
+    QRect needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+    QRect changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+    QRect accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+};
+
+#endif /* __KIS_ABSTRACT_PROJECTION_PLANE_H */
diff --git a/krita/image/kis_adjustment_layer.h b/krita/image/kis_adjustment_layer.h
index 4769962..83584d1 100644
--- a/krita/image/kis_adjustment_layer.h
+++ b/krita/image/kis_adjustment_layer.h
@@ -100,11 +100,14 @@ public:
      */
     void setFilter(KisFilterConfiguration *filterConfig);
 
+    void setChannelFlags(const QBitArray & channelFlags);
+
+protected:
+    // override from KisLayer
     QRect incomingChangeRect(const QRect &rect) const;
+    // override from KisNode
     QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
 
-    void setChannelFlags(const QBitArray & channelFlags);
-    
 public Q_SLOTS:
     /**
      * gets this AdjustmentLayer. Overrides function in
diff --git a/krita/image/kis_async_merger.cpp b/krita/image/kis_async_merger.cpp
index 38a6bda..f8e496d 100644
--- a/krita/image/kis_async_merger.cpp
+++ b/krita/image/kis_async_merger.cpp
@@ -48,6 +48,8 @@
 #include "kis_merge_walker.h"
 #include "kis_refresh_subtree_walker.h"
 
+#include "kis_abstract_projection_plane.h"
+
 
 //#define DEBUG_MERGER
 
@@ -223,7 +225,7 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
                                                      m_currentProjection,
                                                      walker.cropRect());
             currentNode->accept(originalVisitor);
-            currentNode->updateProjection(applyRect, currentNode);
+            currentNode->projectionPlane()->recalculate(applyRect, currentNode);
 
             continue;
         }
@@ -239,18 +241,18 @@ void KisAsyncMerger::startMerge(KisBaseRectsWalker &walker, bool notifyClones) {
         if(item.m_position & KisMergeWalker::N_FILTHY) {
             DEBUG_NODE_ACTION("Updating", "N_FILTHY", currentNode, applyRect);
             currentNode->accept(originalVisitor);
-            currentNode->updateProjection(applyRect, walker.startNode());
+            currentNode->projectionPlane()->recalculate(applyRect, walker.startNode());
         }
         else if(item.m_position & KisMergeWalker::N_ABOVE_FILTHY) {
             DEBUG_NODE_ACTION("Updating", "N_ABOVE_FILTHY", currentNode, applyRect);
             if(dependOnLowerNodes(currentNode)) {
                 currentNode->accept(originalVisitor);
-                currentNode->updateProjection(applyRect, currentNode);
+                currentNode->projectionPlane()->recalculate(applyRect, currentNode);
             }
         }
         else if(item.m_position & KisMergeWalker::N_FILTHY_PROJECTION) {
             DEBUG_NODE_ACTION("Updating", "N_FILTHY_PROJECTION", currentNode, applyRect);
-            currentNode->updateProjection(applyRect, walker.startNode());
+            currentNode->projectionPlane()->recalculate(applyRect, walker.startNode());
         }
         else /*if(item.m_position & KisMergeWalker::N_BELOW_FILTHY)*/ {
             DEBUG_NODE_ACTION("Updating", "N_BELOW_FILTHY", currentNode, applyRect);
@@ -338,49 +340,8 @@ bool KisAsyncMerger::compositeWithProjection(KisLayerSP layer, const QRect &rect
     if (!m_currentProjection) return true;
     if (!layer->visible()) return true;
 
-    KisPaintDeviceSP device = layer->projection();
-    if (!device) return true;
-
-    QRect needRect = rect & device->extent();
-    if(needRect.isEmpty()) return true;
-
-    QBitArray channelFlags = layer->channelFlags();
-
-
-    // if the color spaces don't match we will have a problem with the channel flags
-    // because the channel flags from the source layer doesn't match with the colorspace of the projection device
-    // this leads to the situation that the wrong channels will be enabled/disabled
-    if(!channelFlags.isEmpty() && m_currentProjection->colorSpace() != device->colorSpace()) {
-        const KoColorSpace* src = device->colorSpace();
-        const KoColorSpace* dst = m_currentProjection->colorSpace();
-
-        bool alphaFlagIsSet        = (src->channelFlags(false,true) & channelFlags) == src->channelFlags(false,true);
-        bool allColorFlagsAreSet   = (src->channelFlags(true,false) & channelFlags) == src->channelFlags(true,false);
-        bool allColorFlagsAreUnset = (src->channelFlags(true,false) & channelFlags).count(true) == 0;
-
-        if(allColorFlagsAreSet) {
-            channelFlags = dst->channelFlags(true, alphaFlagIsSet);
-        }
-        else if(allColorFlagsAreUnset) {
-            channelFlags = dst->channelFlags(false, alphaFlagIsSet);
-        }
-        else {
-            //TODO: convert the cannel flags properly
-            //      for now just the alpha channel bit is copied and the other channels are left alone
-            for(quint32 i=0; i<dst->channelCount(); ++i) {
-                if(dst->channels()[i]->channelType() == KoChannelInfo::ALPHA) {
-                    channelFlags.setBit(i, alphaFlagIsSet);
-                    break;
-                }
-            }
-        }
-    }
     KisPainter gc(m_currentProjection);
-    gc.setChannelFlags(channelFlags);
-
-    gc.setCompositeOp(layer->compositeOp());
-    gc.setOpacity(layer->opacity());
-    gc.bitBlt(needRect.topLeft(), device, needRect);
+    layer->projectionPlane()->apply(&gc, rect);
 
     DEBUG_NODE_ACTION("Compositing projection", "", layer, needRect);
     return true;
diff --git a/krita/image/kis_base_rects_walker.h b/krita/image/kis_base_rects_walker.h
index f798f95..afb7984 100644
--- a/krita/image/kis_base_rects_walker.h
+++ b/krita/image/kis_base_rects_walker.h
@@ -24,6 +24,9 @@
 #include "kis_layer.h"
 #include "kis_mask.h"
 
+#include "kis_abstract_projection_plane.h"
+
+
 class KisBaseRectsWalker;
 typedef KisSharedPtr<KisBaseRectsWalker> KisBaseRectsWalkerSP;
 
@@ -276,8 +279,8 @@ protected:
         // We do not work with masks here. It is KisLayer's job.
         if(!isLayer(node)) return;
 
-        QRect currentChangeRect = node->changeRect(m_resultChangeRect,
-                                                   convertPositionToFilthy(position));
+        QRect currentChangeRect = node->projectionPlane()->changeRect(m_resultChangeRect,
+                                                                      convertPositionToFilthy(position));
         currentChangeRect = cropThisRect(currentChangeRect);
 
         if(!m_changeRectVaries)
@@ -285,8 +288,8 @@ protected:
 
         m_resultChangeRect = currentChangeRect;
 
-        m_resultUncroppedChangeRect = node->changeRect(m_resultUncroppedChangeRect,
-                                                       convertPositionToFilthy(position));
+        m_resultUncroppedChangeRect = node->projectionPlane()->changeRect(m_resultUncroppedChangeRect,
+                                                                          convertPositionToFilthy(position));
         registerCloneNotification(node, position);
     }
 
@@ -326,11 +329,11 @@ protected:
                 pushJob(node, position, m_lastNeedRect);
             //else /* Why push empty rect? */;
 
-            m_resultAccessRect |= node->accessRect(m_lastNeedRect,
-                                                   convertPositionToFilthy(position));
+            m_resultAccessRect |= node->projectionPlane()->accessRect(m_lastNeedRect,
+                                                                      convertPositionToFilthy(position));
 
-            m_lastNeedRect = node->needRect(m_lastNeedRect,
-                                            convertPositionToFilthy(position));
+            m_lastNeedRect = node->projectionPlane()->needRect(m_lastNeedRect,
+                                                               convertPositionToFilthy(position));
             m_lastNeedRect = cropThisRect(m_lastNeedRect);
             m_childNeedRect = m_lastNeedRect;
         }
@@ -338,11 +341,11 @@ protected:
             if(!m_lastNeedRect.isEmpty()) {
                 pushJob(node, position, m_lastNeedRect);
 
-                m_resultAccessRect |= node->accessRect(m_lastNeedRect,
-                                                       convertPositionToFilthy(position));
+                m_resultAccessRect |= node->projectionPlane()->accessRect(m_lastNeedRect,
+                                                                          convertPositionToFilthy(position));
 
-                m_lastNeedRect = node->needRect(m_lastNeedRect,
-                                                convertPositionToFilthy(position));
+                m_lastNeedRect = node->projectionPlane()->needRect(m_lastNeedRect,
+                                                                   convertPositionToFilthy(position));
                 m_lastNeedRect = cropThisRect(m_lastNeedRect);
             }
         }
@@ -370,7 +373,7 @@ protected:
                      (!isMask(currentNode) || !currentNode->visible()));
 
             if(currentNode) {
-                QRect changeRect = currentNode->changeRect(m_resultChangeRect);
+                QRect changeRect = currentNode->projectionPlane()->changeRect(m_resultChangeRect);
                 m_changeRectVaries |= changeRect != m_resultChangeRect;
                 m_resultChangeRect = changeRect;
                 m_resultUncroppedChangeRect = changeRect;
@@ -388,11 +391,11 @@ protected:
         qint32 x, y, w, h;
         QRect tempRect;
 
-        tempRect = node->changeRect(requestedRect);
+        tempRect = node->projectionPlane()->changeRect(requestedRect);
         tempRect.getRect(&x, &y, &w, &h);
         checksum += -x - y + w + h;
 
-        tempRect = node->needRect(requestedRect);
+        tempRect = node->projectionPlane()->needRect(requestedRect);
         tempRect.getRect(&x, &y, &w, &h);
         checksum += -x - y + w + h;
 
diff --git a/krita/image/kis_clone_layer.h b/krita/image/kis_clone_layer.h
index 591ebdb..0e6f035 100644
--- a/krita/image/kis_clone_layer.h
+++ b/krita/image/kis_clone_layer.h
@@ -72,10 +72,6 @@ public:
     KisPaintDeviceSP paintDevice() const;
     bool needProjection() const;
 
-    void copyOriginalToProjection(const KisPaintDeviceSP original,
-                                  KisPaintDeviceSP projection,
-                                  const QRect& rect) const;
-
     QIcon icon() const;
     KisDocumentSectionModel::PropertyList sectionModelProperties() const;
 
@@ -91,8 +87,6 @@ public:
     /// Returns the exact bounds of where the actual data resides in this layer
     QRect exactBounds() const;
 
-    QRect accessRect(const QRect &rect, PositionToFilthy pos) const;
-
     bool accept(KisNodeVisitor &);
     void accept(KisProcessingVisitor &visitor, KisUndoAdapter *undoAdapter);
 
@@ -119,6 +113,14 @@ public:
     QRect needRectOnSourceForMasks(const QRect &rc) const;
 
 protected:
+    // override from KisNode
+    QRect accessRect(const QRect &rect, PositionToFilthy pos) const;
+
+    // override from KisLayer
+    void copyOriginalToProjection(const KisPaintDeviceSP original,
+                                  KisPaintDeviceSP projection,
+                                  const QRect& rect) const;
+
     void notifyParentVisibilityChanged(bool value);
     QRect outgoingChangeRect(const QRect &rect) const;
 private:
diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index 080cd8b..00338c9 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -79,6 +79,8 @@
 #include "kis_layer_composition.h"
 #include "kis_wrapped_rect.h"
 
+#include "kis_layer_projection_plane.h"
+
 
 // #define SANITY_CHECKS
 
@@ -884,7 +886,7 @@ QRect KisImage::realNodeExtent(KisNodeSP rootNode, QRect currentRect)
 
     // TODO: it would be better to count up changeRect inside
     // node's extent() method
-    currentRect |= rootNode->changeRect(rootNode->extent());
+    currentRect |= rootNode->projectionPlane()->changeRect(rootNode->extent());
 
     return currentRect;
 }
diff --git a/krita/image/kis_layer.cc b/krita/image/kis_layer.cc
index 55e5fee..966b00b 100644
--- a/krita/image/kis_layer.cc
+++ b/krita/image/kis_layer.cc
@@ -46,6 +46,8 @@
 #include "kis_clone_layer.h"
 
 #include "kis_psd_layer_style.h"
+#include "kis_layer_projection_plane.h"
+
 
 class KisSafeProjection {
 public:
@@ -114,6 +116,8 @@ struct KisLayer::Private
     KisSafeProjection safeProjection;
     KisCloneLayersList clonesList;
     KisPSDLayerStyle* layerStyle;
+
+    QScopedPointer<KisLayerProjectionPlane> projectionPlane;
 };
 
 
@@ -126,6 +130,7 @@ KisLayer::KisLayer(KisImageWSP image, const QString &name, quint8 opacity)
     m_d->image = image;
     m_d->metaDataStore = new KisMetaData::Store();
     m_d->layerStyle = 0;
+    m_d->projectionPlane.reset(new KisLayerProjectionPlane(this));
 }
 
 KisLayer::KisLayer(const KisLayer& rhs)
@@ -142,6 +147,7 @@ KisLayer::KisLayer(const KisLayer& rhs)
             m_d->layerStyle = 0;
         }
         setName(rhs.name());
+        m_d->projectionPlane.reset(new KisLayerProjectionPlane(this));
     }
 }
 
@@ -652,6 +658,10 @@ void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
     gc.bitBlt(rect.topLeft(), original, rect);
 }
 
+KisAbstractProjectionPlane* KisLayer::projectionPlane() const
+{
+    return m_d->projectionPlane.data();
+}
 
 KisPaintDeviceSP KisLayer::projection() const
 {
diff --git a/krita/image/kis_layer.h b/krita/image/kis_layer.h
index a51e2c8..b3b0a56 100644
--- a/krita/image/kis_layer.h
+++ b/krita/image/kis_layer.h
@@ -39,6 +39,8 @@ class QStack;
 class QBitArray;
 class KisCloneLayer;
 class KisPSDLayerStyle;
+class KisAbstractProjectionPlane;
+
 
 namespace KisMetaData
 {
@@ -82,20 +84,15 @@ public:
     void setLayerStyle(KisPSDLayerStyle *layerStyle);
 
     /**
-     * Ask the layer to assemble its data & apply all the effect masks
-     * to it.
+     * \see a comment in KisNode::projectionPlane()
      */
-    QRect updateProjection(const QRect& rect, KisNodeSP filthyNode);
+    virtual KisAbstractProjectionPlane* projectionPlane() const;
 
     QRect partialChangeRect(KisNodeSP lastNode, const QRect& rect);
     void buildProjectionUpToNode(KisPaintDeviceSP projection, KisNodeSP lastNode, const QRect& rect);
 
     virtual bool needProjection() const;
 
-    virtual void copyOriginalToProjection(const KisPaintDeviceSP original,
-                                          KisPaintDeviceSP projection,
-                                          const QRect& rect) const;
-
     /**
      * Return the fully rendered representation of this layer: its
      * data and its effect masks
@@ -250,8 +247,6 @@ public:
      */
     QList<KisEffectMaskSP> effectMasks(KisNodeSP lastNode = 0) const;
 
-    QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
-
     /**
      * Get the group layer that contains this layer.
      */
@@ -263,8 +258,28 @@ public:
     KisMetaData::Store* metaData();
 
 protected:
+    // override from KisNode
+    QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
+
+protected:
 
     /**
+     * Ask the layer to assemble its data & apply all the effect masks
+     * to it.
+     */
+    QRect updateProjection(const QRect& rect, KisNodeSP filthyNode);
+
+    /**
+     * Layers can override this method to get some special behavior
+     * when copying data from \p original to \p projection, e.g. blend
+     * in indirect painting device.  If you need to modify data
+     * outside \p rect, please also override outgoingChangeRect()
+     * method.
+     */
+    virtual void copyOriginalToProjection(const KisPaintDeviceSP original,
+                                          KisPaintDeviceSP projection,
+                                          const QRect& rect) const;
+    /**
      * For KisLayer classes change rect transformation consists of two
      * parts: incoming and outgoing.
      *
@@ -343,6 +358,10 @@ protected:
                      const KisPaintDeviceSP destination,
                      const QRect &requestedRect,
                      KisNodeSP filthyNode, KisNodeSP lastNode) const;
+private:
+    friend class KisLayerProjectionPlane;
+    friend class KisTransformMask;
+    friend class KisLayerTest;
 
 private:
     struct Private;
diff --git a/krita/image/kis_layer_projection_plane.cpp b/krita/image/kis_layer_projection_plane.cpp
new file mode 100644
index 0000000..e825efb
--- /dev/null
+++ b/krita/image/kis_layer_projection_plane.cpp
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_layer_projection_plane.h"
+
+#include <QBitArray>
+#include <KoColorSpace.h>
+#include <KoChannelInfo.h>
+#include "kis_painter.h"
+
+
+struct KisLayerProjectionPlane::Private
+{
+    KisLayer *layer;
+};
+
+
+KisLayerProjectionPlane::KisLayerProjectionPlane(KisLayer *layer)
+    : m_d(new Private)
+{
+    m_d->layer = layer;
+}
+
+KisLayerProjectionPlane::~KisLayerProjectionPlane()
+{
+}
+
+QRect KisLayerProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
+{
+    return m_d->layer->updateProjection(rect, filthyNode);
+}
+
+void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
+{
+    KisPaintDeviceSP device = m_d->layer->projection();
+    if (!device) return;
+
+    QRect needRect = rect & device->extent();
+    if(needRect.isEmpty()) return;
+
+    QBitArray channelFlags = m_d->layer->channelFlags();
+
+
+    // if the color spaces don't match we will have a problem with the channel flags
+    // because the channel flags from the source layer doesn't match with the colorspace of the projection device
+    // this leads to the situation that the wrong channels will be enabled/disabled
+    const KoColorSpace* srcCS = device->colorSpace();
+    const KoColorSpace* dstCS = painter->device()->colorSpace();
+
+    if (!channelFlags.isEmpty() && srcCS != dstCS) {
+        bool alphaFlagIsSet        = (srcCS->channelFlags(false,true) & channelFlags) == srcCS->channelFlags(false,true);
+        bool allColorFlagsAreSet   = (srcCS->channelFlags(true,false) & channelFlags) == srcCS->channelFlags(true,false);
+        bool allColorFlagsAreUnset = (srcCS->channelFlags(true,false) & channelFlags).count(true) == 0;
+
+        if(allColorFlagsAreSet) {
+            channelFlags = dstCS->channelFlags(true, alphaFlagIsSet);
+        } else if(allColorFlagsAreUnset) {
+            channelFlags = dstCS->channelFlags(false, alphaFlagIsSet);
+        } else {
+            //TODO: convert the cannel flags properly
+            //      for now just the alpha channel bit is copied and the other channels are left alone
+            for (quint32 i=0; i < dstCS->channelCount(); ++i) {
+                if (dstCS->channels()[i]->channelType() == KoChannelInfo::ALPHA) {
+                    channelFlags.setBit(i, alphaFlagIsSet);
+                    break;
+                }
+            }
+        }
+    }
+
+    painter->setChannelFlags(channelFlags);
+
+    painter->setCompositeOp(m_d->layer->compositeOp());
+    painter->setOpacity(m_d->layer->opacity());
+    painter->bitBlt(needRect.topLeft(), device, needRect);
+}
+
+
+QRect KisLayerProjectionPlane::needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    return m_d->layer->needRect(rect, pos);
+}
+
+QRect KisLayerProjectionPlane::changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    return m_d->layer->changeRect(rect, pos);
+}
+
+QRect KisLayerProjectionPlane::accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const
+{
+    return m_d->layer->accessRect(rect, pos);
+}
+
diff --git a/krita/image/kis_layer_projection_plane.h b/krita/image/kis_layer_projection_plane.h
new file mode 100644
index 0000000..10605f9
--- /dev/null
+++ b/krita/image/kis_layer_projection_plane.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_LAYER_PROJECTION_PLANE_H
+#define __KIS_LAYER_PROJECTION_PLANE_H
+
+#include "kis_abstract_projection_plane.h"
+
+#include <QScopedPointer>
+
+
+/**
+ * An implementation of the KisAbstractProjectionPlane interface for a
+ * layer object
+ */
+class KisLayerProjectionPlane : public KisAbstractProjectionPlane
+{
+public:
+    KisLayerProjectionPlane(KisLayer *layer);
+    ~KisLayerProjectionPlane();
+
+    QRect recalculate(const QRect& rect, KisNodeSP filthyNode);
+    void apply(KisPainter *painter, const QRect &rect);
+
+    QRect needRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+    QRect changeRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+    QRect accessRect(const QRect &rect, KisLayer::PositionToFilthy pos) const;
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_LAYER_PROJECTION_PLANE_H */
diff --git a/krita/image/kis_mask.cc b/krita/image/kis_mask.cc
index 0a14462..ae3f44a 100644
--- a/krita/image/kis_mask.cc
+++ b/krita/image/kis_mask.cc
@@ -39,10 +39,15 @@
 #include "kis_layer.h"
 
 #include "kis_cached_paint_device.h"
+#include "kis_mask_projection_plane.h"
 
 
 struct KisMask::Private {
-    Private(KisMask *_q) : q(_q) {}
+    Private(KisMask *_q)
+        : q(_q),
+          projectionPlane(new KisMaskProjectionPlane(q))
+    {
+    }
 
     mutable KisSelectionSP selection;
     KisCachedPaintDevice paintDeviceCache;
@@ -58,6 +63,8 @@ struct KisMask::Private {
      */
     QScopedPointer<QPoint> deferredSelectionOffset;
 
+    QScopedPointer<KisMaskProjectionPlane> projectionPlane;
+
     void initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP parentLayer, KisPaintDeviceSP copyFromDevice);
 };
 
@@ -190,6 +197,11 @@ KisPaintDeviceSP KisMask::projection() const
     return paintDevice();
 }
 
+KisAbstractProjectionPlane* KisMask::projectionPlane() const
+{
+    return m_d->projectionPlane.data();
+}
+
 void KisMask::setSelection(KisSelectionSP selection)
 {
     m_d->selection = selection;
diff --git a/krita/image/kis_mask.h b/krita/image/kis_mask.h
index a426fd4..1c9030e 100644
--- a/krita/image/kis_mask.h
+++ b/krita/image/kis_mask.h
@@ -131,6 +131,8 @@ public:
      */
     KisPaintDeviceSP projection() const;
 
+    KisAbstractProjectionPlane* projectionPlane() const;
+
     /**
      * Change the selection to the specified selection object. The
      * selection is deep copied.
@@ -186,6 +188,9 @@ protected:
                                PositionToFilthy maskPos) const;
 
 private:
+    friend class KisMaskProjectionPlane;
+
+private:
 
     struct Private;
 
diff --git a/krita/image/kis_mask_projection_plane.cpp b/krita/image/kis_mask_projection_plane.cpp
new file mode 100644
index 0000000..a98c597
--- /dev/null
+++ b/krita/image/kis_mask_projection_plane.cpp
@@ -0,0 +1,76 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_mask_projection_plane.h"
+
+#include <QBitArray>
+#include <KoColorSpace.h>
+#include <KoChannelInfo.h>
+#include "kis_painter.h"
+#include "kis_mask.h"
+
+
+struct KisMaskProjectionPlane::Private
+{
+    KisMask *mask;
+};
+
+
+KisMaskProjectionPlane::KisMaskProjectionPlane(KisMask *mask)
+    : m_d(new Private)
+{
+    m_d->mask = mask;
+}
+
+KisMaskProjectionPlane::~KisMaskProjectionPlane()
+{
+}
+
+QRect KisMaskProjectionPlane::recalculate(const QRect& rect, KisNodeSP filthyNode)
+{
+    Q_UNUSED(filthyNode);
+
+    KIS_ASSERT_RECOVER_NOOP(0 && "KisMaskProjectionPlane::recalculate() is not defined!");
+
+    return rect;
+}
+
+void KisMaskProjectionPlane::apply(KisPainter *painter, const QRect &rect)
+{
+    Q_UNUSED(painter);
+    Q_UNUSED(rect);
+
+    KIS_ASSERT_RECOVER_NOOP(0 && "KisMaskProjectionPlane::apply() is not defined!");
+}
+
+
+QRect KisMaskProjectionPlane::needRect(const QRect &rect, KisNode::PositionToFilthy pos) const
+{
+    return m_d->mask->needRect(rect, pos);
+}
+
+QRect KisMaskProjectionPlane::changeRect(const QRect &rect, KisNode::PositionToFilthy pos) const
+{
+    return m_d->mask->changeRect(rect, pos);
+}
+
+QRect KisMaskProjectionPlane::accessRect(const QRect &rect, KisNode::PositionToFilthy pos) const
+{
+    return m_d->mask->accessRect(rect, pos);
+}
+
diff --git a/krita/image/kis_mask_projection_plane.h b/krita/image/kis_mask_projection_plane.h
new file mode 100644
index 0000000..c6af70a
--- /dev/null
+++ b/krita/image/kis_mask_projection_plane.h
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c) 2015 Dmitry Kazakov <dimula73 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIS_MASK_PROJECTION_PLANE_H
+#define __KIS_MASK_PROJECTION_PLANE_H
+
+#include "kis_abstract_projection_plane.h"
+
+#include <QScopedPointer>
+
+/**
+ * An implementation of the KisAbstractProjectionPlane interface for a
+ * layer object.
+ *
+ * Please note that recalculate() and apply() methods are not defined
+ * for masks, because the KisLayer code still uses tranditional
+ * methods of KisMask directly.
+ */
+class KisMaskProjectionPlane : public KisAbstractProjectionPlane
+{
+public:
+    KisMaskProjectionPlane(KisMask *mask);
+    ~KisMaskProjectionPlane();
+
+    QRect recalculate(const QRect& rect, KisNodeSP filthyNode);
+    void apply(KisPainter *painter, const QRect &rect);
+
+    QRect needRect(const QRect &rect, KisNode::PositionToFilthy pos) const;
+    QRect changeRect(const QRect &rect, KisNode::PositionToFilthy pos) const;
+    QRect accessRect(const QRect &rect, KisNode::PositionToFilthy pos) const;
+
+private:
+    struct Private;
+    const QScopedPointer<Private> m_d;
+};
+
+#endif /* __KIS_MASK_PROJECTION_PLANE_H */
diff --git a/krita/image/kis_node.cpp b/krita/image/kis_node.cpp
index 4f0d9e6..fa171f0 100644
--- a/krita/image/kis_node.cpp
+++ b/krita/image/kis_node.cpp
@@ -38,6 +38,8 @@
 #include "kis_safe_read_list.h"
 typedef KisSafeReadList<KisNodeSP> KisSafeReadNodeList;
 
+#include "kis_abstract_projection_plane.h"
+
 
 /**
  *The link between KisProjection ans KisImageUpdater
@@ -215,6 +217,15 @@ QRect KisNode::accessRect(const QRect &rect, PositionToFilthy pos) const
     return rect;
 }
 
+KisAbstractProjectionPlane* KisNode::projectionPlane() const
+{
+    KIS_ASSERT_RECOVER_NOOP(0 && "KisNode::projectionPlane() is not defined!");
+    static QScopedPointer<KisAbstractProjectionPlane> plane(
+        new KisDumbProjectionPlane());
+
+    return plane.data();
+}
+
 bool KisNode::accept(KisNodeVisitor &v)
 {
     return v.visit(this);
diff --git a/krita/image/kis_node.h b/krita/image/kis_node.h
index 0845fda..fc5dfd8 100644
--- a/krita/image/kis_node.h
+++ b/krita/image/kis_node.h
@@ -35,6 +35,7 @@ class KoProperties;
 class KisNodeVisitor;
 class KisNodeGraphListener;
 class KisNodeProgressProxy;
+class KisAbstractProjectionPlane;
 
 /**
  * A KisNode is a KisBaseNode that knows about its direct peers, parent
@@ -125,6 +126,25 @@ public:
     virtual void setDirty(const QRegion &region);
 
     /**
+     * \return a pointer to a KisAbstractProjectionPlane interface of
+     *         the node. This interface is used by the image merging
+     *         framework to get information and to blending for the
+     *         layer.
+     *
+     * Please note the difference between need/change/accessRect and
+     * the projectionPlane() interface. The former one gives
+     * information about internal composition of the layer, and the
+     * latter one about the total composition, including layer styles,
+     * pass-through blending and etc.
+     */
+    virtual KisAbstractProjectionPlane* projectionPlane() const;
+
+protected:
+
+    /**
+     * \return internal changeRect() of the node. Do not mix with \see
+     *         projectionPlane()
+     *
      * Some filters will cause a change of pixels those are outside
      * a requested rect. E.g. we change a rect of 2x2, then we want to
      * apply a convolution filter with kernel 4x4 (changeRect is
@@ -135,6 +155,9 @@ public:
     virtual QRect changeRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
 
     /**
+     * \return internal needRect() of the node. Do not mix with \see
+     *         projectionPlane()
+     *
      * Some filters need pixels outside the current processing rect to
      * compute the new value (for instance, convolution filters)
      * See \ref changeRect
@@ -144,6 +167,9 @@ public:
 
 
     /**
+     * \return internal accessRect() of the node. Do not mix with \see
+     *         projectionPlane()
+     *
      * Shows the area of image, that may be accessed during accessing
      * the node.
      *
diff --git a/krita/image/kis_refresh_subtree_walker.h b/krita/image/kis_refresh_subtree_walker.h
index ae4abde..7826b56 100644
--- a/krita/image/kis_refresh_subtree_walker.h
+++ b/krita/image/kis_refresh_subtree_walker.h
@@ -85,7 +85,7 @@ protected:
             currentNode = nextNode;
         }
 
-        tempRect |= startWith->changeRect(requestedRect | childrenRect);
+        tempRect |= startWith->projectionPlane()->changeRect(requestedRect | childrenRect);
 
         if(!changeRectVaries)
             changeRectVaries = tempRect != requestedRect;
diff --git a/krita/image/kis_selection_based_layer.h b/krita/image/kis_selection_based_layer.h
index 8e058ed..979dd53 100644
--- a/krita/image/kis_selection_based_layer.h
+++ b/krita/image/kis_selection_based_layer.h
@@ -68,12 +68,6 @@ public:
     KisPaintDeviceSP paintDevice() const;
 
     bool needProjection() const;
-    void copyOriginalToProjection(const KisPaintDeviceSP original,
-                                  KisPaintDeviceSP projection,
-                                  const QRect& rect) const;
-
-    // From KisNode
-    QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
 
     /**
      * resets cached projection of lower layer to a new device
@@ -159,6 +153,15 @@ public:
      */
     QImage createThumbnail(qint32 w, qint32 h);
 
+
+protected:
+    // override from KisLayer
+    void copyOriginalToProjection(const KisPaintDeviceSP original,
+                                  KisPaintDeviceSP projection,
+                                  const QRect& rect) const;
+    // override from KisNode
+    QRect needRect(const QRect &rect, PositionToFilthy pos = N_FILTHY) const;
+
 protected:
     void initSelection();
 
diff --git a/krita/image/tests/kis_paint_layer_test.cpp b/krita/image/tests/kis_paint_layer_test.cpp
index 92a97ea..b9fad1a 100644
--- a/krita/image/tests/kis_paint_layer_test.cpp
+++ b/krita/image/tests/kis_paint_layer_test.cpp
@@ -35,6 +35,8 @@
 #include "kis_fill_painter.h"
 #include "kis_pixel_selection.h"
 #include <kis_iterator_ng.h>
+#include "kis_layer_projection_plane.h"
+
 
 void KisPaintLayerTest::testProjection()
 {
@@ -59,7 +61,7 @@ void KisPaintLayerTest::testProjection()
     Q_ASSERT(layer->hasEffectMasks());
 
     // And now we're going to update the projection, but nothing is dirty yet
-    layer->updateProjection(qimage.rect(), layer);
+    layer->projectionPlane()->recalculate(qimage.rect(), layer);
 
     // Which also means that the projection is no longer the paint device
     QVERIFY(layer->paintDevice().data() != layer->projection().data());
@@ -68,7 +70,7 @@ void KisPaintLayerTest::testProjection()
     layer->setDirty(qimage.rect());
 
     // And now we're going to update the projection, but nothing is dirty yet
-    layer->updateProjection(qimage.rect(), layer);
+    layer->projectionPlane()->recalculate(qimage.rect(), layer);
 
     // Which also means that the projection is no longer the paint device
     QVERIFY(layer->paintDevice().data() != layer->projection().data());
@@ -78,7 +80,7 @@ void KisPaintLayerTest::testProjection()
     QVERIFY(layer->projection().data() != 0);
 
     // The selection is initially empty, so after an update, all pixels are still visible
-    layer->updateProjection(qimage.rect(), layer);
+    layer->projectionPlane()->recalculate(qimage.rect(), layer);
 
     // We've inverted the mask, so now nothing is seen
     KisSequentialConstIterator it(layer->projection(), qimage.rect());
diff --git a/krita/ui/widgets/kis_scratch_pad.cpp b/krita/ui/widgets/kis_scratch_pad.cpp
index 0906ea3..ddbb493 100644
--- a/krita/ui/widgets/kis_scratch_pad.cpp
+++ b/krita/ui/widgets/kis_scratch_pad.cpp
@@ -46,6 +46,7 @@
 #include "kis_tool_freehand_helper.h"
 #include "kis_image_patch.h"
 #include "kis_canvas_widget_base.h"
+#include "kis_layer_projection_plane.h"
 
 
 class KisScratchPadNodeListener : public KisNodeGraphListener
@@ -298,7 +299,7 @@ void KisScratchPad::paintEvent ( QPaintEvent * event ) {
 
     QPointF offset = alignedImageRect.topLeft();
 
-    m_paintLayer->updateProjection(alignedImageRect, m_paintLayer);
+    m_paintLayer->projectionPlane()->recalculate(alignedImageRect, m_paintLayer);
     KisPaintDeviceSP projection = m_paintLayer->projection();
 
     QImage image = projection->convertToQImage(m_displayProfile,


More information about the kimageshop mailing list