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