Krita Source Code Documentation
Loading...
Searching...
No Matches
histogramdockerwidget.cpp
Go to the documentation of this file.
1
2/*
3 * SPDX-FileCopyrightText: 2016 Eugene Ingerman <geneing at gmail dot com>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
9
10#include <QThread>
11#include <limits>
12#include <algorithm>
13#include <QTime>
14#include <QPainter>
15#include <QPainterPath>
16#include <functional>
17
18#include "KoChannelInfo.h"
19#include "KisViewManager.h"
20#include "kis_canvas2.h"
21
22
23
24HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f)
25 : KisWidgetWithIdleTask<QLabel>(parent, f)
26{
27 setObjectName(name);
28 qRegisterMetaType<HistogramData>();
29}
30
34
43
45{
46 if (m_logarithmic == enable) return;
47 m_logarithmic = enable;
48 update();
49}
50
52{
54
55 return
56 canvas->viewManager()->idleTasksManager()->
57 addIdleTaskWithGuard([this](KisImageSP image) {
60
61 connect(strategy, SIGNAL(computationResultReady(HistogramData)), this, SLOT(receiveNewHistogram(HistogramData)));
62
63 return strategy;
64 });
65}
66
73
74QMap<float, float> calculateLogGridLines(int width, qreal maximumValue) {
75 int power = 1;
76 QList<float> mainLines = {0.0, 0.25, 0.5, 0.75, 1.0};
77 while (mainLines.last() < (maximumValue)) {
78 qreal mainLine = powf(10, power);
79 mainLines.append(mainLine * 0.25);
80 mainLines.append(mainLine * 0.5);
81 mainLines.append(mainLine * 0.75);
82 mainLines.append(mainLine);
83
84 power += 1;
85 }
86 while (mainLines.last() > maximumValue) {
87 mainLines.removeLast();
88 }
89 mainLines.append(maximumValue);
90
91 QMap<float, float> gridLines;
92 Q_FOREACH(float mainLine, mainLines) {
93 if (qFuzzyCompare(mainLine, 0.f)) {
94 gridLines.insert(0.0, 0.0);
95 } else {
96 gridLines.insert(mainLine, std::log10(mainLine*10)*0.1*(width/(std::log10(mainLines.last()*10)*0.1)));
97 }
98 }
99
100 return gridLines;
101}
102
103QMap<float, float> calculateLinearGridLines(int width, qreal maximumValue)
104{
105 float gridValue = 0.25;
106 float gridWidthLength = width / (maximumValue / gridValue);
107 while(gridWidthLength < 25) {
108 gridValue *= 2;
109 gridWidthLength = width / (maximumValue / gridValue);
110 }
111 QMap<float, float> gridLines;
112 for (float i = 0; i < float(width); i+=gridWidthLength) {
113 gridLines.insert((i/gridWidthLength)*gridValue, i);
114 }
115 return gridLines;
116}
117
118void HistogramDockerWidget::paintEvent(QPaintEvent *event)
119{
120 if (m_colorSpace && !m_histogramData.empty()) {
121 int nBins = m_logarithmic? m_histogramData.at(0).size(): m_histogramDataLog.at(0).size();
122 const KoColorSpace* cs = m_colorSpace;
123
124 QLabel::paintEvent(event);
125 QPainter painter(this);
126 painter.fillRect(0, 0, this->width(), this->height(), this->palette().dark().color());
127 painter.setPen(this->palette().light().color());
128
129 const int gridHeight = this->height() - painter.fontMetrics().height();
130
131 painter.save();
132
133 QMap<float, float> horGrid = m_logarithmic? calculateLogGridLines(this->width(), m_maximumValue): calculateLinearGridLines(this->width(), m_maximumValue);
134 Q_FOREACH (float k, horGrid.keys()) {
135 float i = horGrid.value(k);
136 painter.drawLine(qRound(i), 0, qRound(i), gridHeight);
137 painter.drawText(QPointF(qRound(i), this->height()-2), QString::number(k));
138 }
139
140 unsigned int nChannels = cs->channelCount();
141 const QList<KoChannelInfo *> channels = cs->channels();
142 unsigned int highest = 0;
143 //find the most populous bin in the histogram to scale it properly
144 for (int chan = 0; chan < channels.size(); chan++) {
145 if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) {
146 std::vector<quint32> histogramTemp = m_logarithmic? m_histogramDataLog.at(chan): m_histogramData.at(chan);
147 //use 98th percentile, rather than max for better visual appearance
148 int nthPercentile = 2 * histogramTemp.size() / 100;
149 //unsigned int max = *std::max_element(m_histogramData.at(chan).begin(),m_histogramData.at(chan).end());
150 std::nth_element(histogramTemp.begin(),
151 histogramTemp.begin() + nthPercentile, histogramTemp.end(), std::greater<int>());
152 unsigned int max = *(histogramTemp.begin() + nthPercentile);
153
154 highest = std::max(max, highest);
155 }
156 }
157
158 QMap<float, float> vertGrid = m_logarithmic? calculateLogGridLines(gridHeight, highest): calculateLinearGridLines(gridHeight, highest);
159 Q_FOREACH (float k, vertGrid.keys()) {
160 float i = vertGrid.value(k);
161 painter.drawLine(0., gridHeight -i, this->width(), gridHeight -i);
162 }
163
164 painter.restore();
165
166 painter.setCompositionMode(QPainter::CompositionMode_Plus);
167 const float barWidth = float(this->width()-1) / float(nBins);
168 float logVerticalMax = std::log10(vertGrid.keys().last()*10)*0.1;
169
170 for (int chan = 0; chan < (int)nChannels; chan++) {
171 if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) {
172 QColor color = channels.at(chan)->color();
173
174 //special handling of grayscale color spaces. can't use color returned above.
175 if(cs->colorChannelCount()==1){
176 color = QColor(Qt::gray);
177 }
178
179 QColor fill_color = color;
180 fill_color.setAlphaF(.25);
181 painter.setBrush(fill_color);
182 QPen pen = QPen(color);
183 pen.setWidth(0);
184 painter.setPen(pen);
185
186 if (m_smoothHistogram) {
187 QPainterPath path;
188 path.moveTo(QPointF(1, gridHeight));
189 for (qint32 i = 0; i < nBins; ++i) {
190 const float curBinVertical = !m_logarithmic? m_histogramData[chan][i]
191 : std::log(m_histogramDataLog[chan][i]+1);
192 const float maxBin = !m_logarithmic? highest: logVerticalMax;
193 const float v = curBinVertical > 0? ((std::max((float)maxBin - curBinVertical, 0.f))/maxBin)*gridHeight: 0;
194 path.lineTo(QPointF((i*barWidth) - (barWidth*0.5), v));
195
196 }
197 path.lineTo(QPointF(this->width(), gridHeight));
198 path.closeSubpath();
199 painter.drawPath(path);
200 } else {
201 painter.setBrush(color);
202 painter.setPen(Qt::transparent);
203 float start = 1.0;
204 for (qint32 i = 0; i < nBins; ++i) {
205
206 const float curBinEnd = 1.0+(i*barWidth)+barWidth;
207 const float curBinVertical = m_logarithmic? std::log10(m_histogramDataLog[chan][i] *10)*0.1: m_histogramData[chan][i];
208 const float maxBin = m_logarithmic? logVerticalMax: highest;
209 if (curBinVertical > 0) {
210 const int v = std::max(qRound((curBinVertical/maxBin)*gridHeight), 1);
211 const QRect bar(QPoint(qRound(start), std::max(gridHeight - v, 0)),
212 QPoint(qRound(curBinEnd), gridHeight));
213 painter.drawRect(bar);
214 }
215 start = curBinEnd;
216 }
217 }
218 }
219 }
220 }
221}
qreal v
KisIdleTasksManager::TaskGuard registerIdleTask(KisCanvas2 *canvas) override
void enableLogarithmic(bool enable)
const KoColorSpace * m_colorSpace
void receiveNewHistogram(HistogramData data)
void paintEvent(QPaintEvent *event) override
HistogramDockerWidget(QWidget *parent=0, const char *name=0, Qt::WindowFlags f=Qt::WindowFlags())
KisViewManager * viewManager() const
KisIdleTasksManager * idleTasksManager()
@ ALPHA
The channel represents the opacity of a pixel.
QList< KoChannelInfo * > channels
virtual quint32 channelCount() const =0
virtual quint32 colorChannelCount() const =0
static bool qFuzzyCompare(half p1, half p2)
QMap< float, float > calculateLogGridLines(int width, qreal maximumValue)
QMap< float, float > calculateLinearGridLines(int width, qreal maximumValue)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
rgba palette[MAX_PALETTE]
Definition palette.c:35