/* * Copyright (c) 2005 Bart Coppens * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include "config.h" #ifdef HAVE_OPENEXR #include #endif #include "kis_global.h" #include "kis_basic_histogram_producers.h" #include "kis_integer_maths.h" #include "kis_channelinfo.h" #include "kis_colorspace.h" #include "kis_lab_colorspace.h" KisLabColorSpace* KisGenericLabHistogramProducer::m_labCs = 0; KisBasicHistogramProducer::KisBasicHistogramProducer(const KisID& id, int channels, int nrOfBins, KisColorSpace *cs) : m_channels(channels), m_nrOfBins(nrOfBins), m_colorSpace(cs), m_id(id) { m_bins.resize(m_channels); for (int i = 0; i < m_channels; i++) m_bins.at(i).resize(m_nrOfBins); m_outLeft.resize(m_channels); m_outRight.resize(m_channels); m_count = 0; m_from = 0.0; m_width = 1.0; } void KisBasicHistogramProducer::clear() { m_count = 0; for (int i = 0; i < m_channels; i++) { for (int j = 0; j < m_nrOfBins; j++) { m_bins.at(i).at(j) = 0; } m_outRight.at(i) = 0; m_outLeft.at(i) = 0; } } void KisBasicHistogramProducer::makeExternalToInternal() { // This function assumes that the pixel is has no 'gaps'. That is to say: if we start // at byte 0, we can get to the end of the pixel by adding consecutive size()s of // the channels TQValueVector c = channels(); uint count = c.count(); int currentPos = 0; for (uint i = 0; i < count; i++) { for (uint j = 0; j < count; j++) { if (c.at(j)->pos() == currentPos) { m_external.append(j); break; } } currentPos += c.at(m_external.at(m_external.count() - 1))->size(); } } // ------------ U8 --------------------- KisBasicU8HistogramProducer::KisBasicU8HistogramProducer(const KisID& id, KisColorSpace *cs) : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) { } TQString KisBasicU8HistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos * UINT8_MAX)); } void KisBasicU8HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { if (!pixels) return; if (!cs) return; if (nPixels == 0) return; TQ_INT32 pSize = cs->pixelSize(); if ( selectionMask ) { while (nPixels > 0) { if ( ! (m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) { for (int i = 0; i < m_channels; i++) { m_bins.at(i).at(pixels[i])++; } m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) { for (int i = 0; i < m_channels; i++) { m_bins.at(i).at(pixels[i])++; } m_count++; } pixels += pSize; nPixels--; } } } // ------------ U16 --------------------- KisBasicU16HistogramProducer::KisBasicU16HistogramProducer(const KisID& id, KisColorSpace *cs) : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) { } TQString KisBasicU16HistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos * UINT8_MAX)); } double KisBasicU16HistogramProducer::maximalZoom() const { return 1.0 / 255.0; } void KisBasicU16HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { // The view TQ_UINT16 from = static_cast(m_from * UINT16_MAX); TQ_UINT16 width = static_cast(m_width * UINT16_MAX + 0.5); // We include the end TQ_UINT16 to = from + width; double factor = 255.0 / width; TQ_INT32 pSize = cs->pixelSize(); if ( selectionMask ) { TQ_UINT16* pixel = reinterpret_cast(pixels); while (nPixels > 0) { if ( ! ((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { for (int i = 0; i < m_channels; i++) { TQ_UINT16 value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { TQ_UINT16* pixel = reinterpret_cast(pixels); if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { for (int i = 0; i < m_channels; i++) { TQ_UINT16 value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; nPixels--; } } } // ------------ Float32 --------------------- KisBasicF32HistogramProducer::KisBasicF32HistogramProducer(const KisID& id, KisColorSpace *cs) : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) { } TQString KisBasicF32HistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos)); // XXX I doubt this is correct! } double KisBasicF32HistogramProducer::maximalZoom() const { // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment return 1.0 / 255.0; } void KisBasicF32HistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { // The view float from = static_cast(m_from); float width = static_cast(m_width); float to = from + width; float factor = 255.0 / width; TQ_INT32 pSize = cs->pixelSize(); if ( selectionMask ) { while (nPixels > 0) { float* pixel = reinterpret_cast(pixels); if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { for (int i = 0; i < m_channels; i++) { float value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { float* pixel = reinterpret_cast(pixels); if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { for (int i = 0; i < m_channels; i++) { float value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; nPixels--; } } } #ifdef HAVE_OPENEXR // ------------ Float16 Half --------------------- KisBasicF16HalfHistogramProducer::KisBasicF16HalfHistogramProducer(const KisID& id, KisColorSpace *cs) : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) { } TQString KisBasicF16HalfHistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos)); // XXX I doubt this is correct! } double KisBasicF16HalfHistogramProducer::maximalZoom() const { // XXX What _is_ the maximal zoom here? I don't think there is one with floats, so this seems a fine compromis for the moment return 1.0 / 255.0; } void KisBasicF16HalfHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { // The view float from = static_cast(m_from); float width = static_cast(m_width); float to = from + width; float factor = 255.0 / width; TQ_INT32 pSize = cs->pixelSize(); if ( selectionMask ) { while (nPixels > 0) { half* pixel = reinterpret_cast(pixels); if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { for (int i = 0; i < m_channels; i++) { float value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { half* pixel = reinterpret_cast(pixels); if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { for (int i = 0; i < m_channels; i++) { float value = pixel[i]; if (value > to) m_outRight.at(i)++; else if (value < from) m_outLeft.at(i)++; else m_bins.at(i).at(static_cast((value - from) * factor))++; } m_count++; } pixels += pSize; nPixels--; } } } #endif // ------------ Generic RGB --------------------- KisGenericRGBHistogramProducer::KisGenericRGBHistogramProducer() : KisBasicHistogramProducer(KisID("GENRGBHISTO", i18n("Generic RGB Histogram")), 3, 256, 0) { /* we set 0 as colorspece, because we are not based on a specific colorspace. This is no problem for the superclass since we override channels() */ m_channelsList.append(new KisChannelInfo(i18n("R"), i18n("R"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(255,0,0))); m_channelsList.append(new KisChannelInfo(i18n("G"), i18n("G"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,255,0))); m_channelsList.append(new KisChannelInfo(i18n("B"), i18n("B"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, TQColor(0,0,255))); } TQValueVector KisGenericRGBHistogramProducer::channels() { return m_channelsList; } TQString KisGenericRGBHistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos * UINT8_MAX)); } double KisGenericRGBHistogramProducer::maximalZoom() const { return 1.0; } void KisGenericRGBHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { for (int i = 0; i < m_channels; i++) { m_outRight.at(i) = 0; m_outLeft.at(i) = 0; } TQColor c; TQ_INT32 pSize = cs->pixelSize(); if (selectionMask) { while (nPixels > 0) { if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { cs->toTQColor(pixels, &c); m_bins.at(0).at(c.red())++; m_bins.at(1).at(c.green())++; m_bins.at(2).at(c.blue())++; m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { cs->toTQColor(pixels, &c); m_bins.at(0).at(c.red())++; m_bins.at(1).at(c.green())++; m_bins.at(2).at(c.blue())++; m_count++; } pixels += pSize; nPixels--; } } } // ------------ Generic L*a*b* --------------------- KisGenericLabHistogramProducer::KisGenericLabHistogramProducer() : KisBasicHistogramProducer(KisID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256, 0) { /* we set 0 as colorspace, because we are not based on a specific colorspace. This is no problem for the superclass since we override channels() */ m_channelsList.append(new KisChannelInfo(i18n("L*"), i18n("L"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); m_channelsList.append(new KisChannelInfo(i18n("a*"), i18n("a"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); m_channelsList.append(new KisChannelInfo(i18n("b*"), i18n("b"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8)); if (!m_labCs) { KisProfile *labProfile = new KisProfile(cmsCreateLabProfile(NULL)); m_labCs = new KisLabColorSpace(0, labProfile); } m_colorSpace = m_labCs; } KisGenericLabHistogramProducer::~KisGenericLabHistogramProducer() { delete m_channelsList[0]; delete m_channelsList[1]; delete m_channelsList[2]; } TQValueVector KisGenericLabHistogramProducer::channels() { return m_channelsList; } TQString KisGenericLabHistogramProducer::positionToString(double pos) const { return TQString("%1").arg(static_cast(pos * UINT16_MAX)); } double KisGenericLabHistogramProducer::maximalZoom() const { return 1.0; } void KisGenericLabHistogramProducer::addRegionToBin(TQ_UINT8 * pixels, TQ_UINT8 * selectionMask, TQ_UINT32 nPixels, KisColorSpace *cs) { for (int i = 0; i < m_channels; i++) { m_outRight.at(i) = 0; m_outLeft.at(i) = 0; } TQ_UINT8 dst[8]; TQ_INT32 pSize = cs->pixelSize(); if (selectionMask) { while (nPixels > 0) { if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) { /* cs->toTQColor(pixels, &c); m_bins.at(0).at(c.red())++; */ m_count++; } pixels += pSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) { cs->convertPixelsTo(pixels, dst, m_colorSpace, 1); m_bins.at(0).at(m_colorSpace->scaleToU8(dst, 0))++; m_bins.at(1).at(m_colorSpace->scaleToU8(dst, 1))++; m_bins.at(2).at(m_colorSpace->scaleToU8(dst, 2))++; m_count++; } pixels += pSize; nPixels--; } } }