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 }