1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * qv4l2 - Test Pattern Generator Tab
4 *
5 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6 */
7
8 #include "qv4l2.h"
9 #include "general-tab.h"
10
11 #include <QFrame>
12 #include <QVBoxLayout>
13 #include <QStatusBar>
14 #include <QLineEdit>
15 #include <QValidator>
16 #include <QLayout>
17 #include <QGridLayout>
18 #include <QLabel>
19 #include <QSlider>
20 #include <QSpinBox>
21 #include <QComboBox>
22 #include <QCheckBox>
23 #include <QPushButton>
24 #include <QToolButton>
25 #include <QToolTip>
26
27 #include <math.h>
28 #include <fcntl.h>
29 #include <sys/ioctl.h>
30 #include <errno.h>
31
addTpgTab(int m_winWidth)32 void ApplicationWindow::addTpgTab(int m_winWidth)
33 {
34 QWidget *t = new QWidget(m_tabs);
35 QVBoxLayout *vbox = new QVBoxLayout(t);
36 QWidget *w = new QWidget(t);
37 QCheckBox *check;
38 QComboBox *combo;
39 QSpinBox *spin;
40
41 m_col = m_row = 0;
42 m_cols = 4;
43 for (int j = 0; j < m_cols; j++) {
44 m_maxw[j] = 0;
45 }
46
47 vbox->addWidget(w);
48
49 QGridLayout *grid = new QGridLayout(w);
50 QLabel *title_tab = new QLabel("Test Pattern Generator", parentWidget());
51 QFont f = title_tab->font();
52 f.setBold(true);
53 title_tab->setFont(f);
54 grid->addWidget(title_tab, m_row, m_col, 1, m_cols, Qt::AlignLeft);
55 grid->setRowMinimumHeight(m_row, 25);
56 m_row++;
57
58 QFrame *m_line = new QFrame(grid->parentWidget());
59 m_line->setFrameShape(QFrame::HLine);
60 m_line->setFrameShadow(QFrame::Sunken);
61 grid->addWidget(m_line, m_row, m_col, 1, m_cols, Qt::AlignVCenter);
62 m_row++;
63
64 m_tabs->addTab(t, "Test Pattern Generator");
65 grid->addWidget(new QWidget(w), grid->rowCount(), 0, 1, m_cols);
66
67 addLabel(grid, "Test Pattern");
68 combo = new QComboBox(w);
69 for (int i = 0; tpg_pattern_strings[i]; i++)
70 combo->addItem(tpg_pattern_strings[i]);
71 addWidget(grid, combo);
72 connect(combo, SIGNAL(activated(int)), SLOT(testPatternChanged(int)));
73
74 m_row++;
75 m_col = 0;
76
77 addLabel(grid, "Horizontal Movement");
78 combo = new QComboBox(w);
79 combo->addItem("Move Left Fast");
80 combo->addItem("Move Left");
81 combo->addItem("Move Left Slow");
82 combo->addItem("No Movement");
83 combo->addItem("Move Right Slow");
84 combo->addItem("Move Right");
85 combo->addItem("Move Right Fast");
86 combo->setCurrentIndex(3);
87 addWidget(grid, combo);
88 connect(combo, SIGNAL(activated(int)), SLOT(horMovementChanged(int)));
89
90 addLabel(grid, "Video Aspect Ratio");
91 combo = new QComboBox(w);
92 combo->addItem("Source Width x Height");
93 combo->addItem("4x3");
94 combo->addItem("14x9");
95 combo->addItem("16x9");
96 combo->addItem("16x9 Anamorphic");
97 addWidget(grid, combo);
98 connect(combo, SIGNAL(activated(int)), SLOT(videoAspectRatioChanged(int)));
99
100 addLabel(grid, "Vertical Movement");
101 combo = new QComboBox(w);
102 combo->addItem("Move Up Fast");
103 combo->addItem("Move Up");
104 combo->addItem("Move Up Slow");
105 combo->addItem("No Movement");
106 combo->addItem("Move Down Slow");
107 combo->addItem("Move Down");
108 combo->addItem("Move Down Fast");
109 combo->setCurrentIndex(3);
110 addWidget(grid, combo);
111 connect(combo, SIGNAL(activated(int)), SLOT(vertMovementChanged(int)));
112
113 addLabel(grid, "Show Border");
114 check = new QCheckBox(w);
115 addWidget(grid, check);
116 connect(check, SIGNAL(stateChanged(int)), SLOT(showBorderChanged(int)));
117
118 addLabel(grid, "Insert SAV Code in Image");
119 check = new QCheckBox(w);
120 addWidget(grid, check);
121 connect(check, SIGNAL(stateChanged(int)), SLOT(insSAVChanged(int)));
122
123 addLabel(grid, "Show Square");
124 check = new QCheckBox(w);
125 addWidget(grid, check);
126 connect(check, SIGNAL(stateChanged(int)), SLOT(showSquareChanged(int)));
127
128 addLabel(grid, "Insert EAV Code in Image");
129 check = new QCheckBox(w);
130 addWidget(grid, check);
131 connect(check, SIGNAL(stateChanged(int)), SLOT(insEAVChanged(int)));
132
133 addLabel(grid, "Fill Percentage of Frame");
134 spin = new QSpinBox(w);
135 spin->setRange(0, 100);
136 spin->setValue(100);
137 addWidget(grid, spin);
138 connect(spin, SIGNAL(valueChanged(int)), SLOT(fillPercentageChanged(int)));
139
140 addLabel(grid, "Colorspace");
141 m_tpgColorspace = new QComboBox(w);
142 m_tpgColorspace->addItem("Use Format", QVariant(0));
143 m_tpgColorspace->addItem("SMPTE 170M", QVariant(V4L2_COLORSPACE_SMPTE170M));
144 m_tpgColorspace->addItem("Rec. 709", QVariant(V4L2_COLORSPACE_REC709));
145 m_tpgColorspace->addItem("sRGB", QVariant(V4L2_COLORSPACE_SRGB));
146 m_tpgColorspace->addItem("opRGB", QVariant(V4L2_COLORSPACE_OPRGB));
147 m_tpgColorspace->addItem("BT.2020", QVariant(V4L2_COLORSPACE_BT2020));
148 m_tpgColorspace->addItem("DCI-P3", QVariant(V4L2_COLORSPACE_DCI_P3));
149 m_tpgColorspace->addItem("SMPTE 240M", QVariant(V4L2_COLORSPACE_SMPTE240M));
150 m_tpgColorspace->addItem("470 System M", QVariant(V4L2_COLORSPACE_470_SYSTEM_M));
151 m_tpgColorspace->addItem("470 System BG", QVariant(V4L2_COLORSPACE_470_SYSTEM_BG));
152 addWidget(grid, m_tpgColorspace);
153 connect(m_tpgColorspace, SIGNAL(activated(int)), SLOT(tpgColorspaceChanged()));
154
155 addLabel(grid, "Transfer Function");
156 m_tpgXferFunc = new QComboBox(w);
157 m_tpgXferFunc->addItem("Use Format", QVariant(V4L2_XFER_FUNC_DEFAULT));
158 m_tpgXferFunc->addItem("Rec. 709", QVariant(V4L2_XFER_FUNC_709));
159 m_tpgXferFunc->addItem("sRGB", QVariant(V4L2_XFER_FUNC_SRGB));
160 m_tpgXferFunc->addItem("opRGB", QVariant(V4L2_XFER_FUNC_OPRGB));
161 m_tpgXferFunc->addItem("DCI-P3", QVariant(V4L2_XFER_FUNC_DCI_P3));
162 m_tpgXferFunc->addItem("SMPTE 2084", QVariant(V4L2_XFER_FUNC_SMPTE2084));
163 m_tpgXferFunc->addItem("SMPTE 240M", QVariant(V4L2_XFER_FUNC_SMPTE240M));
164 m_tpgXferFunc->addItem("None", QVariant(V4L2_XFER_FUNC_NONE));
165 addWidget(grid, m_tpgXferFunc);
166 connect(m_tpgXferFunc, SIGNAL(activated(int)), SLOT(tpgXferFuncChanged()));
167
168 addLabel(grid, "Y'CbCr/HSV Encoding");
169 m_tpgYCbCrEnc = new QComboBox(w);
170 m_tpgYCbCrEnc->addItem("Use Format", QVariant(V4L2_YCBCR_ENC_DEFAULT));
171 m_tpgYCbCrEnc->addItem("ITU-R 601", QVariant(V4L2_YCBCR_ENC_601));
172 m_tpgYCbCrEnc->addItem("Rec. 709", QVariant(V4L2_YCBCR_ENC_709));
173 m_tpgYCbCrEnc->addItem("xvYCC 601", QVariant(V4L2_YCBCR_ENC_XV601));
174 m_tpgYCbCrEnc->addItem("xvYCC 709", QVariant(V4L2_YCBCR_ENC_XV709));
175 m_tpgYCbCrEnc->addItem("BT.2020", QVariant(V4L2_YCBCR_ENC_BT2020));
176 m_tpgYCbCrEnc->addItem("BT.2020 Constant Luminance", QVariant(V4L2_YCBCR_ENC_BT2020_CONST_LUM));
177 m_tpgYCbCrEnc->addItem("SMPTE 240M", QVariant(V4L2_YCBCR_ENC_SMPTE240M));
178 m_tpgYCbCrEnc->addItem("HSV with Hue 0-179", QVariant(V4L2_HSV_ENC_180));
179 m_tpgYCbCrEnc->addItem("HSV with Hue 0-255", QVariant(V4L2_HSV_ENC_256));
180 addWidget(grid, m_tpgYCbCrEnc);
181 connect(m_tpgYCbCrEnc, SIGNAL(activated(int)), SLOT(tpgColorspaceChanged()));
182
183 addLabel(grid, "Quantization");
184 m_tpgQuantRange = new QComboBox(w);
185 m_tpgQuantRange->addItem("Use Format", QVariant(V4L2_QUANTIZATION_DEFAULT));
186 m_tpgQuantRange->addItem("Full Range", QVariant(V4L2_QUANTIZATION_FULL_RANGE));
187 m_tpgQuantRange->addItem("Limited Range", QVariant(V4L2_QUANTIZATION_LIM_RANGE));
188 addWidget(grid, m_tpgQuantRange);
189 connect(m_tpgQuantRange, SIGNAL(activated(int)), SLOT(tpgColorspaceChanged()));
190
191 addLabel(grid, "Limited RGB Range (16-235)");
192 m_tpgLimRGBRange = new QCheckBox(w);
193 addWidget(grid, m_tpgLimRGBRange);
194 connect(m_tpgLimRGBRange, SIGNAL(stateChanged(int)), SLOT(limRGBRangeChanged(int)));
195
196 addLabel(grid, "Alpha Component");
197 spin = new QSpinBox(w);
198 spin->setRange(0, 255);
199 spin->setValue(0);
200 addWidget(grid, spin);
201 connect(spin, SIGNAL(valueChanged(int)), SLOT(alphaComponentChanged(int)));
202
203 addLabel(grid, "Apply Alpha To Red Only");
204 check = new QCheckBox(w);
205 addWidget(grid, check);
206 connect(check, SIGNAL(stateChanged(int)), SLOT(applyToRedChanged(int)));
207
208 m_row++;
209 m_col = 0;
210 addWidget(grid, new QWidget(w));
211 grid->setRowStretch(grid->rowCount() - 1, 1);
212 w = new QWidget(t);
213 vbox->addWidget(w);
214 fixWidth(grid);
215
216 int totalw = 0;
217 int diff = 0;
218 for (int i = 0; i < m_cols; i++) {
219 totalw += m_maxw[i] + m_pxw;
220 }
221 if (totalw > m_winWidth)
222 m_winWidth = totalw;
223 else {
224 diff = m_winWidth - totalw;
225 grid->setHorizontalSpacing(diff/5);
226 }
227 }
228
testPatternChanged(int val)229 void ApplicationWindow::testPatternChanged(int val)
230 {
231 tpg_s_pattern(&m_tpg, (tpg_pattern)val);
232 }
233
horMovementChanged(int val)234 void ApplicationWindow::horMovementChanged(int val)
235 {
236 tpg_s_mv_hor_mode(&m_tpg, (tpg_move_mode)val);
237 }
238
vertMovementChanged(int val)239 void ApplicationWindow::vertMovementChanged(int val)
240 {
241 tpg_s_mv_vert_mode(&m_tpg, (tpg_move_mode)val);
242 }
243
showBorderChanged(int val)244 void ApplicationWindow::showBorderChanged(int val)
245 {
246 tpg_s_show_border(&m_tpg, val);
247 }
248
showSquareChanged(int val)249 void ApplicationWindow::showSquareChanged(int val)
250 {
251 tpg_s_show_square(&m_tpg, val);
252 }
253
insSAVChanged(int val)254 void ApplicationWindow::insSAVChanged(int val)
255 {
256 tpg_s_insert_sav(&m_tpg, val);
257 }
258
insEAVChanged(int val)259 void ApplicationWindow::insEAVChanged(int val)
260 {
261 tpg_s_insert_eav(&m_tpg, val);
262 }
263
videoAspectRatioChanged(int val)264 void ApplicationWindow::videoAspectRatioChanged(int val)
265 {
266 tpg_s_video_aspect(&m_tpg, (tpg_video_aspect)val);
267 }
268
updateLimRGBRange()269 void ApplicationWindow::updateLimRGBRange()
270 {
271 if (m_tpgLimRGBRange == NULL)
272 return;
273
274 v4l2_output out;
275
276 g_output(out.index);
277 enum_output(out, true, out.index);
278
279 if (out.capabilities & V4L2_OUT_CAP_STD) {
280 m_tpgLimRGBRange->setChecked(false);
281 } else if (out.capabilities & V4L2_OUT_CAP_DV_TIMINGS) {
282 v4l2_dv_timings timings;
283
284 g_dv_timings(timings);
285 if (timings.bt.standards & V4L2_DV_BT_STD_CEA861)
286 m_tpgLimRGBRange->setChecked(true);
287 else
288 m_tpgLimRGBRange->setChecked(false);
289 } else {
290 m_tpgLimRGBRange->setChecked(false);
291 }
292 }
293
tpgDefaultColorspace()294 __u32 ApplicationWindow::tpgDefaultColorspace()
295 {
296 v4l2_dv_timings timings = { 0 };
297 v4l2_output out;
298 __u32 io_caps;
299 bool dvi_d = false;
300
301 g_output(out.index);
302 enum_output(out, true, out.index);
303 io_caps = out.capabilities;
304 v4l2_control ctrl = { V4L2_CID_DV_TX_MODE };
305
306 if (!g_ctrl(ctrl))
307 dvi_d = ctrl.value == V4L2_DV_TX_MODE_DVI_D;
308
309 if (io_caps & V4L2_OUT_CAP_STD)
310 return V4L2_COLORSPACE_SMPTE170M;
311 if (!(io_caps & V4L2_OUT_CAP_DV_TIMINGS))
312 return V4L2_COLORSPACE_SRGB;
313
314 g_dv_timings(timings);
315
316 if (!(timings.bt.standards & V4L2_DV_BT_STD_CEA861) || dvi_d)
317 return V4L2_COLORSPACE_SRGB;
318 if (timings.bt.width == 720 && timings.bt.height <= 576)
319 return V4L2_COLORSPACE_SMPTE170M;
320 return V4L2_COLORSPACE_REC709;
321 }
322
combo2int(QComboBox * combo)323 static int combo2int(QComboBox *combo)
324 {
325 return combo->itemData(combo->currentIndex()).toInt();
326 }
327
tpgColorspaceChanged()328 void ApplicationWindow::tpgColorspaceChanged()
329 {
330 cv4l_fmt fmt;
331 int colorspace = combo2int(m_tpgColorspace);
332 int xferFunc = combo2int(m_tpgXferFunc);
333 int ycbcrEnc = combo2int(m_tpgYCbCrEnc);
334 int quantization = combo2int(m_tpgQuantRange);
335
336 g_fmt(fmt);
337 if (colorspace == V4L2_COLORSPACE_DEFAULT)
338 colorspace = fmt.g_colorspace();
339 if (colorspace == V4L2_COLORSPACE_DEFAULT)
340 colorspace = tpgDefaultColorspace();
341 if (xferFunc == V4L2_XFER_FUNC_DEFAULT)
342 xferFunc = fmt.g_xfer_func();
343 if (ycbcrEnc == V4L2_YCBCR_ENC_DEFAULT)
344 ycbcrEnc = fmt.g_ycbcr_enc();
345 if (quantization == V4L2_QUANTIZATION_DEFAULT)
346 quantization = fmt.g_quantization();
347 tpg_s_colorspace(&m_tpg, colorspace);
348 tpg_s_xfer_func(&m_tpg, xferFunc);
349 tpg_s_ycbcr_enc(&m_tpg, ycbcrEnc);
350 tpg_s_quantization(&m_tpg, quantization);
351 }
352
limRGBRangeChanged(int val)353 void ApplicationWindow::limRGBRangeChanged(int val)
354 {
355 tpg_s_real_rgb_range(&m_tpg, val ? V4L2_DV_RGB_RANGE_LIMITED : V4L2_DV_RGB_RANGE_FULL);
356 }
357
fillPercentageChanged(int val)358 void ApplicationWindow::fillPercentageChanged(int val)
359 {
360 tpg_s_perc_fill(&m_tpg, val);
361 }
362
alphaComponentChanged(int val)363 void ApplicationWindow::alphaComponentChanged(int val)
364 {
365 tpg_s_alpha_component(&m_tpg, val);
366 }
367
applyToRedChanged(int val)368 void ApplicationWindow::applyToRedChanged(int val)
369 {
370 tpg_s_alpha_mode(&m_tpg, val);
371 }
372