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 }