Commit 7667b4e7 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix Adjustment layers composition

This was a very old bug related the way how selection is applied
to adjustment layers. Now selection defines not "how the filtered
data is blended into the image" but "how the filter effect is applied
to the composed image". In the result, erasing the selection now will
*not* make your image fully transparent, but just removes the filter
effect from that area.

BUG:324505,294122
CCBUG:349078
CC:kimageshop@kde.org
parent 400c6d4f
......@@ -46,6 +46,7 @@ KisAdjustmentLayer::KisAdjustmentLayer(KisImageWSP image,
// https://bugs.kde.org/show_bug.cgi?id=294122
// demand the opposite from each other...
setCompositeOp(COMPOSITE_COPY);
setUseSelectionInProjection(false);
}
KisAdjustmentLayer::KisAdjustmentLayer(const KisAdjustmentLayer& rhs)
......@@ -78,7 +79,7 @@ QRect KisAdjustmentLayer::incomingChangeRect(const QRect &rect) const
/**
* We can't paint outside a selection, that is why we call
* KisSelectionBasedLayer::cropChangeRectBySelection to crop
* actual change area in the end
* actual change area in the end.
*/
filteredRect = cropChangeRectBySelection(filteredRect);
......
......@@ -90,7 +90,7 @@ public:
KisPaintDeviceSP originalDevice = layer->original();
originalDevice->clear(m_updateRect);
QRect applyRect = m_updateRect & m_projection->extent();
const QRect applyRect = m_updateRect & m_projection->extent();
// If the intersection of the updaterect and the projection extent is
// null, we are finish here.
......@@ -107,6 +107,9 @@ public:
return true;
}
KisSelectionSP selection = layer->fetchComposedInternalSelection(applyRect);
const QRect filterRect = selection ? applyRect & selection->selectedRect() : applyRect;
KisFilterSP filter = KisFilterRegistry::instance()->value(filterConfig->name());
if (!filter) return false;
......@@ -116,8 +119,21 @@ public:
updater.start(100, filter->name());
QPointer<KoUpdater> updaterPtr = updater.startSubtask();
// We do not create a transaction here, as srcDevice != dstDevice
filter->process(m_projection, originalDevice, 0, applyRect, filterConfig.data(), updaterPtr);
KisPaintDeviceSP dstDevice = originalDevice;
if (selection) {
dstDevice = new KisPaintDevice(originalDevice->colorSpace());
}
if (!filterRect.isEmpty()) {
// We do not create a transaction here, as srcDevice != dstDevice
filter->process(m_projection, dstDevice, 0, filterRect, filterConfig.data(), updaterPtr);
}
if (selection) {
KisPainter::copyAreaOptimized(applyRect.topLeft(), m_projection, originalDevice, applyRect);
KisPainter::copyAreaOptimized(filterRect.topLeft(), dstDevice, originalDevice, filterRect, selection);
}
updaterPtr->setProgress(100);
......
......@@ -39,8 +39,11 @@
struct KisSelectionBasedLayer::Private
{
public:
Private() : useSelectionInProjection(true) {}
KisSelectionSP selection;
KisPaintDeviceSP paintDevice;
bool useSelectionInProjection;
};
......@@ -113,40 +116,51 @@ bool KisSelectionBasedLayer::needProjection() const
return m_d->selection;
}
void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
void KisSelectionBasedLayer::setUseSelectionInProjection(bool value) const
{
lockTemporaryTarget();
m_d->useSelectionInProjection = value;
}
KisSelectionSP KisSelectionBasedLayer::fetchComposedInternalSelection(const QRect &rect) const
{
if (!m_d->selection) return 0;
m_d->selection->updateProjection(rect);
KisSelectionSP tempSelection = m_d->selection;
KisPainter gc(projection);
if (m_d->selection) {
if (hasTemporaryTarget()) {
/**
* Cloning a selection with COW
* FIXME: check whether it's faster than usual bitBlt'ing
*/
tempSelection = new KisSelection(*tempSelection);
KisPainter gc2(tempSelection->pixelSelection());
setupTemporaryPainter(&gc2);
gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect);
}
projection->clear(rect);
gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_OVER));
gc.setSelection(tempSelection);
} else {
gc.setCompositeOp(colorSpace()->compositeOp(COMPOSITE_COPY));
}
lockTemporaryTarget();
if (hasTemporaryTarget()) {
/**
* Cloning a selection with COW
* FIXME: check whether it's faster than usual bitBlt'ing
*/
tempSelection = new KisSelection(*tempSelection);
gc.bitBlt(rect.topLeft(), original, rect);
KisPainter gc2(tempSelection->pixelSelection());
setupTemporaryPainter(&gc2);
gc2.bitBlt(rect.topLeft(), temporaryTarget(), rect);
}
unlockTemporaryTarget();
return tempSelection;
}
void KisSelectionBasedLayer::copyOriginalToProjection(const KisPaintDeviceSP original,
KisPaintDeviceSP projection,
const QRect& rect) const
{
KisPainter gc(projection);
KisSelectionSP tempSelection;
if (m_d->useSelectionInProjection) {
tempSelection = fetchComposedInternalSelection(rect);
}
KisPainter::copyAreaOptimized(rect.topLeft(), original, projection, rect, tempSelection);
}
QRect KisSelectionBasedLayer::cropChangeRectBySelection(const QRect &rect) const
......
......@@ -106,6 +106,16 @@ public:
void setInternalSelection(KisSelectionSP selection);
/**
* When painted in indirect painting mode, the internal selection
* might not contain actual selection, because a part of it is
* stored on an indirect painting device. This method returns the
* merged copy of the real selection. The area in \p rect only is
* guaranteed to be prepared. The content of the rest of the
* selection is undefined.
*/
KisSelectionSP fetchComposedInternalSelection(const QRect &rect) const;
/**
* gets this layer's x coordinate, taking selection into account
* @return x-coordinate value
......@@ -167,6 +177,17 @@ protected:
QRect cropChangeRectBySelection(const QRect &rect) const;
/**
* Sets if the selection should be used in
* copyOriginalToProjection() method.
*
* Default value is 'true'. The descendants should override it to
* get desired behaviour.
*
* Must be called only once in the child's constructor
*/
void setUseSelectionInProjection(bool value) const;
public Q_SLOTS:
/**
......
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