1 /*
2     SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com>
3     This file is part of kdenlive. See www.kdenlive.org.
4 
5 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6 */
7 
8 #include "vectorscope.h"
9 #include "colorplaneexport.h"
10 #include "colortools.h"
11 #include "vectorscopegenerator.h"
12 
13 #include "kdenlive_debug.h"
14 #include "klocalizedstring.h"
15 #include <KConfigGroup>
16 #include <KSharedConfig>
17 #include <QAction>
18 #include <QPainter>
19 #include <QElapsedTimer>
20 #include <cmath>
21 const double P75 = .75;
22 
23 const QPointF YUV_R(-.147, .615);
24 const QPointF YUV_G(-.289, -.515);
25 const QPointF YUV_B(.437, -.100);
26 const QPointF YUV_Cy(.147, -.615);
27 const QPointF YUV_Mg(.289, .515);
28 const QPointF YUV_Yl(-.437, .100);
29 
30 const QPointF YPbPr_R(-.169, .5);
31 const QPointF YPbPr_G(-.331, -.419);
32 const QPointF YPbPr_B(.5, -.081);
33 const QPointF YPbPr_Cy(.169, -.5);
34 const QPointF YPbPr_Mg(.331, .419);
35 const QPointF YPbPr_Yl(-.5, .081);
36 
Vectorscope(QWidget * parent)37 Vectorscope::Vectorscope(QWidget *parent)
38     : AbstractGfxScopeWidget(true, parent)
39 
40 {
41     m_ui = new Ui::Vectorscope_UI();
42     m_ui->setupUi(this);
43 
44     m_colorTools = new ColorTools();
45     m_vectorscopeGenerator = new VectorscopeGenerator();
46 
47     m_ui->paintMode->addItem(i18n("Green 2"), QVariant(VectorscopeGenerator::PaintMode_Green2));
48     m_ui->paintMode->addItem(i18n("Green"), QVariant(VectorscopeGenerator::PaintMode_Green));
49     m_ui->paintMode->addItem(i18n("Black"), QVariant(VectorscopeGenerator::PaintMode_Black));
50     m_ui->paintMode->addItem(i18n("Modified YUV (Chroma)"), QVariant(VectorscopeGenerator::PaintMode_Chroma));
51     m_ui->paintMode->addItem(i18n("YUV"), QVariant(VectorscopeGenerator::PaintMode_YUV));
52     m_ui->paintMode->addItem(i18n("Original Color"), QVariant(VectorscopeGenerator::PaintMode_Original));
53 
54     m_ui->backgroundMode->addItem(i18n("None"), QVariant(BG_NONE));
55     m_ui->backgroundMode->addItem(i18n("YUV"), QVariant(BG_YUV));
56     m_ui->backgroundMode->addItem(i18n("Modified YUV (Chroma)"), QVariant(BG_CHROMA));
57     m_ui->backgroundMode->addItem(i18n("YPbPr"), QVariant(BG_YPbPr));
58 
59     m_ui->sliderGain->setMinimum(0);
60     m_ui->sliderGain->setMaximum(40);
61 
62     connect(m_ui->backgroundMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotBackgroundChanged()));
63     connect(m_ui->sliderGain, &QAbstractSlider::valueChanged, this, &Vectorscope::slotGainChanged);
64     connect(m_ui->paintMode, SIGNAL(currentIndexChanged(int)), this, SLOT(forceUpdateScope()));
65     connect(this, &Vectorscope::signalMousePositionChanged, this, &Vectorscope::forceUpdateHUD);
66     m_ui->sliderGain->setValue(0);
67 
68     ///// Build context menu /////
69 
70     m_menu->addSeparator()->setText(i18n("Tools"));
71 
72     m_aExportBackground = new QAction(i18n("Export background"), this);
73     m_menu->addAction(m_aExportBackground);
74     connect(m_aExportBackground, &QAction::triggered, this, &Vectorscope::slotExportBackground);
75 
76     m_menu->addSeparator()->setText(i18n("Drawing options"));
77 
78     m_a75PBox = new QAction(i18n("75% box"), this);
79     m_a75PBox->setCheckable(true);
80     m_menu->addAction(m_a75PBox);
81     connect(m_a75PBox, &QAction::changed, this, &Vectorscope::forceUpdateBackground);
82 
83     m_aAxisEnabled = new QAction(i18n("Draw axis"), this);
84     m_aAxisEnabled->setCheckable(true);
85     m_menu->addAction(m_aAxisEnabled);
86     connect(m_aAxisEnabled, &QAction::changed, this, &Vectorscope::forceUpdateBackground);
87 
88     m_aIQLines = new QAction(i18n("Draw I/Q lines"), this);
89     m_aIQLines->setCheckable(true);
90     m_menu->addAction(m_aIQLines);
91     connect(m_aIQLines, &QAction::changed, this, &Vectorscope::forceUpdateBackground);
92 
93     m_menu->addSeparator()->setText(i18n("Color Space"));
94     m_aColorSpace_YPbPr = new QAction(i18n("YPbPr"), this);
95     m_aColorSpace_YPbPr->setCheckable(true);
96     m_aColorSpace_YUV = new QAction(i18n("YUV"), this);
97     m_aColorSpace_YUV->setCheckable(true);
98     m_agColorSpace = new QActionGroup(this);
99     m_agColorSpace->addAction(m_aColorSpace_YPbPr);
100     m_agColorSpace->addAction(m_aColorSpace_YUV);
101     m_menu->addAction(m_aColorSpace_YPbPr);
102     m_menu->addAction(m_aColorSpace_YUV);
103     connect(m_aColorSpace_YPbPr, &QAction::toggled, this, &Vectorscope::slotColorSpaceChanged);
104     connect(m_aColorSpace_YUV, &QAction::toggled, this, &Vectorscope::slotColorSpaceChanged);
105 
106     // To make the 1.0x text show
107     slotGainChanged(m_ui->sliderGain->value());
108 
109     init();
110 }
111 
~Vectorscope()112 Vectorscope::~Vectorscope()
113 {
114     writeConfig();
115 
116     delete m_colorTools;
117     delete m_vectorscopeGenerator;
118 
119     delete m_aColorSpace_YPbPr;
120     delete m_aColorSpace_YUV;
121     delete m_aExportBackground;
122     delete m_aAxisEnabled;
123     delete m_a75PBox;
124     delete m_agColorSpace;
125     delete m_ui;
126 }
127 
widgetName() const128 QString Vectorscope::widgetName() const
129 {
130     return QStringLiteral("Vectorscope");
131 }
132 
readConfig()133 void Vectorscope::readConfig()
134 {
135     AbstractGfxScopeWidget::readConfig();
136 
137     KSharedConfigPtr config = KSharedConfig::openConfig();
138     KConfigGroup scopeConfig(config, configName());
139     m_a75PBox->setChecked(scopeConfig.readEntry("75PBox", false));
140     m_aAxisEnabled->setChecked(scopeConfig.readEntry("axis", false));
141     m_aIQLines->setChecked(scopeConfig.readEntry("iqlines", false));
142     m_ui->backgroundMode->setCurrentIndex(scopeConfig.readEntry("backgroundmode").toInt());
143     m_ui->paintMode->setCurrentIndex(scopeConfig.readEntry("paintmode").toInt());
144     m_ui->sliderGain->setValue(scopeConfig.readEntry("gain", 1));
145     m_aColorSpace_YPbPr->setChecked(scopeConfig.readEntry("colorspace_ypbpr", false));
146     m_aColorSpace_YUV->setChecked(!m_aColorSpace_YPbPr->isChecked());
147 }
148 
writeConfig()149 void Vectorscope::writeConfig()
150 {
151     KSharedConfigPtr config = KSharedConfig::openConfig();
152     KConfigGroup scopeConfig(config, configName());
153     scopeConfig.writeEntry("75PBox", m_a75PBox->isChecked());
154     scopeConfig.writeEntry("axis", m_aAxisEnabled->isChecked());
155     scopeConfig.writeEntry("iqlines", m_aIQLines->isChecked());
156     scopeConfig.writeEntry("backgroundmode", m_ui->backgroundMode->currentIndex());
157     scopeConfig.writeEntry("paintmode", m_ui->paintMode->currentIndex());
158     scopeConfig.writeEntry("gain", m_ui->sliderGain->value());
159     scopeConfig.writeEntry("colorspace_ypbpr", m_aColorSpace_YPbPr->isChecked());
160     scopeConfig.sync();
161 }
162 
scopeRect()163 QRect Vectorscope::scopeRect()
164 {
165     // Distance from top/left/right
166     int border = 6;
167 
168     // We want to paint below the controls area. The line is the lowest element.
169     QPoint topleft(border, m_ui->verticalSpacer->geometry().y() + border);
170     QPoint bottomright(m_ui->horizontalSpacer->geometry().right() - border, this->size().height() - border);
171 
172     m_visibleRect = QRect(topleft, bottomright);
173 
174     QRect scopeRect(topleft, bottomright);
175 
176     // Circle Width: min of width and height
177     m_cw = (scopeRect.height() < scopeRect.width()) ? scopeRect.height() : scopeRect.width();
178     scopeRect.setWidth(m_cw);
179     scopeRect.setHeight(m_cw);
180 
181     m_centerPoint = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), QPointF(0, 0));
182     m_pR75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_R);
183     m_pG75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_G);
184     m_pB75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_B);
185     m_pCy75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_Cy);
186     m_pMg75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_Mg);
187     m_pYl75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YUV_Yl);
188     m_qR75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_R);
189     m_qG75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_G);
190     m_qB75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_B);
191     m_qCy75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_Cy);
192     m_qMg75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_Mg);
193     m_qYl75 = m_vectorscopeGenerator->mapToCircle(scopeRect.size(), P75 * VectorscopeGenerator::scaling * YPbPr_Yl);
194 
195     return scopeRect;
196 }
197 
isHUDDependingOnInput() const198 bool Vectorscope::isHUDDependingOnInput() const
199 {
200     return false;
201 }
isScopeDependingOnInput() const202 bool Vectorscope::isScopeDependingOnInput() const
203 {
204     return true;
205 }
isBackgroundDependingOnInput() const206 bool Vectorscope::isBackgroundDependingOnInput() const
207 {
208     return false;
209 }
210 
renderHUD(uint)211 QImage Vectorscope::renderHUD(uint)
212 {
213 
214     QImage hud;
215     QLocale locale; // Used for UI → OK
216     locale.setNumberOptions(QLocale::OmitGroupSeparator);
217     if (m_mouseWithinWidget) {
218         // Mouse moved: Draw a circle over the scope
219 
220         hud = QImage(m_visibleRect.size(), QImage::Format_ARGB32);
221         hud.fill(qRgba(0, 0, 0, 0));
222 
223         QPainter davinci;
224         bool ok = davinci.begin(&hud);
225         if (!ok) {
226             qDebug() << "Could not initialise QPainter for Vectorscope HUD.";
227             return hud;
228         }
229         QPoint widgetCenterPoint = m_scopeRect.topLeft() + m_centerPoint;
230 
231         int dx = -widgetCenterPoint.x() + m_mousePos.x();
232         int dy = widgetCenterPoint.y() - m_mousePos.y();
233 
234         QPoint reference = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(1, 0));
235 
236         float r = sqrtf(dx * dx + dy * dy);
237         float percent = 100.f * r / float(VectorscopeGenerator::scaling) / m_gain / (reference.x() - widgetCenterPoint.x());
238 
239         switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
240         case BG_NONE:
241             davinci.setPen(penLight);
242             break;
243         default:
244             if (r > m_cw / 2.0f) {
245                 davinci.setPen(penLight);
246             } else {
247                 davinci.setPen(penDark);
248             }
249             break;
250         }
251         davinci.drawEllipse(m_centerPoint, int(r), int(r));
252         davinci.setPen(penThin);
253         davinci.drawText(QPoint(m_scopeRect.width() - 40, m_scopeRect.height()), i18n("%1 %%", locale.toString(percent, 'f', 0)));
254 
255         float angle = float(copysignf(std::acos(dx / r) * 180.f / float(M_PI), dy));
256         davinci.drawText(QPoint(10, m_scopeRect.height()), i18n("%1°", locale.toString(angle, 'f', 1)));
257 
258         //        m_circleEnabled = false;
259     } else {
260         hud = QImage(0, 0, QImage::Format_ARGB32);
261     }
262     emit signalHUDRenderingFinished(0, 1);
263     return hud;
264 }
265 
renderGfxScope(uint accelerationFactor,const QImage & qimage)266 QImage Vectorscope::renderGfxScope(uint accelerationFactor, const QImage &qimage)
267 {
268     QElapsedTimer timer;
269     timer.start();
270     QImage scope;
271 
272     if (m_cw <= 0) {
273         qCDebug(KDENLIVE_LOG) << "Scope size not known yet. Aborting.";
274     } else {
275 
276         VectorscopeGenerator::ColorSpace colorSpace =
277             m_aColorSpace_YPbPr->isChecked() ? VectorscopeGenerator::ColorSpace_YPbPr : VectorscopeGenerator::ColorSpace_YUV;
278         VectorscopeGenerator::PaintMode paintMode = VectorscopeGenerator::PaintMode(m_ui->paintMode->itemData(m_ui->paintMode->currentIndex()).toInt());
279         scope = m_vectorscopeGenerator->calculateVectorscope(m_scopeRect.size(), qimage, m_gain, paintMode, colorSpace, m_aAxisEnabled->isChecked(),
280                                                              accelerationFactor);
281     }
282     emit signalScopeRenderingFinished(uint(timer.elapsed()), accelerationFactor);
283     return scope;
284 }
285 
renderBackground(uint)286 QImage Vectorscope::renderBackground(uint)
287 {
288     QElapsedTimer timer;
289     timer.start();
290 
291     QImage bg(m_visibleRect.size(), QImage::Format_ARGB32);
292     bg.fill(qRgba(0, 0, 0, 0));
293 
294     // Set up tools
295     QPainter davinci;
296     bool ok = davinci.begin(&bg);
297     if (!ok) {
298         qDebug() << "Could not initialise QPainter for Vectorscope background.";
299         return bg;
300     }
301     davinci.setRenderHint(QPainter::Antialiasing, true);
302 
303     QPoint vinciPoint;
304     QPoint vinciPoint2;
305 
306     // Draw the color plane (if selected)
307     QImage colorPlane;
308     switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
309     case BG_YUV:
310         colorPlane = m_colorTools->yuvColorWheel(m_scopeRect.size(), 128, 1 / float(VectorscopeGenerator::scaling), false, true);
311         davinci.drawImage(0, 0, colorPlane);
312         break;
313     case BG_CHROMA:
314         colorPlane = m_colorTools->yuvColorWheel(m_scopeRect.size(), 255, 1 / float(VectorscopeGenerator::scaling), true, true);
315         davinci.drawImage(0, 0, colorPlane);
316         break;
317     case BG_YPbPr:
318         colorPlane = m_colorTools->yPbPrColorWheel(m_scopeRect.size(), 128, 1 / float(VectorscopeGenerator::scaling), true);
319         davinci.drawImage(0, 0, colorPlane);
320         break;
321     }
322 
323     // Draw I/Q lines (from the YIQ color space; Skin tones lie on the I line)
324     // Positions are calculated by transforming YIQ:[0 1 0] or YIQ:[0 0 1] to YUV/YPbPr.
325     if (m_aIQLines->isChecked()) {
326 
327         switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
328         case BG_NONE:
329             davinci.setPen(penLightDots);
330             break;
331         default:
332             davinci.setPen(penDarkDots);
333             break;
334         }
335 
336         if (m_aColorSpace_YUV->isChecked()) {
337             vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(-.544, .838));
338             vinciPoint2 = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(.544, -.838));
339         } else {
340             vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(-.675, .737));
341             vinciPoint2 = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(.675, -.737));
342         }
343 
344         davinci.drawLine(vinciPoint, vinciPoint2);
345         davinci.setPen(penThick);
346         davinci.drawText(vinciPoint - QPoint(11, 5), QStringLiteral("I"));
347 
348         switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
349         case BG_NONE:
350             davinci.setPen(penLightDots);
351             break;
352         default:
353             davinci.setPen(penDarkDots);
354             break;
355         }
356 
357         if (m_aColorSpace_YUV->isChecked()) {
358             vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(.838, .544));
359             vinciPoint2 = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(-.838, -.544));
360         } else {
361             vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(.908, .443));
362             vinciPoint2 = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(-.908, -.443));
363         }
364 
365         davinci.drawLine(vinciPoint, vinciPoint2);
366         davinci.setPen(penThick);
367         davinci.drawText(vinciPoint - QPoint(-7, 2), QStringLiteral("Q"));
368     }
369 
370     // Draw the vectorscope circle
371     davinci.setPen(penThick);
372     davinci.drawEllipse(0, 0, m_cw, m_cw);
373 
374     // Draw RGB/CMY points with 100% chroma
375     if (m_aColorSpace_YUV->isChecked()) {
376         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_R);
377         davinci.drawEllipse(vinciPoint, 4, 4);
378         davinci.drawText(vinciPoint - QPoint(20, -10), QStringLiteral("R"));
379 
380         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_G);
381         davinci.drawEllipse(vinciPoint, 4, 4);
382         davinci.drawText(vinciPoint - QPoint(20, 0), QStringLiteral("G"));
383 
384         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_B);
385         davinci.drawEllipse(vinciPoint, 4, 4);
386         davinci.drawText(vinciPoint + QPoint(15, 10), QStringLiteral("B"));
387 
388         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_Cy);
389         davinci.drawEllipse(vinciPoint, 4, 4);
390         davinci.drawText(vinciPoint + QPoint(15, -5), QStringLiteral("Cy"));
391 
392         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_Mg);
393         davinci.drawEllipse(vinciPoint, 4, 4);
394         davinci.drawText(vinciPoint + QPoint(15, 10), QStringLiteral("Mg"));
395 
396         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YUV_Yl);
397         davinci.drawEllipse(vinciPoint, 4, 4);
398         davinci.drawText(vinciPoint - QPoint(25, 0), QStringLiteral("Yl"));
399     } else {
400         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_R);
401         davinci.drawEllipse(vinciPoint, 4, 4);
402         davinci.drawText(vinciPoint - QPoint(20, -10), QStringLiteral("R"));
403 
404         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_G);
405         davinci.drawEllipse(vinciPoint, 4, 4);
406         davinci.drawText(vinciPoint - QPoint(20, 0), QStringLiteral("G"));
407 
408         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_B);
409         davinci.drawEllipse(vinciPoint, 4, 4);
410         davinci.drawText(vinciPoint + QPoint(15, 10), QStringLiteral("B"));
411 
412         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_Cy);
413         davinci.drawEllipse(vinciPoint, 4, 4);
414         davinci.drawText(vinciPoint + QPoint(15, -5), QStringLiteral("Cy"));
415 
416         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_Mg);
417         davinci.drawEllipse(vinciPoint, 4, 4);
418         davinci.drawText(vinciPoint + QPoint(15, 10), QStringLiteral("Mg"));
419 
420         vinciPoint = m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), VectorscopeGenerator::scaling * YPbPr_Yl);
421         davinci.drawEllipse(vinciPoint, 4, 4);
422         davinci.drawText(vinciPoint - QPoint(25, 0), QStringLiteral("Yl"));
423     }
424 
425     switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
426     case BG_NONE:
427         davinci.setPen(penLight);
428         break;
429     default:
430         davinci.setPen(penDark);
431         break;
432     }
433 
434     // Draw axis
435     if (m_aAxisEnabled->isChecked()) {
436         davinci.drawLine(m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(0, -.9)),
437                          m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(0, .9)));
438         davinci.drawLine(m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(-.9, 0)),
439                          m_vectorscopeGenerator->mapToCircle(m_scopeRect.size(), QPointF(.9, 0)));
440     }
441 
442     // Draw center point
443     switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
444     case BG_CHROMA:
445         davinci.setPen(penDark);
446         break;
447     default:
448         davinci.setPen(penThin);
449         break;
450     }
451     davinci.drawEllipse(m_centerPoint, 5, 5);
452 
453     // Draw 75% box
454     if (m_a75PBox->isChecked()) {
455         if (m_aColorSpace_YUV->isChecked()) {
456             davinci.drawLine(m_pR75, m_pYl75);
457             davinci.drawLine(m_pYl75, m_pG75);
458             davinci.drawLine(m_pG75, m_pCy75);
459             davinci.drawLine(m_pCy75, m_pB75);
460             davinci.drawLine(m_pB75, m_pMg75);
461             davinci.drawLine(m_pMg75, m_pR75);
462         } else {
463             davinci.drawLine(m_qR75, m_qYl75);
464             davinci.drawLine(m_qYl75, m_qG75);
465             davinci.drawLine(m_qG75, m_qCy75);
466             davinci.drawLine(m_qCy75, m_qB75);
467             davinci.drawLine(m_qB75, m_qMg75);
468             davinci.drawLine(m_qMg75, m_qR75);
469         }
470     }
471 
472     // Draw RGB/CMY points with 75% chroma (for NTSC)
473     davinci.setPen(penThin);
474     if (m_aColorSpace_YUV->isChecked()) {
475         davinci.drawEllipse(m_pR75, 3, 3);
476         davinci.drawEllipse(m_pG75, 3, 3);
477         davinci.drawEllipse(m_pB75, 3, 3);
478         davinci.drawEllipse(m_pCy75, 3, 3);
479         davinci.drawEllipse(m_pMg75, 3, 3);
480         davinci.drawEllipse(m_pYl75, 3, 3);
481     } else {
482         davinci.drawEllipse(m_qR75, 3, 3);
483         davinci.drawEllipse(m_qG75, 3, 3);
484         davinci.drawEllipse(m_qB75, 3, 3);
485         davinci.drawEllipse(m_qCy75, 3, 3);
486         davinci.drawEllipse(m_qMg75, 3, 3);
487         davinci.drawEllipse(m_qYl75, 3, 3);
488     }
489 
490     // Draw realtime factor (number of skipped pixels)
491     if (m_aRealtime->isChecked()) {
492         davinci.setPen(penThin);
493         davinci.drawText(QPoint(m_scopeRect.width() - 40, m_scopeRect.height() - 15), QVariant(m_accelFactorScope).toString().append(QStringLiteral("x")));
494     }
495 
496     emit signalBackgroundRenderingFinished(uint(timer.elapsed()), 1);
497     return bg;
498 }
499 
500 ///// Slots /////
501 
slotGainChanged(int newval)502 void Vectorscope::slotGainChanged(int newval)
503 {
504     QLocale locale; // Used for UI → OK
505     locale.setNumberOptions(QLocale::OmitGroupSeparator);
506     m_gain = 1 + newval / 10.f;
507     m_ui->lblGain->setText(locale.toString(m_gain, 'f', 1) + QLatin1Char('x'));
508     forceUpdateScope();
509 }
510 
slotExportBackground()511 void Vectorscope::slotExportBackground()
512 {
513     QPointer<ColorPlaneExport> colorPlaneExportDialog = new ColorPlaneExport(this);
514     colorPlaneExportDialog->exec();
515     delete colorPlaneExportDialog;
516 }
517 
slotBackgroundChanged()518 void Vectorscope::slotBackgroundChanged()
519 {
520     // Background changed, switch to a suitable color mode now
521     int index;
522     switch (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt()) {
523     case BG_YUV:
524         index = m_ui->paintMode->findData(QVariant(VectorscopeGenerator::PaintMode_Black));
525         if (index >= 0) {
526             m_ui->paintMode->setCurrentIndex(index);
527         }
528         break;
529 
530     case BG_NONE:
531         if (m_ui->paintMode->itemData(m_ui->paintMode->currentIndex()).toInt() == VectorscopeGenerator::PaintMode_Black) {
532             index = m_ui->paintMode->findData(QVariant(VectorscopeGenerator::PaintMode_Green2));
533             m_ui->paintMode->setCurrentIndex(index);
534         }
535         break;
536     }
537     forceUpdateBackground();
538 }
539 
slotColorSpaceChanged()540 void Vectorscope::slotColorSpaceChanged()
541 {
542     int index;
543     if (m_aColorSpace_YPbPr->isChecked()) {
544         if (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt() == BG_YUV) {
545             index = m_ui->backgroundMode->findData(QVariant(BG_YPbPr));
546             if (index >= 0) {
547                 m_ui->backgroundMode->setCurrentIndex(index);
548             }
549         }
550     } else {
551         if (m_ui->backgroundMode->itemData(m_ui->backgroundMode->currentIndex()).toInt() == BG_YPbPr) {
552             index = m_ui->backgroundMode->findData(QVariant(BG_YUV));
553             if (index >= 0) {
554                 m_ui->backgroundMode->setCurrentIndex(index);
555             }
556         }
557     }
558     forceUpdate();
559 }
560