File indexing completed on 2024-12-22 04:16:03
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Peter Schatz <voronwe13@gmail.com> 0003 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <KoCompositeOpRegistry.h> 0009 #include "KisColorSmudgeStrategyBase.h" 0010 #include "kis_painter.h" 0011 #include "kis_fixed_paint_device.h" 0012 #include "kis_paint_device.h" 0013 #include "KisColorSmudgeSampleUtils.h" 0014 0015 /**********************************************************************************/ 0016 /* DabColoringStrategyMask */ 0017 /**********************************************************************************/ 0018 0019 bool KisColorSmudgeStrategyBase::DabColoringStrategyMask::supportsFusedDullingBlending() const 0020 { 0021 return true; 0022 } 0023 0024 void KisColorSmudgeStrategyBase::DabColoringStrategyMask::blendInFusedBackgroundAndColorRateWithDulling( 0025 KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, const QRect &dstRect, 0026 const KoColor &preparedDullingColor, const KoCompositeOp *smearOp, const quint8 smudgeRateOpacity, 0027 const KoColor &paintColor, const KoCompositeOp *colorRateOp, const quint8 colorRateOpacity) const 0028 { 0029 KoColor dullingFillColor(preparedDullingColor); 0030 0031 KIS_SAFE_ASSERT_RECOVER_RETURN(*paintColor.colorSpace() == *colorRateOp->colorSpace()); 0032 colorRateOp->composite(dullingFillColor.data(), 1, paintColor.data(), 1, 0, 0, 1, 1, colorRateOpacity); 0033 0034 if (smearOp->id() == COMPOSITE_COPY && smudgeRateOpacity == OPACITY_OPAQUE_U8) { 0035 dst->fill(dst->bounds(), dullingFillColor); 0036 } else { 0037 src->readBytes(dst->data(), dstRect); 0038 smearOp->composite(dst->data(), dstRect.width() * dst->pixelSize(), 0039 dullingFillColor.data(), 0, 0040 0, 0, 0041 1, dstRect.width() * dstRect.height(), 0042 smudgeRateOpacity); 0043 } 0044 } 0045 0046 void KisColorSmudgeStrategyBase::DabColoringStrategyMask::blendInColorRate(const KoColor &paintColor, 0047 const KoCompositeOp *colorRateOp, 0048 quint8 colorRateOpacity, 0049 KisFixedPaintDeviceSP dstDevice, 0050 const QRect &dstRect) const 0051 { 0052 KIS_SAFE_ASSERT_RECOVER_RETURN(*paintColor.colorSpace() == *colorRateOp->colorSpace()); 0053 0054 colorRateOp->composite(dstDevice->data(), dstRect.width() * dstDevice->pixelSize(), 0055 paintColor.data(), 0, 0056 0, 0, 0057 dstRect.height(), dstRect.width(), 0058 colorRateOpacity); 0059 } 0060 0061 /**********************************************************************************/ 0062 /* DabColoringStrategyStamp */ 0063 /**********************************************************************************/ 0064 0065 void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::setStampDab(KisFixedPaintDeviceSP device) 0066 { 0067 m_origDab = device; 0068 } 0069 0070 void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::blendInColorRate(const KoColor &paintColor, 0071 const KoCompositeOp *colorRateOp, 0072 quint8 colorRateOpacity, 0073 KisFixedPaintDeviceSP dstDevice, 0074 const QRect &dstRect) const 0075 { 0076 Q_UNUSED(paintColor); 0077 0078 // TODO: check correctness for composition source device (transparency masks) 0079 KIS_ASSERT_RECOVER_RETURN(*dstDevice->colorSpace() == *m_origDab->colorSpace()); 0080 0081 colorRateOp->composite(dstDevice->data(), dstRect.width() * dstDevice->pixelSize(), 0082 m_origDab->data(), dstRect.width() * m_origDab->pixelSize(), 0083 0, 0, 0084 dstRect.height(), dstRect.width(), 0085 colorRateOpacity); 0086 } 0087 0088 bool KisColorSmudgeStrategyBase::DabColoringStrategyStamp::supportsFusedDullingBlending() const 0089 { 0090 return false; 0091 } 0092 0093 void KisColorSmudgeStrategyBase::DabColoringStrategyStamp::blendInFusedBackgroundAndColorRateWithDulling( 0094 KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, const QRect &dstRect, 0095 const KoColor &preparedDullingColor, const KoCompositeOp *smearOp, const quint8 smudgeRateOpacity, 0096 const KoColor &paintColor, const KoCompositeOp *colorRateOp, const quint8 colorRateOpacity) const 0097 { 0098 Q_UNUSED(dst); 0099 Q_UNUSED(src); 0100 Q_UNUSED(dstRect); 0101 Q_UNUSED(preparedDullingColor); 0102 Q_UNUSED(smearOp); 0103 Q_UNUSED(smudgeRateOpacity); 0104 Q_UNUSED(paintColor); 0105 Q_UNUSED(colorRateOp); 0106 Q_UNUSED(colorRateOpacity); 0107 } 0108 0109 /**********************************************************************************/ 0110 /* KisColorSmudgeStrategyBase */ 0111 /**********************************************************************************/ 0112 0113 KisColorSmudgeStrategyBase::KisColorSmudgeStrategyBase(bool useDullingMode) 0114 : m_useDullingMode(useDullingMode) 0115 { 0116 } 0117 0118 void KisColorSmudgeStrategyBase::initializePaintingImpl(const KoColorSpace *dstColorSpace, bool smearAlpha, 0119 const QString &colorRateCompositeOpId) 0120 { 0121 m_blendDevice = new KisFixedPaintDevice(dstColorSpace, m_memoryAllocator); 0122 m_smearOp = dstColorSpace->compositeOp(smearCompositeOp(smearAlpha)); 0123 m_colorRateOp = dstColorSpace->compositeOp(colorRateCompositeOpId); 0124 m_preparedDullingColor.convertTo(dstColorSpace); 0125 } 0126 0127 const KoColorSpace *KisColorSmudgeStrategyBase::preciseColorSpace() const 0128 { 0129 // verify that initialize() has already been called! 0130 KIS_ASSERT_RECOVER_RETURN_VALUE(m_smearOp, KoColorSpaceRegistry::instance()->rgb8()); 0131 0132 return m_smearOp->colorSpace(); 0133 } 0134 0135 QString KisColorSmudgeStrategyBase::smearCompositeOp(bool smearAlpha) const 0136 { 0137 return smearAlpha ? COMPOSITE_COPY : COMPOSITE_OVER; 0138 } 0139 0140 QString KisColorSmudgeStrategyBase::finalCompositeOp(bool smearAlpha) const 0141 { 0142 Q_UNUSED(smearAlpha); 0143 return COMPOSITE_COPY; 0144 } 0145 0146 quint8 KisColorSmudgeStrategyBase::finalPainterOpacity(qreal opacity, qreal smudgeRateValue) 0147 { 0148 Q_UNUSED(opacity); 0149 Q_UNUSED(smudgeRateValue); 0150 0151 return OPACITY_OPAQUE_U8; 0152 } 0153 0154 quint8 KisColorSmudgeStrategyBase::colorRateOpacity(qreal opacity, qreal smudgeRateValue, qreal colorRateValue, 0155 qreal maxPossibleSmudgeRateValue) 0156 { 0157 Q_UNUSED(smudgeRateValue); 0158 Q_UNUSED(maxPossibleSmudgeRateValue); 0159 return qRound(colorRateValue * colorRateValue * opacity * 255.0); 0160 } 0161 0162 quint8 KisColorSmudgeStrategyBase::dullingRateOpacity(qreal opacity, qreal smudgeRateValue) 0163 { 0164 return qRound(0.8 * smudgeRateValue * opacity * 255.0); 0165 } 0166 0167 quint8 KisColorSmudgeStrategyBase::smearRateOpacity(qreal opacity, qreal smudgeRateValue) 0168 { 0169 return qRound(smudgeRateValue * opacity * 255.0); 0170 } 0171 0172 void KisColorSmudgeStrategyBase::sampleDullingColor(const QRect &srcRect, qreal sampleRadiusValue, 0173 KisColorSmudgeSourceSP sourceDevice, 0174 KisFixedPaintDeviceSP tempFixedDevice, 0175 KisFixedPaintDeviceSP maskDab, KoColor *resultColor) 0176 { 0177 using namespace KisColorSmudgeSampleUtils; 0178 sampleColor<WeightedSampleWrapper>(srcRect, sampleRadiusValue, 0179 sourceDevice, tempFixedDevice, 0180 maskDab, resultColor); 0181 } 0182 0183 void 0184 KisColorSmudgeStrategyBase::blendBrush(const QVector<KisPainter *> dstPainters, KisColorSmudgeSourceSP srcSampleDevice, 0185 KisFixedPaintDeviceSP maskDab, bool preserveMaskDab, const QRect &srcRect, 0186 const QRect &dstRect, const KoColor ¤tPaintColor, qreal opacity, 0187 qreal smudgeRateValue, qreal maxPossibleSmudgeRateValue, qreal colorRateValue, 0188 qreal smudgeRadiusValue) 0189 { 0190 const quint8 colorRateOpacity = this->colorRateOpacity(opacity, smudgeRateValue, colorRateValue, maxPossibleSmudgeRateValue); 0191 0192 if (m_useDullingMode) { 0193 this->sampleDullingColor(srcRect, 0194 smudgeRadiusValue, 0195 srcSampleDevice, m_blendDevice, 0196 maskDab, &m_preparedDullingColor); 0197 0198 KIS_SAFE_ASSERT_RECOVER(*m_preparedDullingColor.colorSpace() == *m_colorRateOp->colorSpace()) { 0199 m_preparedDullingColor.convertTo(m_colorRateOp->colorSpace()); 0200 } 0201 } 0202 0203 m_blendDevice->setRect(dstRect); 0204 m_blendDevice->lazyGrowBufferWithoutInitialization(); 0205 0206 DabColoringStrategy &coloringStrategy = this->coloringStrategy(); 0207 0208 const quint8 dullingRateOpacity = this->dullingRateOpacity(opacity, smudgeRateValue); 0209 0210 if (colorRateOpacity > 0 && 0211 m_useDullingMode && 0212 coloringStrategy.supportsFusedDullingBlending() && 0213 ((m_smearOp->id() == COMPOSITE_OVER && 0214 m_colorRateOp->id() == COMPOSITE_OVER) || 0215 (m_smearOp->id() == COMPOSITE_COPY && 0216 dullingRateOpacity == OPACITY_OPAQUE_U8))) { 0217 0218 coloringStrategy.blendInFusedBackgroundAndColorRateWithDulling(m_blendDevice, 0219 srcSampleDevice, 0220 dstRect, 0221 m_preparedDullingColor, 0222 m_smearOp, 0223 dullingRateOpacity, 0224 currentPaintColor.convertedTo( 0225 m_preparedDullingColor.colorSpace()), 0226 m_colorRateOp, 0227 colorRateOpacity); 0228 0229 } else { 0230 if (!m_useDullingMode) { 0231 const quint8 smudgeRateOpacity = this->smearRateOpacity(opacity, smudgeRateValue); 0232 blendInBackgroundWithSmearing(m_blendDevice, srcSampleDevice, 0233 srcRect, dstRect, smudgeRateOpacity); 0234 } else { 0235 blendInBackgroundWithDulling(m_blendDevice, srcSampleDevice, 0236 dstRect, 0237 m_preparedDullingColor, dullingRateOpacity); 0238 } 0239 0240 if (colorRateOpacity > 0) { 0241 coloringStrategy.blendInColorRate( 0242 currentPaintColor.convertedTo(m_preparedDullingColor.colorSpace()), 0243 m_colorRateOp, 0244 colorRateOpacity, 0245 m_blendDevice, dstRect); 0246 } 0247 } 0248 0249 const bool preserveDab = preserveMaskDab && dstPainters.size() > 1; 0250 0251 Q_FOREACH (KisPainter *dstPainter, dstPainters) { 0252 dstPainter->setOpacity(finalPainterOpacity(opacity, smudgeRateValue)); 0253 0254 dstPainter->bltFixedWithFixedSelection(dstRect.x(), dstRect.y(), 0255 m_blendDevice, maskDab, 0256 maskDab->bounds().x(), maskDab->bounds().y(), 0257 m_blendDevice->bounds().x(), m_blendDevice->bounds().y(), 0258 dstRect.width(), dstRect.height()); 0259 dstPainter->renderMirrorMaskSafe(dstRect, m_blendDevice, maskDab, preserveDab); 0260 } 0261 0262 } 0263 0264 void KisColorSmudgeStrategyBase::blendInBackgroundWithSmearing(KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, 0265 const QRect &srcRect, const QRect &dstRect, 0266 const quint8 smudgeRateOpacity) 0267 { 0268 if (m_smearOp->id() == COMPOSITE_COPY && smudgeRateOpacity == OPACITY_OPAQUE_U8) { 0269 src->readBytes(dst->data(), srcRect); 0270 } else { 0271 src->readBytes(dst->data(), dstRect); 0272 0273 KisFixedPaintDevice tempDevice(src->colorSpace(), m_memoryAllocator); 0274 tempDevice.setRect(srcRect); 0275 tempDevice.lazyGrowBufferWithoutInitialization(); 0276 0277 src->readBytes(tempDevice.data(), srcRect); 0278 m_smearOp->composite(dst->data(), dstRect.width() * dst->pixelSize(), 0279 tempDevice.data(), dstRect.width() * tempDevice.pixelSize(), // stride should be random non-zero 0280 0, 0, 0281 1, dstRect.width() * dstRect.height(), 0282 smudgeRateOpacity); 0283 } 0284 } 0285 0286 void KisColorSmudgeStrategyBase::blendInBackgroundWithDulling(KisFixedPaintDeviceSP dst, KisColorSmudgeSourceSP src, 0287 const QRect &dstRect, const KoColor &preparedDullingColor, 0288 const quint8 smudgeRateOpacity) 0289 { 0290 Q_UNUSED(preparedDullingColor); 0291 0292 if (m_smearOp->id() == COMPOSITE_COPY && smudgeRateOpacity == OPACITY_OPAQUE_U8) { 0293 dst->fill(dst->bounds(), m_preparedDullingColor); 0294 } else { 0295 src->readBytes(dst->data(), dstRect); 0296 m_smearOp->composite(dst->data(), dstRect.width() * dst->pixelSize(), 0297 m_preparedDullingColor.data(), 0, 0298 0, 0, 0299 1, dstRect.width() * dstRect.height(), 0300 smudgeRateOpacity); 0301 } 0302 }