1 #include "cameracapturelevelcontrol_qt.h"
2
3 #include "toonzqt/intfield.h"
4 #include "toonzqt/doublefield.h"
5
6 #include <QPainter>
7 #include <QMouseEvent>
8 #include <QHBoxLayout>
9 #include <QVBoxLayout>
10 #include <cmath>
11 #include <iostream>
12
13 using namespace DVGui;
14
15 namespace {
16 const int HISTOGRAM_HEIGHT = 50;
17 const int SIDE_MARGIN = 7;
18 static bool histogramObtained = false;
19
20 // returns the horizontal position of gamma slider (0-255)
gammaToHPos(float gamma,int black,int white)21 int gammaToHPos(float gamma, int black, int white) {
22 float ratio = std::pow(0.5f, gamma);
23 return black + (int)std::round((float)(white - black) * ratio);
24 }
25
26 // returns the gamma value from the slider position
hPosToGamma(int hPos,int black,int white)27 float hPosToGamma(int hPos, int black, int white) {
28 if (hPos <= black + 1)
29 return 9.99f;
30 else if (hPos >= white - 1)
31 return 0.01f;
32
33 float ratio = (float)(hPos - black) / (float)(white - black);
34
35 float gamma = std::log(ratio) / std::log(0.5);
36 int decimals = 2;
37 gamma =
38 std::round(gamma * std::pow(10.0, decimals)) / std::pow(10.0, decimals);
39 return gamma;
40 }
41
drawSliderHandle(QPoint pos,QPainter & p,QColor color,bool selected)42 void drawSliderHandle(QPoint pos, QPainter& p, QColor color, bool selected) {
43 p.setPen((selected) ? Qt::yellow : Qt::black);
44 p.setBrush(color);
45
46 p.translate(pos);
47
48 static const QPoint points[5] = {QPoint(0, 0), QPoint(-6, 8), QPoint(-3, 12),
49 QPoint(3, 12), QPoint(6, 8)};
50 p.drawConvexPolygon(points, 5);
51 p.resetTransform();
52 }
53 }; // namespace
54 //-----------------------------------------------------------------------------
55
CameraCaptureLevelHistogram(QWidget * parent)56 CameraCaptureLevelHistogram::CameraCaptureLevelHistogram(QWidget* parent)
57 : QFrame(parent)
58 , m_histogramCue(false)
59 , m_currentItem(None)
60 , m_black(0)
61 , m_white(255)
62 , m_gamma(1.0)
63 , m_threshold(128)
64 , m_offset(0)
65 , m_mode(Color_GrayScale)
66 , m_histogramData(256) {
67 setFixedSize(256 + SIDE_MARGIN * 2, HISTOGRAM_HEIGHT + 15);
68 setMouseTracking(true);
69
70 m_histogramData.fill(0);
71 }
72
73 //-----------------------------------------------------------------------------
74
updateHistogram(QImage & image)75 void CameraCaptureLevelHistogram::updateHistogram(QImage& image) {
76 // obtain histogram only when clicked
77 if (!m_histogramCue) return;
78
79 QVector<int> tmpHisto(256);
80 tmpHisto.fill(0);
81 for (int y = 0; y < image.height(); y++) {
82 QRgb* rgb_p = (QRgb*)(image.scanLine(y));
83 for (int x = 0; x < image.width(); x++, rgb_p++) {
84 tmpHisto[qGray(*rgb_p)]++;
85 }
86 }
87 // obtain max value and normalize
88 int max = 0;
89 for (int c = 0; c < 256; c++) {
90 if (tmpHisto.at(c) > max) max = tmpHisto.at(c);
91 }
92 for (int c = 0; c < 256; c++) {
93 m_histogramData[c] = tmpHisto.at(c) * HISTOGRAM_HEIGHT / max;
94 }
95 histogramObtained = true;
96 update();
97 m_histogramCue = false;
98 }
99
100 //-----------------------------------------------------------------------------
101
paintEvent(QPaintEvent * event)102 void CameraCaptureLevelHistogram::paintEvent(QPaintEvent* event) {
103 QPainter p(this);
104
105 QRect histoRect(SIDE_MARGIN, 1, 256, HISTOGRAM_HEIGHT);
106
107 // draw histogram
108 p.setPen(Qt::black);
109 p.setBrush((m_currentItem == Histogram) ? Qt::darkGray : QColor(32, 32, 32));
110 p.drawRect(histoRect.adjusted(-1, -1, 0, 0));
111
112 if (histogramObtained) {
113 p.setPen(Qt::white);
114 for (int c = 0; c < 256; c++) {
115 if (m_histogramData.at(c) == 0) continue;
116 p.drawLine(histoRect.bottomLeft() + QPoint(c, 0),
117 histoRect.bottomLeft() + QPoint(c, -m_histogramData.at(c)));
118 }
119 }
120 if (!histogramObtained || m_currentItem == Histogram) {
121 p.setPen(Qt::white);
122 p.drawText(histoRect, Qt::AlignCenter, tr("Click to Update Histogram"));
123 }
124
125 p.setRenderHint(QPainter::Antialiasing);
126 // draw slider handles
127 QPoint sliderBasePos(SIDE_MARGIN, HISTOGRAM_HEIGHT + 2);
128 if (m_mode == Color_GrayScale) {
129 // black
130 drawSliderHandle(sliderBasePos + QPoint(m_black, 0), p, QColor(32, 32, 32),
131 m_currentItem == BlackSlider);
132 // gamma
133 drawSliderHandle(
134 sliderBasePos + QPoint(gammaToHPos(m_gamma, m_black, m_white), 0), p,
135 Qt::gray, m_currentItem == GammaSlider);
136 // white
137 drawSliderHandle(sliderBasePos + QPoint(m_white, 0), p,
138 QColor(220, 220, 220), m_currentItem == WhiteSlider);
139 } else if (m_mode == BlackAndWhite)
140 // threshold
141 drawSliderHandle(sliderBasePos + QPoint(m_threshold, 0), p, Qt::gray,
142 m_currentItem == ThresholdSlider);
143 p.setRenderHint(QPainter::Antialiasing, false);
144 }
145
146 //-----------------------------------------------------------------------------
147
mousePressEvent(QMouseEvent * event)148 void CameraCaptureLevelHistogram::mousePressEvent(QMouseEvent* event) {
149 if (event->button() != Qt::LeftButton) return;
150 if (m_currentItem == Histogram) {
151 m_histogramCue = true;
152 return;
153 }
154 if (m_currentItem == None) return;
155 QPoint pos = event->pos();
156 if (m_currentItem == BlackSlider)
157 m_offset = pos.x() - SIDE_MARGIN - m_black;
158 else if (m_currentItem == GammaSlider)
159 m_offset = pos.x() - SIDE_MARGIN - gammaToHPos(m_gamma, m_black, m_white);
160 else if (m_currentItem == BlackSlider)
161 m_offset = pos.x() - SIDE_MARGIN - m_white;
162 else if (m_currentItem == ThresholdSlider)
163 m_offset = pos.x() - SIDE_MARGIN - m_threshold;
164 }
165
166 //-----------------------------------------------------------------------------
167
mouseMoveEvent(QMouseEvent * event)168 void CameraCaptureLevelHistogram::mouseMoveEvent(QMouseEvent* event) {
169 QPoint pos = event->pos();
170 if (event->buttons() & Qt::LeftButton) {
171 if (m_currentItem == None || m_currentItem == Histogram) return;
172
173 int hPos = pos.x() - SIDE_MARGIN - m_offset;
174 bool changed = false;
175 if (m_currentItem == BlackSlider) {
176 if (hPos < 0)
177 hPos = 0;
178 else if (hPos > m_white - 2)
179 hPos = m_white - 2;
180 if (hPos != m_black) {
181 m_black = hPos;
182 changed = true;
183 }
184 } else if (m_currentItem == GammaSlider) {
185 if (hPos < m_black + 1)
186 hPos = m_black + 1;
187 else if (hPos > m_white - 1)
188 hPos = m_white - 1;
189 float gamma = hPosToGamma(hPos, m_black, m_white);
190 if (gamma != m_gamma) {
191 m_gamma = gamma;
192 changed = true;
193 }
194 } else if (m_currentItem == WhiteSlider) {
195 if (hPos < m_black + 2)
196 hPos = m_black + 2;
197 else if (hPos > 255)
198 hPos = 255;
199 if (hPos != m_white) {
200 m_white = hPos;
201 changed = true;
202 }
203 } else if (m_currentItem == ThresholdSlider) {
204 if (hPos < 0)
205 hPos = 0;
206 else if (hPos > 255)
207 hPos = 255;
208 if (hPos != m_threshold) {
209 m_threshold = hPos;
210 changed = true;
211 }
212 }
213 if (changed) {
214 update();
215 emit valueChange(m_currentItem);
216 }
217 return;
218 }
219
220 // on hover
221 LevelControlItem oldItem = m_currentItem;
222 m_currentItem = None;
223 setToolTip("");
224 QRect histoRect(5, 1, 256, HISTOGRAM_HEIGHT);
225 if (histoRect.contains(pos)) {
226 setToolTip(tr("Click to Update Histogram"));
227 m_currentItem = Histogram;
228 } else {
229 // slider handles
230 QPoint sliderBasePos(SIDE_MARGIN, HISTOGRAM_HEIGHT + 2);
231 QRect sliderRect(-6, 0, 12, 12);
232 if (m_mode == Color_GrayScale) {
233 // white
234 if (sliderRect.translated(sliderBasePos + QPoint(m_white, 0))
235 .contains(pos)) {
236 m_currentItem = WhiteSlider;
237 setToolTip(tr("Drag to Move White Point"));
238 }
239 // gamma
240 else if (sliderRect
241 .translated(
242 sliderBasePos +
243 QPoint(gammaToHPos(m_gamma, m_black, m_white), 0))
244 .contains(pos)) {
245 m_currentItem = GammaSlider;
246 setToolTip(tr("Drag to Move Gamma"));
247 }
248 // black
249 else if (sliderRect.translated(sliderBasePos + QPoint(m_black, 0))
250 .contains(pos)) {
251 m_currentItem = BlackSlider;
252 setToolTip(tr("Drag to Move Black Point"));
253 }
254 } else if (m_mode == BlackAndWhite) {
255 // threshold
256 if (sliderRect.translated(sliderBasePos + QPoint(m_threshold, 0))
257 .contains(pos)) {
258 m_currentItem = ThresholdSlider;
259 setToolTip(tr("Drag to Move Threshold Point"));
260 }
261 }
262 }
263 if (oldItem != m_currentItem) update();
264 }
265
266 //-----------------------------------------------------------------------------
267
mouseReleaseEvent(QMouseEvent * event)268 void CameraCaptureLevelHistogram::mouseReleaseEvent(QMouseEvent* event) {
269 m_offset = 0;
270 }
271
272 //-----------------------------------------------------------------------------
273
leaveEvent(QEvent * event)274 void CameraCaptureLevelHistogram::leaveEvent(QEvent* event) {
275 m_currentItem = None;
276 m_offset = 0;
277 update();
278 }
279
280 //=============================================================================
281
CameraCaptureLevelControl(QWidget * parent)282 CameraCaptureLevelControl::CameraCaptureLevelControl(QWidget* parent)
283 : QFrame(parent) {
284 m_histogram = new CameraCaptureLevelHistogram(this);
285 m_blackFld = new IntLineEdit(this, m_histogram->black(), 0, 253);
286 m_whiteFld = new IntLineEdit(this, m_histogram->white(), 2, 255);
287 m_thresholdFld = new IntLineEdit(this, m_histogram->threshold(), 0, 255);
288 m_gammaFld = new DoubleLineEdit(this, m_histogram->gamma());
289
290 m_blackFld->setToolTip(tr("Black Point Value"));
291 m_whiteFld->setToolTip(tr("White Point Value"));
292 m_thresholdFld->setToolTip(tr("Threshold Value"));
293 m_thresholdFld->hide();
294 m_gammaFld->setRange(0.01, 9.99);
295 m_gammaFld->setDecimals(2);
296 m_gammaFld->setFixedWidth(54);
297 m_gammaFld->setToolTip(tr("Gamma Value"));
298
299 QVBoxLayout* mainLay = new QVBoxLayout();
300 mainLay->setMargin(0);
301 mainLay->setSpacing(4);
302 {
303 mainLay->addWidget(m_histogram, 0, Qt::AlignHCenter);
304 QHBoxLayout* fieldsLay = new QHBoxLayout();
305 fieldsLay->setMargin(1);
306 fieldsLay->setSpacing(0);
307 {
308 fieldsLay->addWidget(m_blackFld, 0);
309 fieldsLay->addStretch(1);
310 fieldsLay->addWidget(m_gammaFld, 0);
311 fieldsLay->addWidget(m_thresholdFld, 0);
312 fieldsLay->addStretch(1);
313 fieldsLay->addWidget(m_whiteFld, 0);
314 }
315 mainLay->addLayout(fieldsLay, 0);
316 }
317 setLayout(mainLay);
318
319 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
320
321 connect(m_histogram, SIGNAL(valueChange(int)), this,
322 SLOT(onHistogramValueChanged(int)));
323 connect(m_blackFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
324 connect(m_whiteFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
325 connect(m_gammaFld, SIGNAL(editingFinished()), this, SLOT(onFieldChanged()));
326 connect(m_thresholdFld, SIGNAL(editingFinished()), this,
327 SLOT(onFieldChanged()));
328 }
329
330 //-----------------------------------------------------------------------------
331
onHistogramValueChanged(int itemId)332 void CameraCaptureLevelControl::onHistogramValueChanged(int itemId) {
333 if (itemId == CameraCaptureLevelHistogram::BlackSlider) {
334 m_blackFld->setValue(m_histogram->black());
335 m_whiteFld->setRange(m_histogram->black() + 2, 255);
336 } else if (itemId == CameraCaptureLevelHistogram::WhiteSlider) {
337 m_whiteFld->setValue(m_histogram->white());
338 m_blackFld->setRange(0, m_histogram->white() - 2);
339 } else if (itemId == CameraCaptureLevelHistogram::GammaSlider) {
340 m_gammaFld->setValue(m_histogram->gamma());
341 } else if (itemId == CameraCaptureLevelHistogram::ThresholdSlider) {
342 m_thresholdFld->setValue(m_histogram->threshold());
343 }
344 }
345
346 //-----------------------------------------------------------------------------
347
onFieldChanged()348 void CameraCaptureLevelControl::onFieldChanged() {
349 if (m_histogram->mode() == CameraCaptureLevelHistogram::Color_GrayScale)
350 m_histogram->setValues(m_blackFld->getValue(), m_whiteFld->getValue(),
351 m_gammaFld->getValue());
352 else if (m_histogram->mode() == CameraCaptureLevelHistogram::BlackAndWhite)
353 m_histogram->setThreshold(m_thresholdFld->getValue());
354
355 m_histogram->update();
356 }
357
358 //-----------------------------------------------------------------------------
359
setMode(bool color_grayscale)360 void CameraCaptureLevelControl::setMode(bool color_grayscale) {
361 m_histogram->setMode((color_grayscale)
362 ? CameraCaptureLevelHistogram::Color_GrayScale
363 : CameraCaptureLevelHistogram::BlackAndWhite);
364 m_blackFld->setVisible(color_grayscale);
365 m_whiteFld->setVisible(color_grayscale);
366 m_gammaFld->setVisible(color_grayscale);
367 m_thresholdFld->setVisible(!color_grayscale);
368 update();
369 }
370