Commit fda4a0d5 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

[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@kde.org
parent 716d55e5
......@@ -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);
}
}
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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)
......
......@@ -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);
......
......@@ -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
......
......@@ -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:
......
......@@ -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);
......
......@@ -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 {
......
......@@ -50,6 +50,7 @@ public:
protected:
KisMergeWalker() : m_flags(DEFAULT) {}
KisMergeWalker(Flags flags) : m_flags(flags) {}
/**
* Begins visiting nodes starting with @startWith.
......
......@@ -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()
......
......@@ -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);
}
......
......@@ -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);
......
......@@ -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.
*/
......
......@@ -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) {
......
......@@ -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);
}
}
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment