krita

kis_basic_histogram_producers.cc

00001 /*
00002  *  Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include <qstring.h>
00020 #include <klocale.h>
00021 
00022 #include "config.h"
00023 
00024 #ifdef HAVE_OPENEXR
00025 #include <half.h>
00026 #endif
00027 
00028 #include "kis_global.h"
00029 #include "kis_basic_histogram_producers.h"
00030 #include "kis_integer_maths.h"
00031 #include "kis_channelinfo.h"
00032 #include "kis_colorspace.h"
00033 #include "kis_lab_colorspace.h"
00034 
00035 KisLabColorSpace* KisGenericLabHistogramProducer::m_labCs = 0;
00036 
00037 
00038 KisBasicHistogramProducer::KisBasicHistogramProducer(const KisID& id, int channels, int nrOfBins, KisColorSpace *cs)
00039     : m_channels(channels),
00040       m_nrOfBins(nrOfBins),
00041       m_colorSpace(cs),
00042       m_id(id)
00043 {
00044     m_bins.resize(m_channels);
00045     for (int i = 0; i < m_channels; i++)
00046         m_bins.at(i).resize(m_nrOfBins);
00047     m_outLeft.resize(m_channels);
00048     m_outRight.resize(m_channels);
00049     m_count = 0;
00050     m_from = 0.0;
00051     m_width = 1.0;
00052 }
00053 
00054 void KisBasicHistogramProducer::clear() {
00055     m_count = 0;
00056     for (int i = 0; i < m_channels; i++) {
00057         for (int j = 0; j < m_nrOfBins; j++) {
00058             m_bins.at(i).at(j) = 0;
00059         }
00060         m_outRight.at(i) = 0;
00061         m_outLeft.at(i) = 0;
00062     }
00063 }
00064 
00065 void KisBasicHistogramProducer::makeExternalToInternal() {
00066     // This function assumes that the pixel is has no 'gaps'. That is to say: if we start
00067     // at byte 0, we can get to the end of the pixel by adding consecutive size()s of
00068     // the channels
00069     QValueVector<KisChannelInfo *> c = channels();
00070     uint count = c.count();
00071     int currentPos = 0;
00072 
00073     for (uint i = 0; i < count; i++) {
00074         for (uint j = 0; j < count; j++) {
00075             if (c.at(j)->pos() == currentPos) {
00076                 m_external.append(j);
00077                 break;
00078             }
00079         }
00080         currentPos += c.at(m_external.at(m_external.count() - 1))->size();
00081     }
00082 }
00083 
00084 // ------------ U8 ---------------------
00085 
00086 KisBasicU8HistogramProducer::KisBasicU8HistogramProducer(const KisID& id, KisColorSpace *cs)
00087     : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
00088 {
00089 }
00090 
00091 QString KisBasicU8HistogramProducer::positionToString(double pos) const {
00092     return QString("%1").arg(static_cast<Q_UINT8>(pos * UINT8_MAX));
00093 }
00094 
00095 void KisBasicU8HistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels, KisColorSpace *cs)
00096 {
00097     if (!pixels) return;
00098     if (!cs) return;
00099     if (!selectionMask) return;
00100     if (nPixels == 0) return;
00101 
00102     Q_INT32 pSize = cs->pixelSize();
00103 
00104     if ( selectionMask ) {
00105         while (nPixels > 0) {
00106             if ( ! (m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) {
00107 
00108                 for (int i = 0; i < m_channels; i++) {
00109                     m_bins.at(i).at(pixels[i])++;
00110                 }
00111                 m_count++;
00112 
00113             }
00114 
00115             pixels += pSize;
00116             selectionMask++;
00117             nPixels--;
00118         }
00119     }
00120     else {
00121         while (nPixels > 0) {
00122             if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT) ) {
00123 
00124                 for (int i = 0; i < m_channels; i++) {
00125                     m_bins.at(i).at(pixels[i])++;
00126                 }
00127                 m_count++;
00128 
00129             }
00130 
00131             pixels += pSize;
00132             nPixels--;
00133         }
00134     }
00135 }
00136 
00137 // ------------ U16 ---------------------
00138 
00139 KisBasicU16HistogramProducer::KisBasicU16HistogramProducer(const KisID& id, KisColorSpace *cs)
00140     : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
00141 {
00142 }
00143 
00144 QString KisBasicU16HistogramProducer::positionToString(double pos) const
00145 {
00146     return QString("%1").arg(static_cast<Q_UINT8>(pos * UINT8_MAX));
00147 }
00148 
00149 double KisBasicU16HistogramProducer::maximalZoom() const
00150 {
00151     return 1.0 / 255.0;
00152 }
00153 
00154 void KisBasicU16HistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels, KisColorSpace *cs)
00155 {
00156     // The view
00157     Q_UINT16 from = static_cast<Q_UINT16>(m_from * UINT16_MAX);
00158     Q_UINT16 width = static_cast<Q_UINT16>(m_width * UINT16_MAX + 0.5); // We include the end
00159     Q_UINT16 to = from + width;
00160     double factor = 255.0 / width;
00161 
00162     Q_INT32 pSize = cs->pixelSize();
00163 
00164     if ( selectionMask ) {
00165         Q_UINT16* pixel = reinterpret_cast<Q_UINT16*>(pixels);
00166         while (nPixels > 0) {
00167             if ( ! ((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
00168                 for (int i = 0; i < m_channels; i++) {
00169                     Q_UINT16 value = pixel[i];
00170                     if (value > to)
00171                         m_outRight.at(i)++;
00172                     else if (value < from)
00173                         m_outLeft.at(i)++;
00174                     else
00175                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00176                 }
00177                 m_count++;
00178             }
00179             pixels += pSize;
00180             selectionMask++;
00181             nPixels--;
00182         }
00183     }
00184     else {
00185         while (nPixels > 0) {
00186             Q_UINT16* pixel = reinterpret_cast<Q_UINT16*>(pixels);
00187 
00188             if ( ! (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
00189                 for (int i = 0; i < m_channels; i++) {
00190                     Q_UINT16 value = pixel[i];
00191                     if (value > to)
00192                         m_outRight.at(i)++;
00193                     else if (value < from)
00194                         m_outLeft.at(i)++;
00195                     else
00196                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00197                 }
00198                 m_count++;
00199             }
00200             pixels += pSize;
00201             nPixels--;
00202 
00203         }
00204     }
00205 }
00206 
00207 // ------------ Float32 ---------------------
00208 KisBasicF32HistogramProducer::KisBasicF32HistogramProducer(const KisID& id, KisColorSpace *cs)
00209     : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs)
00210 {
00211 }
00212 
00213 QString KisBasicF32HistogramProducer::positionToString(double pos) const {
00214     return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
00215 }
00216 
00217 double KisBasicF32HistogramProducer::maximalZoom() const {
00218     // 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
00219     return 1.0 / 255.0;
00220 }
00221 
00222 void KisBasicF32HistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels, KisColorSpace *cs) {
00223     // The view
00224     float from = static_cast<float>(m_from);
00225     float width = static_cast<float>(m_width);
00226     float to = from + width;
00227     float factor = 255.0 / width;
00228 
00229     Q_INT32 pSize = cs->pixelSize();
00230 
00231     if ( selectionMask ) {
00232         while (nPixels > 0) {
00233 
00234             float* pixel = reinterpret_cast<float*>(pixels);
00235             if ( !((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
00236                 for (int i = 0; i < m_channels; i++) {
00237                     float value = pixel[i];
00238                     if (value > to)
00239                         m_outRight.at(i)++;
00240                     else if (value < from)
00241                         m_outLeft.at(i)++;
00242                     else
00243                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00244                 }
00245                 m_count++;
00246             }
00247 
00248             pixels += pSize;
00249             selectionMask++;
00250             nPixels--;
00251 
00252         }
00253     }
00254     else {
00255         while (nPixels > 0) {
00256 
00257             float* pixel = reinterpret_cast<float*>(pixels);
00258             if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
00259                 for (int i = 0; i < m_channels; i++) {
00260                     float value = pixel[i];
00261                     if (value > to)
00262                         m_outRight.at(i)++;
00263                     else if (value < from)
00264                         m_outLeft.at(i)++;
00265                     else
00266                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00267                 }
00268                 m_count++;
00269             }
00270 
00271             pixels += pSize;
00272             nPixels--;
00273 
00274         }
00275     }
00276 }
00277 
00278 #ifdef HAVE_OPENEXR
00279 // ------------ Float16 Half ---------------------
00280 KisBasicF16HalfHistogramProducer::KisBasicF16HalfHistogramProducer(const KisID& id,
00281                                                                    KisColorSpace *cs)
00282     : KisBasicHistogramProducer(id, cs->nChannels(), 256, cs) {
00283 }
00284 
00285 QString KisBasicF16HalfHistogramProducer::positionToString(double pos) const {
00286     return QString("%1").arg(static_cast<float>(pos)); // XXX I doubt this is correct!
00287 }
00288 
00289 double KisBasicF16HalfHistogramProducer::maximalZoom() const {
00290     // 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
00291     return 1.0 / 255.0;
00292 }
00293 
00294 void KisBasicF16HalfHistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels, KisColorSpace *cs) {
00295     // The view
00296     float from = static_cast<float>(m_from);
00297     float width = static_cast<float>(m_width);
00298     float to = from + width;
00299     float factor = 255.0 / width;
00300 
00301     Q_INT32 pSize = cs->pixelSize();
00302     if ( selectionMask ) {
00303         while (nPixels > 0) {
00304             half* pixel = reinterpret_cast<half*>(pixels);
00305             if ( !((m_skipUnselected  && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
00306                 for (int i = 0; i < m_channels; i++) {
00307                     float value = pixel[i];
00308                     if (value > to)
00309                         m_outRight.at(i)++;
00310                     else if (value < from)
00311                         m_outLeft.at(i)++;
00312                     else
00313                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00314                 }
00315                 m_count++;
00316             }
00317             pixels += pSize;
00318             selectionMask++;
00319             nPixels--;
00320         }
00321     }
00322     else {
00323         while (nPixels > 0) {
00324             half* pixel = reinterpret_cast<half*>(pixels);
00325             if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
00326                 for (int i = 0; i < m_channels; i++) {
00327                     float value = pixel[i];
00328                     if (value > to)
00329                         m_outRight.at(i)++;
00330                     else if (value < from)
00331                         m_outLeft.at(i)++;
00332                     else
00333                         m_bins.at(i).at(static_cast<Q_UINT8>((value - from) * factor))++;
00334                 }
00335                 m_count++;
00336             }
00337             pixels += pSize;
00338             nPixels--;
00339         }
00340     }
00341 }
00342 #endif
00343 
00344 // ------------ Generic RGB ---------------------
00345 KisGenericRGBHistogramProducer::KisGenericRGBHistogramProducer()
00346     : KisBasicHistogramProducer(KisID("GENRGBHISTO", i18n("Generic RGB Histogram")),
00347                                 3, 256, 0) {
00348     /* we set 0 as colorspece, because we are not based on a specific colorspace. This
00349        is no problem for the superclass since we override channels() */
00350     m_channelsList.append(new KisChannelInfo(i18n("R"), i18n("R"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, QColor(255,0,0)));
00351     m_channelsList.append(new KisChannelInfo(i18n("G"), i18n("G"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, QColor(0,255,0)));
00352     m_channelsList.append(new KisChannelInfo(i18n("B"), i18n("B"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8, 1, QColor(0,0,255)));
00353 }
00354 
00355 QValueVector<KisChannelInfo *> KisGenericRGBHistogramProducer::channels() {
00356     return m_channelsList;
00357 }
00358 
00359 QString KisGenericRGBHistogramProducer::positionToString(double pos) const {
00360     return QString("%1").arg(static_cast<Q_UINT8>(pos * UINT8_MAX));
00361 }
00362 
00363 double KisGenericRGBHistogramProducer::maximalZoom() const {
00364     return 1.0;
00365 }
00366 
00367 
00368 void KisGenericRGBHistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels, KisColorSpace *cs)
00369 {
00370     for (int i = 0; i < m_channels; i++) {
00371         m_outRight.at(i) = 0;
00372         m_outLeft.at(i) = 0;
00373     }
00374 
00375     QColor c;
00376     Q_INT32 pSize = cs->pixelSize();
00377     if (selectionMask) {
00378         while (nPixels > 0) {
00379             if ( !((m_skipUnselected  && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
00380                 cs->toQColor(pixels, &c);
00381                 m_bins.at(0).at(c.red())++;
00382                 m_bins.at(1).at(c.green())++;
00383                 m_bins.at(2).at(c.blue())++;
00384 
00385                 m_count++;
00386             }
00387             pixels += pSize;
00388             selectionMask++;
00389             nPixels--;
00390         }
00391 
00392     }
00393     else {
00394         while (nPixels > 0) {
00395 
00396             if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) {
00397                 cs->toQColor(pixels, &c);
00398                 m_bins.at(0).at(c.red())++;
00399                 m_bins.at(1).at(c.green())++;
00400                 m_bins.at(2).at(c.blue())++;
00401 
00402                 m_count++;
00403             }
00404             pixels += pSize;
00405             nPixels--;
00406         }
00407     }
00408 }
00409 
00410 // ------------ Generic L*a*b* ---------------------
00411 KisGenericLabHistogramProducer::KisGenericLabHistogramProducer()
00412     : KisBasicHistogramProducer(KisID("GENLABHISTO", i18n("L*a*b* Histogram")), 3, 256, 0) {
00413     /* we set 0 as colorspace, because we are not based on a specific colorspace. This
00414        is no problem for the superclass since we override channels() */
00415     m_channelsList.append(new KisChannelInfo(i18n("L*"), i18n("L"), 0, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
00416     m_channelsList.append(new KisChannelInfo(i18n("a*"), i18n("a"), 1, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
00417     m_channelsList.append(new KisChannelInfo(i18n("b*"), i18n("b"), 2, KisChannelInfo::COLOR, KisChannelInfo::UINT8));
00418 
00419     if (!m_labCs) {
00420         KisProfile *labProfile = new KisProfile(cmsCreateLabProfile(NULL));
00421         m_labCs = new KisLabColorSpace(0, labProfile);
00422     }
00423     m_colorSpace = m_labCs;
00424 }
00425 KisGenericLabHistogramProducer::~KisGenericLabHistogramProducer()
00426 {
00427     delete m_channelsList[0];
00428     delete m_channelsList[1];
00429     delete m_channelsList[2];
00430 }
00431 
00432 QValueVector<KisChannelInfo *> KisGenericLabHistogramProducer::channels() {
00433     return m_channelsList;
00434 }
00435 
00436 QString KisGenericLabHistogramProducer::positionToString(double pos) const {
00437     return QString("%1").arg(static_cast<Q_UINT16>(pos * UINT16_MAX));
00438 }
00439 
00440 double KisGenericLabHistogramProducer::maximalZoom() const {
00441     return 1.0;
00442 }
00443 
00444 
00445 void KisGenericLabHistogramProducer::addRegionToBin(Q_UINT8 * pixels, Q_UINT8 * selectionMask, Q_UINT32 nPixels,  KisColorSpace *cs)
00446 {
00447     for (int i = 0; i < m_channels; i++) {
00448         m_outRight.at(i) = 0;
00449         m_outLeft.at(i) = 0;
00450     }
00451 
00452     Q_UINT8 dst[8];
00453     Q_INT32 pSize = cs->pixelSize();
00454 
00455     if (selectionMask) {
00456         while (nPixels > 0) {
00457             if ( !((m_skipUnselected  && *selectionMask == 0) || (m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT)) ) {
00458 /*
00459   cs->toQColor(pixels, &c);
00460   m_bins.at(0).at(c.red())++;
00461 */
00462                 m_count++;
00463             }
00464             pixels += pSize;
00465             selectionMask++;
00466             nPixels--;
00467         }
00468     }
00469     else {
00470         while (nPixels > 0) {
00471             if ( !(m_skipTransparent && cs->getAlpha(pixels) == OPACITY_TRANSPARENT))  {
00472 
00473   cs->convertPixelsTo(pixels, dst, m_colorSpace, 1);
00474   m_bins.at(0).at(m_colorSpace->scaleToU8(dst, 0))++;
00475   m_bins.at(1).at(m_colorSpace->scaleToU8(dst, 1))++;
00476   m_bins.at(2).at(m_colorSpace->scaleToU8(dst, 2))++;
00477 
00478                 m_count++;
00479             }
00480             pixels += pSize;
00481             nPixels--;
00482         }
00483     }
00484 }
00485 
KDE Home | KDE Accessibility Home | Description of Access Keys