krita

kis_histogram_view.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 <math.h>
00020 
00021 #include <qpainter.h>
00022 #include <qpixmap.h>
00023 #include <qlabel.h>
00024 #include <qcombobox.h>
00025 #include <qbuttongroup.h>
00026 #include <qpushbutton.h>
00027 #include <qscrollbar.h>
00028 
00029 #include <kdebug.h>
00030 
00031 #include "kis_channelinfo.h"
00032 #include "kis_histogram.h"
00033 #include "kis_global.h"
00034 #include "kis_types.h"
00035 #include "kis_layer.h"
00036 #include "kis_colorspace.h"
00037 #include "kis_histogram_view.h"
00038 #include "kis_basic_histogram_producers.h"
00039 #include "kis_paint_device.h"
00040 
00041 KisHistogramView::KisHistogramView(QWidget *parent, const char *name, WFlags f)
00042     : QLabel(parent, name, f)
00043 {
00044     // This is needed until we can computationally scale it well. Until then, this is needed
00045     // And when we have it, it won't hurt to have it around
00046     setScaledContents(true);
00047     setFrameShape(QFrame::Box); // Draw a box around ourselves
00048 }
00049 
00050 KisHistogramView::~KisHistogramView()
00051 {
00052 }
00053 
00054 void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev)
00055 {
00056     m_cs = dev->colorSpace();
00057 
00058     setChannels(); // Sets m_currentProducer to the first in the list
00059 
00060     if (!m_currentProducer)
00061         return;
00062 
00063     m_from = m_currentProducer->viewFrom();
00064     m_width = m_currentProducer->viewWidth();
00065 
00066     m_histogram = new KisHistogram(dev, m_currentProducer, LINEAR);
00067 
00068     updateHistogram();
00069 }
00070 
00071 void KisHistogramView::setHistogram(KisHistogramSP histogram)
00072 {
00073     m_cs = 0;
00074     m_histogram = histogram;
00075     m_currentProducer = m_histogram->producer();
00076     m_from = m_currentProducer->viewFrom();
00077     m_width = m_currentProducer->viewWidth();
00078 
00079     m_comboInfo.clear();
00080     m_channelStrings.clear();
00081     m_channels.clear();
00082     m_channelToOffset.clear();
00083 
00084     addProducerChannels(m_currentProducer);
00085 
00086     // Set the currently viewed channel:
00087     m_color = false;
00088     m_channels.append(m_comboInfo.at(1).channel);
00089     m_channelToOffset.append(0);
00090 
00091     updateHistogram();
00092 }
00093 
00094 void KisHistogramView::setView(double from, double size)
00095 {
00096     m_from = from;
00097     m_width = size;
00098     if (m_from + m_width > 1.0)
00099         m_from = 1.0 - m_width;
00100     m_histogram->producer()->setView(m_from, m_width);
00101 
00102     m_histogram->updateHistogram();
00103     updateHistogram();
00104 }
00105 
00106 KisHistogramProducerSP KisHistogramView::currentProducer()
00107 {
00108     return m_currentProducer;
00109 }
00110 
00111 QStringList KisHistogramView::channelStrings()
00112 {
00113     return m_channelStrings;
00114 }
00115 
00116 KisIDList KisHistogramView::listProducers()
00117 {
00118     if (m_cs)
00119         return KisHistogramProducerFactoryRegistry::instance()->listKeysCompatibleWith(m_cs);
00120     return KisIDList();
00121 }
00122 
00123 void KisHistogramView::setCurrentChannels(const KisID& producerID, QValueVector<KisChannelInfo *> channels)
00124 {
00125     setCurrentChannels(
00126         KisHistogramProducerFactoryRegistry::instance()->get(producerID)->generate(),
00127         channels);
00128 }
00129 
00130 void KisHistogramView::setCurrentChannels(KisHistogramProducerSP producer, QValueVector<KisChannelInfo *> channels)
00131 {
00132     m_currentProducer = producer;
00133     m_currentProducer->setView(m_from, m_width);
00134     m_histogram->setProducer(m_currentProducer);
00135     m_histogram->updateHistogram();
00136     m_histogram->setChannel(0); // Set a default channel, just being nice
00137 
00138     m_channels.clear();
00139     m_channelToOffset.clear();
00140 
00141     if (channels.count() == 0) {
00142         updateHistogram();
00143         return;
00144     }
00145 
00146     QValueVector<KisChannelInfo *> producerChannels = m_currentProducer->channels();
00147 
00148     for (uint i = 0; i < channels.count(); i++) {
00149         // Also makes sure the channel is actually in the producer's list
00150         for (uint j = 0; j < producerChannels.count(); j++) {
00151             if (channels.at(i)->name() == producerChannels.at(j)->name()) {
00152                 m_channelToOffset.append(m_channels.count()); // The first we append maps to 0
00153                 m_channels.append(channels.at(i));
00154             }
00155         }
00156     }
00157 
00158     updateHistogram();
00159 }
00160 
00161 bool KisHistogramView::hasColor()
00162 {
00163     return m_color;
00164 }
00165 
00166 void KisHistogramView::setColor(bool set)
00167 {
00168     if (set != m_color) {
00169         m_color = set;
00170         updateHistogram();
00171     }
00172 }
00173 
00174 void KisHistogramView::setActiveChannel(int channel)
00175 {
00176     ComboboxInfo info = m_comboInfo.at(channel);
00177     if (info.producer.data() != m_currentProducer.data()) {
00178         m_currentProducer = info.producer;
00179         m_currentProducer->setView(m_from, m_width);
00180         m_histogram->setProducer(m_currentProducer);
00181         m_histogram->updateHistogram();
00182     }
00183 
00184     m_channels.clear();
00185     m_channelToOffset.clear();
00186 
00187     if (!m_currentProducer) {
00188         updateHistogram();
00189         return;
00190     }
00191 
00192     if (info.isProducer) {
00193         m_color = true;
00194         m_channels = m_currentProducer->channels();
00195         for (uint i = 0; i < m_channels.count(); i++)
00196             m_channelToOffset.append(i);
00197         m_histogram->setChannel(0); // Set a default channel, just being nice
00198     } else {
00199         m_color = false;
00200         QValueVector<KisChannelInfo *> channels = m_currentProducer->channels();
00201         for (uint i = 0; i < channels.count(); i++) {
00202             KisChannelInfo* channel = channels.at(i);
00203             if (channel->name() == info.channel->name()) {
00204                 m_channels.append(channel);
00205                 m_channelToOffset.append(i);
00206                 break;
00207             }
00208         }
00209     }
00210 
00211     updateHistogram();
00212 }
00213 
00214 void KisHistogramView::setHistogramType(enumHistogramType type)
00215 {
00216     m_histogram->setHistogramType(type);
00217     updateHistogram();
00218 }
00219 
00220 void KisHistogramView::setChannels()
00221 {
00222     m_comboInfo.clear();
00223     m_channelStrings.clear();
00224     m_channels.clear();
00225     m_channelToOffset.clear();
00226 
00227     KisIDList list = KisHistogramProducerFactoryRegistry::instance()->listKeysCompatibleWith(m_cs);
00228 
00229     if (list.count() == 0) {
00230         // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning
00231         KisGenericRGBHistogramProducerFactory f;
00232         addProducerChannels(f.generate());
00233     } else {
00234         for (uint i = 0; i < list.count(); i++) {
00235             KisID id(*(list.at(i)));
00236             addProducerChannels( KisHistogramProducerFactoryRegistry::instance()->get(id)->generate() );
00237         }
00238     }
00239 
00240     m_currentProducer = m_comboInfo.at(0).producer;
00241     m_color = false;
00242     // The currently displayed channel and its offset
00243     m_channels.append(m_comboInfo.at(1).channel);
00244     m_channelToOffset.append(0);
00245 }
00246 
00247 void KisHistogramView::addProducerChannels(KisHistogramProducerSP producer) {
00248         ComboboxInfo info;
00249         info.isProducer = true;
00250         info.producer = producer;
00251         // channel not used for a producer
00252         QValueVector<KisChannelInfo *> channels = info.producer->channels();
00253         int count = channels.count();
00254         m_comboInfo.append(info);
00255         m_channelStrings.append(producer->id() . name());
00256         for (int j = 0; j < count; j++) {
00257             info.isProducer = false;
00258             info.channel = channels.at(j);
00259             m_comboInfo.append(info);
00260             m_channelStrings.append(QString(" ").append(info.channel->name()));
00261         }
00262 }
00263 
00264 void KisHistogramView::updateHistogram()
00265 {
00266     Q_UINT32 height = this->height();
00267     int selFrom, selTo; // from - to in bins
00268 
00269     if (!m_currentProducer) { // Something's very wrong: no producer for this colorspace to update histogram with!
00270         return;
00271     }
00272 
00273     Q_INT32 bins = m_histogram->producer()->numberOfBins();
00274     m_pix = QPixmap(bins, height);
00275     m_pix.fill();
00276     QPainter p(&m_pix);
00277     p.setBrush(Qt::black);
00278 
00279     // Draw the box of the selection, if any
00280     if (m_histogram->hasSelection()) {
00281         double width = m_histogram->selectionTo() - m_histogram->selectionFrom();
00282         double factor = static_cast<double>(bins) / m_histogram->producer()->viewWidth();
00283         selFrom = static_cast<int>(m_histogram->selectionFrom() * factor);
00284         selTo = selFrom + static_cast<int>(width * factor);
00285         p.drawRect(selFrom, 0, selTo - selFrom, height);
00286     } else {
00287         // We don't want the drawing to think we're in a selected area
00288         selFrom = -1;
00289         selTo = 2;
00290     }
00291 
00292     Q_INT32 i = 0;
00293     double highest = 0;
00294     bool blackOnBlack = false;
00295 
00296     // First we iterate once, so that we have the overall maximum. This is a bit inefficient,
00297     // but not too much since the histogram caches the calculations
00298     for (uint chan = 0; chan < m_channels.count(); chan++) {
00299         m_histogram->setChannel(m_channelToOffset.at(chan));
00300         if ((double)m_histogram->calculations().getHighest() > highest)
00301             highest = (double)m_histogram->calculations().getHighest();
00302     }
00303 
00304     for (uint chan = 0; chan < m_channels.count(); chan++) {
00305         QColor color;
00306         m_histogram->setChannel(m_channelToOffset.at(chan));
00307 
00308         if (m_color) {
00309             color = m_channels.at(chan)->color();
00310             p.setPen(color);
00311         } else {
00312             color = Qt::black;
00313         }
00314         blackOnBlack = (color == Qt::black);
00315 
00316         if (m_histogram->getHistogramType() == LINEAR) {
00317             double factor = (double)height / highest;
00318             for( i=0; i<bins; ++i ) {
00319                 // So that we get a good view even with a selection box with
00320                 // black colors on background of black selection
00321                 if (i >= selFrom && i < selTo && blackOnBlack) {
00322                     p.setPen(Qt::white);
00323                 } else {
00324                     p.setPen(color);
00325                 }
00326                 p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor));
00327             }
00328         } else {
00329             double factor = (double)height / (double)log(highest);
00330             for( i = 0; i < bins; ++i ) {
00331                 // Same as above
00332                 if (i >= selFrom && i < selTo && blackOnBlack) {
00333                     p.setPen(Qt::white);
00334                 } else {
00335                     p.setPen(color);
00336                 }
00337                 p.drawLine(i, height, i,
00338                            height - int(log((double)m_histogram->getValue(i)) * factor));
00339             }
00340         }
00341     }
00342 
00343     setPixmap(m_pix);
00344 }
00345 
00346 void KisHistogramView::mousePressEvent(QMouseEvent * e) {
00347     if (e->button() == Qt::RightButton)
00348         emit rightClicked(e->globalPos());
00349     else
00350         QLabel::mousePressEvent(e);
00351 }
00352 
00353 
00354 #include "kis_histogram_view.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys