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