File indexing completed on 2024-05-12 15:59:30
0001 /* 0002 * SPDX-FileCopyrightText: 2005 Bart Coppens <kde@bartcoppens.be> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "KoBasicHistogramProducers.h" 0008 0009 #include <QString> 0010 #include <klocalizedstring.h> 0011 0012 #include <KoConfig.h> 0013 #ifdef HAVE_OPENEXR 0014 #include <half.h> 0015 #endif 0016 0017 // #include "Ko_global.h" 0018 #include "KoIntegerMaths.h" 0019 #include "KoChannelInfo.h" 0020 0021 static const KoColorSpace* m_labCs = 0; 0022 0023 0024 KoBasicHistogramProducer::KoBasicHistogramProducer(const KoID& id, int channelCount, int nrOfBins) 0025 : m_channels(channelCount) 0026 , m_nrOfBins(nrOfBins) 0027 , m_colorSpace(0) 0028 , m_id(id) 0029 { 0030 m_bins.resize(m_channels); 0031 for (int i = 0; i < m_channels; i++) 0032 m_bins[i].resize(m_nrOfBins); 0033 m_outLeft.resize(m_channels); 0034 m_outRight.resize(m_channels); 0035 m_count = 0; 0036 m_from = 0.0; 0037 m_width = 1.0; 0038 } 0039 0040 KoBasicHistogramProducer::KoBasicHistogramProducer(const KoID& id, int nrOfBins, const KoColorSpace *cs) 0041 : m_nrOfBins(nrOfBins), 0042 m_colorSpace(cs), 0043 m_id(id) 0044 { 0045 Q_ASSERT(cs); 0046 m_channels = cs->channelCount(); 0047 0048 m_bins.resize(m_channels); 0049 for (int i = 0; i < m_channels; i++) 0050 m_bins[i].resize(m_nrOfBins); 0051 m_outLeft.resize(m_channels); 0052 m_outRight.resize(m_channels); 0053 m_count = 0; 0054 m_from = 0.0; 0055 m_width = 1.0; 0056 } 0057 0058 0059 void KoBasicHistogramProducer::clear() 0060 { 0061 m_count = 0; 0062 for (int i = 0; i < m_channels; i++) { 0063 for (int j = 0; j < m_nrOfBins; j++) { 0064 m_bins[i][j] = 0; 0065 } 0066 m_outRight[i] = 0; 0067 m_outLeft[i] = 0; 0068 } 0069 } 0070 0071 void KoBasicHistogramProducer::makeExternalToInternal() 0072 { 0073 // This function assumes that the pixel is has no 'gaps'. That is to say: if we start 0074 // at byte 0, we can get to the end of the pixel by adding consecutive size()s of 0075 // the channels 0076 QList<KoChannelInfo *> c = channels(); 0077 uint count = c.count(); 0078 int currentPos = 0; 0079 0080 for (uint i = 0; i < count; i++) { 0081 for (uint j = 0; j < count; j++) { 0082 if (c[j]->pos() == currentPos) { 0083 m_external.append(j); 0084 break; 0085 } 0086 } 0087 currentPos += c.at(m_external.at(m_external.count() - 1))->size(); 0088 } 0089 } 0090 0091 // ------------ U8 --------------------- 0092 0093 KoBasicU8HistogramProducer::KoBasicU8HistogramProducer(const KoID& id, const KoColorSpace *cs) 0094 : KoBasicHistogramProducer(id, 256, cs) 0095 { 0096 } 0097 0098 QString KoBasicU8HistogramProducer::positionToString(qreal pos) const 0099 { 0100 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX)); 0101 } 0102 0103 void KoBasicU8HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) 0104 { 0105 quint32 dstPixelSize = m_colorSpace->pixelSize(); 0106 quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; 0107 cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); 0108 0109 if (selectionMask) { 0110 quint8 *dst = dstPixels; 0111 while (nPixels > 0) { 0112 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0113 0114 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0115 m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; 0116 } 0117 m_count++; 0118 } 0119 dst += dstPixelSize; 0120 selectionMask++; 0121 nPixels--; 0122 } 0123 } else { 0124 quint8 *dst = dstPixels; 0125 while (nPixels > 0) { 0126 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0127 0128 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0129 m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; 0130 } 0131 m_count++; 0132 } 0133 dst += dstPixelSize; 0134 nPixels--; 0135 } 0136 } 0137 } 0138 0139 // ------------ U16 --------------------- 0140 0141 KoBasicU16HistogramProducer::KoBasicU16HistogramProducer(const KoID& id, const KoColorSpace *cs) 0142 : KoBasicHistogramProducer(id, 256, cs) 0143 { 0144 } 0145 0146 QString KoBasicU16HistogramProducer::positionToString(qreal pos) const 0147 { 0148 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX)); 0149 } 0150 0151 qreal KoBasicU16HistogramProducer::maximalZoom() const 0152 { 0153 return 1.0 / 255.0; 0154 } 0155 0156 void KoBasicU16HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) 0157 { 0158 // The view 0159 quint16 from = static_cast<quint16>(m_from * UINT16_MAX); 0160 quint16 width = static_cast<quint16>(m_width * UINT16_MAX + 0.5); // We include the end 0161 quint16 to = from + width; 0162 qreal factor = 255.0 / width; 0163 0164 quint32 dstPixelSize = m_colorSpace->pixelSize(); 0165 quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; 0166 cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); 0167 quint8 *dst = dstPixels; 0168 QVector<float> channels(m_colorSpace->channelCount()); 0169 0170 if (selectionMask) { 0171 while (nPixels > 0) { 0172 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { 0173 m_colorSpace->normalisedChannelsValue(dst,channels); 0174 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0175 quint16 value = channels[i]*UINT16_MAX; 0176 if (value > to) 0177 m_outRight[i]++; 0178 else if (value < from) 0179 m_outLeft[i]++; 0180 else 0181 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0182 } 0183 m_count++; 0184 } 0185 dst += dstPixelSize; 0186 selectionMask++; 0187 nPixels--; 0188 } 0189 } else { 0190 while (nPixels > 0) { 0191 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0192 m_colorSpace->normalisedChannelsValue(dst,channels); 0193 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0194 quint16 value = channels[i]*UINT16_MAX; 0195 0196 if (value > to) 0197 m_outRight[i]++; 0198 else if (value < from) 0199 m_outLeft[i]++; 0200 else 0201 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0202 } 0203 m_count++; 0204 } 0205 dst += dstPixelSize; 0206 nPixels--; 0207 } 0208 } 0209 } 0210 0211 // ------------ Float32 --------------------- 0212 KoBasicF32HistogramProducer::KoBasicF32HistogramProducer(const KoID& id, const KoColorSpace *cs) 0213 : KoBasicHistogramProducer(id, 256, cs) 0214 { 0215 } 0216 0217 QString KoBasicF32HistogramProducer::positionToString(qreal pos) const 0218 { 0219 return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct! 0220 } 0221 0222 qreal KoBasicF32HistogramProducer::maximalZoom() const 0223 { 0224 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromise for the moment 0225 return 1.0 / 255.0; 0226 } 0227 0228 void KoBasicF32HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) 0229 { 0230 // The view 0231 float from = static_cast<float>(m_from); 0232 float width = static_cast<float>(m_width); 0233 float to = from + width; 0234 float factor = 255.0 / width; 0235 0236 quint32 dstPixelSize = m_colorSpace->pixelSize(); 0237 quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; 0238 cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); 0239 quint8 *dst = dstPixels; 0240 QVector<float> channels(m_colorSpace->channelCount()); 0241 0242 if (selectionMask) { 0243 while (nPixels > 0) { 0244 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { 0245 m_colorSpace->normalisedChannelsValue(dst,channels); 0246 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0247 float value = channels[i]; 0248 if (value > to) 0249 m_outRight[i]++; 0250 else if (value < from) 0251 m_outLeft[i]++; 0252 else 0253 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0254 } 0255 m_count++; 0256 } 0257 dst += dstPixelSize; 0258 selectionMask++; 0259 nPixels--; 0260 0261 } 0262 } else { 0263 while (nPixels > 0) { 0264 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0265 m_colorSpace->normalisedChannelsValue(dst,channels); 0266 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0267 float value = channels[i]; 0268 if (value > to) 0269 m_outRight[i]++; 0270 else if (value < from) 0271 m_outLeft[i]++; 0272 else 0273 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0274 } 0275 m_count++; 0276 } 0277 dst += dstPixelSize; 0278 nPixels--; 0279 0280 } 0281 } 0282 } 0283 0284 #ifdef HAVE_OPENEXR 0285 // ------------ Float16 Half --------------------- 0286 KoBasicF16HalfHistogramProducer::KoBasicF16HalfHistogramProducer(const KoID& id, 0287 const KoColorSpace *cs) 0288 : KoBasicHistogramProducer(id, 256, cs) 0289 { 0290 } 0291 0292 QString KoBasicF16HalfHistogramProducer::positionToString(qreal pos) const 0293 { 0294 return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct! 0295 } 0296 0297 qreal KoBasicF16HalfHistogramProducer::maximalZoom() const 0298 { 0299 // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromise for the moment 0300 return 1.0 / 255.0; 0301 } 0302 0303 void KoBasicF16HalfHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) 0304 { 0305 // The view 0306 float from = static_cast<float>(m_from); 0307 float width = static_cast<float>(m_width); 0308 float to = from + width; 0309 float factor = 255.0 / width; 0310 0311 quint32 dstPixelSize = m_colorSpace->pixelSize(); 0312 quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; 0313 cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); 0314 quint8 *dst = dstPixels; 0315 QVector<float> channels(m_colorSpace->channelCount()); 0316 0317 if (selectionMask) { 0318 while (nPixels > 0) { 0319 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { 0320 m_colorSpace->normalisedChannelsValue(dst,channels); 0321 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0322 float value = channels[i]; 0323 if (value > to) 0324 m_outRight[i]++; 0325 else if (value < from) 0326 m_outLeft[i]++; 0327 else 0328 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0329 } 0330 m_count++; 0331 } 0332 dst += dstPixelSize; 0333 selectionMask++; 0334 nPixels--; 0335 } 0336 } else { 0337 while (nPixels > 0) { 0338 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0339 m_colorSpace->normalisedChannelsValue(dst,channels); 0340 for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { 0341 float value = channels[i]; 0342 if (value > to) 0343 m_outRight[i]++; 0344 else if (value < from) 0345 m_outLeft[i]++; 0346 else 0347 m_bins[i][static_cast<quint8>((value - from) * factor)]++; 0348 } 0349 m_count++; 0350 } 0351 dst += dstPixelSize; 0352 nPixels--; 0353 } 0354 } 0355 } 0356 #endif 0357 0358 // ------------ Generic RGB --------------------- 0359 KoGenericRGBHistogramProducer::KoGenericRGBHistogramProducer() 0360 : KoBasicHistogramProducer(KoID("GENRGBHISTO", i18n("Generic RGB Histogram")), 3, 256) 0361 { 0362 /* we set 0 as colorspece, because we are not based on a specific colorspace. This 0363 is no problem for the superclass since we override channels() */ 0364 m_channelsList.append(new KoChannelInfo(i18n("R"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); 0365 m_channelsList.append(new KoChannelInfo(i18n("G"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); 0366 m_channelsList.append(new KoChannelInfo(i18n("B"), 2, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); 0367 } 0368 0369 QList<KoChannelInfo *> KoGenericRGBHistogramProducer::channels() 0370 { 0371 return m_channelsList; 0372 } 0373 0374 QString KoGenericRGBHistogramProducer::positionToString(qreal pos) const 0375 { 0376 return QString("%1").arg(static_cast<quint8>(pos * UINT8_MAX)); 0377 } 0378 0379 qreal KoGenericRGBHistogramProducer::maximalZoom() const 0380 { 0381 return 1.0; 0382 } 0383 0384 0385 void KoGenericRGBHistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) 0386 { 0387 for (int i = 0; i < m_channels; i++) { 0388 m_outRight[i] = 0; 0389 m_outLeft[i] = 0; 0390 } 0391 0392 QColor c; 0393 qint32 pSize = cs->pixelSize(); 0394 if (selectionMask) { 0395 while (nPixels > 0) { 0396 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { 0397 cs->toQColor(pixels, &c); 0398 m_bins[0][c.red()]++; 0399 m_bins[1][c.green()]++; 0400 m_bins[2][c.blue()]++; 0401 0402 m_count++; 0403 } 0404 pixels += pSize; 0405 selectionMask++; 0406 nPixels--; 0407 } 0408 0409 } else { 0410 while (nPixels > 0) { 0411 0412 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0413 cs->toQColor(pixels, &c); 0414 m_bins[0][c.red()]++; 0415 m_bins[1][c.green()]++; 0416 m_bins[2][c.blue()]++; 0417 0418 m_count++; 0419 } 0420 pixels += pSize; 0421 nPixels--; 0422 } 0423 } 0424 } 0425 0426 KoGenericRGBHistogramProducerFactory::KoGenericRGBHistogramProducerFactory() 0427 : KoHistogramProducerFactory(KoID("GENRGBHISTO", i18n("Generic RGB Histogram"))) 0428 { 0429 } 0430 0431 // ------------ Generic L*a*b* --------------------- 0432 KoGenericLabHistogramProducer::KoGenericLabHistogramProducer() 0433 : KoBasicHistogramProducer(KoID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256) 0434 { 0435 /* we set 0 as colorspace, because we are not based on a specific colorspace. This 0436 is no problem for the superclass since we override channels() */ 0437 m_channelsList.append(new KoChannelInfo(i18n("L*"), 0, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); 0438 m_channelsList.append(new KoChannelInfo(i18n("a*"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); 0439 m_channelsList.append(new KoChannelInfo(i18n("b*"), 2, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8)); 0440 0441 if (!m_labCs) { 0442 m_labCs = KoColorSpaceRegistry::instance()->lab16(); 0443 } 0444 m_colorSpace = m_labCs; 0445 } 0446 KoGenericLabHistogramProducer::~KoGenericLabHistogramProducer() 0447 { 0448 delete m_channelsList[0]; 0449 delete m_channelsList[1]; 0450 delete m_channelsList[2]; 0451 } 0452 0453 QList<KoChannelInfo *> KoGenericLabHistogramProducer::channels() 0454 { 0455 return m_channelsList; 0456 } 0457 0458 QString KoGenericLabHistogramProducer::positionToString(qreal pos) const 0459 { 0460 return QString("%1").arg(static_cast<quint16>(pos * UINT16_MAX)); 0461 } 0462 0463 qreal KoGenericLabHistogramProducer::maximalZoom() const 0464 { 0465 return 1.0; 0466 } 0467 0468 0469 void KoGenericLabHistogramProducer::addRegionToBin(const quint8 *pixels, const quint8 *selectionMask, quint32 nPixels, const KoColorSpace *cs) 0470 { 0471 for (int i = 0; i < m_channels; i++) { 0472 m_outRight[i] = 0; 0473 m_outLeft[i] = 0; 0474 } 0475 0476 qint32 dstPixelSize = m_colorSpace->pixelSize(); 0477 0478 quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; 0479 cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); 0480 0481 qint32 pSize = cs->pixelSize(); 0482 0483 if (selectionMask) { 0484 while (nPixels > 0) { 0485 if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { 0486 m_count++; 0487 } 0488 pixels += pSize; 0489 selectionMask++; 0490 nPixels--; 0491 } 0492 } else { 0493 quint8 *dst = dstPixels; 0494 while (nPixels > 0) { 0495 if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { 0496 0497 m_bins[0][m_colorSpace->scaleToU8(dst, 0)]++; 0498 m_bins[1][m_colorSpace->scaleToU8(dst, 1)]++; 0499 m_bins[2][m_colorSpace->scaleToU8(dst, 2)]++; 0500 0501 m_count++; 0502 } 0503 dst+= dstPixelSize; 0504 nPixels--; 0505 } 0506 } 0507 delete[] dstPixels; 0508 } 0509 0510 KoGenericLabHistogramProducerFactory::KoGenericLabHistogramProducerFactory() 0511 : KoHistogramProducerFactory(KoID("GENLABHISTO", i18n("Generic L*a*b* Histogram"))) 0512 { 0513 }