File indexing completed on 2024-05-12 15:58:19

0001 /*
0002  *  SPDX-FileCopyrightText: 2004 Boudewijn Rempt
0003  *  SPDX-FileCopyrightText: 2005 Bart Coppens <kde@bartcoppens.be>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_histogram.h"
0009 
0010 #include <QVector>
0011 
0012 #include "kis_image.h"
0013 #include "kis_paint_layer.h"
0014 #include "kis_paint_device.h"
0015 #include "KoColorSpace.h"
0016 #include "kis_debug.h"
0017 #include "kis_iterator_ng.h"
0018 
0019 KisHistogram::KisHistogram(const KisPaintLayerSP layer,
0020                            KoHistogramProducer *producer,
0021                            const enumHistogramType type)
0022     : m_paintDevice(layer->projection())
0023 {
0024     Q_ASSERT(producer);
0025 
0026     KisImageSP imageSP = layer->image().toStrongRef();
0027     if (imageSP) {
0028         m_bounds = imageSP->bounds();
0029     }
0030     m_type = type;
0031     m_producer = producer;
0032     m_selection = false;
0033     m_channel = 0;
0034 
0035     updateHistogram();
0036 }
0037 
0038 KisHistogram::KisHistogram(const KisPaintDeviceSP paintdev,
0039                            const QRect &bounds,
0040                            KoHistogramProducer *producer,
0041                            const enumHistogramType type)
0042     : m_paintDevice(paintdev)
0043 {
0044     Q_ASSERT(producer);
0045 
0046     m_bounds = bounds;
0047     m_producer = producer;
0048     m_type = type;
0049 
0050     m_selection = false;
0051     m_channel = 0;
0052 
0053     // TODO: Why does Krita crash when updateHistogram() is *not* called here?
0054     updateHistogram();
0055 }
0056 
0057 KisHistogram::~KisHistogram()
0058 {
0059     delete m_producer;
0060 }
0061 
0062 void KisHistogram::updateHistogram()
0063 {
0064     if (m_bounds.isEmpty()) {
0065         int numChannels = m_producer->channels().count();
0066 
0067         m_completeCalculations.clear();
0068         m_completeCalculations.resize(numChannels);
0069 
0070         m_completeCalculations.clear();
0071         m_completeCalculations.resize(numChannels);
0072 
0073         return;
0074     }
0075 
0076     KisSequentialConstIterator srcIt(m_paintDevice, m_bounds);
0077     const KoColorSpace* cs = m_paintDevice->colorSpace();
0078 
0079     // Let the producer do it's work
0080     m_producer->clear();
0081 
0082 
0083     // XXX: the original code depended on their being a selection mask in the iterator
0084     //      if the paint device had a selection. When we changed that to passing an
0085     //      explicit selection to the createRectIterator call, that broke because
0086     //      paint devices didn't know about their selections anymore.
0087     //      updateHistogram should get a selection parameter.
0088     int numConseqPixels = srcIt.nConseqPixels();
0089     while (srcIt.nextPixels(numConseqPixels)) {
0090 
0091         numConseqPixels = srcIt.nConseqPixels();
0092         m_producer->addRegionToBin(srcIt.oldRawData(), 0, numConseqPixels, cs);
0093     }
0094 
0095     computeHistogram();
0096 }
0097 
0098 void KisHistogram::computeHistogram()
0099 {
0100     if (!m_producer) return;
0101 
0102     m_completeCalculations = calculateForRange(m_producer->viewFrom(),
0103                              m_producer->viewFrom() + m_producer->viewWidth());
0104 
0105     if (m_selection) {
0106         m_selectionCalculations = calculateForRange(m_selFrom, m_selTo);
0107     } else {
0108         m_selectionCalculations.clear();
0109     }
0110 
0111 #if 1
0112     dump();
0113 #endif
0114 }
0115 
0116 KisHistogram::Calculations KisHistogram::calculations()
0117 {
0118     return m_completeCalculations.at(m_channel);
0119 }
0120 
0121 KisHistogram::Calculations KisHistogram::selectionCalculations()
0122 {
0123     return m_selectionCalculations.at(m_channel);
0124 }
0125 
0126 QVector<KisHistogram::Calculations> KisHistogram::calculateForRange(double from, double to)
0127 {
0128     QVector<Calculations> calculations;
0129     if (m_producer) {
0130         uint count = m_producer->channels().count();
0131 
0132         for (uint i = 0; i < count; i++) {
0133             calculations.append(calculateSingleRange(i, from, to));
0134         }
0135     }
0136     return calculations;
0137 }
0138 
0139 KisHistogram::Calculations KisHistogram::calculateSingleRange(int channel, double from, double to)
0140 {
0141     Calculations c;
0142 
0143     // XXX If from == to, we only want a specific bin, handle that properly!
0144 
0145     double max = from, min = to, total = 0.0, mean = 0.0; //, median = 0.0, stddev = 0.0;
0146     quint32 high = 0, low = (quint32) - 1, count = 0;
0147 
0148     if (m_producer->count() == 0) {
0149         // We won't get anything, even if a range is specified
0150         // XXX make sure all initial '0' values are correct here!
0151         return c;
0152     }
0153 
0154     qint32 totbins = m_producer->numberOfBins();
0155     quint32 current;
0156 
0157     // convert the double range into actual bins:
0158     double factor = static_cast<double>(totbins) / m_producer->viewWidth();
0159 
0160     qint32 fromBin = static_cast<qint32>((from - m_producer->viewFrom()) * factor);
0161     qint32 toBin = fromBin + static_cast<qint32>((to - from) * factor);
0162 
0163     // Min, max, count, low, high
0164     for (qint32 i = fromBin; i < toBin; i++) {
0165         current = m_producer->getBinAt(channel, i);
0166         double pos = static_cast<double>(i) / factor + from;
0167         if (current > high)
0168             high = current;
0169         if (current < low)
0170             low = current;
0171         if (current > 0) {
0172             if (pos < min)
0173                 min = pos;
0174             if (pos > max)
0175                 max = pos;
0176         }
0177         // We do the count here as well.
0178         // we can't use m_producer->count() for this, because of the range
0179         count += current;
0180         total += current * pos;
0181     }
0182 
0183     if (count > 0)
0184         mean = total / count;
0185 
0186     c.m_high = high;
0187     c.m_low = low;
0188     c.m_count = count;
0189     c.m_min = min;
0190     c.m_max = max;
0191     c.m_mean = mean;
0192     c.m_total = total;
0193 
0194     return c;
0195 }
0196 
0197 
0198 void KisHistogram::dump()
0199 {
0200     dbgMath << "Histogram";
0201 
0202     switch (m_type) {
0203     case LINEAR:
0204         dbgMath << "Linear histogram";
0205         break;
0206     case LOGARITHMIC:
0207         dbgMath << "Logarithmic histogram";
0208     }
0209 
0210     dbgMath << "Dumping channel" << m_channel;
0211     Calculations c = calculations();
0212 
0213     /*        for( int i = 0; i <256; ++i ) {
0214             dbgMath <<"Value"
0215                   << QString().setNum(i)
0216                   << ": "
0217                   <<  QString().setNum(m_values[i])
0218                   << "\n";
0219             }*/
0220     dbgMath << "";
0221 
0222     dbgMath << "Max:" << QString().setNum(c.getMax()) << "";
0223     dbgMath << "Min:" << QString().setNum(c.getMin()) << "";
0224     dbgMath << "High:" << QString().setNum(c.getHighest()) << "";
0225     dbgMath << "Low:" << QString().setNum(c.getLowest()) << "";
0226     dbgMath << "Mean:" << m_producer->positionToString(c.getMean()) << "";
0227     dbgMath << "Total:" << QString().setNum(c.getTotal()) << "";
0228     //    dbgMath <<"Median:" << QString().setNum(m_median) <<"";
0229     //    dbgMath <<"Stddev:" << QString().setNum(m_stddev) <<"";
0230     //    dbgMath <<"percentile:" << QString().setNum(m_percentile) <<"";
0231 
0232     dbgMath << "";
0233 }