File indexing completed on 2024-12-22 04:13:16

0001 /*
0002  * This file is part of Krita
0003  *
0004  * SPDX-FileCopyrightText: 2021 Deif Lou <ginoba@gmail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include <QMouseEvent>
0010 
0011 #include <kis_histogram.h>
0012 #include <KoColorSpace.h>
0013 #include <KisHistogramPainter.h>
0014 
0015 #include "KisHistogramView.h"
0016 
0017 class KisHistogramView::Private
0018 {
0019 public:
0020     QVector<KisHistogramPainter> histogramPainters;
0021     int histogramIndex{0};
0022 
0023     qreal preDraggingScale;
0024     int draggingStartMouseY;
0025     bool isScaling{false};
0026 };
0027 
0028 KisHistogramView::KisHistogramView(QWidget *parent)
0029     : QWidget(parent)
0030     , m_d(new Private)
0031 {}
0032 
0033 KisHistogramView::~KisHistogramView()
0034 {}
0035 
0036 void KisHistogramView::setup(const QVector<KisHistogram*> &histograms,
0037                              const QVector<const KoColorSpace*> &colorSpaces,
0038                              const QVector<QVector<int>> &channels)
0039 {
0040     Q_ASSERT(colorSpaces.size() == histograms.size());
0041     Q_ASSERT(channels.size() == 0 || channels.size() == histograms.size());
0042 
0043     m_d->histogramIndex = 0;
0044     m_d->histogramPainters.clear();
0045 
0046     if (histograms.size() == 0) {
0047         return;
0048     }
0049 
0050     QVector<QVector<int>> autoGeneratedChannels;
0051     if (channels.size() == 0) {
0052         for (int i = 0; i < histograms.size(); ++i) {
0053             autoGeneratedChannels.append(QVector<int>());
0054         }
0055     }
0056 
0057     const QVector<QVector<int>> &finalChannels =
0058         channels.size() == 0 ? autoGeneratedChannels : channels;
0059 
0060     for (int i = 0; i < histograms.size(); ++i) {
0061         m_d->histogramPainters.append(KisHistogramPainter());
0062         m_d->histogramPainters[i].setup(histograms[i], colorSpaces[i], finalChannels[i]);
0063     }
0064 }
0065 
0066 const QVector<int>& KisHistogramView::channels() const
0067 {
0068     return m_d->histogramPainters[m_d->histogramIndex].channels();
0069 }
0070 
0071 void KisHistogramView::setChannel(int channel, int histogramIndex)
0072 {
0073     setChannels({channel}, histogramIndex);
0074 }
0075 
0076 void KisHistogramView::setChannels(const QVector<int> &channels, int histogramIndex)
0077 {
0078     Q_ASSERT(m_d->histogramPainters.size() > 0);
0079     Q_ASSERT(histogramIndex >= 0 && histogramIndex < m_d->histogramPainters.size());
0080 
0081     const QColor defaultColor = m_d->histogramPainters[m_d->histogramIndex].defaultColor();
0082     const bool isLogarithmic = m_d->histogramPainters[m_d->histogramIndex].isLogarithmic();
0083 
0084     m_d->histogramIndex = histogramIndex;
0085     m_d->histogramPainters[m_d->histogramIndex].setChannels(channels);
0086     m_d->histogramPainters[m_d->histogramIndex].setDefaultColor(defaultColor);
0087     m_d->histogramPainters[m_d->histogramIndex].setLogarithmic(isLogarithmic);
0088     setScaleToFit();
0089     
0090     update();
0091 }
0092 
0093 void KisHistogramView::clearChannels()
0094 {
0095     setChannels({});
0096 }
0097 
0098 QColor KisHistogramView::defaultColor() const
0099 {
0100     Q_ASSERT(m_d->histogramPainters.size() > 0);
0101 
0102     return m_d->histogramPainters[m_d->histogramIndex].defaultColor();
0103 }
0104 
0105 void KisHistogramView::setDefaultColor(const QColor &newDefaultColor)
0106 {
0107     Q_ASSERT(m_d->histogramPainters.size() > 0);
0108 
0109     m_d->histogramPainters[m_d->histogramIndex].setDefaultColor(newDefaultColor);
0110     update();
0111 }
0112 
0113 qreal KisHistogramView::scale() const
0114 {
0115     Q_ASSERT(m_d->histogramPainters.size() > 0);
0116     
0117     return m_d->histogramPainters[m_d->histogramIndex].scale();
0118 }
0119 
0120 void KisHistogramView::setScale(qreal newScale)
0121 {
0122     Q_ASSERT(m_d->histogramPainters.size() > 0);
0123     
0124     m_d->histogramPainters[m_d->histogramIndex].setScale(newScale);
0125     update();
0126 }
0127 
0128 void KisHistogramView::setScaleToFit()
0129 {
0130     setScale(1.0);
0131 }
0132 
0133 void KisHistogramView::setScaleToCutLongPeaks()
0134 {
0135     Q_ASSERT(m_d->histogramPainters.size() > 0);
0136     
0137     m_d->histogramPainters[m_d->histogramIndex].setScaleToCutLongPeaks();
0138     update();
0139 }
0140 
0141 bool KisHistogramView::isLogarithmic() const
0142 {
0143     Q_ASSERT(m_d->histogramPainters.size() > 0);
0144     
0145     return m_d->histogramPainters[m_d->histogramIndex].isLogarithmic();
0146 }
0147 
0148 void KisHistogramView::setLogarithmic(bool logarithmic)
0149 {
0150     Q_ASSERT(m_d->histogramPainters.size() > 0);
0151     
0152     m_d->histogramPainters[m_d->histogramIndex].setLogarithmic(logarithmic);
0153     setScaleToFit();
0154     update();
0155 }
0156 
0157 void KisHistogramView::paintEvent(QPaintEvent *e)
0158 {
0159     Q_UNUSED(e);
0160 
0161     QPainter painter(this);
0162 
0163     // Background
0164     painter.fillRect(rect(), palette().base());
0165 
0166     // Histogram
0167     if (m_d->histogramPainters.size() > 0 &&
0168         m_d->histogramPainters[m_d->histogramIndex].channels().size() > 0) {
0169         // Histogram
0170         QImage histogramImage = m_d->histogramPainters[m_d->histogramIndex].paint(size());
0171         // Shadow
0172         QLinearGradient shadowGradient(QPointF(0.0, 0.0), QPointF(0.0, static_cast<qreal>(height()) * 0.2));
0173         shadowGradient.setColorAt(0.00, QColor(0, 0, 0, 64));
0174         shadowGradient.setColorAt(0.25, QColor(0, 0, 0, 36));
0175         shadowGradient.setColorAt(0.50, QColor(0, 0, 0, 16));
0176         shadowGradient.setColorAt(0.75, QColor(0, 0, 0, 4));
0177         shadowGradient.setColorAt(1.00, QColor(0, 0, 0, 0));
0178         QPainter histogramPainter(&histogramImage);
0179         histogramPainter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
0180         histogramPainter.fillRect(histogramImage.rect(), shadowGradient);
0181         if (!isEnabled()) {
0182             painter.setOpacity(0.5);
0183         }
0184         painter.drawImage(0, 0, histogramImage);
0185         painter.setOpacity(1.0);
0186     } else {
0187         const qreal w = static_cast<qreal>(width());
0188         const qreal h = static_cast<qreal>(height());
0189         const qreal radius = qMin(w, h) * 0.3;
0190         const QPointF center = QPointF(w / 2.0, h / 2.0);
0191         const int penWidth = static_cast<int>(qRound(radius / 8));
0192         painter.setPen(QPen(palette().alternateBase(), penWidth, Qt::SolidLine, Qt::FlatCap));
0193         painter.setBrush(Qt::NoBrush);
0194         painter.setRenderHint(QPainter::Antialiasing);
0195         painter.drawEllipse(center, radius, radius);
0196         painter.drawLine(center + QPointF(radius, -radius), center + QPointF(-radius, radius));
0197     }
0198 
0199     // Border
0200     QColor c = palette().text().color();
0201     c.setAlpha(64);
0202     painter.setPen(QPen(c, 1));
0203     painter.setBrush(Qt::NoBrush);
0204     painter.setRenderHint(QPainter::Antialiasing, false);
0205     painter.drawRect(0, 0, width() - 1, height() - 1);
0206 }
0207 
0208 void KisHistogramView::mouseDoubleClickEvent(QMouseEvent *e)
0209 {
0210     if (m_d->histogramPainters.size() == 0 ||
0211         m_d->histogramPainters[m_d->histogramIndex].channels().size() == 0) {
0212         return;
0213     }
0214 
0215     if (e->button() != Qt::LeftButton) {
0216         return;
0217     }
0218 
0219     if (qFuzzyCompare(scale(), 1.0)) {
0220         setScaleToCutLongPeaks();
0221     } else {
0222         setScaleToFit();
0223     }
0224 }
0225 
0226 void KisHistogramView::mousePressEvent(QMouseEvent *e)
0227 {
0228     if (m_d->histogramPainters.size() == 0 ||
0229         m_d->histogramPainters[m_d->histogramIndex].channels().size() == 0) {
0230         return;
0231     }
0232 
0233     if (e->button() != Qt::LeftButton) {
0234         return;
0235     }
0236 
0237     m_d->preDraggingScale = scale();
0238     m_d->draggingStartMouseY = e->y();
0239     m_d->isScaling = false;
0240 }
0241 
0242 void KisHistogramView::mouseMoveEvent(QMouseEvent *e)
0243 {
0244     if (m_d->histogramPainters.size() == 0 ||
0245         m_d->histogramPainters[m_d->histogramIndex].channels().size() == 0) {
0246         return;
0247     }
0248 
0249     if (!(e->buttons() & Qt::LeftButton)) {
0250         return;
0251     }
0252 
0253     if (m_d->isScaling) {
0254         const qreal newScale =
0255             m_d->preDraggingScale * static_cast<qreal>(height() - e->y()) /
0256                 static_cast<qreal>(height() - m_d->draggingStartMouseY);
0257         setScale(qMax(newScale, 1.0));
0258     } else {
0259         if (qAbs(e->y() - m_d->draggingStartMouseY) > 4) {
0260             m_d->isScaling = true;
0261         }
0262     }
0263 
0264 }