Commit 7e2f7373 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Move all colorsmudge strategies into separate files

parent 78a7d439
......@@ -9,7 +9,15 @@ set(kritacolorsmudgepaintop_SOURCES
kis_smudge_option.cpp
kis_smudge_option_widget.cpp
kis_smudge_radius_option.cpp
)
KisColorSmudgeStrategy.cpp
KisColorSmudgeSource.cpp
KisColorSmudgeStrategyBase.cpp
KisColorSmudgeInterstrokeData.cpp
KisColorSmudgeStrategyLightness.cpp
KisColorSmudgeStrategyWithOverlay.cpp
KisColorSmudgeStrategyMask.cpp
KisColorSmudgeStrategyStamp.cpp
KisColorSmudgeStrategyMaskLegacy.cpp)
add_library(kritacolorsmudgepaintop MODULE ${kritacolorsmudgepaintop_SOURCES})
......
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "KisColorSmudgeInterstrokeData.h"
#include <KoColorSpaceRegistry.h>
#include "kis_transaction.h"
#include "kis_paint_device.h"
KisColorSmudgeInterstrokeData::KisColorSmudgeInterstrokeData(KisPaintDeviceSP source)
: KisInterstrokeData(source)
, overlayDeviceWrapper(source, 2, KisOverlayPaintDeviceWrapper::PreciseMode)
{
projectionDevice = overlayDeviceWrapper.overlay(0);
colorBlendDevice = overlayDeviceWrapper.overlay(1);
heightmapDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8());
}
KisColorSmudgeInterstrokeData::~KisColorSmudgeInterstrokeData()
{
KIS_SAFE_ASSERT_RECOVER_NOOP(!m_parentCommand);
}
void KisColorSmudgeInterstrokeData::beginTransaction()
{
KIS_SAFE_ASSERT_RECOVER_RETURN(!m_parentCommand);
m_parentCommand.reset(new KUndo2Command());
m_colorBlendDeviceTransaction.reset(new KisTransaction(colorBlendDevice, m_parentCommand.data()));
m_heightmapDeviceTransaction.reset(new KisTransaction(heightmapDevice, m_parentCommand.data()));
m_projectionDeviceTransaction.reset(new KisTransaction(projectionDevice, m_parentCommand.data()));
}
KUndo2Command *KisColorSmudgeInterstrokeData::endTransaction()
{
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_parentCommand, 0);
// the internal undo commands are owned by m_parentCommand
(void) m_colorBlendDeviceTransaction->endAndTake();
(void) m_heightmapDeviceTransaction->endAndTake();
(void) m_projectionDeviceTransaction->endAndTake();
return m_parentCommand.take();
}
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KRITA_KISCOLORSMUDGEINTERSTROKEDATA_H
#define KRITA_KISCOLORSMUDGEINTERSTROKEDATA_H
#include "kis_types.h"
#include "KisInterstrokeData.h"
#include "KisOverlayPaintDeviceWrapper.h"
class KisTransaction;
struct KisColorSmudgeInterstrokeData : public KisInterstrokeData
{
KisPaintDeviceSP colorBlendDevice;
KisPaintDeviceSP heightmapDevice;
KisPaintDeviceSP projectionDevice;
KisOverlayPaintDeviceWrapper overlayDeviceWrapper;
KisColorSmudgeInterstrokeData(KisPaintDeviceSP source);
~KisColorSmudgeInterstrokeData() override;
void beginTransaction() override;
KUndo2Command * endTransaction() override;
private:
QScopedPointer<KUndo2Command> m_parentCommand;
QScopedPointer<KisTransaction> m_colorBlendDeviceTransaction;
QScopedPointer<KisTransaction> m_heightmapDeviceTransaction;
QScopedPointer<KisTransaction> m_projectionDeviceTransaction;
};
#endif //KRITA_KISCOLORSMUDGEINTERSTROKEDATA_H
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KRITA_KISCOLORSMUDGESAMPLEUTILS_H
#define KRITA_KISCOLORSMUDGESAMPLEUTILS_H
#include "kis_fixed_paint_device.h"
#include "KoMixColorsOp.h"
#include "kis_algebra_2d.h"
namespace KisColorSmudgeSampleUtils {
struct WeightedSampleWrapper
{
WeightedSampleWrapper(KoMixColorsOp::Mixer *mixer,
KisFixedPaintDeviceSP maskDab, const QRect &maskRect,
KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
: m_mixer(mixer),
m_maskRect(maskRect),
m_maskPtr(maskDab->data()),
m_maskStride(maskDab->bounds().width()),
m_samplePixelSize(sampleDab->colorSpace()->pixelSize()),
m_sampleRect(sampleRect),
m_samplePtr(sampleDab->data()),
m_sampleStride(sampleDab->bounds().width() * m_samplePixelSize)
{
}
inline void samplePixel(const QPoint &relativeSamplePoint) {
const QPoint maskPt(relativeSamplePoint - m_maskRect.topLeft() + m_sampleRect.topLeft());
const qint16 opacity = *(m_maskPtr + maskPt.x() + maskPt.y() * m_maskStride);
const quint8 *ptr = m_samplePtr + relativeSamplePoint.x() * m_samplePixelSize + relativeSamplePoint.y() * m_sampleStride;
m_mixer->accumulate(ptr, &opacity, opacity, 1);
}
static void verifySampleRadiusValue(qreal *sampleRadiusValue) {
KIS_SAFE_ASSERT_RECOVER(*sampleRadiusValue <= 1.0) {
*sampleRadiusValue = 1.0;
}
}
KoMixColorsOp::Mixer *m_mixer;
const QRect m_maskRect;
quint8 *m_maskPtr;
const int m_maskStride;
int m_samplePixelSize;
const QRect m_sampleRect;
quint8 *m_samplePtr;
const int m_sampleStride;
};
struct AveragedSampleWrapper
{
AveragedSampleWrapper(KoMixColorsOp::Mixer *mixer,
KisFixedPaintDeviceSP maskDab, const QRect &maskRect,
KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
: m_mixer(mixer),
m_samplePixelSize(sampleDab->colorSpace()->pixelSize()),
m_sampleRect(sampleRect),
m_samplePtr(sampleDab->data()),
m_sampleStride(sampleDab->bounds().width() * m_samplePixelSize)
{
Q_UNUSED(maskDab);
Q_UNUSED(maskRect);
}
inline void samplePixel(const QPoint &relativeSamplePoint) {
const quint8 *ptr = m_samplePtr + relativeSamplePoint.x() * m_samplePixelSize + relativeSamplePoint.y() * m_sampleStride;
m_mixer->accumulateAverage(ptr, 1);
}
static void verifySampleRadiusValue(qreal *sampleRadiusValue) {
Q_UNUSED(sampleRadiusValue);
}
KoMixColorsOp::Mixer *m_mixer;
int m_samplePixelSize;
const QRect m_sampleRect;
quint8 *m_samplePtr;
const int m_sampleStride;
};
template<class WeightingModeWrapper>
void sampleColor(const QRect &srcRect,
qreal sampleRadiusValue,
KisColorSmudgeSourceSP sourceDevice,
KisFixedPaintDeviceSP tempFixedDevice,
KisFixedPaintDeviceSP maskDab,
KoColor *resultColor)
{
WeightingModeWrapper::verifySampleRadiusValue(&sampleRadiusValue);
KIS_ASSERT_RECOVER_RETURN(*resultColor->colorSpace() == *sourceDevice->colorSpace());
KIS_ASSERT_RECOVER_RETURN(*tempFixedDevice->colorSpace() == *sourceDevice->colorSpace());
const QRect minimalRect = QRect(srcRect.center(), QSize(1,1));
const QRect sampleRect = sampleRadiusValue > 0 ?
KisAlgebra2D::blowRect(srcRect, 0.5 * (sampleRadiusValue - 1.0)) | minimalRect :
minimalRect;
tempFixedDevice->setRect(sampleRect);
tempFixedDevice->lazyGrowBufferWithoutInitialization();
const KoColorSpace *cs = tempFixedDevice->colorSpace();
const int numPixels = sampleRect.width() * sampleRect.height();
sourceDevice->readBytes(tempFixedDevice->data(), sampleRect);
KisAlgebra2D::HaltonSequenceGenerator hGen(2);
KisAlgebra2D::HaltonSequenceGenerator vGen(3);
QScopedPointer<KoMixColorsOp::Mixer> mixer(cs->mixColorsOp()->createMixer());
const int minSamples =
qMin(numPixels, qMax(64, qRound(0.02 * numPixels)));
WeightingModeWrapper weightingModeWrapper(mixer.data(),
maskDab, srcRect,
tempFixedDevice, sampleRect);
KoColor lastPickedColor(*resultColor);
for (int i = 0; i < minSamples; i++) {
const QPoint pt(hGen.generate(sampleRect.width() - 1),
vGen.generate(sampleRect.height() - 1));
weightingModeWrapper.samplePixel(pt);
}
mixer->computeMixedColor(resultColor->data());
lastPickedColor = *resultColor;
const int batchSize = 16;
int numSamplesLeft = numPixels - minSamples;
while (numSamplesLeft > 0) {
const int currentBatchSize = qMin(numSamplesLeft, batchSize);
for (int i = 0; i < currentBatchSize; i++) {
const QPoint pt(hGen.generate(sampleRect.width()),
vGen.generate(sampleRect.height()));
weightingModeWrapper.samplePixel(pt);
}
mixer->computeMixedColor(resultColor->data());
const quint8 difference =
cs->differenceA(resultColor->data(), lastPickedColor.data());
if (difference <= 2) break;
lastPickedColor = *resultColor;
numSamplesLeft -= currentBatchSize;
}
}
}
#endif //KRITA_KISCOLORSMUDGESAMPLEUTILS_H
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "KisColorSmudgeSource.h"
#include "kis_paint_device.h"
#include "kis_image.h"
#include "KisOverlayPaintDeviceWrapper.h"
/**********************************************************************************/
/* KisColorSmudgeSourcePaintDevice */
/**********************************************************************************/
KisColorSmudgeSourcePaintDevice::KisColorSmudgeSourcePaintDevice(KisPaintDeviceSP sourceDevice)
: m_sourceDevice(sourceDevice)
{
}
void KisColorSmudgeSourcePaintDevice::readBytes(quint8 *dstPtr, const QRect &rect) {
m_sourceDevice->readBytes(dstPtr, rect);
}
const KoColorSpace *KisColorSmudgeSourcePaintDevice::colorSpace() const {
return m_sourceDevice->colorSpace();
}
/**********************************************************************************/
/* KisColorSmudgeSourceImage */
/**********************************************************************************/
KisColorSmudgeSourceImage::KisColorSmudgeSourceImage(KisImageSP image, KisOverlayPaintDeviceWrapper &overlayDevice)
: m_image(image),
m_overlayDevice(overlayDevice)
{
KIS_ASSERT(m_image->projection() == m_overlayDevice.source());
}
void KisColorSmudgeSourceImage::readBytes(quint8 *dstPtr, const QRect &rect) {
m_image->blockUpdates();
m_overlayDevice.readRect(rect);
m_image->unblockUpdates();
m_overlayDevice.overlay()->readBytes(dstPtr, rect);
}
const KoColorSpace *KisColorSmudgeSourceImage::colorSpace() const {
return m_overlayDevice.overlayColorSpace();
}
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KRITA_KISCOLORSMUDGESOURCE_H
#define KRITA_KISCOLORSMUDGESOURCE_H
#include <QtGlobal>
#include <kis_types.h>
class KoColorSpace;
class QRect;
class KisOverlayPaintDeviceWrapper;
class KisColorSmudgeSource {
public:
virtual ~KisColorSmudgeSource() = default;
virtual void readBytes(quint8 *dstPtr, const QRect &rect) = 0;
virtual const KoColorSpace* colorSpace() const = 0;
};
using KisColorSmudgeSourceSP = QSharedPointer<KisColorSmudgeSource>;
struct KisColorSmudgeSourcePaintDevice : public KisColorSmudgeSource
{
KisColorSmudgeSourcePaintDevice(KisPaintDeviceSP sourceDevice);
void readBytes(quint8 *dstPtr, const QRect &rect) override;
const KoColorSpace* colorSpace() const override;
private:
KisPaintDeviceSP m_sourceDevice;
};
struct KisColorSmudgeSourceImage : public KisColorSmudgeSource
{
KisColorSmudgeSourceImage(KisImageSP image,
KisOverlayPaintDeviceWrapper &overlayDevice);
void readBytes(quint8 *dstPtr, const QRect &rect) override;
const KoColorSpace* colorSpace() const override;
private:
KisImageSP m_image;
KisOverlayPaintDeviceWrapper &m_overlayDevice;
};
#endif //KRITA_KISCOLORSMUDGESOURCE_H
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "KisColorSmudgeStrategy.h"
KisColorSmudgeStrategy::KisColorSmudgeStrategy()
: m_memoryAllocator(new KisOptimizedByteArray::PooledMemoryAllocator())
{
}
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KRITA_KISCOLORSMUDGESTRATEGY_H
#define KRITA_KISCOLORSMUDGESTRATEGY_H
#include <KisOptimizedByteArray.h>
#include <kis_dab_cache.h>
class KisColorSmudgeStrategy
{
public:
KisColorSmudgeStrategy();
virtual ~KisColorSmudgeStrategy() = default;
virtual void initializePainting() = 0;
virtual void updateMask(KisDabCache *dabCache,
const KisPaintInformation& info,
const KisDabShape &shape,
const QPointF &cursorPoint,
QRect *dstDabRect) = 0;
virtual QVector<QRect> paintDab(const QRect &srcRect, const QRect &dstRect,
const KoColor &currentPaintColor,
qreal opacity,
qreal colorRateValue,
qreal smudgeRateValue,
qreal maxPossibleSmudgeRateValue,
qreal lightnessStrengthValue,
qreal smudgeRadiusValue) = 0;
virtual const KoColorSpace* preciseColorSpace() const = 0;
protected:
KisOptimizedByteArray::MemoryAllocatorSP m_memoryAllocator;
};
#endif //KRITA_KISCOLORSMUDGESTRATEGY_H
/*
* SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <KoCompositeOpRegistry.h>
#include "KisColorSmudgeStrategyBase.h"
#include "kis_painter.h"
#include "kis_fixed_paint_device.h"
#include "KisColorSmudgeSampleUtils.h"
/**********************************************************************************/
/* DabColoringStrategyMask */
/**********************************************************************************/
bool KisColorSmudgeStrategyBase::DabColoringStrategyMask::supportsFusedDullingBlending() const
{
return true;
}
void KisColorSmudgeStrategyBase::DabColoringStrategyMask::blendInFusedBackgroundAndColorRateWithDulling(
KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, const QRect &dstRect,
const KoColor &preparedDullingColor, const KoCompositeOp *smearOp, const quint8 smudgeRateOpacity,
const KoColor &paintColor, const KoCompositeOp *colorRateOp, const quint8 colorRateOpacity) const
{
KoColor dullingFillColor(preparedDullingColor);
KIS_SAFE_ASSERT_RECOVER_RETURN(*paintColor.colorSpace() == *colorRateOp->colorSpace());
colorRateOp->composite(dullingFillColor.data(), 1, paintColor.data(), 1, 0, 0, 1, 1, colorRateOpacity);
if (smearOp->id() == COMPOSITE_COPY && smudgeRateOpacity == OPACITY_OPAQUE_U8) {
dst->fill(dst->bounds(), dullingFillColor);
} else {
src->readBytes(dst->data(), dstRect);
smearOp->composite(dst->data(), dstRect.width() * dst->pixelSize(),
dullingFillColor.data(), 0,
0, 0,
1, dstRect.width() * dstRect.height(),
smudgeRateOpacity);
}
}
void KisColorSmudgeStrategyBase::DabColoringStrategyMask::blendInColorRate(const KoColor &paintColor,
const KoCompositeOp *colorRateOp,
quint8 colorRateOpacity,
KisFixedPaintDeviceSP dstDevice,
const QRect &dstRect) const
{
KIS_SAFE_ASSERT_RECOVER_RETURN(*paintColor.colorSpace() == *colorRateOp->colorSpace());
colorRateOp->composite(dstDevice->data(), dstRect.width() * dstDevice->pixelSize(),
paintColor.data(), 0,
0, 0,
dstRect.height(), dstRect.width(),
colorRateOpacity);
}
/**********************************************************************************/
/* DabColoringStrategyStamp */
/**********************************************************************************/
void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::setStampDab(KisFixedPaintDeviceSP device)
{
m_origDab = device;
}
void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::blendInColorRate(const KoColor &paintColor,
const KoCompositeOp *colorRateOp,
quint8 colorRateOpacity,
KisFixedPaintDeviceSP dstDevice,
const QRect &dstRect) const
{
Q_UNUSED(paintColor);
// TODO: check correctness for composition source device (transparency masks)
KIS_ASSERT_RECOVER_RETURN(*dstDevice->colorSpace() == *m_origDab->colorSpace());
colorRateOp->composite(dstDevice->data(), dstRect.width() * dstDevice->pixelSize(),
m_origDab->data(), dstRect.width() * m_origDab->pixelSize(),
0, 0,
dstRect.height(), dstRect.width(),
colorRateOpacity);
}
bool KisColorSmudgeStrategyBase::DabColoringStrategyStamp::supportsFusedDullingBlending() const
{
return false;
}
void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::blendInFusedBackgroundAndColorRateWithDulling(
KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, const QRect &dstRect,
const KoColor &preparedDullingColor, const KoCompositeOp *smearOp, const quint8 smudgeRateOpacity,
const KoColor &paintColor, const KoCompositeOp *colorRateOp, const quint8 colorRateOpacity) const
{
Q_UNUSED(dst);
Q_UNUSED(src);
Q_UNUSED(dstRect);
Q_UNUSED(preparedDullingColor);
Q_UNUSED(smearOp);
Q_UNUSED(smudgeRateOpacity);
Q_UNUSED(paintColor);
Q_UNUSED(colorRateOp);
Q_UNUSED(colorRateOpacity);
}
/**********************************************************************************/
/* KisColorSmudgeStrategyBase */
/**********************************************************************************/
KisColorSmudgeStrategyBase::KisColorSmudgeStrategyBase(bool useDullingMode)
: m_useDullingMode(useDullingMode)
{
}
void KisColorSmudgeStrategyBase::initializePaintingImpl(const KoColorSpace *dstColorSpace, bool smearAlpha,
const QString &colorRateCompositeOpId)
{
m_blendDevice = new KisFixedPaintDevice(dstColorSpace, m_memoryAllocator);
m_smearOp = dstColorSpace->compositeOp(smearCompositeOp(smearAlpha));
m_colorRateOp = dstColorSpace->compositeOp(colorRateCompositeOpId);
m_preparedDullingColor.convertTo(dstColorSpace);
}
const KoColorSpace *KisColorSmudgeStrategyBase::preciseColorSpace() const
{
// verify that initialize() has already been called!
KIS_ASSERT_RECOVER_RETURN_VALUE(m_smearOp, KoColorSpaceRegistry::instance()->rgb8());
return m_smearOp->colorSpace();
}
QString KisColorSmudgeStrategyBase::smearCompositeOp(bool smearAlpha) const
{
return smearAlpha ? COMPOSITE_COPY : COMPOSITE_OVER;
}
QString KisColorSmudgeStrategyBase::finalCompositeOp(bool smearAlpha) const
{
Q_UNUSED(smearAlpha);
return COMPOSITE_COPY;
}
quint8 KisColorSmudgeStrategyBase::finalPainterOpacity(qreal opacity, qreal smudgeRateValue)
{