Krita Source Code Documentation
Loading...
Searching...
No Matches
HistogramComputationStrokeStrategy.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Agata Cacko <tamtamy.tymona@gmail.com>
3 * SPDX-FileCopyrightText: 2023 Dmitry Kazakov <dimula73@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
8
9#include "KoColorSpace.h"
10
11#include "krita_utils.h"
12#include "kis_image.h"
14
16{
17
19 {
20 public:
21 ProcessData(QRect rect, int _jobId)
24 , jobId(_jobId)
25 {}
26
28 int jobId; // id in the list of results
29 };
30
32 std::vector<HistVector> results;
33 std::vector<HistVector> resultsLog;
34 float maximumValue = 0.0f;
35};
36
37
39 : KisIdleTaskStrokeStrategy(QLatin1String("ComputeHistogram"), kundo2_i18n("Update histogram"))
40 , m_d(new Private)
41{
42 m_d->image = image;
43}
44
48
50{
52
54 int i = 0;
56 m_d->results.resize(tileRects.size());
57 m_d->resultsLog.resize(tileRects.size());
58
59 Q_FOREACH (const QRect &tileRectangle, tileRects) {
60 jobsData << new HistogramComputationStrokeStrategy::Private::ProcessData(tileRectangle, i);
61 i++;
62 }
63 addMutatedJobs(jobsData);
64}
65
67{
68 Private::ProcessData *d_pd = dynamic_cast<Private::ProcessData*>(data);
69
70 if (!d_pd) {
72 return;
73 }
74
75 QRect calculate = d_pd->rectToCalculate;
76
77 KisPaintDeviceSP m_dev = m_d->image->projection();
78 QRect imageBounds = m_d->image->bounds();
79
80 const KoColorSpace *cs = m_dev->colorSpace();
81 quint32 channelCount = m_dev->channelCount();
82 quint32 pixelSize = m_dev->pixelSize();
83
84 int imageSize = imageBounds.width() * imageBounds.height();
85 int nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms
86
87 if (calculate.isEmpty())
88 return;
89
90 initiateVector(m_d->results[d_pd->jobId], cs);
91 initiateVector(m_d->resultsLog[d_pd->jobId], cs);
92
93 quint32 toSkip = nSkip;
94
95 float maximum = 1.0;
96 QVector<float> channelValues(channelCount);
97 if (cs->hasHighDynamicRange()) {
98 KisSequentialConstIterator it(m_dev, calculate);
99
100 int numConseqPixels = it.nConseqPixels();
101 while (it.nextPixels(numConseqPixels)) {
102 numConseqPixels = it.nConseqPixels();
103 const quint8* pixel = it.rawDataConst();
104 for (int k = 0; k < numConseqPixels; ++k) {
105 cs->normalisedChannelsValue(pixel, channelValues);
106 for (int chan = 0; chan < (int)channelCount; ++chan) {
107 maximum = qMax(maximum, channelValues.at(chan));
108 }
109 pixel += pixelSize;
110 }
111 }
112 }
113 m_d->maximumValue = maximum;
114
115 const double maximumMultiplier = 255.0/maximum;
116 const double logMaxMultiplier = 255.0/(std::log10(maximum*10)*0.1);
117
118
119 KisSequentialConstIterator it(m_dev, calculate);
120
121 int numConseqPixels = it.nConseqPixels();
122 while (it.nextPixels(numConseqPixels)) {
123
124 numConseqPixels = it.nConseqPixels();
125 const quint8* pixel = it.rawDataConst();
126 for (int k = 0; k < numConseqPixels; ++k) {
127 cs->normalisedChannelsValue(pixel, channelValues);
128 if (--toSkip == 0) {
129
130 for (int chan = 0; chan < (int)channelCount; ++chan) {
131 m_d->results[d_pd->jobId][chan][ qBound(0, qRound(channelValues.at(chan)*maximumMultiplier), 255) ]++;
132 m_d->resultsLog[d_pd->jobId][chan][ qBound(0, qRound(std::log10(channelValues.at(chan)*10)*0.1*logMaxMultiplier), 255) ]++;
133 }
134 toSkip = nSkip;
135 }
136 pixel += pixelSize;
137 }
138 }
139}
140
142{
143 HistogramData hisData;
144 hisData.colorSpace = m_d->image->projection()->colorSpace();
145 hisData.maximumValue = m_d->maximumValue;
146
147 if (m_d->results.size() == 1) {
148 hisData.bins = m_d->results[0];
149 hisData.binsLog = m_d->resultsLog[0];
150 Q_EMIT computationResultReady(hisData);
151 } else {
152
153 quint32 channelCount = m_d->image->projection()->channelCount();
154
155 initiateVector(hisData.bins, hisData.colorSpace);
156 initiateVector(hisData.binsLog, hisData.colorSpace);
157
158 for (int chan = 0; chan < (int)channelCount; chan++) {
159 int bsize = hisData.bins[chan].size();
160
161 for (int bi = 0; bi < bsize; bi++) {
162 hisData.bins[chan][bi] = 0;
163 for (int i = 0; i < (int)m_d->results.size(); i++) {
164 hisData.bins[chan][bi] += m_d->results[i][chan][bi];
165 }
166 }
167 int b2size = hisData.binsLog[chan].size();
168
169 for (int bi = 0; bi < b2size; bi++) {
170 hisData.binsLog[chan][bi] = 0;
171 for (int i = 0; i < (int)m_d->resultsLog.size(); i++) {
172 hisData.binsLog[chan][bi] += m_d->resultsLog[i][chan][bi];
173 }
174 }
175 }
176
177 Q_EMIT computationResultReady(hisData);
178 }
179
181}
182
184{
185 vec.resize(colorSpace->channelCount());
186 for (auto &bin : vec) {
187 bin.resize(std::numeric_limits<quint8>::max() + 1);
188 }
189}
std::vector< std::vector< quint32 > > HistVector
void initiateVector(HistVector &vec, const KoColorSpace *colorSpace)
void computationResultReady(HistogramData data)
void doStrokeCallback(KisStrokeJobData *data) override
quint32 pixelSize() const
quint32 channelCount() const
const KoColorSpace * colorSpace() const
ALWAYS_INLINE const quint8 * rawDataConst() const
virtual void doStrokeCallback(KisStrokeJobData *data)
void addMutatedJobs(const QVector< KisStrokeJobData * > list)
virtual bool hasHighDynamicRange() const =0
virtual quint32 channelCount() const =0
virtual void normalisedChannelsValue(const quint8 *pixel, QVector< float > &channels) const =0
KUndo2MagicString kundo2_i18n(const char *text)
QVector< QRect > splitRectIntoPatches(const QRect &rc, const QSize &patchSize)
QSize optimalPatchSize()