[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 ®ion);
/**
+ * \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