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 }