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