File indexing completed on 2024-12-22 04:14:49

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Agata Cacko <tamtamy.tymona@gmail.com>
0003  *  SPDX-FileCopyrightText: 2023 Dmitry Kazakov <dimula73@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "HistogramComputationStrokeStrategy.h"
0008 
0009 #include "KoColorSpace.h"
0010 
0011 #include "krita_utils.h"
0012 #include "kis_image.h"
0013 #include "kis_sequential_iterator.h"
0014 
0015 struct HistogramComputationStrokeStrategy::Private
0016 {
0017 
0018     class ProcessData : public KisStrokeJobData
0019     {
0020     public:
0021         ProcessData(QRect rect, int _jobId)
0022             : KisStrokeJobData(CONCURRENT)
0023             , rectToCalculate(rect)
0024             , jobId(_jobId)
0025         {}
0026 
0027         QRect rectToCalculate;
0028         int jobId; // id in the list of results
0029     };
0030 
0031     KisImageSP image;
0032     std::vector<HistVector> results;
0033 };
0034 
0035 
0036 HistogramComputationStrokeStrategy::HistogramComputationStrokeStrategy(KisImageSP image)
0037     : KisIdleTaskStrokeStrategy(QLatin1String("ComputeHistogram"), kundo2_i18n("Update histogram"))
0038     , m_d(new Private)
0039 {
0040     m_d->image = image;
0041 }
0042 
0043 HistogramComputationStrokeStrategy::~HistogramComputationStrokeStrategy()
0044 {
0045 }
0046 
0047 void HistogramComputationStrokeStrategy::initStrokeCallback()
0048 {
0049     KisIdleTaskStrokeStrategy::initStrokeCallback();
0050 
0051     QVector<KisStrokeJobData*> jobsData;
0052     int i = 0;
0053     QVector<QRect> tileRects = KritaUtils::splitRectIntoPatches(m_d->image->bounds(), KritaUtils::optimalPatchSize());
0054     m_d->results.resize(tileRects.size());
0055 
0056     Q_FOREACH (const QRect &tileRectangle, tileRects) {
0057         jobsData << new HistogramComputationStrokeStrategy::Private::ProcessData(tileRectangle, i);
0058         i++;
0059     }
0060     addMutatedJobs(jobsData);
0061 }
0062 
0063 void HistogramComputationStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
0064 {
0065     Private::ProcessData *d_pd = dynamic_cast<Private::ProcessData*>(data);
0066 
0067     if (!d_pd) {
0068         KisIdleTaskStrokeStrategy::doStrokeCallback(data);
0069         return;
0070     }
0071 
0072     QRect calculate = d_pd->rectToCalculate;
0073 
0074     KisPaintDeviceSP m_dev = m_d->image->projection();
0075     QRect imageBounds = m_d->image->bounds();
0076 
0077     const KoColorSpace *cs = m_dev->colorSpace();
0078     quint32 channelCount = m_dev->channelCount();
0079     quint32 pixelSize = m_dev->pixelSize();
0080 
0081     int imageSize = imageBounds.width() * imageBounds.height();
0082     int nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms
0083 
0084     if (calculate.isEmpty())
0085         return;
0086 
0087     initiateVector(m_d->results[d_pd->jobId], cs);
0088 
0089     quint32 toSkip = nSkip;
0090 
0091     KisSequentialConstIterator it(m_dev, calculate);
0092 
0093     int numConseqPixels = it.nConseqPixels();
0094     while (it.nextPixels(numConseqPixels)) {
0095 
0096         numConseqPixels = it.nConseqPixels();
0097         const quint8* pixel = it.rawDataConst();
0098         for (int k = 0; k < numConseqPixels; ++k) {
0099             if (--toSkip == 0) {
0100                 for (int chan = 0; chan < (int)channelCount; ++chan) {
0101                     m_d->results[d_pd->jobId][chan][cs->scaleToU8(pixel, chan)]++;
0102                 }
0103                 toSkip = nSkip;
0104             }
0105             pixel += pixelSize;
0106         }
0107     }
0108 }
0109 
0110 void HistogramComputationStrokeStrategy::finishStrokeCallback()
0111 {
0112     HistogramData hisData;
0113     hisData.colorSpace = m_d->image->projection()->colorSpace();
0114 
0115     if (m_d->results.size() == 1) {
0116         hisData.bins = m_d->results[0];
0117         emit computationResultReady(hisData);
0118     } else {
0119 
0120         quint32 channelCount = m_d->image->projection()->channelCount();
0121 
0122         initiateVector(hisData.bins, hisData.colorSpace);
0123 
0124         for (int chan = 0; chan < (int)channelCount; chan++) {
0125             int bsize = hisData.bins[chan].size();
0126 
0127             for (int bi = 0; bi < bsize; bi++) {
0128                 hisData.bins[chan][bi] = 0;
0129                 for (int i = 0; i < (int)m_d->results.size(); i++) {
0130                     hisData.bins[chan][bi] += m_d->results[i][chan][bi];
0131                 }
0132             }
0133         }
0134 
0135         emit computationResultReady(hisData);
0136     }
0137 
0138     KisIdleTaskStrokeStrategy::finishStrokeCallback();
0139 }
0140 
0141 void HistogramComputationStrokeStrategy::initiateVector(HistVector &vec, const KoColorSpace *colorSpace)
0142 {
0143     vec.resize(colorSpace->channelCount());
0144     for (auto &bin : vec) {
0145         bin.resize(std::numeric_limits<quint8>::max() + 1);
0146     }
0147 }