[calligra/calligra/2.9] krita: [NEED TESTING FROM USERS!] A HUGE optimization for the merger framework

Dmitry Kazakov dimula73 at gmail.com
Wed Apr 29 16:17:27 UTC 2015


Git commit f93a715b5a5287f0cc8976f8c76815f8646b07e5 by Dmitry Kazakov.
Committed on 29/04/2015 at 16:06.
Pushed by dkazakov into branch 'calligra/2.9'.

[NEED TESTING FROM USERS!] A HUGE optimization for the merger framework

Now the merger deals much more carefully with the layer's extent and
doesn't write data if the area in question is empty! That is really
important when working with transformation and transparency masks.

What needs to be tested really thoroughly:

1) Using Move Tool

* on usual layers
* on group layers
* on layers with transparency masks
* on layers with transformation masks

2) Updates when using Transformation Masks

3) Updates when using Transparency Masks


CCMAIL:kimageshop at kde.org

M  +1    -4    krita/image/filter/kis_filter.cc
M  +3    -7    krita/image/kis_async_merger.cpp
M  +4    -4    krita/image/kis_base_rects_walker.h
M  +1    -3    krita/image/kis_clone_layer.cpp
M  +5    -7    krita/image/kis_full_refresh_walker.h
M  +3    -7    krita/image/kis_layer.cc
M  +1    -1    krita/image/kis_layer.h
M  +7    -2    krita/image/kis_layer_projection_plane.cpp
M  +2    -8    krita/image/kis_mask.cc
M  +1    -0    krita/image/kis_merge_walker.h
M  +12   -2    krita/image/kis_paint_device.cc
M  +2    -3    krita/image/kis_paint_layer.cc
M  +71   -0    krita/image/kis_painter.cc
M  +16   -0    krita/image/kis_painter.h
M  +3    -4    krita/image/kis_perspectivetransform_worker.cpp
M  +1    -4    krita/image/kis_pixel_selection.cpp
M  +2    -2    krita/image/kis_refresh_subtree_walker.h
M  +2    -2    krita/image/kis_selection_based_layer.cpp
M  +2    -4    krita/image/kis_transform_mask.cpp
M  +1    -3    krita/image/kis_transform_mask_params_interface.cpp
M  +10   -20   krita/image/kis_transform_worker.cc
M  +1    -3    krita/image/kis_transparency_mask.cc
M  +124  -0    krita/image/tests/kis_transform_mask_test.cpp
M  +2    -0    krita/image/tests/kis_transform_mask_test.h
M  +2    -2    krita/image/tests/kis_transparency_mask_test.cpp
M  +36   -23   krita/image/tiles3/kis_tiled_data_manager.cc
M  +2    -0    krita/image/tiles3/kis_tiled_data_manager.h
M  +1    -4    krita/sketch/ImageBuilder.cpp
M  +2    -9    krita/ui/actions/kis_selection_action_factories.cpp
M  +3    -4    krita/ui/flake/kis_shape_layer_canvas.cpp
M  +3    -3    krita/ui/kis_layer_manager.cc
M  +1    -4    krita/ui/tool/strokes/kis_filter_stroke_strategy.cpp
M  +3    -4    krita/ui/widgets/kis_image_from_clipboard_widget.cpp

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

diff --git a/krita/image/filter/kis_filter.cc b/krita/image/filter/kis_filter.cc
index 978197c..ab7763f 100644
--- a/krita/image/filter/kis_filter.cc
+++ b/krita/image/filter/kis_filter.cc
@@ -134,10 +134,7 @@ void KisFilter::process(const KisPaintDeviceSP src,
 
     if(transaction) {
         delete transaction;
-        KisPainter p(dst);
-        p.setCompositeOp(COMPOSITE_COPY);
-        p.setSelection(selection);
-        p.bitBlt(applyRect.topLeft(), temporary, applyRect);
+        KisPainter::copyAreaOptimized(applyRect.topLeft(), temporary, dst, applyRect, selection);
     }
 }
 
diff --git a/krita/image/kis_async_merger.cpp b/krita/image/kis_async_merger.cpp
index 8fe103e..b796f66 100644
--- a/krita/image/kis_async_merger.cpp
+++ b/krita/image/kis_async_merger.cpp
@@ -103,9 +103,7 @@ public:
              * filter inside. Then the layer has work as a pass-through
              * node. Just copy the merged data to the layer's original.
              */
-            KisPainter gc(originalDevice);
-            gc.setCompositeOp(COMPOSITE_COPY);
-            gc.bitBlt(applyRect.topLeft(), m_projection, applyRect);
+            KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
             return true;
         }
 
@@ -327,9 +325,7 @@ void KisAsyncMerger::writeProjection(KisNodeSP topmostNode, bool useTempProjecti
     if (!m_currentProjection) return;
 
     if(m_currentProjection != m_finalProjection) {
-        KisPainter gc(m_finalProjection);
-        gc.setCompositeOp(m_finalProjection->colorSpace()->compositeOp(COMPOSITE_COPY));
-        gc.bitBlt(rect.topLeft(), m_currentProjection, rect);
+        KisPainter::copyAreaOptimized(rect.topLeft(), m_currentProjection, m_finalProjection, rect);
     }
     DEBUG_NODE_ACTION("Writing projection", "", topmostNode->parent(), rect);
 }
@@ -342,7 +338,7 @@ bool KisAsyncMerger::compositeWithProjection(KisLayerSP layer, const QRect &rect
     KisPainter gc(m_currentProjection);
     layer->projectionPlane()->apply(&gc, rect);
 
-    DEBUG_NODE_ACTION("Compositing projection", "", layer, needRect);
+    DEBUG_NODE_ACTION("Compositing projection", "", layer, rect);
     return true;
 }
 
diff --git a/krita/image/kis_base_rects_walker.h b/krita/image/kis_base_rects_walker.h
index afb7984..6aefac0 100644
--- a/krita/image/kis_base_rects_walker.h
+++ b/krita/image/kis_base_rects_walker.h
@@ -167,15 +167,15 @@ public:
         return m_cloneNotifications;
     }
 
-    inline const QRect& accessRect() const {
+    inline QRect accessRect() const {
         return m_resultAccessRect;
     }
 
-    inline const QRect& changeRect() const {
+    inline QRect changeRect() const {
         return m_resultChangeRect;
     }
 
-    inline const QRect& uncroppedChangeRect() const {
+    inline QRect uncroppedChangeRect() const {
         return m_resultUncroppedChangeRect;
     }
 
@@ -191,7 +191,7 @@ public:
         return m_startNode;
     }
 
-    inline const QRect& requestedRect() const {
+    inline QRect requestedRect() const {
         return m_requestedRect;
     }
 
diff --git a/krita/image/kis_clone_layer.cpp b/krita/image/kis_clone_layer.cpp
index f37c860..d93b0a1 100644
--- a/krita/image/kis_clone_layer.cpp
+++ b/krita/image/kis_clone_layer.cpp
@@ -136,9 +136,7 @@ void KisCloneLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
     QRect copyRect = rect;
     copyRect.translate(-m_d->x, -m_d->y);
 
-    KisPainter gc(projection);
-    gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_COPY));
-    gc.bitBlt(rect.topLeft(), original, copyRect);
+    KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, copyRect);
 }
 
 void KisCloneLayer::setDirtyOriginal(const QRect &rect)
diff --git a/krita/image/kis_full_refresh_walker.h b/krita/image/kis_full_refresh_walker.h
index 2fac5dc..e39fc0c 100644
--- a/krita/image/kis_full_refresh_walker.h
+++ b/krita/image/kis_full_refresh_walker.h
@@ -27,7 +27,7 @@ class KisFullRefreshWalker : public KisRefreshSubtreeWalker, public KisMergeWalk
 {
 public:
     KisFullRefreshWalker(QRect cropRect)
-        : m_firstRun(true)
+        : KisMergeWalker(NO_FILTHY), m_firstRun(true)
     {
         setCropRect(cropRect);
     }
@@ -69,14 +69,12 @@ public:
              * true in case of full refresh walker, because all the
              * children of the dirty node are dirty as well, that is
              * why we shouldn't rely on usual registerChangeRect()
-             * mechanism for this node. Actually, node->changeRect()
-             * may not be valid in case its masks have been changes.
-             * That is why we just unite the changeRects of all its
-             * children here.
+             * mechanism for this node. That is why we just unite the
+             * changeRects of all its children here.
              */
 
-            if(node == startNode()) {
-                KisRefreshSubtreeWalker::calculateChangeRect(node, changeRect());
+            if(node == startNode() && node->parent()) {
+                KisRefreshSubtreeWalker::calculateChangeRect(node, requestedRect());
             }
             else {
                 KisMergeWalker::registerChangeRect(node, position);
diff --git a/krita/image/kis_layer.cc b/krita/image/kis_layer.cc
index eca1d07..5e46a7c 100644
--- a/krita/image/kis_layer.cc
+++ b/krita/image/kis_layer.cc
@@ -513,7 +513,7 @@ KisNode::PositionToFilthy calculatePositionToFilthy(KisNodeSP nodeInQuestion,
 }
 
 QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
-                           const KisPaintDeviceSP destination,
+                           KisPaintDeviceSP destination,
                            const QRect &requestedRect,
                            KisNodeSP filthyNode,
                            KisNodeSP lastNode) const
@@ -594,9 +594,7 @@ QRect KisLayer::applyMasks(const KisPaintDeviceSP source,
             }
             Q_ASSERT(applyRects.isEmpty());
 
-            KisPainter gc2(destination);
-            gc2.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_COPY));
-            gc2.bitBlt(changeRect.topLeft(), tempDevice, changeRect);
+            KisPainter::copyAreaOptimized(changeRect.topLeft(), tempDevice, destination, changeRect);
         }
     }
 
@@ -663,9 +661,7 @@ void KisLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
                                         KisPaintDeviceSP projection,
                                         const QRect& rect) const
 {
-    KisPainter gc(projection);
-    gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_COPY));
-    gc.bitBlt(rect.topLeft(), original, rect);
+    KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
 }
 
 KisAbstractProjectionPlaneSP KisLayer::projectionPlane() const
diff --git a/krita/image/kis_layer.h b/krita/image/kis_layer.h
index 18569a7..0803075 100644
--- a/krita/image/kis_layer.h
+++ b/krita/image/kis_layer.h
@@ -362,7 +362,7 @@ protected:
                         bool &rectVariesFlag) const;
 
     QRect applyMasks(const KisPaintDeviceSP source,
-                     const KisPaintDeviceSP destination,
+                     KisPaintDeviceSP destination,
                      const QRect &requestedRect,
                      KisNodeSP filthyNode, KisNodeSP lastNode) const;
 private:
diff --git a/krita/image/kis_layer_projection_plane.cpp b/krita/image/kis_layer_projection_plane.cpp
index e825efb..231a9ee 100644
--- a/krita/image/kis_layer_projection_plane.cpp
+++ b/krita/image/kis_layer_projection_plane.cpp
@@ -21,6 +21,7 @@
 #include <QBitArray>
 #include <KoColorSpace.h>
 #include <KoChannelInfo.h>
+#include <KoCompositeOpRegistry.h>
 #include "kis_painter.h"
 
 
@@ -50,7 +51,12 @@ void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
     KisPaintDeviceSP device = m_d->layer->projection();
     if (!device) return;
 
-    QRect needRect = rect & device->extent();
+    QRect needRect = rect;
+
+    if (m_d->layer->compositeOpId() != COMPOSITE_COPY) {
+        needRect &= device->extent();
+    }
+
     if(needRect.isEmpty()) return;
 
     QBitArray channelFlags = m_d->layer->channelFlags();
@@ -84,7 +90,6 @@ void KisLayerProjectionPlane::apply(KisPainter *painter, const QRect &rect)
     }
 
     painter->setChannelFlags(channelFlags);
-
     painter->setCompositeOp(m_d->layer->compositeOp());
     painter->setOpacity(m_d->layer->opacity());
     painter->bitBlt(needRect.topLeft(), device, needRect);
diff --git a/krita/image/kis_mask.cc b/krita/image/kis_mask.cc
index 5826287..c41584c 100644
--- a/krita/image/kis_mask.cc
+++ b/krita/image/kis_mask.cc
@@ -155,10 +155,8 @@ void KisMask::Private::initSelectionImpl(KisSelectionSP copyFrom, KisLayerSP par
     } else if (copyFromDevice) {
         selection = new KisSelection(new KisSelectionDefaultBounds(parentPaintDevice, parentLayer->image()));
 
-        KisPainter gc(selection->pixelSelection());
-        gc.setCompositeOp(COMPOSITE_COPY);
         QRect rc(copyFromDevice->extent());
-        gc.bitBlt(rc.topLeft(), copyFromDevice, rc);
+        KisPainter::copyAreaOptimized(rc.topLeft(), copyFromDevice, selection->pixelSelection(), rc);
         selection->pixelSelection()->invalidateOutlineCache();
 
     } else {
@@ -246,12 +244,8 @@ void KisMask::apply(KisPaintDeviceSP projection, const QRect &applyRect, const Q
 
         QRect updatedRect = decorateRect(projection, cacheDevice, applyRect, maskPos);
 
-        KisPainter gc(projection);
         // masks don't have any compositioning
-        gc.setCompositeOp(COMPOSITE_COPY);
-        gc.setSelection(m_d->selection);
-        gc.bitBlt(updatedRect.topLeft(), cacheDevice, updatedRect);
-
+        KisPainter::copyAreaOptimized(updatedRect.topLeft(), cacheDevice, projection, updatedRect, m_d->selection);
         m_d->paintDeviceCache.putDevice(cacheDevice);
 
     } else {
diff --git a/krita/image/kis_merge_walker.h b/krita/image/kis_merge_walker.h
index 1a7878c..cd0cb34 100644
--- a/krita/image/kis_merge_walker.h
+++ b/krita/image/kis_merge_walker.h
@@ -50,6 +50,7 @@ public:
 
 protected:
     KisMergeWalker() : m_flags(DEFAULT) {}
+    KisMergeWalker(Flags flags) : m_flags(flags) {}
 
     /**
      * Begins visiting nodes starting with @startWith.
diff --git a/krita/image/kis_paint_device.cc b/krita/image/kis_paint_device.cc
index 91ddaf9..6f61016 100644
--- a/krita/image/kis_paint_device.cc
+++ b/krita/image/kis_paint_device.cc
@@ -323,13 +323,23 @@ void KisPaintDevice::prepareClone(KisPaintDeviceSP src)
 void KisPaintDevice::makeCloneFrom(KisPaintDeviceSP src, const QRect &rect)
 {
     prepareClone(src);
-    fastBitBlt(src, rect);
+
+     // we guarantee that *this is totally empty, so copy pixels that
+    // are areally present on the source image only
+    const QRect optimizedRect = rect & src->extent();
+
+    fastBitBlt(src, optimizedRect);
 }
 
 void KisPaintDevice::makeCloneFromRough(KisPaintDeviceSP src, const QRect &minimalRect)
 {
     prepareClone(src);
-    fastBitBltRough(src, minimalRect);
+
+    // we guarantee that *this is totally empty, so copy pixels that
+    // are areally present on the source image only
+    const QRect optimizedRect = minimalRect & src->extent();
+
+    fastBitBltRough(src, optimizedRect);
 }
 
 void KisPaintDevice::setDirty()
diff --git a/krita/image/kis_paint_layer.cc b/krita/image/kis_paint_layer.cc
index cbffc32..1c0c5b0 100644
--- a/krita/image/kis_paint_layer.cc
+++ b/krita/image/kis_paint_layer.cc
@@ -115,11 +115,10 @@ void KisPaintLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
 {
     lockTemporaryTarget();
 
-    KisPainter gc(projection);
-    gc.setCompositeOp(projection->colorSpace()->compositeOp(COMPOSITE_COPY));
-    gc.bitBlt(rect.topLeft(), original, rect);
+    KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect);
 
     if (hasTemporaryTarget()) {
+        KisPainter gc(projection);
         setupTemporaryPainter(&gc);
         gc.bitBlt(rect.topLeft(), temporaryTarget(), rect);
     }
diff --git a/krita/image/kis_painter.cc b/krita/image/kis_painter.cc
index 4406aa6..c0f8509 100644
--- a/krita/image/kis_painter.cc
+++ b/krita/image/kis_painter.cc
@@ -186,6 +186,77 @@ KisPainter::~KisPainter()
     delete d;
 }
 
+template <bool useOldData>
+void copyAreaOptimizedImpl(const QPoint &dstPt,
+                           KisPaintDeviceSP src,
+                           KisPaintDeviceSP dst,
+                           const QRect &srcRect)
+{
+    const QRect dstRect(dstPt, srcRect.size());
+
+    const bool srcEmpty = (src->extent() & srcRect).isEmpty();
+    const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
+
+    if (!srcEmpty || !dstEmpty) {
+        if (srcEmpty) {
+            dst->clear(dstRect);
+        } else {
+            KisPainter gc(dst);
+            gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
+
+            if (useOldData) {
+                gc.bitBltOldData(dstRect.topLeft(), src, srcRect);
+            } else {
+                gc.bitBlt(dstRect.topLeft(), src, srcRect);
+            }
+        }
+    }
+}
+
+void KisPainter::copyAreaOptimized(const QPoint &dstPt,
+                                   KisPaintDeviceSP src,
+                                   KisPaintDeviceSP dst,
+                                   const QRect &srcRect)
+{
+    copyAreaOptimizedImpl<false>(dstPt, src, dst, srcRect);
+}
+
+void KisPainter::copyAreaOptimizedOldData(const QPoint &dstPt,
+                                          KisPaintDeviceSP src,
+                                          KisPaintDeviceSP dst,
+                                          const QRect &srcRect)
+{
+    copyAreaOptimizedImpl<true>(dstPt, src, dst, srcRect);
+}
+
+void KisPainter::copyAreaOptimized(const QPoint &dstPt,
+                                   KisPaintDeviceSP src,
+                                   KisPaintDeviceSP dst,
+                                   const QRect &originalSrcRect,
+                                   KisSelectionSP selection)
+{
+    const QRect selectionRect = selection->selectedRect();
+    const QRect srcRect = originalSrcRect & selectionRect;
+    const QPoint dstOffset = srcRect.topLeft() - originalSrcRect.topLeft();
+    const QRect dstRect = QRect(dstPt + dstOffset, srcRect.size());
+
+    const bool srcEmpty = (src->extent() & srcRect).isEmpty();
+    const bool dstEmpty = (dst->extent() & dstRect).isEmpty();
+
+    if (!srcEmpty || !dstEmpty) {
+        //if (srcEmpty) {
+        // doesn't support dstRect
+        // dst->clearSelection(selection);
+        // } else */
+        {
+            KisPainter gc(dst);
+            gc.setSelection(selection);
+            gc.setCompositeOp(dst->colorSpace()->compositeOp(COMPOSITE_COPY));
+            gc.bitBlt(dstRect.topLeft(), src, srcRect);
+        }
+    }
+}
+
 void KisPainter::begin(KisPaintDeviceSP device)
 {
     begin(device, d->selection);
diff --git a/krita/image/kis_painter.h b/krita/image/kis_painter.h
index b441909..6013a78 100644
--- a/krita/image/kis_painter.h
+++ b/krita/image/kis_painter.h
@@ -87,6 +87,22 @@ public:
     virtual ~KisPainter();
 
 public:
+    static void copyAreaOptimized(const QPoint &dstPt,
+                                  KisPaintDeviceSP src,
+                                  KisPaintDeviceSP dst,
+                                  const QRect &originalSrcRect);
+
+    static void copyAreaOptimizedOldData(const QPoint &dstPt,
+                                         KisPaintDeviceSP src,
+                                         KisPaintDeviceSP dst,
+                                         const QRect &originalSrcRect);
+
+    static void copyAreaOptimized(const QPoint &dstPt,
+                                  KisPaintDeviceSP src,
+                                  KisPaintDeviceSP dst,
+                                  const QRect &originalSrcRect,
+                                  KisSelectionSP selection);
+
     /**
      * Start painting on the specified device. Not undoable.
      */
diff --git a/krita/image/kis_perspectivetransform_worker.cpp b/krita/image/kis_perspectivetransform_worker.cpp
index 6dde2e1..d46d959 100644
--- a/krita/image/kis_perspectivetransform_worker.cpp
+++ b/krita/image/kis_perspectivetransform_worker.cpp
@@ -150,18 +150,17 @@ void KisPerspectiveTransformWorker::runPartialDst(KisPaintDeviceSP srcDev,
                                                   const QRect &dstRect)
 {
     if (m_isIdentity) {
-        KisPainter gc(dstDev);
-        gc.setCompositeOp(COMPOSITE_COPY);
-        gc.bitBltOldData(dstRect.topLeft(), srcDev, dstRect);
+        KisPainter::copyAreaOptimizedOldData(dstRect.topLeft(), srcDev, dstDev, dstRect);
         return;
     }
 
     QRectF srcClipRect = srcDev->exactBounds();
+    if (srcClipRect.isEmpty()) return;
 
     KisProgressUpdateHelper progressHelper(m_progressUpdater, 100, dstRect.height());
 
     KisRandomSubAccessorSP srcAcc = srcDev->createRandomSubAccessor();
-    KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG(0, 0);
+    KisRandomAccessorSP accessor = dstDev->createRandomAccessorNG(dstRect.x(), dstRect.y());
 
     for (int y = dstRect.y(); y < dstRect.y() + dstRect.height(); ++y) {
         for (int x = dstRect.x(); x < dstRect.x() + dstRect.width(); ++x) {
diff --git a/krita/image/kis_pixel_selection.cpp b/krita/image/kis_pixel_selection.cpp
index 25f2be5..0fbd3ae 100644
--- a/krita/image/kis_pixel_selection.cpp
+++ b/krita/image/kis_pixel_selection.cpp
@@ -525,9 +525,6 @@ void KisPixelSelection::renderToProjection(KisPaintDeviceSP projection, const QR
     QRect updateRect = rc & selectedExactRect();
 
     if (updateRect.isValid()) {
-        KisPainter painter(projection);
-        painter.setCompositeOp(COMPOSITE_COPY);
-        painter.bitBlt(updateRect.topLeft(), KisPaintDeviceSP(this), updateRect);
-        painter.end();
+        KisPainter::copyAreaOptimized(updateRect.topLeft(), KisPaintDeviceSP(this), projection, updateRect);
     }
 }
diff --git a/krita/image/kis_refresh_subtree_walker.h b/krita/image/kis_refresh_subtree_walker.h
index 7826b56..9f0e842 100644
--- a/krita/image/kis_refresh_subtree_walker.h
+++ b/krita/image/kis_refresh_subtree_walker.h
@@ -73,7 +73,7 @@ protected:
             nextNode = currentNode->nextSibling();
 
             if(isLayer(currentNode)) {
-                tempRect = calculateChangeRect(currentNode, tempRect);
+                tempRect |= calculateChangeRect(currentNode, requestedRect);
 
                 if(!changeRectVaries)
                     changeRectVaries = tempRect != requestedRect;
@@ -96,7 +96,7 @@ protected:
     }
 
     void startTrip(KisNodeSP startWith) {
-        calculateChangeRect(startWith, requestedRect());
+        setExplicitChangeRect(startWith, requestedRect(), false);
 
         if(startWith == startNode()) {
             NodePosition pos = N_EXTRA | calculateNodePosition(startWith);
diff --git a/krita/image/kis_selection_based_layer.cpp b/krita/image/kis_selection_based_layer.cpp
index 07b2cb2..d8c1729 100644
--- a/krita/image/kis_selection_based_layer.cpp
+++ b/krita/image/kis_selection_based_layer.cpp
@@ -140,9 +140,9 @@ void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP ori
         projection->clear(rect);
         gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_OVER));
         gc.setSelection(tempSelection);
-    } else
+    } else {
         gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_COPY));
-
+    }
 
     gc.bitBlt(rect.topLeft(), original, rect);
 
diff --git a/krita/image/kis_transform_mask.cpp b/krita/image/kis_transform_mask.cpp
index 29a3a14..8d71687 100644
--- a/krita/image/kis_transform_mask.cpp
+++ b/krita/image/kis_transform_mask.cpp
@@ -249,15 +249,13 @@ QRect KisTransformMask::decorateRect(KisPaintDeviceSP &src,
         m_d->worker.runPartialDst(src, dst, rc);
 
 #ifdef DEBUG_RENDERING
-        qDebug() << "Partial" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
+        qDebug() << "Partial" << name() << ppVar(src->exactBounds()) << ppVar(src->extent()) << ppVar(dst->exactBounds()) << ppVar(dst->extent()) << ppVar(rc);
         KIS_DUMP_DEVICE_2(src, DUMP_RECT, "partial_src", "dd");
         KIS_DUMP_DEVICE_2(dst, DUMP_RECT, "partial_dst", "dd");
 #endif /* DEBUG_RENDERING */
 
     } else {
-        KisPainter gc(dst);
-        gc.setCompositeOp(COMPOSITE_COPY);
-        gc.bitBlt(rc.topLeft(), m_d->staticCacheDevice, rc);
+        KisPainter::copyAreaOptimized(rc.topLeft(), m_d->staticCacheDevice, dst, rc);
 
 #ifdef DEBUG_RENDERING
         qDebug() << "Fetch" << name() << ppVar(src->exactBounds()) << ppVar(dst->exactBounds()) << ppVar(rc);
diff --git a/krita/image/kis_transform_mask_params_interface.cpp b/krita/image/kis_transform_mask_params_interface.cpp
index 6ffbfad..446cb5e 100644
--- a/krita/image/kis_transform_mask_params_interface.cpp
+++ b/krita/image/kis_transform_mask_params_interface.cpp
@@ -95,9 +95,7 @@ void KisDumbTransformMaskParams::transformDevice(KisNodeSP node, KisPaintDeviceS
         qWarning() << ppVar(t);
     }
 
-    KisPainter gc(dst);
-    gc.setCompositeOp(COMPOSITE_COPY);
-    gc.bitBlt(dstTopLeft, src, rc);
+    KisPainter::copyAreaOptimized(dstTopLeft, src, dst, rc);
 }
 
 QString KisDumbTransformMaskParams::id() const
diff --git a/krita/image/kis_transform_worker.cc b/krita/image/kis_transform_worker.cc
index ecf5a91..dd57ea1 100644
--- a/krita/image/kis_transform_worker.cc
+++ b/krita/image/kis_transform_worker.cc
@@ -635,8 +635,6 @@ void KisTransformWorker::offset(KisPaintDeviceSP device, const QPoint& offsetPos
     }
 
     KisPaintDeviceSP offsetDevice = new KisPaintDevice(device->colorSpace());
-    KisPainter gc(offsetDevice);
-    gc.setCompositeOp(COMPOSITE_COPY);
 
     int srcX = 0;
     int srcY = 0;
@@ -647,10 +645,9 @@ void KisTransformWorker::offset(KisPaintDeviceSP device, const QPoint& offsetPos
     width = qBound<int>(0, width - offsetX, width);
     height = qBound<int>(0, height - offsetY, height);
 
-    if ((width != 0) && (height != 0))
-    {
+    if ((width != 0) && (height != 0)) {
         // convert back to paint device space
-        gc.bitBlt(destX + sx, destY + sy, device, srcX + sx, srcY + sy, width, height);
+        KisPainter::copyAreaOptimized(QPoint(destX + sx, destY + sy), device, offsetDevice, QRect(srcX + sx, srcY + sy, width, height));
     }
 
     srcX = wrapRect.width() - offsetX;
@@ -659,28 +656,21 @@ void KisTransformWorker::offset(KisPaintDeviceSP device, const QPoint& offsetPos
     destX = (srcX + offsetX) % wrapRect.width();
     destY = (srcY + offsetY) % wrapRect.height();
 
-    if (offsetX != 0 && offsetY != 0)
-    {
-          gc.bitBlt(destX + sx, destY + sy, device, srcX + sx, srcY + sy, offsetX, offsetY);
+    if (offsetX != 0 && offsetY != 0) {
+          KisPainter::copyAreaOptimized(QPoint(destX + sx, destY + sy), device, offsetDevice, QRect(srcX + sx, srcY + sy, offsetX, offsetY));
     }
 
-    if (offsetX != 0)
-    {
-        gc.bitBlt(destX + sx, (destY + offsetY) + sy, device, srcX + sx, 0 + sy, offsetX, wrapRect.height() - offsetY);
+    if (offsetX != 0) {
+        KisPainter::copyAreaOptimized(QPoint(destX + sx, (destY + offsetY) + sy), device, offsetDevice, QRect(srcX + sx, 0 + sy, offsetX, wrapRect.height() - offsetY));
     }
 
-    if (offsetY != 0)
-    {
-        gc.bitBlt((destX + offsetX) + sx, destY + sy, device, 0 + sx, srcY + sy, wrapRect.width() - offsetX, offsetY);
+    if (offsetY != 0) {
+        KisPainter::copyAreaOptimized(QPoint((destX + offsetX) + sx, destY + sy), device, offsetDevice, QRect(0 + sx, srcY + sy, wrapRect.width() - offsetX, offsetY));
     }
 
-    gc.end();
-
     // bitblt the result back
-    KisPainter gc2(device);
-    gc2.setCompositeOp(COMPOSITE_COPY);
-    gc2.bitBlt(sx,sy,offsetDevice, sx, sy, wrapRect.width(), wrapRect.height());
-    gc2.end();
+    QRect resultRect(sx, sy, wrapRect.width(), wrapRect.height());
+    KisPainter::copyAreaOptimized(resultRect.topLeft(), offsetDevice, device, resultRect);
 }
 
 
diff --git a/krita/image/kis_transparency_mask.cc b/krita/image/kis_transparency_mask.cc
index 44b9917..22e155c 100644
--- a/krita/image/kis_transparency_mask.cc
+++ b/krita/image/kis_transparency_mask.cc
@@ -52,9 +52,7 @@ QRect KisTransparencyMask::decorateRect(KisPaintDeviceSP &src,
     Q_UNUSED(maskPos);
 
     if (src != dst) {
-        KisPainter gc(dst);
-        gc.setCompositeOp(src->colorSpace()->compositeOp(COMPOSITE_COPY));
-        gc.bitBlt(rc.topLeft(), src, rc);
+        KisPainter::copyAreaOptimized(rc.topLeft(), src, dst, rc);
         src->fill(rc, KoColor(Qt::transparent, src->colorSpace()));
     }
 
diff --git a/krita/image/tests/kis_transform_mask_test.cpp b/krita/image/tests/kis_transform_mask_test.cpp
index af8d56f..53f757e 100644
--- a/krita/image/tests/kis_transform_mask_test.cpp
+++ b/krita/image/tests/kis_transform_mask_test.cpp
@@ -810,4 +810,128 @@ void KisTransformMaskTest::testMaskWithOffset()
     QVERIFY(chk.testPassed());
 }
 
+void KisTransformMaskTest::testWeirdFullUpdates()
+{
+    //TestUtil::ExternalImageChecker chk("mask_with_offset", "transform_mask_updates");
+
+    QRect imageRect(0,0,512,512);
+    QRect fillRect(10, 10, 236, 236);
+    TestUtil::MaskParent p(imageRect);
+
+    p.layer->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
+
+    KisPaintLayerSP player1 = new KisPaintLayer(p.image, "pl1", OPACITY_OPAQUE_U8, p.image->colorSpace());
+    player1->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
+    p.image->addNode(player1, p.image->root());
+
+    KisTransformMaskSP mask1 = new KisTransformMask();
+    mask1->setName("mask1");
+    QTransform transform1 =
+        QTransform::fromTranslate(256, 0);
+    mask1->setTransformParams(KisTransformMaskParamsInterfaceSP(
+                                  new KisDumbTransformMaskParams(transform1)));
+
+    p.image->addNode(mask1, player1);
+
+
+    KisPaintLayerSP player2 = new KisPaintLayer(p.image, "pl2", OPACITY_OPAQUE_U8, p.image->colorSpace());
+    player2->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
+    p.image->addNode(player2, p.image->root());
+
+    KisTransformMaskSP mask2 = new KisTransformMask();
+    mask2->setName("mask2");
+    QTransform transform2 =
+        QTransform::fromTranslate(0, 256);
+    mask2->setTransformParams(KisTransformMaskParamsInterfaceSP(
+                                  new KisDumbTransformMaskParams(transform2)));
+
+    p.image->addNode(mask2, player2);
+
+
+    KisPaintLayerSP player3 = new KisPaintLayer(p.image, "pl3", OPACITY_OPAQUE_U8, p.image->colorSpace());
+    player3->paintDevice()->fill(fillRect, KoColor(Qt::red, p.layer->colorSpace()));
+    p.image->addNode(player3, p.image->root());
+
+    KisTransformMaskSP mask3 = new KisTransformMask();
+    mask3->setName("mask3");
+    QTransform transform3 =
+        QTransform::fromTranslate(256, 256);
+    mask3->setTransformParams(KisTransformMaskParamsInterfaceSP(
+                                  new KisDumbTransformMaskParams(transform3)));
+
+    p.image->addNode(mask3, player3);
+
+
+
+    //p.image->initialRefreshGraph();
+
+    p.image->refreshGraphAsync(0, QRect(0,0,256,256), QRect());
+    p.image->waitForDone();
+
+    QVERIFY(player1->projection()->extent().isEmpty());
+    QVERIFY(player1->projection()->exactBounds().isEmpty());
+
+    QVERIFY(player2->projection()->extent().isEmpty());
+    QVERIFY(player2->projection()->exactBounds().isEmpty());
+
+    QVERIFY(player3->projection()->extent().isEmpty());
+    QVERIFY(player3->projection()->exactBounds().isEmpty());
+
+    QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,236,236)));
+
+
+
+    p.image->refreshGraphAsync(0, QRect(0,256,256,256), QRect());
+    p.image->waitForDone();
+
+    QVERIFY(player1->projection()->extent().isEmpty());
+    QVERIFY(player1->projection()->exactBounds().isEmpty());
+
+    QVERIFY(!player2->projection()->extent().isEmpty());
+    QVERIFY(!player2->projection()->exactBounds().isEmpty());
+
+    QVERIFY(player3->projection()->extent().isEmpty());
+    QVERIFY(player3->projection()->exactBounds().isEmpty());
+
+    QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,236,492)));
+
+
+    p.image->refreshGraphAsync(0, QRect(256,0,256,256), QRect());
+    p.image->waitForDone();
+
+    QVERIFY(!player1->projection()->extent().isEmpty());
+    QVERIFY(!player1->projection()->exactBounds().isEmpty());
+
+    QVERIFY(!player2->projection()->extent().isEmpty());
+    QVERIFY(!player2->projection()->exactBounds().isEmpty());
+
+    QVERIFY(player3->projection()->extent().isEmpty());
+    QVERIFY(player3->projection()->exactBounds().isEmpty());
+
+    QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,492,492)));
+    QVERIFY((p.image->projection()->region() & QRect(256,256,256,256)).isEmpty());
+
+
+
+    p.image->refreshGraphAsync(0, QRect(256,256,256,256), QRect());
+    p.image->waitForDone();
+
+    QVERIFY(!player1->projection()->extent().isEmpty());
+    QVERIFY(!player1->projection()->exactBounds().isEmpty());
+
+    QVERIFY(!player2->projection()->extent().isEmpty());
+    QVERIFY(!player2->projection()->exactBounds().isEmpty());
+
+    QVERIFY(!player3->projection()->extent().isEmpty());
+    QVERIFY(!player3->projection()->exactBounds().isEmpty());
+
+    QCOMPARE(p.image->projection()->exactBounds(), QRect(QRect(10,10,492,492)));
+    QVERIFY(!(p.image->projection()->region() & QRect(256,256,256,256)).isEmpty());
+
+    p.image->waitForDone();
+
+    KIS_DUMP_DEVICE_2(p.image->projection(), imageRect, "image_proj", "dd");
+
+}
+
 QTEST_KDEMAIN(KisTransformMaskTest, GUI)
diff --git a/krita/image/tests/kis_transform_mask_test.h b/krita/image/tests/kis_transform_mask_test.h
index 286637f..76cce00 100644
--- a/krita/image/tests/kis_transform_mask_test.h
+++ b/krita/image/tests/kis_transform_mask_test.h
@@ -34,6 +34,8 @@ private Q_SLOTS:
 
     void testMultipleMasks();
     void testMaskWithOffset();
+
+    void testWeirdFullUpdates();
 };
 
 #endif /* __KIS_TRANSFORM_MASK_TEST_H */
diff --git a/krita/image/tests/kis_transparency_mask_test.cpp b/krita/image/tests/kis_transparency_mask_test.cpp
index c565632..26bd96d 100644
--- a/krita/image/tests/kis_transparency_mask_test.cpp
+++ b/krita/image/tests/kis_transparency_mask_test.cpp
@@ -141,12 +141,12 @@ void KisTransparencyMaskTest::testMoveParentLayer()
     layer->setX(100);
     layer->setY(100);
 
-    qDebug() << "Sel. rect before:" << mask->selection()->selectedRect();
+    qDebug() << "Sel. rect before:" << mask->selection()->selectedExactRect();
 
     mask->setX(100);
     mask->setY(100);
 
-    qDebug() << "Sel. rect after:" << mask->selection()->selectedRect();
+    qDebug() << "Sel. rect after:" << mask->selection()->selectedExactRect();
 
     QRect finalRect(100,100,200,100);
     QCOMPARE(layer->exactBounds(), finalRect);
diff --git a/krita/image/tiles3/kis_tiled_data_manager.cc b/krita/image/tiles3/kis_tiled_data_manager.cc
index eeea258..559bf52 100644
--- a/krita/image/tiles3/kis_tiled_data_manager.cc
+++ b/krita/image/tiles3/kis_tiled_data_manager.cc
@@ -338,6 +338,10 @@ void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
         }
     }
 
+    if (pixelBytesAreDefault) {
+        clearRect &= extentImpl();
+    }
+
     qint32 firstColumn = xToCol(clearRect.left());
     qint32 lastColumn = xToCol(clearRect.right());
 
@@ -352,20 +356,16 @@ void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
     clearPixelData = duplicatePixel(maxRunLength, clearPixel);
 
     KisTileData *td = 0;
-    if (clearRect.width() >= KisTileData::WIDTH &&
-            clearRect.height() >= KisTileData::HEIGHT) {
-        if (pixelBytesAreDefault)
-            /**
-             * FIXME: Theoretical race condition
-             *        if setDefaultPixel has been called first
-             */
-            td = m_hashTable->defaultTileData();
-        else
-            td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel);
+    if (!pixelBytesAreDefault &&
+        clearRect.width() >= KisTileData::WIDTH &&
+        clearRect.height() >= KisTileData::HEIGHT) {
 
+        td = KisTileDataStore::instance()->createDefaultTileData(pixelSize, clearPixel);
         td->acquire();
     }
 
+    bool needsRecalculateExtent = false;
+
     for (qint32 row = firstRow; row <= lastRow; ++row) {
         for (qint32 column = firstColumn; column <= lastColumn; ++column) {
 
@@ -376,9 +376,13 @@ void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
             if (clearTileRect == tileRect) {
                  // Clear whole tile
                  m_hashTable->deleteTile(column, row);
-                 KisTileSP clearedTile = new KisTile(column, row, td, m_mementoManager);
-                 m_hashTable->addTile(clearedTile);
-                 updateExtent(column, row);
+                 needsRecalculateExtent = true;
+
+                 if (!pixelBytesAreDefault) {
+                     KisTileSP clearedTile = new KisTile(column, row, td, m_mementoManager);
+                     m_hashTable->addTile(clearedTile);
+                     updateExtent(column, row);
+                 }
             } else {
                 const qint32 lineSize = clearTileRect.width() * pixelSize;
                 qint32 rowsRemaining = clearTileRect.height();
@@ -405,6 +409,11 @@ void KisTiledDataManager::clear(QRect clearRect, const quint8 *clearPixel)
             }
         }
     }
+
+    if (needsRecalculateExtent) {
+        recalculateExtent();
+    }
+
     if (td) td->release();
     delete[] clearPixelData;
 }
@@ -650,22 +659,26 @@ void KisTiledDataManager::updateExtent(qint32 col, qint32 row)
     m_extentMaxY = qMax(m_extentMaxY, tileMaxY);
 }
 
-void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
+QRect KisTiledDataManager::extentImpl() const
 {
-    QReadLocker locker(&m_lock);
+    qint32 x = m_extentMinX;
+    qint32 y = m_extentMinY;
+    qint32 w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0;
+    qint32 h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0;
 
-    x = m_extentMinX;
-    y = m_extentMinY;
-    w = (m_extentMaxX >= m_extentMinX) ? m_extentMaxX - m_extentMinX + 1 : 0;
-    h = (m_extentMaxY >= m_extentMinY) ? m_extentMaxY - m_extentMinY + 1 : 0;
+    return QRect(x, y, w, h);
 }
 
-QRect KisTiledDataManager::extent() const
+void KisTiledDataManager::extent(qint32 &x, qint32 &y, qint32 &w, qint32 &h) const
 {
-    qint32 x, y, w, h;
-    extent(x, y, w, h);
+    QRect rect = extent();
+    rect.getRect(&x, &y, &w, &h);
+}
 
-    return QRect(x, y, w, h);
+QRect KisTiledDataManager::extent() const
+{
+    QReadLocker locker(&m_lock);
+    return extentImpl();
 }
 
 QRegion KisTiledDataManager::region() const
diff --git a/krita/image/tiles3/kis_tiled_data_manager.h b/krita/image/tiles3/kis_tiled_data_manager.h
index fba08ec..014ffaa 100644
--- a/krita/image/tiles3/kis_tiled_data_manager.h
+++ b/krita/image/tiles3/kis_tiled_data_manager.h
@@ -324,6 +324,8 @@ private:
 private:
     void setDefaultPixelImpl(const quint8 *defPixel);
 
+    QRect extentImpl() const;
+
     bool writeTilesHeader(KisPaintDeviceWriter &store, quint32 numTiles);
     bool processTilesHeader(QIODevice *stream, quint32 &numTiles);
 
diff --git a/krita/sketch/ImageBuilder.cpp b/krita/sketch/ImageBuilder.cpp
index 59015cf..7a15b9d 100644
--- a/krita/sketch/ImageBuilder.cpp
+++ b/krita/sketch/ImageBuilder.cpp
@@ -86,10 +86,7 @@ void ImageBuilder::createImageFromClipboardDelayed()
         layer->setOpacity(OPACITY_OPAQUE_U8);
         QRect r = clipDevice->exactBounds();
 
-        KisPainter painter;
-        painter.begin(layer->paintDevice());
-        painter.setCompositeOp(COMPOSITE_COPY);
-        painter.bitBlt(QPoint(0, 0), clipDevice, r);
+        KisPainter::copyAreaOptimized(QPoint(), clipDevice, layer->paintDevice(), r);
         layer->setDirty(QRect(0, 0, sz.width(), sz.height()));
     }
 }
diff --git a/krita/ui/actions/kis_selection_action_factories.cpp b/krita/ui/actions/kis_selection_action_factories.cpp
index d737ca8..232a270 100644
--- a/krita/ui/actions/kis_selection_action_factories.cpp
+++ b/krita/ui/actions/kis_selection_action_factories.cpp
@@ -81,11 +81,7 @@ namespace ActionHelper {
     // TODO if the source is linked... copy from all linked layers?!?
 
     // Copy image data
-    KisPainter gc;
-    gc.begin(clip);
-    gc.setCompositeOp(COMPOSITE_COPY);
-    gc.bitBlt(0, 0, device, rc.x(), rc.y(), rc.width(), rc.height());
-    gc.end();
+    KisPainter::copyAreaOptimized(QPoint(), device, clip, rc);
 
     if (selection) {
         // Apply selection mask.
@@ -416,10 +412,7 @@ void KisPasteNewActionFactory::run(KisViewManager *viewManager)
             new KisPaintLayer(image.data(), clip->objectName(),
                               OPACITY_OPAQUE_U8, clip->colorSpace());
 
-    KisPainter p(layer->paintDevice());
-    p.setCompositeOp(COMPOSITE_COPY);
-    p.bitBlt(0, 0, clip, rect.x(), rect.y(), rect.width(), rect.height());
-    p.end();
+    KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), rect);
 
     image->addNode(layer.data(), image->rootLayer());
     doc->setCurrentImage(image);
diff --git a/krita/ui/flake/kis_shape_layer_canvas.cpp b/krita/ui/flake/kis_shape_layer_canvas.cpp
index d60f2e4..6ea18d2 100644
--- a/krita/ui/flake/kis_shape_layer_canvas.cpp
+++ b/krita/ui/flake/kis_shape_layer_canvas.cpp
@@ -134,10 +134,9 @@ void KisShapeLayerCanvas::repaint()
 
     KisPaintDeviceSP dev = new KisPaintDevice(m_projection->colorSpace());
     dev->convertFromQImage(image, 0);
-    KisPainter kp(m_projection.data());
-    kp.setCompositeOp(m_projection->colorSpace()->compositeOp(COMPOSITE_COPY));
-    kp.bitBlt(r.x(), r.y(), dev, 0, 0, r.width(), r.height());
-    kp.end();
+
+    KisPainter::copyAreaOptimized(r.topLeft(), dev, m_projection, QRect(QPoint(), r.size()));
+
     m_parentLayer->setDirty(r);
 }
 
diff --git a/krita/ui/kis_layer_manager.cc b/krita/ui/kis_layer_manager.cc
index 173aa89..520571a 100644
--- a/krita/ui/kis_layer_manager.cc
+++ b/krita/ui/kis_layer_manager.cc
@@ -492,10 +492,10 @@ void KisLayerManager::convertNodeToPaintLayer(KisNodeSP source)
           *srcDevice->compositionSourceColorSpace())) {
 
         clone = new KisPaintDevice(srcDevice->compositionSourceColorSpace());
-        KisPainter gc(clone);
-        gc.setCompositeOp(COMPOSITE_COPY);
+
         QRect rc(srcDevice->extent());
-        gc.bitBlt(rc.topLeft(), srcDevice, rc);
+        KisPainter::copyAreaOptimized(rc.topLeft(), srcDevice, clone, rc);
+
     } else {
         clone = new KisPaintDevice(*srcDevice);
     }
diff --git a/krita/ui/tool/strokes/kis_filter_stroke_strategy.cpp b/krita/ui/tool/strokes/kis_filter_stroke_strategy.cpp
index 31aed80..92cc947 100644
--- a/krita/ui/tool/strokes/kis_filter_stroke_strategy.cpp
+++ b/krita/ui/tool/strokes/kis_filter_stroke_strategy.cpp
@@ -106,10 +106,7 @@ void KisFilterStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
                                  m_d->filterConfig.data(), helper.updater());
 
         if (m_d->secondaryTransaction) {
-            KisPainter p(targetDevice());
-            p.setCompositeOp(COMPOSITE_COPY);
-            p.setSelection(activeSelection());
-            p.bitBlt(rc.topLeft(), m_d->filterDevice, rc);
+            KisPainter::copyAreaOptimized(rc.topLeft(), m_d->filterDevice, targetDevice(), rc, activeSelection());
 
             // Free memory
             m_d->filterDevice->clear(rc);
diff --git a/krita/ui/widgets/kis_image_from_clipboard_widget.cpp b/krita/ui/widgets/kis_image_from_clipboard_widget.cpp
index 67840d8..b348e35 100644
--- a/krita/ui/widgets/kis_image_from_clipboard_widget.cpp
+++ b/krita/ui/widgets/kis_image_from_clipboard_widget.cpp
@@ -94,10 +94,9 @@ void KisImageFromClipboard::createImage()
         KisPaintDeviceSP clip = KisClipboard::instance()->clip(QRect(), true);
         if (clip) {
             QRect r = clip->exactBounds();
-            KisPainter painter;
-            painter.begin(layer->paintDevice());
-            painter.setCompositeOp(COMPOSITE_COPY);
-            painter.bitBlt(0, 0, clip, r.x(), r.y(), r.width(), r.height());
+
+            KisPainter::copyAreaOptimized(QPoint(), clip, layer->paintDevice(), r);
+
             layer->setDirty();
         }
 


More information about the kimageshop mailing list