File indexing completed on 2024-12-22 04:14:49

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Eugene Ingerman <geneing at gmail dot com>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "histogramdockerwidget.h"
0008 
0009 #include <QThread>
0010 #include <QVector>
0011 #include <limits>
0012 #include <algorithm>
0013 #include <QTime>
0014 #include <QPainter>
0015 #include <QPainterPath>
0016 #include <functional>
0017 
0018 #include "KoChannelInfo.h"
0019 #include "KisViewManager.h"
0020 #include "kis_canvas2.h"
0021 
0022 
0023 
0024 HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f)
0025     : KisWidgetWithIdleTask<QLabel>(parent, f)
0026 {
0027     setObjectName(name);
0028     qRegisterMetaType<HistogramData>();
0029 }
0030 
0031 HistogramDockerWidget::~HistogramDockerWidget()
0032 {
0033 }
0034 
0035 void HistogramDockerWidget::receiveNewHistogram(HistogramData data)
0036 {
0037     m_histogramData = data.bins;
0038     m_colorSpace = data.colorSpace;
0039     update();
0040 }
0041 
0042 KisIdleTasksManager::TaskGuard HistogramDockerWidget::registerIdleTask(KisCanvas2 *canvas)
0043 {
0044     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(canvas, KisIdleTasksManager::TaskGuard());
0045 
0046     return
0047         canvas->viewManager()->idleTasksManager()->
0048         addIdleTaskWithGuard([this](KisImageSP image) {
0049             HistogramComputationStrokeStrategy* strategy =
0050                 new HistogramComputationStrokeStrategy(image);
0051 
0052             connect(strategy, SIGNAL(computationResultReady(HistogramData)), this, SLOT(receiveNewHistogram(HistogramData)));
0053 
0054             return strategy;
0055         });
0056 }
0057 
0058 void HistogramDockerWidget::clearCachedState()
0059 {
0060     m_colorSpace = 0;
0061     m_histogramData.clear();
0062 }
0063 
0064 void HistogramDockerWidget::paintEvent(QPaintEvent *event)
0065 {
0066     if (m_colorSpace && !m_histogramData.empty()) {
0067         int nBins = m_histogramData.at(0).size();
0068         const KoColorSpace* cs = m_colorSpace;
0069 
0070         QLabel::paintEvent(event);
0071         QPainter painter(this);
0072         painter.fillRect(0, 0, this->width(), this->height(), this->palette().dark().color());
0073         painter.setPen(this->palette().light().color());
0074 
0075         const int NGRID = 4;
0076         for (int i = 0; i <= NGRID; ++i) {
0077             painter.drawLine(this->width()*i / NGRID, 0., this->width()*i / NGRID, this->height());
0078             painter.drawLine(0., this->height()*i / NGRID, this->width(), this->height()*i / NGRID);
0079         }
0080 
0081         unsigned int nChannels = cs->channelCount();
0082         const QList<KoChannelInfo *> channels = cs->channels();
0083         unsigned int highest = 0;
0084         //find the most populous bin in the histogram to scale it properly
0085         for (int chan = 0; chan < channels.size(); chan++) {
0086             if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) {
0087                 std::vector<quint32> histogramTemp = m_histogramData.at(chan);
0088                 //use 98th percentile, rather than max for better visual appearance
0089                 int nthPercentile = 2 * histogramTemp.size() / 100;
0090                 //unsigned int max = *std::max_element(m_histogramData.at(chan).begin(),m_histogramData.at(chan).end());
0091                 std::nth_element(histogramTemp.begin(),
0092                                  histogramTemp.begin() + nthPercentile, histogramTemp.end(), std::greater<int>());
0093                 unsigned int max = *(histogramTemp.begin() + nthPercentile);
0094 
0095                 highest = std::max(max, highest);
0096             }
0097         }
0098 
0099         painter.setWindow(QRect(-1, 0, nBins + 1, highest));
0100         painter.setCompositionMode(QPainter::CompositionMode_Plus);
0101 
0102         for (int chan = 0; chan < (int)nChannels; chan++) {
0103             if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) {
0104                 QColor color = channels.at(chan)->color();
0105 
0106                 //special handling of grayscale color spaces. can't use color returned above.
0107                 if(cs->colorChannelCount()==1){
0108                     color = QColor(Qt::gray);
0109                 }
0110 
0111                 QColor fill_color = color;
0112                 fill_color.setAlphaF(.25);
0113                 painter.setBrush(fill_color);
0114                 QPen pen = QPen(color);
0115                 pen.setWidth(0);
0116                 painter.setPen(pen);
0117 
0118                 if (m_smoothHistogram) {
0119                     QPainterPath path;
0120                     path.moveTo(QPointF(-1, highest));
0121                     for (qint32 i = 0; i < nBins; ++i) {
0122                         float v = std::max((float)highest - m_histogramData[chan][i], 0.f);
0123                         path.lineTo(QPointF(i, v));
0124 
0125                     }
0126                     path.lineTo(QPointF(nBins + 1, highest));
0127                     path.closeSubpath();
0128                     painter.drawPath(path);
0129                 } else {
0130                     pen.setWidth(1);
0131                     painter.setPen(pen);
0132                     for (qint32 i = 0; i < nBins; ++i) {
0133                         float v = std::max((float)highest - m_histogramData[chan][i], 0.f);
0134                         painter.drawLine(QPointF(i, highest), QPointF(i, v));
0135                     }
0136                 }
0137             }
0138         }
0139     }
0140 }