Krita Source Code Documentation
Loading...
Searching...
No Matches
KisColorSmudgeSampleUtils.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef KRITA_KISCOLORSMUDGESAMPLEUTILS_H
8#define KRITA_KISCOLORSMUDGESAMPLEUTILS_H
9
11#include "KoMixColorsOp.h"
12#include "kis_algebra_2d.h"
13
14#include <KoColor.h>
15
18
19
21
23{
25 KisFixedPaintDeviceSP maskDab, const QRect &maskRect,
26 KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
27 : m_mixer(mixer),
28 m_maskRect(maskRect),
29 m_maskPtr(maskDab->data()),
30 m_maskStride(maskDab->bounds().width()),
31 m_samplePixelSize(sampleDab->colorSpace()->pixelSize()),
32 m_sampleRect(sampleRect),
33 m_samplePtr(sampleDab->data()),
34 m_sampleStride(sampleDab->bounds().width() * m_samplePixelSize)
35 {
36
37 }
38
39 inline void samplePixel(const QPoint &relativeSamplePoint) {
40 const QPoint maskPt(relativeSamplePoint - m_maskRect.topLeft() + m_sampleRect.topLeft());
41
42 const qint16 opacity = *(m_maskPtr + maskPt.x() + maskPt.y() * m_maskStride);
43 const quint8 *ptr = m_samplePtr + relativeSamplePoint.x() * m_samplePixelSize + relativeSamplePoint.y() * m_sampleStride;
44
45 m_mixer->accumulate(ptr, &opacity, opacity, 1);
46 }
47
48 static void verifySampleRadiusValue(qreal *sampleRadiusValue) {
49 KIS_SAFE_ASSERT_RECOVER(*sampleRadiusValue <= 1.0) {
50 *sampleRadiusValue = 1.0;
51 }
52 }
53
54
56 // if all the pixels we sampled appeared to be masked out
57 // we should ask the sampling algorithm to restart with
58 // bigger sampling radius
59
60 return m_mixer->currentWeightsSum() < 128;
61 }
62
64 const QRect m_maskRect;
65 quint8 *m_maskPtr;
66 const int m_maskStride;
68 const QRect m_sampleRect;
69 quint8 *m_samplePtr;
70 const int m_sampleStride;
71};
72
74{
76 KisFixedPaintDeviceSP maskDab, const QRect &maskRect,
77 KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
78 : m_mixer(mixer),
79 m_samplePixelSize(sampleDab->colorSpace()->pixelSize()),
80 m_sampleRect(sampleRect),
81 m_samplePtr(sampleDab->data()),
82 m_sampleStride(sampleDab->bounds().width() * m_samplePixelSize)
83 {
84 Q_UNUSED(maskDab);
85 Q_UNUSED(maskRect);
86 }
87
88 inline void samplePixel(const QPoint &relativeSamplePoint) {
89 const quint8 *ptr = m_samplePtr + relativeSamplePoint.x() * m_samplePixelSize + relativeSamplePoint.y() * m_sampleStride;
91 }
92
93 static void verifySampleRadiusValue(qreal *sampleRadiusValue) {
94 Q_UNUSED(sampleRadiusValue);
95 }
96
98 return false;
99 }
100
103 const QRect m_sampleRect;
104 quint8 *m_samplePtr;
105 const int m_sampleStride;
106};
107
134template<class WeightingModeWrapper>
135void sampleColor(const QRect &srcRect,
136 qreal sampleRadiusValue,
137 KisColorSmudgeSourceSP sourceDevice,
138 KisFixedPaintDeviceSP tempFixedDevice,
139 KisFixedPaintDeviceSP maskDab,
140 KoColor *resultColor)
141
142{
143 WeightingModeWrapper::verifySampleRadiusValue(&sampleRadiusValue);
144
145 KIS_ASSERT_RECOVER_RETURN(*resultColor->colorSpace() == *sourceDevice->colorSpace());
146 KIS_ASSERT_RECOVER_RETURN(*tempFixedDevice->colorSpace() == *sourceDevice->colorSpace());
147
148 const QRect minimalRect = QRect(srcRect.center(), QSize(1,1));
149
150 do {
151 const QRect sampleRect = sampleRadiusValue > 0 ?
152 KisAlgebra2D::blowRect(srcRect, 0.5 * (sampleRadiusValue - 1.0)) | minimalRect :
153 minimalRect;
154
155 tempFixedDevice->setRect(sampleRect);
156 tempFixedDevice->lazyGrowBufferWithoutInitialization();
157
158 const KoColorSpace *cs = tempFixedDevice->colorSpace();
159 const int numPixels = sampleRect.width() * sampleRect.height();
160 sourceDevice->readRect(sampleRect);
161 sourceDevice->readBytes(tempFixedDevice->data(), sampleRect);
162
165
166 QScopedPointer<KoMixColorsOp::Mixer> mixer(cs->mixColorsOp()->createMixer());
167
168 const int minSamples =
169 qMin(numPixels, qMax(64, qRound(0.02 * numPixels)));
170
171 WeightingModeWrapper weightingModeWrapper(mixer.data(),
172 maskDab, srcRect,
173 tempFixedDevice, sampleRect);
174
175 KoColor lastPickedColor(*resultColor);
176
177 for (int i = 0; i < minSamples; i++) {
178 const QPoint pt(hGen.generate(sampleRect.width() - 1),
179 vGen.generate(sampleRect.height() - 1));
180
181 weightingModeWrapper.samplePixel(pt);
182 }
183
184 mixer->computeMixedColor(resultColor->data());
185 lastPickedColor = *resultColor;
186
187 const int batchSize = 16;
188 int numSamplesLeft = numPixels - minSamples;
189
190 while (numSamplesLeft > 0) {
191 const int currentBatchSize = qMin(numSamplesLeft, batchSize);
192 for (int i = 0; i < currentBatchSize; i++) {
193 const QPoint pt(hGen.generate(sampleRect.width() - 1),
194 vGen.generate(sampleRect.height() - 1));
195
196 weightingModeWrapper.samplePixel(pt);
197 }
198
199 mixer->computeMixedColor(resultColor->data());
200
201 const quint8 difference =
202 cs->differenceA(resultColor->data(), lastPickedColor.data());
203
204 if (difference <= 2) break;
205
206 lastPickedColor = *resultColor;
207 numSamplesLeft -= currentBatchSize;
208 }
209
210 if (!weightingModeWrapper.shouldRestartWithBiggerRadius() || sampleRadiusValue >= 1.0) {
211 break;
212 }
213
214 sampleRadiusValue = qMin(1.0, sampleRadiusValue + 0.05);
215
216 } while (1);
217}
218
219}
220
221
222#endif //KRITA_KISCOLORSMUDGESAMPLEUTILS_H
a simple class to generate Halton sequence
void setRect(const QRect &rc)
const KoColorSpace * colorSpace() const
virtual quint8 differenceA(const quint8 *src1, const quint8 *src2) const =0
KoMixColorsOp * mixColorsOp
quint8 * data()
Definition KoColor.h:144
const KoColorSpace * colorSpace() const
return the current colorSpace
Definition KoColor.h:82
virtual void accumulate(const quint8 *data, const qint16 *weights, int weightSum, int nPixels)=0
virtual qint64 currentWeightsSum() const =0
virtual void accumulateAverage(const quint8 *data, int nPixels)=0
virtual Mixer * createMixer() const =0
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define bounds(x, a, b)
Rect blowRect(const Rect &rect, qreal coeff)
void sampleColor(const QRect &srcRect, qreal sampleRadiusValue, KisColorSmudgeSourceSP sourceDevice, KisFixedPaintDeviceSP tempFixedDevice, KisFixedPaintDeviceSP maskDab, KoColor *resultColor)
void samplePixel(const QPoint &relativeSamplePoint)
static void verifySampleRadiusValue(qreal *sampleRadiusValue)
AveragedSampleWrapper(KoMixColorsOp::Mixer *mixer, KisFixedPaintDeviceSP maskDab, const QRect &maskRect, KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
WeightedSampleWrapper(KoMixColorsOp::Mixer *mixer, KisFixedPaintDeviceSP maskDab, const QRect &maskRect, KisFixedPaintDeviceSP sampleDab, const QRect &sampleRect)
void samplePixel(const QPoint &relativeSamplePoint)
static void verifySampleRadiusValue(qreal *sampleRadiusValue)