diff --git a/libs/pigment/KoBasicHistogramProducers.h b/libs/pigment/KoBasicHistogramProducers.h --- a/libs/pigment/KoBasicHistogramProducers.h +++ b/libs/pigment/KoBasicHistogramProducers.h @@ -165,7 +165,10 @@ return producer; } - virtual bool isCompatibleWith(const KoColorSpace* colorSpace) const { + virtual bool isCompatibleWith(const KoColorSpace* colorSpace, bool strict = false) const { + if( strict ){ + return colorSpace->colorDepthId().id() == m_depthId; + } return colorSpace->colorModelId().id() == m_modelId || colorSpace->colorDepthId().id() == m_depthId; } virtual float preferrednessLevelWith(const KoColorSpace* colorSpace) const { @@ -205,7 +208,8 @@ return new KoGenericRGBHistogramProducer(); } - virtual bool isCompatibleWith(const KoColorSpace*) const { + virtual bool isCompatibleWith(const KoColorSpace*, bool strict = false) const { + Q_UNUSED(strict); return true; } @@ -244,7 +248,8 @@ return new KoGenericLabHistogramProducer(); } - virtual bool isCompatibleWith(const KoColorSpace*) const { + virtual bool isCompatibleWith(const KoColorSpace*, bool strict = false) const { + Q_UNUSED(strict); return true; } diff --git a/libs/pigment/KoBasicHistogramProducers.cpp b/libs/pigment/KoBasicHistogramProducers.cpp --- a/libs/pigment/KoBasicHistogramProducers.cpp +++ b/libs/pigment/KoBasicHistogramProducers.cpp @@ -114,35 +114,35 @@ void KoBasicU8HistogramProducer::addRegionToBin(const quint8 * pixels, const quint8 * selectionMask, quint32 nPixels, const KoColorSpace *cs) { - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); if (selectionMask) { + quint8 *dst = dstPixels; while (nPixels > 0) { - if (!(m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { + if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - m_bins[i][pixels[i]]++; + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; } m_count++; - } - - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { + quint8 *dst = dstPixels; while (nPixels > 0) { if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - m_bins[i][pixels[i]]++; + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + m_bins[i][m_colorSpace->scaleToU8(dst,i)]++; } m_count++; - } - - pixels += pSize; + dst += dstPixelSize; nPixels--; } } @@ -173,14 +173,18 @@ quint16 to = from + width; qreal factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); if (selectionMask) { - const quint16* pixel = reinterpret_cast(pixels); while (nPixels > 0) { if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - quint16 value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + quint16 value = channels[i]*UINT16_MAX; if (value > to) m_outRight[i]++; else if (value < from) @@ -190,17 +194,17 @@ } m_count++; } - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - const quint16* pixel = reinterpret_cast(pixels); - if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - quint16 value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + quint16 value = channels[i]*UINT16_MAX; + if (value > to) m_outRight[i]++; else if (value < from) @@ -210,9 +214,8 @@ } m_count++; } - pixels += pSize; + dst += dstPixelSize; nPixels--; - } } } @@ -242,15 +245,18 @@ float to = from + width; float factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); if (selectionMask) { while (nPixels > 0) { - - const float* pixel = reinterpret_cast(pixels); if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) @@ -260,19 +266,17 @@ } m_count++; } - - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - - const float* pixel = reinterpret_cast(pixels); if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) @@ -282,8 +286,7 @@ } m_count++; } - - pixels += pSize; + dst += dstPixelSize; nPixels--; } @@ -317,13 +320,18 @@ float to = from + width; float factor = 255.0 / width; - qint32 pSize = cs->pixelSize(); + quint32 dstPixelSize = m_colorSpace->pixelSize(); + quint8 *dstPixels = new quint8[nPixels * dstPixelSize]; + cs->convertPixelsTo(pixels, dstPixels, m_colorSpace, nPixels, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::Empty); + quint8 *dst = dstPixels; + QVector channels(m_colorSpace->channelCount()); + if (selectionMask) { while (nPixels > 0) { - const half* pixel = reinterpret_cast(pixels); if (!((m_skipUnselected && *selectionMask == 0) || (m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8))) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) @@ -333,16 +341,16 @@ } m_count++; } - pixels += pSize; + dst += dstPixelSize; selectionMask++; nPixels--; } } else { while (nPixels > 0) { - const half* pixel = reinterpret_cast(pixels); if (!(m_skipTransparent && cs->opacityU8(pixels) == OPACITY_TRANSPARENT_U8)) { - for (int i = 0; i < m_channels; i++) { - float value = pixel[i]; + m_colorSpace->normalisedChannelsValue(dst,channels); + for (int i = 0; i < (int)m_colorSpace->channelCount(); i++) { + float value = channels[i]; if (value > to) m_outRight[i]++; else if (value < from) @@ -352,7 +360,7 @@ } m_count++; } - pixels += pSize; + dst += dstPixelSize; nPixels--; } } diff --git a/libs/pigment/KoHistogramProducer.h b/libs/pigment/KoHistogramProducer.h --- a/libs/pigment/KoHistogramProducer.h +++ b/libs/pigment/KoHistogramProducer.h @@ -108,7 +108,7 @@ virtual KoHistogramProducer *generate() = 0; /// Returns if a colorspace can be used with this producer - virtual bool isCompatibleWith(const KoColorSpace* colorSpace) const = 0; + virtual bool isCompatibleWith(const KoColorSpace* colorSpace, bool strict = false) const = 0; /// Returns a float in the [0.0, 1.0] range, 0.0 means this is a very generic method virtual float preferrednessLevelWith(const KoColorSpace* colorSpace) const = 0; @@ -133,7 +133,7 @@ virtual ~KoHistogramProducerFactoryRegistry(); static KoHistogramProducerFactoryRegistry* instance(); /// returns a list, sorted by preferrence: higher preferance comes first - QList keysCompatibleWith(const KoColorSpace* colorSpace) const; + QList keysCompatibleWith(const KoColorSpace* colorSpace, bool isStrict=false) const; private: KoHistogramProducerFactoryRegistry(const KoHistogramProducerFactoryRegistry&); diff --git a/libs/pigment/KoHistogramProducer.cpp b/libs/pigment/KoHistogramProducer.cpp --- a/libs/pigment/KoHistogramProducer.cpp +++ b/libs/pigment/KoHistogramProducer.cpp @@ -44,13 +44,14 @@ } -QList KoHistogramProducerFactoryRegistry::keysCompatibleWith(const KoColorSpace* colorSpace) const +QList KoHistogramProducerFactoryRegistry::keysCompatibleWith(const KoColorSpace* colorSpace, bool isStrict) const { QList list; QList preferredList; Q_FOREACH (const QString &id, keys()) { KoHistogramProducerFactory *f = value(id); - if (f->isCompatibleWith(colorSpace)) { + + if (f->isCompatibleWith(colorSpace, isStrict)) { float preferred = f->preferrednessLevelWith(colorSpace); QList::iterator pit = preferredList.begin(); QList::iterator pend = preferredList.end(); diff --git a/libs/ui/kis_histogram_view.h b/libs/ui/kis_histogram_view.h --- a/libs/ui/kis_histogram_view.h +++ b/libs/ui/kis_histogram_view.h @@ -24,9 +24,9 @@ #include #include #include +#include #include "kis_types.h" -#include "KoHistogramProducer.h" #include "kis_histogram.h" #include @@ -52,8 +52,6 @@ * listProducers(). Setting a histogram will discard info on the * layer, and setting a layer will discard info on the histogram. * - * XXX: make beautiful, for instance by using alpha and a bit of a - * gradient! **/ class KRITAUI_EXPORT KisHistogramView : public QLabel { @@ -65,39 +63,25 @@ virtual ~KisHistogramView(); - void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds); - - void setHistogram(KisHistogramSP histogram); + void setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer *producer, const QRect &bounds); void setView(double from, double size); KoHistogramProducer *currentProducer(); - QStringList channelStrings(); - - /** Lists all producers currently available */ - QList producers(); - - /** Sets the currently displayed channels to channels of the producer with producerID as ID*/ - void setCurrentChannels(const KoID& producerID, QList channels); - - /** Be careful, producer will be modified */ - void setCurrentChannels(KoHistogramProducer *producer, QList channels); - bool hasColor(); - void setColor(bool set); + void setProducer(KoHistogramProducer* producer); + void setChannels(QList & channels); + virtual void paintEvent(QPaintEvent* event); + virtual void updateHistogramCalculation(); + void setSmoothHistogram(bool smoothHistogram); public Q_SLOTS: - - void setActiveChannel(int channel); - - void setHistogramType(enumHistogramType type); - - void updateHistogram(); + virtual void setHistogramType(enumHistogramType type); + virtual void startUpdateCanvasProjection(); Q_SIGNALS: - void rightClicked(const QPoint& pos); protected: @@ -106,28 +90,17 @@ private: - void setChannels(); + void setChannels(void); void addProducerChannels(KoHistogramProducer *producer); - - typedef struct { - bool isProducer; - KoHistogramProducer *producer; - KoChannelInfo * channel; - } ComboboxInfo; - - QVector m_comboInfo; - QPixmap m_pix; KisHistogramSP m_histogram; - const KoColorSpace* m_cs; + KisPaintDeviceSP m_currentDev; + QRect m_currentBounds; KoHistogramProducer *m_currentProducer; QList m_channels; - // Maps the channels in m_channels to a real channel offset in the producer->channels() - QVector m_channelToOffset; - QStringList m_channelStrings; bool m_color; - double m_from; - double m_width; + bool m_smoothHistogram; + enumHistogramType m_histogram_type; }; #endif // _KIS_HISTOGRAM_VIEW_ diff --git a/libs/ui/kis_histogram_view.cc b/libs/ui/kis_histogram_view.cc --- a/libs/ui/kis_histogram_view.cc +++ b/libs/ui/kis_histogram_view.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -40,129 +41,67 @@ #include "kis_global.h" #include "kis_layer.h" - +#include #include "kis_paint_device.h" KisHistogramView::KisHistogramView(QWidget *parent, const char *name, Qt::WFlags f) - : QLabel(parent, f) + : QLabel(parent, f), + m_currentDev(nullptr), m_currentProducer(nullptr), + m_smoothHistogram(false),m_histogram_type(LINEAR) { setObjectName(name); - // This is needed until we can computationally scale it well. Until then, this is needed - // And when we have it, it won't hurt to have it around - setScaledContents(true); - setFrameShape(QFrame::Box); // Draw a box around ourselves } KisHistogramView::~KisHistogramView() { } -void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) -{ - m_cs = dev->colorSpace(); - - setChannels(); // Sets m_currentProducer to the first in the list - - if (!m_currentProducer) - return; - - m_from = m_currentProducer->viewFrom(); - m_width = m_currentProducer->viewWidth(); - - m_histogram = new KisHistogram(dev, bounds, m_currentProducer, LINEAR); - - updateHistogram(); -} - -void KisHistogramView::setHistogram(KisHistogramSP histogram) -{ - if (!histogram) return; - m_cs = 0; - m_histogram = histogram; - m_currentProducer = m_histogram->producer(); - m_from = m_currentProducer->viewFrom(); - m_width = m_currentProducer->viewWidth(); - - m_comboInfo.clear(); - m_channelStrings.clear(); - m_channels.clear(); - m_channelToOffset.clear(); - - addProducerChannels(m_currentProducer); - - // Set the currently viewed channel: - m_color = false; - m_channels.append(m_comboInfo.at(1).channel); - m_channelToOffset.append(0); - - updateHistogram(); -} - -void KisHistogramView::setView(double from, double size) -{ - m_from = from; - m_width = size; - if (m_from + m_width > 1.0) - m_from = 1.0 - m_width; - m_histogram->producer()->setView(m_from, m_width); - - m_histogram->updateHistogram(); - updateHistogram(); -} KoHistogramProducer *KisHistogramView::currentProducer() { return m_currentProducer; } -QStringList KisHistogramView::channelStrings() +void KisHistogramView::startUpdateCanvasProjection() { - return m_channelStrings; + updateHistogramCalculation(); } -QList KisHistogramView::producers() +void KisHistogramView::setChannels(QList & channels) { - if (m_cs) - return KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); - return QList(); + m_channels = channels; + updateHistogramCalculation(); } -void KisHistogramView::setCurrentChannels(const KoID& producerID, QList channels) +void KisHistogramView::setProducer(KoHistogramProducer* producer) { - setCurrentChannels( - KoHistogramProducerFactoryRegistry::instance()->value(producerID.id())->generate(), - channels); + m_currentProducer = producer; + m_channels = m_currentProducer->channels(); + if( !m_histogram.isNull() ){ + m_histogram->setProducer( m_currentProducer ); + } + updateHistogramCalculation(); } -void KisHistogramView::setCurrentChannels(KoHistogramProducer *producer, QList channels) +void KisHistogramView::setPaintDevice(KisPaintDeviceSP dev, KoHistogramProducer* producer, const QRect &bounds) { m_currentProducer = producer; - m_currentProducer->setView(m_from, m_width); - m_histogram->setProducer(m_currentProducer); - m_histogram->updateHistogram(); - m_histogram->setChannel(0); // Set a default channel, just being nice - - m_channels.clear(); - m_channelToOffset.clear(); - - if (channels.count() == 0) { - updateHistogram(); - return; - } + m_channels = m_currentProducer->channels(); + m_currentDev = dev; + m_currentBounds = bounds; + m_histogram = new KisHistogram(m_currentDev, m_currentBounds, m_currentProducer, m_histogram_type); - QList producerChannels = m_currentProducer->channels(); - - for (int i = 0; i < channels.count(); i++) { - // Also makes sure the channel is actually in the producer's list - for (int j = 0; j < producerChannels.count(); j++) { - if (channels.at(i)->name() == producerChannels.at(j)->name()) { - m_channelToOffset.append(m_channels.count()); // The first we append maps to 0 - m_channels.append(channels.at(i)); - } - } - } + updateHistogramCalculation(); +} - updateHistogram(); +void KisHistogramView::setView(double from, double size) +{ + double m_from = from; + double m_width = size; + if (m_from + m_width > 1.0) + m_from = 1.0 - m_width; + m_histogram->producer()->setView(m_from, m_width); + updateHistogramCalculation(); } bool KisHistogramView::hasColor() @@ -174,206 +113,127 @@ { if (set != m_color) { m_color = set; - updateHistogram(); } + update(); } -void KisHistogramView::setActiveChannel(int channel) +void KisHistogramView::setHistogramType(enumHistogramType type) { - ComboboxInfo info = m_comboInfo.at(channel); - if (info.producer != m_currentProducer) { - m_currentProducer = info.producer; - m_currentProducer->setView(m_from, m_width); - m_histogram->setProducer(m_currentProducer); - m_histogram->updateHistogram(); - } - - m_channels.clear(); - m_channelToOffset.clear(); + m_histogram_type = type; + updateHistogramCalculation(); +} - if (!m_currentProducer) { - updateHistogram(); +void KisHistogramView::updateHistogramCalculation() +{ + if (!m_currentProducer || m_currentDev.isNull() || m_histogram.isNull() ) { // Something's very wrong: not initialized return; } - - if (info.isProducer) { - m_color = true; - m_channels = m_currentProducer->channels(); - for (int i = 0; i < m_channels.count(); i++) - m_channelToOffset.append(i); - m_histogram->setChannel(0); // Set a default channel, just being nice - } else { - m_color = false; - QList channels = m_currentProducer->channels(); - for (int i = 0; i < channels.count(); i++) { - KoChannelInfo* channel = channels.at(i); - if (channel->name() == info.channel->name()) { - m_channels.append(channel); - m_channelToOffset.append(i); - break; - } - } - } - - updateHistogram(); + m_histogram->updateHistogram(); + update(); } -void KisHistogramView::setHistogramType(enumHistogramType type) +void KisHistogramView::mousePressEvent(QMouseEvent * e) { - m_histogram->setHistogramType(type); - updateHistogram(); + if (e->button() == Qt::RightButton) + emit rightClicked(e->globalPos()); + else + QLabel::mousePressEvent(e); } -void KisHistogramView::setChannels() +void KisHistogramView::setSmoothHistogram(bool smoothHistogram) { - m_comboInfo.clear(); - m_channelStrings.clear(); - m_channels.clear(); - m_channelToOffset.clear(); - - QList list = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs); - - if (list.count() == 0) { - // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning - KoGenericRGBHistogramProducerFactory f; - addProducerChannels(f.generate()); - } else { - Q_FOREACH (const QString &id, list) { - KoHistogramProducer *producer = KoHistogramProducerFactoryRegistry::instance()->value(id)->generate(); - if (producer) { - addProducerChannels(producer); - } - } - } - - m_currentProducer = m_comboInfo.at(0).producer; - m_color = false; - // The currently displayed channel and its offset - m_channels.append(m_comboInfo.at(1).channel); - m_channelToOffset.append(0); + m_smoothHistogram = smoothHistogram; } -void KisHistogramView::addProducerChannels(KoHistogramProducer *producer) -{ - if (!producer) return; - - ComboboxInfo info; - info.isProducer = true; - info.producer = producer; - // channel not used for a producer - QList channels = info.producer->channels(); - int count = channels.count(); - m_comboInfo.append(info); - m_channelStrings.append(producer->id() . name()); - for (int j = 0; j < count; j++) { - info.isProducer = false; - info.channel = channels.at(j); - m_comboInfo.append(info); - m_channelStrings.append(QString(" ").append(info.channel->name())); - } -} -void KisHistogramView::updateHistogram() +void KisHistogramView::paintEvent(QPaintEvent *event) { - quint32 height = this->height(); - int selFrom, selTo; // from - to in bins - - if (!m_currentProducer) { // Something's very wrong: no producer for this colorspace to update histogram with! - return; - } + QLabel::paintEvent(event); - qint32 bins = m_histogram->producer()->numberOfBins(); - - m_pix = QPixmap(bins, height); - m_pix.fill(); - - QPainter p(&m_pix); - p.setBrush(Qt::black); - - // Draw the box of the selection, if any - if (m_histogram->hasSelection()) { - double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); - double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); - selFrom = static_cast(m_histogram->selectionFrom() * factor); - selTo = selFrom + static_cast(width * factor); - p.drawRect(selFrom, 0, selTo - selFrom, height); - } else { - // We don't want the drawing to think we're in a selected area - selFrom = -1; - selTo = 2; - } + if( this->height() > 0 && this->width()>0 && !m_histogram.isNull() ){ + qint32 height = this->height(); + int selFrom, selTo; // from - to in bins - qint32 i = 0; - double highest = 0; - bool blackOnBlack = false; + qint32 bins = m_histogram->producer()->numberOfBins(); - // First we iterate once, so that we have the overall maximum. This is a bit inefficient, - // but not too much since the histogram caches the calculations - for (int chan = 0; chan < m_channels.count(); chan++) { - m_histogram->setChannel(m_channelToOffset.at(chan)); - if ((double)m_histogram->calculations().getHighest() > highest) - highest = (double)m_histogram->calculations().getHighest(); - } + QPainter painter(this); + painter.setPen(this->palette().light().color()); - QPen pen(Qt::white); + const int NGRID = 4; + for(int i=0; i<=NGRID; ++i){ + painter.drawLine(this->width()*i/NGRID,0.,this->width()*i/NGRID,this->height()); + painter.drawLine(0.,this->height()*i/NGRID,this->width(),this->height()*i/NGRID); + } - for (int chan = 0; chan < m_channels.count(); chan++) { - QColor color; - m_histogram->setChannel(m_channelToOffset.at(chan)); - if (m_color) { - color = m_channels.at(chan)->color(); - QLinearGradient gradient; - gradient.setColorAt(0, Qt::white); - gradient.setColorAt(1, color); - QBrush brush(gradient); - pen = QPen(brush, 0); + // Draw the box of the selection, if any + if (m_histogram->hasSelection()) { + double width = m_histogram->selectionTo() - m_histogram->selectionFrom(); + double factor = static_cast(bins) / m_histogram->producer()->viewWidth(); + selFrom = static_cast(m_histogram->selectionFrom() * factor); + selTo = selFrom + static_cast(width * factor); + painter.drawRect(selFrom, 0, selTo - selFrom, height); } else { - color = Qt::black; + // We don't want the drawing to think we're in a selected area + selFrom = -1; + selTo = 2; } - blackOnBlack = (color == Qt::black); - - if (m_histogram->getHistogramType() == LINEAR) { + float highest = 0; - double factor = (double)height / highest; - for (i = 0; i < bins; ++i) { - // So that we get a good view even with a selection box with - // black colors on background of black selection - if (i >= selFrom && i < selTo && blackOnBlack) { - p.setPen(Qt::white); - } else { - p.setPen(pen); - } - p.drawLine(i, height, i, height - int(m_histogram->getValue(i) * factor)); + if(m_channels.count()==0){ + m_channels=m_currentProducer->channels(); + } + int nChannels = m_channels.size(); + + // First we iterate once, so that we have the overall maximum. This is a bit inefficient, + // but not too much since the histogram caches the calculations + for (int chan = 0; chan < m_channels.size(); chan++) { + if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ + m_histogram->setChannel(chan); + if ((float)m_histogram->calculations().getHighest() > highest) + highest = (float)m_histogram->calculations().getHighest(); } - } else { - - double factor = (double)height / (double)log(highest); - for (i = 0; i < bins; ++i) { - // Same as above - if (i >= selFrom && i < selTo && blackOnBlack) { - p.setPen(Qt::white); - } else { - p.setPen(pen); + } + highest = (m_histogram_type==LINEAR)? highest: std::log2(highest); + + painter.setWindow(QRect(-1,0,bins+1,highest)); + painter.setCompositionMode(QPainter::CompositionMode_Plus); + + for (int chan = 0; chan < nChannels; chan++) { + if( m_channels.at(chan)->channelType() != KoChannelInfo::ALPHA ){ + auto color = m_channels.at(chan)->color(); + auto fill_color = color; + fill_color.setAlphaF(.25); + painter.setBrush(fill_color); + auto pen = QPen(color); + pen.setWidth(0); + painter.setPen(pen); + + if (m_smoothHistogram){ + QPainterPath path; + + m_histogram->setChannel(chan); + path.moveTo(QPointF(-1,highest)); + for (qint32 i = 0; i < bins; ++i) { + float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); + path.lineTo(QPointF(i,v)); + + } + path.lineTo(QPointF(bins+1,highest)); + path.closeSubpath(); + painter.drawPath(path); } - if (m_histogram->getValue(i) > 0) { // Don't try to calculate log(0) - p.drawLine(i, height, i, - height - int(log((double)m_histogram->getValue(i)) * factor)); + else { + pen.setWidth(1); + painter.setPen(pen); + m_histogram->setChannel(chan); + for (qint32 i = 0; i < bins; ++i) { + float v = (m_histogram_type==LINEAR)? highest-m_histogram->getValue(i): highest-std::log2(m_histogram->getValue(i)); + painter.drawLine(QPointF(i,highest),QPointF(i,v)); + } } } } } - - setPixmap(m_pix); } - -void KisHistogramView::mousePressEvent(QMouseEvent * e) -{ - if (e->button() == Qt::RightButton) - emit rightClicked(e->globalPos()); - else - QLabel::mousePressEvent(e); -} - - diff --git a/plugins/dockers/CMakeLists.txt b/plugins/dockers/CMakeLists.txt --- a/plugins/dockers/CMakeLists.txt +++ b/plugins/dockers/CMakeLists.txt @@ -21,3 +21,4 @@ add_subdirectory(animation) add_subdirectory(presethistory) add_subdirectory(shapedockers) +add_subdirectory(histogram) diff --git a/plugins/dockers/histogram/CMakeLists.txt b/plugins/dockers/histogram/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/CMakeLists.txt @@ -0,0 +1,4 @@ +set(KRITA_HISTOGRAMDOCKER_SOURCES histogramdocker.cpp histogramdocker_dock.cpp ) +add_library(kritahistogramdocker MODULE ${KRITA_HISTOGRAMDOCKER_SOURCES}) +target_link_libraries(kritahistogramdocker kritaui) +install(TARGETS kritahistogramdocker DESTINATION ${KRITA_PLUGIN_INSTALL_DIR}) diff --git a/plugins/dockers/histogram/histogramdocker.h b/plugins/dockers/histogram/histogramdocker.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 2.1 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + */ + +#ifndef _HISTOGRAM_DOCKER_H_ +#define _HISTOGRAM_DOCKER_H_ + +#include +#include + +class KisViewManager; + +/** + * Template of view plugin + */ +class HistogramDockerPlugin : public QObject +{ + Q_OBJECT + public: + HistogramDockerPlugin(QObject *parent, const QVariantList &); + virtual ~HistogramDockerPlugin(); + private: + KisViewManager* m_view; +}; + +#endif diff --git a/plugins/dockers/histogram/histogramdocker.cpp b/plugins/dockers/histogram/histogramdocker.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 2.1 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "histogramdocker.h" + +#include + +#include + + +#include +#include +#include + +#include + +#include "kis_config.h" +#include "kis_cursor.h" +#include "kis_global.h" +#include "kis_types.h" +#include "KisViewManager.h" + +#include "histogramdocker_dock.h" +#include + +K_PLUGIN_FACTORY_WITH_JSON(HistogramDockerPluginFactory, "krita_histogramdocker.json", registerPlugin();) + +class HistogramDockerDockFactory : public KoDockFactoryBase { +public: + HistogramDockerDockFactory() + { + } + + virtual QString id() const + { + return QString( "HistogramDocker" ); + } + + virtual Qt::DockWidgetArea defaultDockWidgetArea() const + { + return Qt::RightDockWidgetArea; + } + + virtual QDockWidget* createDockWidget() + { + HistogramDockerDock * dockWidget = new HistogramDockerDock(); + dockWidget->setObjectName(id()); + + return dockWidget; + } + + DockPosition defaultDockPosition() const + { + return DockRight; + } +private: + + +}; + + +HistogramDockerPlugin::HistogramDockerPlugin(QObject *parent, const QVariantList &) + : QObject(parent) +{ + KoDockRegistry::instance()->add(new HistogramDockerDockFactory()); +} + +HistogramDockerPlugin::~HistogramDockerPlugin() +{ + m_view = 0; +} + +#include "histogramdocker.moc" diff --git a/plugins/dockers/histogram/histogramdocker_dock.h b/plugins/dockers/histogram/histogramdocker_dock.h new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker_dock.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 2.1 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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. + */ + +#ifndef _HISTOGRAM_DOCK_H_ +#define _HISTOGRAM_DOCK_H_ + +#include +#include +#include + +class QVBoxLayout; +class KisCanvas2; +class KisHistogramView; +class KisSignalCompressor; +class KoHistogramProducer; + +class HistogramDockerDock : public QDockWidget, public KoCanvasObserverBase { + Q_OBJECT +public: + HistogramDockerDock(); + + QString observerName() { return "HistogramDockerDock"; } + virtual void setCanvas(KoCanvasBase *canvas); + virtual void unsetCanvas(); + +public Q_SLOTS: + virtual void startUpdateCanvasProjection(); + //virtual void sigProfileChanged(const KoColorProfile* cp); + virtual void sigColorSpaceChanged(const KoColorSpace* cs); + +private: + QVBoxLayout *m_layout; + KisSignalCompressor *m_compressor; + KisHistogramView *m_histogramWidget; + KisCanvas2 *m_canvas; + KoHistogramProducer *m_producer; +}; + + +#endif diff --git a/plugins/dockers/histogram/histogramdocker_dock.cpp b/plugins/dockers/histogram/histogramdocker_dock.cpp new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/histogramdocker_dock.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Eugene Ingerman + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 2.1 of the License. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "histogramdocker_dock.h" + +#include +#include +#include + +#include "kis_canvas2.h" +#include +#include +#include "kis_image.h" +#include "kis_paint_device.h" +#include "kis_signal_compressor.h" +#include "kis_histogram_view.h" + +HistogramDockerDock::HistogramDockerDock( ) + : QDockWidget(i18n("Histogram")), + m_compressor(new KisSignalCompressor(200, KisSignalCompressor::POSTPONE, this)), + m_canvas(0), m_producer(nullptr) +{ + QWidget *page = new QWidget(this); + m_layout = new QVBoxLayout(page); + + m_histogramWidget = new KisHistogramView(this); + m_histogramWidget->setMinimumHeight(50); + m_histogramWidget->setSmoothHistogram(true); + m_layout->addWidget(m_histogramWidget, 1); + setWidget(page); + connect(m_compressor,SIGNAL(timeout()),SLOT(startUpdateCanvasProjection())); +} + + +void HistogramDockerDock::setCanvas(KoCanvasBase * canvas) +{ + if(m_canvas == canvas) + return; + + setEnabled(canvas != 0); + + if (m_canvas) { + m_canvas->disconnectCanvasObserver(this); + m_canvas->image()->disconnect(this); + } + m_canvas = dynamic_cast(canvas); + if (m_canvas && m_canvas->imageView() && m_canvas->imageView()->image() ) { + + KisPaintDeviceSP dev = m_canvas->image()->projection(); + auto cs = m_canvas->image()->colorSpace(); + + QList producers = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(cs,true); + m_producer = KoHistogramProducerFactoryRegistry::instance()->get(producers.at(0))->generate(); + m_histogramWidget->setPaintDevice( dev, m_producer, m_canvas->image()->bounds() ); + + connect(m_canvas->image(), SIGNAL(sigImageUpdated(QRect)), m_compressor, SLOT(start()), Qt::UniqueConnection); + connect(m_canvas->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), this, SLOT(sigColorSpaceChanged(const KoColorSpace*)), Qt::UniqueConnection); + + m_compressor->start(); + } +} + +void HistogramDockerDock::unsetCanvas() +{ + setEnabled(false); + m_canvas = 0; +} + +void HistogramDockerDock::startUpdateCanvasProjection() +{ + m_histogramWidget->startUpdateCanvasProjection(); +} + +void HistogramDockerDock::sigColorSpaceChanged(const KoColorSpace *cs) +{ + QList producers = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(cs,true); + m_producer = KoHistogramProducerFactoryRegistry::instance()->get(producers.at(0))->generate(); + m_histogramWidget->setProducer(m_producer); +} + diff --git a/plugins/dockers/histogram/krita_histogramdocker.json b/plugins/dockers/histogram/krita_histogramdocker.json new file mode 100644 --- /dev/null +++ b/plugins/dockers/histogram/krita_histogramdocker.json @@ -0,0 +1,9 @@ +{ + "Id": "Histogram Docker", + "Type": "Service", + "X-KDE-Library": "kritahistogramdocker", + "X-KDE-ServiceTypes": [ + "Krita/Dock" + ], + "X-Krita-Version": "28" +} diff --git a/plugins/extensions/histogram/kis_histogram_widget.h b/plugins/extensions/histogram/kis_histogram_widget.h --- a/plugins/extensions/histogram/kis_histogram_widget.h +++ b/plugins/extensions/histogram/kis_histogram_widget.h @@ -20,7 +20,7 @@ #include "kis_types.h" #include "ui_wdghistogram.h" - +#include "KoHistogramProducer.h" class WdgHistogram : public QWidget, public Ui::WdgHistogram { @@ -47,6 +47,12 @@ void setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds); + /** Sets the currently displayed channels to channels of the producer with producerID as ID*/ + void setCurrentChannels(const KoID& producerID, QList channels); + + /** Be careful, producer will be modified */ + void setCurrentChannels(KoHistogramProducer *producer, QList channels); + private Q_SLOTS: void setActiveChannel(int channel); void slotTypeSwitched(void); @@ -55,9 +61,31 @@ void slide(int val); private: + void setChannels(void); + void addProducerChannels(KoHistogramProducer *producer); + + typedef struct { + bool isProducer; + KoHistogramProducer *producer; + KoChannelInfo * channel; + } ComboboxInfo; + + QVector m_comboInfo; + // Maps the channels in m_channels to a real channel offset in the producer->channels() + QVector m_channelToOffset; + QStringList m_channelStrings; + QList m_channels; + const KoColorSpace* m_cs; + + QStringList channelStrings(); + /** Lists all producers currently available */ + QList producers(); + void setView(double from, double size); void updateEnabled(); double m_from, m_width; + KoHistogramProducer* m_currentProducer; + bool m_color; }; diff --git a/plugins/extensions/histogram/kis_histogram_widget.cc b/plugins/extensions/histogram/kis_histogram_widget.cc --- a/plugins/extensions/histogram/kis_histogram_widget.cc +++ b/plugins/extensions/histogram/kis_histogram_widget.cc @@ -28,6 +28,7 @@ #include #include "KoChannelInfo.h" +#include "KoBasicHistogramProducers.h" #include "kis_histogram_view.h" #include "kis_histogram.h" @@ -49,17 +50,142 @@ { } +QList KisHistogramWidget::producers() +{ + if (m_cs) + return KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs,true); + return QList(); +} + + +void KisHistogramWidget::setChannels(void) +{ + m_comboInfo.clear(); + m_channelStrings.clear(); + m_channels.clear(); + m_channelToOffset.clear(); + + QList list = KoHistogramProducerFactoryRegistry::instance()->keysCompatibleWith(m_cs,false); + + if (list.count() == 0) { + // XXX: No native histogram for this colorspace. Using converted RGB. We should have a warning + KoGenericRGBHistogramProducerFactory f; + addProducerChannels(f.generate()); + } else { + Q_FOREACH (const QString &id, list) { + KoHistogramProducer *producer = KoHistogramProducerFactoryRegistry::instance()->value(id)->generate(); + if (producer) { + addProducerChannels(producer); + } + } + } + + m_currentProducer = m_comboInfo.at(0).producer; + m_color = false; + // The currently displayed channel and its offset + m_channels.append(m_comboInfo.at(1).channel); + m_channelToOffset.append(0); +} + +void KisHistogramWidget::setCurrentChannels(const KoID& producerID, QList channels) +{ + setCurrentChannels( + KoHistogramProducerFactoryRegistry::instance()->value(producerID.id())->generate(), + channels); +} + +void KisHistogramWidget::setCurrentChannels(KoHistogramProducer *producer, QList channels) +{ + m_currentProducer = producer; + m_currentProducer->setView(m_from, m_width); + m_histogramView->setProducer(m_currentProducer); + //m_histogram->setChannel(0); // Set a default channel, just being nice + + m_channels.clear(); + m_channelToOffset.clear(); + + if (channels.count() == 0) { + return; + } + + QList producerChannels = m_currentProducer->channels(); + + for (int i = 0; i < channels.count(); i++) { + // Also makes sure the channel is actually in the producer's list + for (int j = 0; j < producerChannels.count(); j++) { + if (channels.at(i)->name() == producerChannels.at(j)->name()) { + m_channelToOffset.append(m_channels.count()); // The first we append maps to 0 + m_channels.append(channels.at(i)); + } + } + } + m_histogramView->setChannels(m_channels); +} + +void KisHistogramWidget::addProducerChannels(KoHistogramProducer *producer) +{ + if (!producer) return; + + ComboboxInfo info; + info.isProducer = true; + info.producer = producer; + m_comboInfo.append(info); + m_channelStrings.append(producer->id().name()); +} + +void KisHistogramWidget::setActiveChannel(int channel) +{ + ComboboxInfo info = m_comboInfo.at(channel); + if (info.producer != m_currentProducer) { + m_currentProducer = info.producer; + m_currentProducer->setView(m_from, m_width); + m_histogramView->setProducer(m_currentProducer); + } + + m_channels.clear(); + m_channelToOffset.clear(); + + if (info.isProducer) { + m_color = true; + m_channels = m_currentProducer->channels(); + for (int i = 0; i < m_channels.count(); i++) + m_channelToOffset.append(i); + //setChannel(0); // Set a default channel, just being nice + } else { + m_color = false; + QList channels = m_currentProducer->channels(); + for (int i = 0; i < channels.count(); i++) { + KoChannelInfo* channel = channels.at(i); + if (channel->name() == info.channel->name()) { + m_channels.append(channel); + m_channelToOffset.append(i); + break; + } + } + } + updateEnabled(); +} + +QStringList KisHistogramWidget::channelStrings() +{ + return m_channelStrings; +} + void KisHistogramWidget::setPaintDevice(KisPaintDeviceSP dev, const QRect &bounds) { - grpType->disconnect(this); + radioLinear->disconnect(this); + radioLog->disconnect(this); cmbChannel->disconnect(this); - m_histogramView->setPaintDevice(dev, bounds); + m_cs = dev->colorSpace(); + + setChannels(); // Sets m_currentProducer to the first in the list + m_histogramView->setPaintDevice(dev, m_currentProducer, bounds); setActiveChannel(0); // So we have the colored one if there are colors // The channels cmbChannel->clear(); - cmbChannel->addItems(m_histogramView->channelStrings()); + cmbChannel->addItems(this->channelStrings()); cmbChannel->setCurrentIndex(0); // View display @@ -79,11 +205,6 @@ connect(currentView, SIGNAL(valueChanged(int)), this, SLOT(slide(int))); } -void KisHistogramWidget::setActiveChannel(int channel) -{ - m_histogramView->setActiveChannel(channel); - updateEnabled(); -} void KisHistogramWidget::slotTypeSwitched(void) {