Changeset View
Changeset View
Standalone View
Standalone View
plugins/dockers/histogram/histogramdockerwidget.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2016 Eugene Ingerman <geneing at gmail dot com> | ||||
3 | * | ||||
4 | * This library is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU Lesser General Public License as published by | ||||
6 | * the Free Software Foundation; version 2.1 of the License. | ||||
7 | * | ||||
8 | * This library is distributed in the hope that it will be useful, | ||||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
11 | * GNU Lesser General Public License for more details. | ||||
12 | * | ||||
13 | * You should have received a copy of the GNU Lesser General Public License | ||||
14 | * along with this program; if not, write to the Free Software | ||||
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
16 | */ | ||||
17 | | ||||
18 | #include "histogramdockerwidget.h" | ||||
19 | | ||||
20 | #include <QThread> | ||||
21 | #include <QVector> | ||||
22 | #include <limits> | ||||
23 | #include <algorithm> | ||||
24 | #include <QTime> | ||||
25 | #include <QPainter> | ||||
26 | | ||||
27 | #include "KoChannelInfo.h" | ||||
28 | #include "kis_paint_device.h" | ||||
29 | #include "KoColorSpace.h" | ||||
30 | #include "kis_iterator_ng.h" | ||||
31 | #include "kis_canvas2.h" | ||||
32 | | ||||
33 | HistogramDockerWidget::HistogramDockerWidget(QWidget *parent, const char *name, Qt::WindowFlags f) | ||||
34 | : QLabel(parent, f), m_paintDevice(nullptr), m_smoothHistogram(true) | ||||
35 | { | ||||
36 | setObjectName(name); | ||||
37 | } | ||||
38 | | ||||
39 | HistogramDockerWidget::~HistogramDockerWidget() | ||||
40 | { | ||||
41 | | ||||
42 | } | ||||
43 | | ||||
44 | void HistogramDockerWidget::setPaintDevice(KisCanvas2* canvas) | ||||
45 | { | ||||
46 | if (canvas) { | ||||
47 | m_paintDevice = canvas->image()->projection(); | ||||
48 | m_bounds = canvas->image()->bounds(); | ||||
49 | } else { | ||||
50 | m_paintDevice.clear(); | ||||
51 | m_bounds = QRect(); | ||||
52 | m_histogramData.clear(); | ||||
53 | } | ||||
54 | } | ||||
55 | | ||||
56 | void HistogramDockerWidget::updateHistogram() | ||||
57 | { | ||||
58 | if (!m_paintDevice.isNull()) { | ||||
59 | KisPaintDeviceSP m_devClone = new KisPaintDevice(m_paintDevice->colorSpace()); | ||||
60 | | ||||
61 | m_devClone->makeCloneFrom(m_paintDevice, m_bounds); | ||||
62 | | ||||
63 | HistogramComputationThread *workerThread = new HistogramComputationThread(m_devClone, m_bounds); | ||||
64 | connect(workerThread, &HistogramComputationThread::resultReady, this, &HistogramDockerWidget::receiveNewHistogram); | ||||
65 | connect(workerThread, &HistogramComputationThread::finished, workerThread, &QObject::deleteLater); | ||||
66 | workerThread->start(); | ||||
67 | } else { | ||||
68 | m_histogramData.clear(); | ||||
69 | update(); | ||||
70 | } | ||||
71 | } | ||||
72 | | ||||
73 | void HistogramDockerWidget::receiveNewHistogram(HistVector *histogramData) | ||||
74 | { | ||||
75 | m_histogramData = *histogramData; | ||||
76 | update(); | ||||
77 | } | ||||
78 | | ||||
79 | void HistogramDockerWidget::paintEvent(QPaintEvent *event) | ||||
80 | { | ||||
81 | if (!m_histogramData.empty()) { | ||||
82 | int nBins = m_histogramData.at(0).size(); | ||||
83 | | ||||
84 | QLabel::paintEvent(event); | ||||
85 | QPainter painter(this); | ||||
86 | painter.setPen(this->palette().light().color()); | ||||
87 | | ||||
88 | const int NGRID = 4; | ||||
89 | for (int i = 0; i <= NGRID; ++i) { | ||||
90 | painter.drawLine(this->width()*i / NGRID, 0., this->width()*i / NGRID, this->height()); | ||||
91 | painter.drawLine(0., this->height()*i / NGRID, this->width(), this->height()*i / NGRID); | ||||
92 | } | ||||
93 | | ||||
94 | unsigned int nChannels = m_paintDevice->colorSpace()->channelCount(); | ||||
95 | QList<KoChannelInfo *> channels = m_paintDevice->colorSpace()->channels(); | ||||
96 | unsigned int highest = 0; | ||||
97 | //find the most populous bin in the histogram to scale it properly | ||||
98 | for (int chan = 0; chan < channels.size(); chan++) { | ||||
99 | if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { | ||||
100 | std::vector<quint32> histogramTemp = m_histogramData.at(chan); | ||||
101 | //use 98th percentile, rather than max for better visual appearance | ||||
102 | int nthPercentile = 2 * histogramTemp.size() / 100; | ||||
103 | //unsigned int max = *std::max_element(m_histogramData.at(chan).begin(),m_histogramData.at(chan).end()); | ||||
104 | std::nth_element(histogramTemp.begin(), | ||||
105 | histogramTemp.begin() + nthPercentile, histogramTemp.end(), std::greater<int>()); | ||||
106 | unsigned int max = *(histogramTemp.begin() + nthPercentile); | ||||
107 | | ||||
108 | highest = std::max(max, highest); | ||||
109 | } | ||||
110 | } | ||||
111 | | ||||
112 | painter.setWindow(QRect(-1, 0, nBins + 1, highest)); | ||||
113 | painter.setCompositionMode(QPainter::CompositionMode_Plus); | ||||
114 | | ||||
115 | for (int chan = 0; chan < nChannels; chan++) { | ||||
116 | if (channels.at(chan)->channelType() != KoChannelInfo::ALPHA) { | ||||
117 | QColor color = channels.at(chan)->color(); | ||||
118 | QColor fill_color = color; | ||||
119 | fill_color.setAlphaF(.25); | ||||
120 | painter.setBrush(fill_color); | ||||
121 | QPen pen = QPen(color); | ||||
122 | pen.setWidth(0); | ||||
123 | painter.setPen(pen); | ||||
124 | | ||||
125 | if (m_smoothHistogram) { | ||||
126 | QPainterPath path; | ||||
127 | path.moveTo(QPointF(-1, highest)); | ||||
128 | for (qint32 i = 0; i < nBins; ++i) { | ||||
129 | float v = std::max((float)highest - m_histogramData[chan][i], 0.f); | ||||
130 | path.lineTo(QPointF(i, v)); | ||||
131 | | ||||
132 | } | ||||
133 | path.lineTo(QPointF(nBins + 1, highest)); | ||||
134 | path.closeSubpath(); | ||||
135 | painter.drawPath(path); | ||||
136 | } else { | ||||
137 | pen.setWidth(1); | ||||
138 | painter.setPen(pen); | ||||
139 | for (qint32 i = 0; i < nBins; ++i) { | ||||
140 | float v = std::max((float)highest - m_histogramData[chan][i], 0.f); | ||||
141 | painter.drawLine(QPointF(i, highest), QPointF(i, v)); | ||||
142 | } | ||||
143 | } | ||||
144 | } | ||||
145 | } | ||||
146 | } | ||||
147 | } | ||||
148 | | ||||
149 | void HistogramComputationThread::run() | ||||
150 | { | ||||
151 | const KoColorSpace *cs = m_dev->colorSpace(); | ||||
152 | quint32 channelCount = m_dev->channelCount(); | ||||
153 | quint32 pixelSize = m_dev->pixelSize(); | ||||
154 | | ||||
155 | quint32 imageSize = m_bounds.width() * m_bounds.height(); | ||||
156 | quint32 nSkip = 1 + (imageSize >> 20); //for speed use about 1M pixels for computing histograms | ||||
157 | | ||||
158 | //allocate space for the histogram data | ||||
159 | bins.resize((int)channelCount); | ||||
160 | for (auto &bin : bins) { | ||||
161 | bin.resize(std::numeric_limits<quint8>::max() + 1); | ||||
162 | } | ||||
163 | | ||||
164 | QRect bounds = m_dev->exactBounds(); | ||||
165 | if (bounds.isEmpty()) | ||||
166 | return; | ||||
167 | | ||||
168 | KisSequentialConstIterator it(m_dev, m_dev->exactBounds()); | ||||
169 | int i; | ||||
170 | quint32 toSkip = nSkip; | ||||
171 | | ||||
172 | do { | ||||
173 | i = it.nConseqPixels(); | ||||
174 | const quint8* pixel = it.rawDataConst(); | ||||
175 | for (int k = 0; k < i; ++k) { | ||||
176 | if (--toSkip == 0) { | ||||
177 | for (int chan = 0; chan < (int)channelCount; ++chan) { | ||||
178 | bins[chan][cs->scaleToU8(pixel, chan)]++; | ||||
179 | } | ||||
180 | toSkip = nSkip; | ||||
181 | } | ||||
182 | pixel += pixelSize; | ||||
183 | } | ||||
184 | } while (it.nextPixels(i)); | ||||
185 | | ||||
186 | emit resultReady(&bins); | ||||
187 | } |