2 * vivid-tpg.c - Test Pattern Generator
4 * Note: gen_twopix and tpg_gen_text are based on code from vivi.c. See the
5 * vivi.c source for the copyright information of those functions.
7 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
9 * This program is free software; you may redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
17 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 #include "vivid-tpg.h"
25 /* Must remain in sync with enum tpg_pattern */
26 const char * const tpg_pattern_strings
[] = {
30 "Horizontal 100% Colorbar",
40 "2x2 Red/Green Checkers",
41 "1x1 Red/Green Checkers",
42 "Alternating Hor Lines",
43 "Alternating Vert Lines",
44 "One Pixel Wide Cross",
45 "Two Pixels Wide Cross",
46 "Ten Pixels Wide Cross",
52 /* Must remain in sync with enum tpg_aspect */
53 const char * const tpg_aspect_strings
[] = {
54 "Source Width x Height",
63 * Sine table: sin[0] = 127 * sin(-180 degrees)
64 * sin[128] = 127 * sin(0 degrees)
65 * sin[256] = 127 * sin(180 degrees)
67 static const s8 sin
[257] = {
68 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48,
69 -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88,
70 -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117,
71 -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127,
72 -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118,
73 -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91,
74 -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50,
75 -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2,
76 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46,
77 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87,
78 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116,
79 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127,
80 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119,
81 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93,
82 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52,
83 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4,
87 #define cos(idx) sin[((idx) + 64) % sizeof(sin)]
89 /* Global font descriptor */
90 static const u8
*font8x16
;
92 void tpg_set_font(const u8
*f
)
97 void tpg_init(struct tpg_data
*tpg
, unsigned w
, unsigned h
)
99 memset(tpg
, 0, sizeof(*tpg
));
100 tpg
->scaled_width
= tpg
->src_width
= w
;
101 tpg
->src_height
= tpg
->buf_height
= h
;
102 tpg
->crop
.width
= tpg
->compose
.width
= w
;
103 tpg
->crop
.height
= tpg
->compose
.height
= h
;
104 tpg
->recalc_colors
= true;
105 tpg
->recalc_square_border
= true;
106 tpg
->brightness
= 128;
108 tpg
->saturation
= 128;
110 tpg
->mv_hor_mode
= TPG_MOVE_NONE
;
111 tpg
->mv_vert_mode
= TPG_MOVE_NONE
;
112 tpg
->field
= V4L2_FIELD_NONE
;
113 tpg_s_fourcc(tpg
, V4L2_PIX_FMT_RGB24
);
114 tpg
->colorspace
= V4L2_COLORSPACE_SRGB
;
115 tpg
->perc_fill
= 100;
118 int tpg_alloc(struct tpg_data
*tpg
, unsigned max_w
)
123 tpg
->max_line_width
= max_w
;
124 for (pat
= 0; pat
< TPG_MAX_PAT_LINES
; pat
++) {
125 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
126 unsigned pixelsz
= plane
? 2 : 4;
128 tpg
->lines
[pat
][plane
] = vzalloc(max_w
* 2 * pixelsz
);
129 if (!tpg
->lines
[pat
][plane
])
133 tpg
->downsampled_lines
[pat
][plane
] = vzalloc(max_w
* 2 * pixelsz
);
134 if (!tpg
->downsampled_lines
[pat
][plane
])
138 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
139 unsigned pixelsz
= plane
? 2 : 4;
141 tpg
->contrast_line
[plane
] = vzalloc(max_w
* pixelsz
);
142 if (!tpg
->contrast_line
[plane
])
144 tpg
->black_line
[plane
] = vzalloc(max_w
* pixelsz
);
145 if (!tpg
->black_line
[plane
])
147 tpg
->random_line
[plane
] = vzalloc(max_w
* 2 * pixelsz
);
148 if (!tpg
->random_line
[plane
])
154 void tpg_free(struct tpg_data
*tpg
)
159 for (pat
= 0; pat
< TPG_MAX_PAT_LINES
; pat
++)
160 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
161 vfree(tpg
->lines
[pat
][plane
]);
162 tpg
->lines
[pat
][plane
] = NULL
;
165 vfree(tpg
->downsampled_lines
[pat
][plane
]);
166 tpg
->downsampled_lines
[pat
][plane
] = NULL
;
168 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
169 vfree(tpg
->contrast_line
[plane
]);
170 vfree(tpg
->black_line
[plane
]);
171 vfree(tpg
->random_line
[plane
]);
172 tpg
->contrast_line
[plane
] = NULL
;
173 tpg
->black_line
[plane
] = NULL
;
174 tpg
->random_line
[plane
] = NULL
;
178 bool tpg_s_fourcc(struct tpg_data
*tpg
, u32 fourcc
)
180 tpg
->fourcc
= fourcc
;
183 tpg
->recalc_colors
= true;
184 tpg
->interleaved
= false;
185 tpg
->vdownsampling
[0] = 1;
186 tpg
->hdownsampling
[0] = 1;
192 case V4L2_PIX_FMT_SBGGR8
:
193 case V4L2_PIX_FMT_SGBRG8
:
194 case V4L2_PIX_FMT_SGRBG8
:
195 case V4L2_PIX_FMT_SRGGB8
:
196 tpg
->interleaved
= true;
197 tpg
->vdownsampling
[1] = 1;
198 tpg
->hdownsampling
[1] = 1;
201 case V4L2_PIX_FMT_RGB332
:
202 case V4L2_PIX_FMT_RGB565
:
203 case V4L2_PIX_FMT_RGB565X
:
204 case V4L2_PIX_FMT_RGB444
:
205 case V4L2_PIX_FMT_XRGB444
:
206 case V4L2_PIX_FMT_ARGB444
:
207 case V4L2_PIX_FMT_RGB555
:
208 case V4L2_PIX_FMT_XRGB555
:
209 case V4L2_PIX_FMT_ARGB555
:
210 case V4L2_PIX_FMT_RGB555X
:
211 case V4L2_PIX_FMT_XRGB555X
:
212 case V4L2_PIX_FMT_ARGB555X
:
213 case V4L2_PIX_FMT_BGR666
:
214 case V4L2_PIX_FMT_RGB24
:
215 case V4L2_PIX_FMT_BGR24
:
216 case V4L2_PIX_FMT_RGB32
:
217 case V4L2_PIX_FMT_BGR32
:
218 case V4L2_PIX_FMT_XRGB32
:
219 case V4L2_PIX_FMT_XBGR32
:
220 case V4L2_PIX_FMT_ARGB32
:
221 case V4L2_PIX_FMT_ABGR32
:
222 case V4L2_PIX_FMT_GREY
:
223 case V4L2_PIX_FMT_Y16
:
224 case V4L2_PIX_FMT_Y16_BE
:
227 case V4L2_PIX_FMT_YUV444
:
228 case V4L2_PIX_FMT_YUV555
:
229 case V4L2_PIX_FMT_YUV565
:
230 case V4L2_PIX_FMT_YUV32
:
233 case V4L2_PIX_FMT_YUV420M
:
234 case V4L2_PIX_FMT_YVU420M
:
237 case V4L2_PIX_FMT_YUV420
:
238 case V4L2_PIX_FMT_YVU420
:
239 tpg
->vdownsampling
[1] = 2;
240 tpg
->vdownsampling
[2] = 2;
241 tpg
->hdownsampling
[1] = 2;
242 tpg
->hdownsampling
[2] = 2;
246 case V4L2_PIX_FMT_YUV422P
:
247 tpg
->vdownsampling
[1] = 1;
248 tpg
->vdownsampling
[2] = 1;
249 tpg
->hdownsampling
[1] = 2;
250 tpg
->hdownsampling
[2] = 2;
254 case V4L2_PIX_FMT_NV16M
:
255 case V4L2_PIX_FMT_NV61M
:
258 case V4L2_PIX_FMT_NV16
:
259 case V4L2_PIX_FMT_NV61
:
260 tpg
->vdownsampling
[1] = 1;
261 tpg
->hdownsampling
[1] = 1;
266 case V4L2_PIX_FMT_NV12M
:
267 case V4L2_PIX_FMT_NV21M
:
270 case V4L2_PIX_FMT_NV12
:
271 case V4L2_PIX_FMT_NV21
:
272 tpg
->vdownsampling
[1] = 2;
273 tpg
->hdownsampling
[1] = 1;
278 case V4L2_PIX_FMT_NV24
:
279 case V4L2_PIX_FMT_NV42
:
280 tpg
->vdownsampling
[1] = 1;
281 tpg
->hdownsampling
[1] = 1;
285 case V4L2_PIX_FMT_YUYV
:
286 case V4L2_PIX_FMT_UYVY
:
287 case V4L2_PIX_FMT_YVYU
:
288 case V4L2_PIX_FMT_VYUY
:
297 case V4L2_PIX_FMT_GREY
:
298 case V4L2_PIX_FMT_RGB332
:
299 tpg
->twopixelsize
[0] = 2;
301 case V4L2_PIX_FMT_RGB565
:
302 case V4L2_PIX_FMT_RGB565X
:
303 case V4L2_PIX_FMT_RGB444
:
304 case V4L2_PIX_FMT_XRGB444
:
305 case V4L2_PIX_FMT_ARGB444
:
306 case V4L2_PIX_FMT_RGB555
:
307 case V4L2_PIX_FMT_XRGB555
:
308 case V4L2_PIX_FMT_ARGB555
:
309 case V4L2_PIX_FMT_RGB555X
:
310 case V4L2_PIX_FMT_XRGB555X
:
311 case V4L2_PIX_FMT_ARGB555X
:
312 case V4L2_PIX_FMT_YUYV
:
313 case V4L2_PIX_FMT_UYVY
:
314 case V4L2_PIX_FMT_YVYU
:
315 case V4L2_PIX_FMT_VYUY
:
316 case V4L2_PIX_FMT_YUV444
:
317 case V4L2_PIX_FMT_YUV555
:
318 case V4L2_PIX_FMT_YUV565
:
319 case V4L2_PIX_FMT_Y16
:
320 case V4L2_PIX_FMT_Y16_BE
:
321 tpg
->twopixelsize
[0] = 2 * 2;
323 case V4L2_PIX_FMT_RGB24
:
324 case V4L2_PIX_FMT_BGR24
:
325 tpg
->twopixelsize
[0] = 2 * 3;
327 case V4L2_PIX_FMT_BGR666
:
328 case V4L2_PIX_FMT_RGB32
:
329 case V4L2_PIX_FMT_BGR32
:
330 case V4L2_PIX_FMT_XRGB32
:
331 case V4L2_PIX_FMT_XBGR32
:
332 case V4L2_PIX_FMT_ARGB32
:
333 case V4L2_PIX_FMT_ABGR32
:
334 case V4L2_PIX_FMT_YUV32
:
335 tpg
->twopixelsize
[0] = 2 * 4;
337 case V4L2_PIX_FMT_NV12
:
338 case V4L2_PIX_FMT_NV21
:
339 case V4L2_PIX_FMT_NV12M
:
340 case V4L2_PIX_FMT_NV21M
:
341 case V4L2_PIX_FMT_NV16
:
342 case V4L2_PIX_FMT_NV61
:
343 case V4L2_PIX_FMT_NV16M
:
344 case V4L2_PIX_FMT_NV61M
:
345 case V4L2_PIX_FMT_SBGGR8
:
346 case V4L2_PIX_FMT_SGBRG8
:
347 case V4L2_PIX_FMT_SGRBG8
:
348 case V4L2_PIX_FMT_SRGGB8
:
349 tpg
->twopixelsize
[0] = 2;
350 tpg
->twopixelsize
[1] = 2;
352 case V4L2_PIX_FMT_YUV422P
:
353 case V4L2_PIX_FMT_YUV420
:
354 case V4L2_PIX_FMT_YVU420
:
355 case V4L2_PIX_FMT_YUV420M
:
356 case V4L2_PIX_FMT_YVU420M
:
357 tpg
->twopixelsize
[0] = 2;
358 tpg
->twopixelsize
[1] = 2;
359 tpg
->twopixelsize
[2] = 2;
361 case V4L2_PIX_FMT_NV24
:
362 case V4L2_PIX_FMT_NV42
:
363 tpg
->twopixelsize
[0] = 2;
364 tpg
->twopixelsize
[1] = 4;
370 void tpg_s_crop_compose(struct tpg_data
*tpg
, const struct v4l2_rect
*crop
,
371 const struct v4l2_rect
*compose
)
374 tpg
->compose
= *compose
;
375 tpg
->scaled_width
= (tpg
->src_width
* tpg
->compose
.width
+
376 tpg
->crop
.width
- 1) / tpg
->crop
.width
;
377 tpg
->scaled_width
&= ~1;
378 if (tpg
->scaled_width
> tpg
->max_line_width
)
379 tpg
->scaled_width
= tpg
->max_line_width
;
380 if (tpg
->scaled_width
< 2)
381 tpg
->scaled_width
= 2;
382 tpg
->recalc_lines
= true;
385 void tpg_reset_source(struct tpg_data
*tpg
, unsigned width
, unsigned height
,
390 tpg
->src_width
= width
;
391 tpg
->src_height
= height
;
393 tpg
->buf_height
= height
;
394 if (V4L2_FIELD_HAS_T_OR_B(field
))
395 tpg
->buf_height
/= 2;
396 tpg
->scaled_width
= width
;
397 tpg
->crop
.top
= tpg
->crop
.left
= 0;
398 tpg
->crop
.width
= width
;
399 tpg
->crop
.height
= height
;
400 tpg
->compose
.top
= tpg
->compose
.left
= 0;
401 tpg
->compose
.width
= width
;
402 tpg
->compose
.height
= tpg
->buf_height
;
403 for (p
= 0; p
< tpg
->planes
; p
++)
404 tpg
->bytesperline
[p
] = (width
* tpg
->twopixelsize
[p
]) /
405 (2 * tpg
->hdownsampling
[p
]);
406 tpg
->recalc_square_border
= true;
409 static enum tpg_color
tpg_get_textbg_color(struct tpg_data
*tpg
)
411 switch (tpg
->pattern
) {
413 return TPG_COLOR_100_WHITE
;
414 case TPG_PAT_CSC_COLORBAR
:
415 return TPG_COLOR_CSC_BLACK
;
417 return TPG_COLOR_100_BLACK
;
421 static enum tpg_color
tpg_get_textfg_color(struct tpg_data
*tpg
)
423 switch (tpg
->pattern
) {
424 case TPG_PAT_75_COLORBAR
:
425 case TPG_PAT_CSC_COLORBAR
:
426 return TPG_COLOR_CSC_WHITE
;
428 return TPG_COLOR_100_BLACK
;
430 return TPG_COLOR_100_WHITE
;
434 static inline int rec709_to_linear(int v
)
436 v
= clamp(v
, 0, 0xff0);
437 return tpg_rec709_to_linear
[v
];
440 static inline int linear_to_rec709(int v
)
442 v
= clamp(v
, 0, 0xff0);
443 return tpg_linear_to_rec709
[v
];
446 static void rgb2ycbcr(const int m
[3][3], int r
, int g
, int b
,
447 int y_offset
, int *y
, int *cb
, int *cr
)
449 *y
= ((m
[0][0] * r
+ m
[0][1] * g
+ m
[0][2] * b
) >> 16) + (y_offset
<< 4);
450 *cb
= ((m
[1][0] * r
+ m
[1][1] * g
+ m
[1][2] * b
) >> 16) + (128 << 4);
451 *cr
= ((m
[2][0] * r
+ m
[2][1] * g
+ m
[2][2] * b
) >> 16) + (128 << 4);
454 static void color_to_ycbcr(struct tpg_data
*tpg
, int r
, int g
, int b
,
455 int *y
, int *cb
, int *cr
)
457 #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
459 static const int bt601
[3][3] = {
460 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
461 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
462 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
464 static const int bt601_full
[3][3] = {
465 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
466 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
467 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
469 static const int rec709
[3][3] = {
470 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
471 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
472 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
474 static const int rec709_full
[3][3] = {
475 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
476 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
477 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
479 static const int smpte240m
[3][3] = {
480 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
481 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
482 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
484 static const int smpte240m_full
[3][3] = {
485 { COEFF(0.212, 255), COEFF(0.701, 255), COEFF(0.087, 255) },
486 { COEFF(-0.116, 255), COEFF(-0.384, 255), COEFF(0.5, 255) },
487 { COEFF(0.5, 255), COEFF(-0.445, 255), COEFF(-0.055, 255) },
489 static const int bt2020
[3][3] = {
490 { COEFF(0.2627, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
491 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
492 { COEFF(0.5, 224), COEFF(-0.4598, 224), COEFF(-0.0402, 224) },
494 static const int bt2020_full
[3][3] = {
495 { COEFF(0.2627, 255), COEFF(0.6780, 255), COEFF(0.0593, 255) },
496 { COEFF(-0.1396, 255), COEFF(-0.3604, 255), COEFF(0.5, 255) },
497 { COEFF(0.5, 255), COEFF(-0.4698, 255), COEFF(-0.0402, 255) },
499 static const int bt2020c
[4] = {
500 COEFF(1.0 / 1.9404, 224), COEFF(1.0 / 1.5816, 224),
501 COEFF(1.0 / 1.7184, 224), COEFF(1.0 / 0.9936, 224),
503 static const int bt2020c_full
[4] = {
504 COEFF(1.0 / 1.9404, 255), COEFF(1.0 / 1.5816, 255),
505 COEFF(1.0 / 1.7184, 255), COEFF(1.0 / 0.9936, 255),
508 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
509 unsigned y_offset
= full
? 0 : 16;
512 switch (tpg
->real_ycbcr_enc
) {
513 case V4L2_YCBCR_ENC_601
:
514 case V4L2_YCBCR_ENC_SYCC
:
515 rgb2ycbcr(full
? bt601_full
: bt601
, r
, g
, b
, y_offset
, y
, cb
, cr
);
517 case V4L2_YCBCR_ENC_XV601
:
518 /* Ignore quantization range, there is only one possible
519 * Y'CbCr encoding. */
520 rgb2ycbcr(bt601
, r
, g
, b
, 16, y
, cb
, cr
);
522 case V4L2_YCBCR_ENC_XV709
:
523 /* Ignore quantization range, there is only one possible
524 * Y'CbCr encoding. */
525 rgb2ycbcr(rec709
, r
, g
, b
, 16, y
, cb
, cr
);
527 case V4L2_YCBCR_ENC_BT2020
:
528 rgb2ycbcr(full
? bt2020_full
: bt2020
, r
, g
, b
, y_offset
, y
, cb
, cr
);
530 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
531 lin_y
= (COEFF(0.2627, 255) * rec709_to_linear(r
) +
532 COEFF(0.6780, 255) * rec709_to_linear(g
) +
533 COEFF(0.0593, 255) * rec709_to_linear(b
)) >> 16;
534 yc
= linear_to_rec709(lin_y
);
535 *y
= full
? yc
: (yc
* 219) / 255 + (16 << 4);
537 *cb
= (((b
- yc
) * (full
? bt2020c_full
[0] : bt2020c
[0])) >> 16) + (128 << 4);
539 *cb
= (((b
- yc
) * (full
? bt2020c_full
[1] : bt2020c
[1])) >> 16) + (128 << 4);
541 *cr
= (((r
- yc
) * (full
? bt2020c_full
[2] : bt2020c
[2])) >> 16) + (128 << 4);
543 *cr
= (((r
- yc
) * (full
? bt2020c_full
[3] : bt2020c
[3])) >> 16) + (128 << 4);
545 case V4L2_YCBCR_ENC_SMPTE240M
:
546 rgb2ycbcr(full
? smpte240m_full
: smpte240m
, r
, g
, b
, y_offset
, y
, cb
, cr
);
548 case V4L2_YCBCR_ENC_709
:
550 rgb2ycbcr(full
? rec709_full
: rec709
, r
, g
, b
, y_offset
, y
, cb
, cr
);
555 static void ycbcr2rgb(const int m
[3][3], int y
, int cb
, int cr
,
556 int y_offset
, int *r
, int *g
, int *b
)
561 *r
= m
[0][0] * y
+ m
[0][1] * cb
+ m
[0][2] * cr
;
562 *g
= m
[1][0] * y
+ m
[1][1] * cb
+ m
[1][2] * cr
;
563 *b
= m
[2][0] * y
+ m
[2][1] * cb
+ m
[2][2] * cr
;
564 *r
= clamp(*r
>> 12, 0, 0xff0);
565 *g
= clamp(*g
>> 12, 0, 0xff0);
566 *b
= clamp(*b
>> 12, 0, 0xff0);
569 static void ycbcr_to_color(struct tpg_data
*tpg
, int y
, int cb
, int cr
,
570 int *r
, int *g
, int *b
)
573 #define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
574 static const int bt601
[3][3] = {
575 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
576 { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
577 { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
579 static const int bt601_full
[3][3] = {
580 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
581 { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
582 { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
584 static const int rec709
[3][3] = {
585 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
586 { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
587 { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
589 static const int rec709_full
[3][3] = {
590 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
591 { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
592 { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
594 static const int smpte240m
[3][3] = {
595 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
596 { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
597 { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
599 static const int smpte240m_full
[3][3] = {
600 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5756, 255) },
601 { COEFF(1, 255), COEFF(-0.2253, 255), COEFF(-0.4767, 255) },
602 { COEFF(1, 255), COEFF(1.8270, 255), COEFF(0, 255) },
604 static const int bt2020
[3][3] = {
605 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
606 { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
607 { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
609 static const int bt2020_full
[3][3] = {
610 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4746, 255) },
611 { COEFF(1, 255), COEFF(-0.1646, 255), COEFF(-0.5714, 255) },
612 { COEFF(1, 255), COEFF(1.8814, 255), COEFF(0, 255) },
614 static const int bt2020c
[4] = {
615 COEFF(1.9404, 224), COEFF(1.5816, 224),
616 COEFF(1.7184, 224), COEFF(0.9936, 224),
618 static const int bt2020c_full
[4] = {
619 COEFF(1.9404, 255), COEFF(1.5816, 255),
620 COEFF(1.7184, 255), COEFF(0.9936, 255),
623 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
624 unsigned y_offset
= full
? 0 : 16;
625 int y_fac
= full
? COEFF(1.0, 255) : COEFF(1.0, 219);
626 int lin_r
, lin_g
, lin_b
, lin_y
;
628 switch (tpg
->real_ycbcr_enc
) {
629 case V4L2_YCBCR_ENC_601
:
630 case V4L2_YCBCR_ENC_SYCC
:
631 ycbcr2rgb(full
? bt601_full
: bt601
, y
, cb
, cr
, y_offset
, r
, g
, b
);
633 case V4L2_YCBCR_ENC_XV601
:
634 /* Ignore quantization range, there is only one possible
635 * Y'CbCr encoding. */
636 ycbcr2rgb(bt601
, y
, cb
, cr
, 16, r
, g
, b
);
638 case V4L2_YCBCR_ENC_XV709
:
639 /* Ignore quantization range, there is only one possible
640 * Y'CbCr encoding. */
641 ycbcr2rgb(rec709
, y
, cb
, cr
, 16, r
, g
, b
);
643 case V4L2_YCBCR_ENC_BT2020
:
644 ycbcr2rgb(full
? bt2020_full
: bt2020
, y
, cb
, cr
, y_offset
, r
, g
, b
);
646 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
647 y
-= full
? 0 : 16 << 4;
652 *b
= y_fac
* y
+ (full
? bt2020c_full
[0] : bt2020c
[0]) * cb
;
654 *b
= y_fac
* y
+ (full
? bt2020c_full
[1] : bt2020c
[1]) * cb
;
657 *r
= y_fac
* y
+ (full
? bt2020c_full
[2] : bt2020c
[2]) * cr
;
659 *r
= y_fac
* y
+ (full
? bt2020c_full
[3] : bt2020c
[3]) * cr
;
661 lin_r
= rec709_to_linear(*r
);
662 lin_b
= rec709_to_linear(*b
);
663 lin_y
= rec709_to_linear((y
* 255) / (full
? 255 : 219));
665 lin_g
= COEFF(1.0 / 0.6780, 255) * lin_y
-
666 COEFF(0.2627 / 0.6780, 255) * lin_r
-
667 COEFF(0.0593 / 0.6780, 255) * lin_b
;
668 *g
= linear_to_rec709(lin_g
>> 12);
670 case V4L2_YCBCR_ENC_SMPTE240M
:
671 ycbcr2rgb(full
? smpte240m_full
: smpte240m
, y
, cb
, cr
, y_offset
, r
, g
, b
);
673 case V4L2_YCBCR_ENC_709
:
675 ycbcr2rgb(full
? rec709_full
: rec709
, y
, cb
, cr
, y_offset
, r
, g
, b
);
680 /* precalculate color bar values to speed up rendering */
681 static void precalculate_color(struct tpg_data
*tpg
, int k
)
684 int r
= tpg_colors
[col
].r
;
685 int g
= tpg_colors
[col
].g
;
686 int b
= tpg_colors
[col
].b
;
688 if (k
== TPG_COLOR_TEXTBG
) {
689 col
= tpg_get_textbg_color(tpg
);
691 r
= tpg_colors
[col
].r
;
692 g
= tpg_colors
[col
].g
;
693 b
= tpg_colors
[col
].b
;
694 } else if (k
== TPG_COLOR_TEXTFG
) {
695 col
= tpg_get_textfg_color(tpg
);
697 r
= tpg_colors
[col
].r
;
698 g
= tpg_colors
[col
].g
;
699 b
= tpg_colors
[col
].b
;
700 } else if (tpg
->pattern
== TPG_PAT_NOISE
) {
701 r
= g
= b
= prandom_u32_max(256);
702 } else if (k
== TPG_COLOR_RANDOM
) {
703 r
= g
= b
= tpg
->qual_offset
+ prandom_u32_max(196);
704 } else if (k
>= TPG_COLOR_RAMP
) {
705 r
= g
= b
= k
- TPG_COLOR_RAMP
;
708 if (tpg
->pattern
== TPG_PAT_CSC_COLORBAR
&& col
<= TPG_COLOR_CSC_BLACK
) {
709 r
= tpg_csc_colors
[tpg
->colorspace
][col
].r
;
710 g
= tpg_csc_colors
[tpg
->colorspace
][col
].g
;
711 b
= tpg_csc_colors
[tpg
->colorspace
][col
].b
;
717 if (tpg
->qual
== TPG_QUAL_GRAY
|| tpg
->fourcc
== V4L2_PIX_FMT_GREY
||
718 tpg
->fourcc
== V4L2_PIX_FMT_Y16
||
719 tpg
->fourcc
== V4L2_PIX_FMT_Y16_BE
) {
720 /* Rec. 709 Luma function */
721 /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
722 r
= g
= b
= (13879 * r
+ 46688 * g
+ 4713 * b
) >> 16;
726 * The assumption is that the RGB output is always full range,
727 * so only if the rgb_range overrides the 'real' rgb range do
728 * we need to convert the RGB values.
730 * Remember that r, g and b are still in the 0 - 0xff0 range.
732 if (tpg
->real_rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
&&
733 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_FULL
) {
735 * Convert from full range (which is what r, g and b are)
736 * to limited range (which is the 'real' RGB range), which
737 * is then interpreted as full range.
739 r
= (r
* 219) / 255 + (16 << 4);
740 g
= (g
* 219) / 255 + (16 << 4);
741 b
= (b
* 219) / 255 + (16 << 4);
742 } else if (tpg
->real_rgb_range
!= V4L2_DV_RGB_RANGE_LIMITED
&&
743 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
) {
745 * Clamp r, g and b to the limited range and convert to full
746 * range since that's what we deliver.
748 r
= clamp(r
, 16 << 4, 235 << 4);
749 g
= clamp(g
, 16 << 4, 235 << 4);
750 b
= clamp(b
, 16 << 4, 235 << 4);
751 r
= (r
- (16 << 4)) * 255 / 219;
752 g
= (g
- (16 << 4)) * 255 / 219;
753 b
= (b
- (16 << 4)) * 255 / 219;
756 if (tpg
->brightness
!= 128 || tpg
->contrast
!= 128 ||
757 tpg
->saturation
!= 128 || tpg
->hue
) {
758 /* Implement these operations */
762 /* First convert to YCbCr */
764 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
766 y
= (16 << 4) + ((y
- (16 << 4)) * tpg
->contrast
) / 128;
767 y
+= (tpg
->brightness
<< 4) - (128 << 4);
771 tmp_cb
= (cb
* cos(128 + tpg
->hue
)) / 127 + (cr
* sin
[128 + tpg
->hue
]) / 127;
772 tmp_cr
= (cr
* cos(128 + tpg
->hue
)) / 127 - (cb
* sin
[128 + tpg
->hue
]) / 127;
774 cb
= (128 << 4) + (tmp_cb
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
775 cr
= (128 << 4) + (tmp_cr
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
777 tpg
->colors
[k
][0] = clamp(y
>> 4, 1, 254);
778 tpg
->colors
[k
][1] = clamp(cb
>> 4, 1, 254);
779 tpg
->colors
[k
][2] = clamp(cr
>> 4, 1, 254);
782 ycbcr_to_color(tpg
, y
, cb
, cr
, &r
, &g
, &b
);
786 /* Convert to YCbCr */
789 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
791 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
792 y
= clamp(y
, 16 << 4, 235 << 4);
793 cb
= clamp(cb
, 16 << 4, 240 << 4);
794 cr
= clamp(cr
, 16 << 4, 240 << 4);
796 y
= clamp(y
>> 4, 1, 254);
797 cb
= clamp(cb
>> 4, 1, 254);
798 cr
= clamp(cr
>> 4, 1, 254);
799 switch (tpg
->fourcc
) {
800 case V4L2_PIX_FMT_YUV444
:
805 case V4L2_PIX_FMT_YUV555
:
810 case V4L2_PIX_FMT_YUV565
:
816 tpg
->colors
[k
][0] = y
;
817 tpg
->colors
[k
][1] = cb
;
818 tpg
->colors
[k
][2] = cr
;
820 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
821 r
= (r
* 219) / 255 + (16 << 4);
822 g
= (g
* 219) / 255 + (16 << 4);
823 b
= (b
* 219) / 255 + (16 << 4);
825 switch (tpg
->fourcc
) {
826 case V4L2_PIX_FMT_RGB332
:
831 case V4L2_PIX_FMT_RGB565
:
832 case V4L2_PIX_FMT_RGB565X
:
837 case V4L2_PIX_FMT_RGB444
:
838 case V4L2_PIX_FMT_XRGB444
:
839 case V4L2_PIX_FMT_ARGB444
:
844 case V4L2_PIX_FMT_RGB555
:
845 case V4L2_PIX_FMT_XRGB555
:
846 case V4L2_PIX_FMT_ARGB555
:
847 case V4L2_PIX_FMT_RGB555X
:
848 case V4L2_PIX_FMT_XRGB555X
:
849 case V4L2_PIX_FMT_ARGB555X
:
854 case V4L2_PIX_FMT_BGR666
:
866 tpg
->colors
[k
][0] = r
;
867 tpg
->colors
[k
][1] = g
;
868 tpg
->colors
[k
][2] = b
;
872 static void tpg_precalculate_colors(struct tpg_data
*tpg
)
876 for (k
= 0; k
< TPG_COLOR_MAX
; k
++)
877 precalculate_color(tpg
, k
);
880 /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
881 static void gen_twopix(struct tpg_data
*tpg
,
882 u8 buf
[TPG_MAX_PLANES
][8], int color
, bool odd
)
884 unsigned offset
= odd
* tpg
->twopixelsize
[0] / 2;
885 u8 alpha
= tpg
->alpha_component
;
888 if (tpg
->alpha_red_only
&& color
!= TPG_COLOR_CSC_RED
&&
889 color
!= TPG_COLOR_100_RED
&&
890 color
!= TPG_COLOR_75_RED
)
892 if (color
== TPG_COLOR_RANDOM
)
893 precalculate_color(tpg
, color
);
894 r_y
= tpg
->colors
[color
][0]; /* R or precalculated Y */
895 g_u
= tpg
->colors
[color
][1]; /* G or precalculated U */
896 b_v
= tpg
->colors
[color
][2]; /* B or precalculated V */
898 switch (tpg
->fourcc
) {
899 case V4L2_PIX_FMT_GREY
:
900 buf
[0][offset
] = r_y
;
902 case V4L2_PIX_FMT_Y16
:
904 buf
[0][offset
+1] = r_y
;
906 case V4L2_PIX_FMT_Y16_BE
:
907 buf
[0][offset
] = r_y
;
908 buf
[0][offset
+1] = 0;
910 case V4L2_PIX_FMT_YUV422P
:
911 case V4L2_PIX_FMT_YUV420
:
912 case V4L2_PIX_FMT_YUV420M
:
913 buf
[0][offset
] = r_y
;
915 buf
[1][0] = (buf
[1][0] + g_u
) / 2;
916 buf
[2][0] = (buf
[2][0] + b_v
) / 2;
917 buf
[1][1] = buf
[1][0];
918 buf
[2][1] = buf
[2][0];
924 case V4L2_PIX_FMT_YVU420
:
925 case V4L2_PIX_FMT_YVU420M
:
926 buf
[0][offset
] = r_y
;
928 buf
[1][0] = (buf
[1][0] + b_v
) / 2;
929 buf
[2][0] = (buf
[2][0] + g_u
) / 2;
930 buf
[1][1] = buf
[1][0];
931 buf
[2][1] = buf
[2][0];
938 case V4L2_PIX_FMT_NV12
:
939 case V4L2_PIX_FMT_NV12M
:
940 case V4L2_PIX_FMT_NV16
:
941 case V4L2_PIX_FMT_NV16M
:
942 buf
[0][offset
] = r_y
;
944 buf
[1][0] = (buf
[1][0] + g_u
) / 2;
945 buf
[1][1] = (buf
[1][1] + b_v
) / 2;
951 case V4L2_PIX_FMT_NV21
:
952 case V4L2_PIX_FMT_NV21M
:
953 case V4L2_PIX_FMT_NV61
:
954 case V4L2_PIX_FMT_NV61M
:
955 buf
[0][offset
] = r_y
;
957 buf
[1][0] = (buf
[1][0] + b_v
) / 2;
958 buf
[1][1] = (buf
[1][1] + g_u
) / 2;
965 case V4L2_PIX_FMT_NV24
:
966 buf
[0][offset
] = r_y
;
967 buf
[1][2 * offset
] = g_u
;
968 buf
[1][2 * offset
+ 1] = b_v
;
971 case V4L2_PIX_FMT_NV42
:
972 buf
[0][offset
] = r_y
;
973 buf
[1][2 * offset
] = b_v
;
974 buf
[1][2 * offset
+ 1] = g_u
;
977 case V4L2_PIX_FMT_YUYV
:
978 buf
[0][offset
] = r_y
;
980 buf
[0][1] = (buf
[0][1] + g_u
) / 2;
981 buf
[0][3] = (buf
[0][3] + b_v
) / 2;
987 case V4L2_PIX_FMT_UYVY
:
988 buf
[0][offset
+ 1] = r_y
;
990 buf
[0][0] = (buf
[0][0] + g_u
) / 2;
991 buf
[0][2] = (buf
[0][2] + b_v
) / 2;
997 case V4L2_PIX_FMT_YVYU
:
998 buf
[0][offset
] = r_y
;
1000 buf
[0][1] = (buf
[0][1] + b_v
) / 2;
1001 buf
[0][3] = (buf
[0][3] + g_u
) / 2;
1007 case V4L2_PIX_FMT_VYUY
:
1008 buf
[0][offset
+ 1] = r_y
;
1010 buf
[0][0] = (buf
[0][0] + b_v
) / 2;
1011 buf
[0][2] = (buf
[0][2] + g_u
) / 2;
1017 case V4L2_PIX_FMT_RGB332
:
1018 buf
[0][offset
] = (r_y
<< 5) | (g_u
<< 2) | b_v
;
1020 case V4L2_PIX_FMT_YUV565
:
1021 case V4L2_PIX_FMT_RGB565
:
1022 buf
[0][offset
] = (g_u
<< 5) | b_v
;
1023 buf
[0][offset
+ 1] = (r_y
<< 3) | (g_u
>> 3);
1025 case V4L2_PIX_FMT_RGB565X
:
1026 buf
[0][offset
] = (r_y
<< 3) | (g_u
>> 3);
1027 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
1029 case V4L2_PIX_FMT_RGB444
:
1030 case V4L2_PIX_FMT_XRGB444
:
1033 case V4L2_PIX_FMT_YUV444
:
1034 case V4L2_PIX_FMT_ARGB444
:
1035 buf
[0][offset
] = (g_u
<< 4) | b_v
;
1036 buf
[0][offset
+ 1] = (alpha
& 0xf0) | r_y
;
1038 case V4L2_PIX_FMT_RGB555
:
1039 case V4L2_PIX_FMT_XRGB555
:
1042 case V4L2_PIX_FMT_YUV555
:
1043 case V4L2_PIX_FMT_ARGB555
:
1044 buf
[0][offset
] = (g_u
<< 5) | b_v
;
1045 buf
[0][offset
+ 1] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
1047 case V4L2_PIX_FMT_RGB555X
:
1048 case V4L2_PIX_FMT_XRGB555X
:
1051 case V4L2_PIX_FMT_ARGB555X
:
1052 buf
[0][offset
] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
1053 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
1055 case V4L2_PIX_FMT_RGB24
:
1056 buf
[0][offset
] = r_y
;
1057 buf
[0][offset
+ 1] = g_u
;
1058 buf
[0][offset
+ 2] = b_v
;
1060 case V4L2_PIX_FMT_BGR24
:
1061 buf
[0][offset
] = b_v
;
1062 buf
[0][offset
+ 1] = g_u
;
1063 buf
[0][offset
+ 2] = r_y
;
1065 case V4L2_PIX_FMT_BGR666
:
1066 buf
[0][offset
] = (b_v
<< 2) | (g_u
>> 4);
1067 buf
[0][offset
+ 1] = (g_u
<< 4) | (r_y
>> 2);
1068 buf
[0][offset
+ 2] = r_y
<< 6;
1069 buf
[0][offset
+ 3] = 0;
1071 case V4L2_PIX_FMT_RGB32
:
1072 case V4L2_PIX_FMT_XRGB32
:
1075 case V4L2_PIX_FMT_YUV32
:
1076 case V4L2_PIX_FMT_ARGB32
:
1077 buf
[0][offset
] = alpha
;
1078 buf
[0][offset
+ 1] = r_y
;
1079 buf
[0][offset
+ 2] = g_u
;
1080 buf
[0][offset
+ 3] = b_v
;
1082 case V4L2_PIX_FMT_BGR32
:
1083 case V4L2_PIX_FMT_XBGR32
:
1086 case V4L2_PIX_FMT_ABGR32
:
1087 buf
[0][offset
] = b_v
;
1088 buf
[0][offset
+ 1] = g_u
;
1089 buf
[0][offset
+ 2] = r_y
;
1090 buf
[0][offset
+ 3] = alpha
;
1092 case V4L2_PIX_FMT_SBGGR8
:
1093 buf
[0][offset
] = odd
? g_u
: b_v
;
1094 buf
[1][offset
] = odd
? r_y
: g_u
;
1096 case V4L2_PIX_FMT_SGBRG8
:
1097 buf
[0][offset
] = odd
? b_v
: g_u
;
1098 buf
[1][offset
] = odd
? g_u
: r_y
;
1100 case V4L2_PIX_FMT_SGRBG8
:
1101 buf
[0][offset
] = odd
? r_y
: g_u
;
1102 buf
[1][offset
] = odd
? g_u
: b_v
;
1104 case V4L2_PIX_FMT_SRGGB8
:
1105 buf
[0][offset
] = odd
? g_u
: r_y
;
1106 buf
[1][offset
] = odd
? b_v
: g_u
;
1111 unsigned tpg_g_interleaved_plane(const struct tpg_data
*tpg
, unsigned buf_line
)
1113 switch (tpg
->fourcc
) {
1114 case V4L2_PIX_FMT_SBGGR8
:
1115 case V4L2_PIX_FMT_SGBRG8
:
1116 case V4L2_PIX_FMT_SGRBG8
:
1117 case V4L2_PIX_FMT_SRGGB8
:
1118 return buf_line
& 1;
1124 /* Return how many pattern lines are used by the current pattern. */
1125 static unsigned tpg_get_pat_lines(const struct tpg_data
*tpg
)
1127 switch (tpg
->pattern
) {
1128 case TPG_PAT_CHECKERS_16X16
:
1129 case TPG_PAT_CHECKERS_2X2
:
1130 case TPG_PAT_CHECKERS_1X1
:
1131 case TPG_PAT_COLOR_CHECKERS_2X2
:
1132 case TPG_PAT_COLOR_CHECKERS_1X1
:
1133 case TPG_PAT_ALTERNATING_HLINES
:
1134 case TPG_PAT_CROSS_1_PIXEL
:
1135 case TPG_PAT_CROSS_2_PIXELS
:
1136 case TPG_PAT_CROSS_10_PIXELS
:
1138 case TPG_PAT_100_COLORSQUARES
:
1139 case TPG_PAT_100_HCOLORBAR
:
1146 /* Which pattern line should be used for the given frame line. */
1147 static unsigned tpg_get_pat_line(const struct tpg_data
*tpg
, unsigned line
)
1149 switch (tpg
->pattern
) {
1150 case TPG_PAT_CHECKERS_16X16
:
1151 return (line
>> 4) & 1;
1152 case TPG_PAT_CHECKERS_1X1
:
1153 case TPG_PAT_COLOR_CHECKERS_1X1
:
1154 case TPG_PAT_ALTERNATING_HLINES
:
1156 case TPG_PAT_CHECKERS_2X2
:
1157 case TPG_PAT_COLOR_CHECKERS_2X2
:
1158 return (line
& 2) >> 1;
1159 case TPG_PAT_100_COLORSQUARES
:
1160 case TPG_PAT_100_HCOLORBAR
:
1161 return (line
* 8) / tpg
->src_height
;
1162 case TPG_PAT_CROSS_1_PIXEL
:
1163 return line
== tpg
->src_height
/ 2;
1164 case TPG_PAT_CROSS_2_PIXELS
:
1165 return (line
+ 1) / 2 == tpg
->src_height
/ 4;
1166 case TPG_PAT_CROSS_10_PIXELS
:
1167 return (line
+ 10) / 20 == tpg
->src_height
/ 40;
1174 * Which color should be used for the given pattern line and X coordinate.
1175 * Note: x is in the range 0 to 2 * tpg->src_width.
1177 static enum tpg_color
tpg_get_color(const struct tpg_data
*tpg
,
1178 unsigned pat_line
, unsigned x
)
1180 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
1181 should be modified */
1182 static const enum tpg_color bars
[3][8] = {
1183 /* Standard ITU-R 75% color bar sequence */
1184 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_75_YELLOW
,
1185 TPG_COLOR_75_CYAN
, TPG_COLOR_75_GREEN
,
1186 TPG_COLOR_75_MAGENTA
, TPG_COLOR_75_RED
,
1187 TPG_COLOR_75_BLUE
, TPG_COLOR_100_BLACK
, },
1188 /* Standard ITU-R 100% color bar sequence */
1189 { TPG_COLOR_100_WHITE
, TPG_COLOR_100_YELLOW
,
1190 TPG_COLOR_100_CYAN
, TPG_COLOR_100_GREEN
,
1191 TPG_COLOR_100_MAGENTA
, TPG_COLOR_100_RED
,
1192 TPG_COLOR_100_BLUE
, TPG_COLOR_100_BLACK
, },
1193 /* Color bar sequence suitable to test CSC */
1194 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_CSC_YELLOW
,
1195 TPG_COLOR_CSC_CYAN
, TPG_COLOR_CSC_GREEN
,
1196 TPG_COLOR_CSC_MAGENTA
, TPG_COLOR_CSC_RED
,
1197 TPG_COLOR_CSC_BLUE
, TPG_COLOR_CSC_BLACK
, },
1200 switch (tpg
->pattern
) {
1201 case TPG_PAT_75_COLORBAR
:
1202 case TPG_PAT_100_COLORBAR
:
1203 case TPG_PAT_CSC_COLORBAR
:
1204 return bars
[tpg
->pattern
][((x
* 8) / tpg
->src_width
) % 8];
1205 case TPG_PAT_100_COLORSQUARES
:
1206 return bars
[1][(pat_line
+ (x
* 8) / tpg
->src_width
) % 8];
1207 case TPG_PAT_100_HCOLORBAR
:
1208 return bars
[1][pat_line
];
1210 return TPG_COLOR_100_BLACK
;
1212 return TPG_COLOR_100_WHITE
;
1214 return TPG_COLOR_100_RED
;
1216 return TPG_COLOR_100_GREEN
;
1218 return TPG_COLOR_100_BLUE
;
1219 case TPG_PAT_CHECKERS_16X16
:
1220 return (((x
>> 4) & 1) ^ (pat_line
& 1)) ?
1221 TPG_COLOR_100_BLACK
: TPG_COLOR_100_WHITE
;
1222 case TPG_PAT_CHECKERS_1X1
:
1223 return ((x
& 1) ^ (pat_line
& 1)) ?
1224 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
1225 case TPG_PAT_COLOR_CHECKERS_1X1
:
1226 return ((x
& 1) ^ (pat_line
& 1)) ?
1227 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
1228 case TPG_PAT_CHECKERS_2X2
:
1229 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
1230 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
1231 case TPG_PAT_COLOR_CHECKERS_2X2
:
1232 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
1233 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
1234 case TPG_PAT_ALTERNATING_HLINES
:
1235 return pat_line
? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
1236 case TPG_PAT_ALTERNATING_VLINES
:
1237 return (x
& 1) ? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
1238 case TPG_PAT_CROSS_1_PIXEL
:
1239 if (pat_line
|| (x
% tpg
->src_width
) == tpg
->src_width
/ 2)
1240 return TPG_COLOR_100_BLACK
;
1241 return TPG_COLOR_100_WHITE
;
1242 case TPG_PAT_CROSS_2_PIXELS
:
1243 if (pat_line
|| ((x
% tpg
->src_width
) + 1) / 2 == tpg
->src_width
/ 4)
1244 return TPG_COLOR_100_BLACK
;
1245 return TPG_COLOR_100_WHITE
;
1246 case TPG_PAT_CROSS_10_PIXELS
:
1247 if (pat_line
|| ((x
% tpg
->src_width
) + 10) / 20 == tpg
->src_width
/ 40)
1248 return TPG_COLOR_100_BLACK
;
1249 return TPG_COLOR_100_WHITE
;
1250 case TPG_PAT_GRAY_RAMP
:
1251 return TPG_COLOR_RAMP
+ ((x
% tpg
->src_width
) * 256) / tpg
->src_width
;
1253 return TPG_COLOR_100_RED
;
1258 * Given the pixel aspect ratio and video aspect ratio calculate the
1259 * coordinates of a centered square and the coordinates of the border of
1260 * the active video area. The coordinates are relative to the source
1263 static void tpg_calculate_square_border(struct tpg_data
*tpg
)
1265 unsigned w
= tpg
->src_width
;
1266 unsigned h
= tpg
->src_height
;
1267 unsigned sq_w
, sq_h
;
1269 sq_w
= (w
* 2 / 5) & ~1;
1270 if (((w
- sq_w
) / 2) & 1)
1273 tpg
->square
.width
= sq_w
;
1274 if (tpg
->vid_aspect
== TPG_VIDEO_ASPECT_16X9_ANAMORPHIC
) {
1275 unsigned ana_sq_w
= (sq_w
/ 4) * 3;
1277 if (((w
- ana_sq_w
) / 2) & 1)
1279 tpg
->square
.width
= ana_sq_w
;
1281 tpg
->square
.left
= (w
- tpg
->square
.width
) / 2;
1282 if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
)
1283 sq_h
= sq_w
* 10 / 11;
1284 else if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_PAL
)
1285 sq_h
= sq_w
* 59 / 54;
1286 tpg
->square
.height
= sq_h
;
1287 tpg
->square
.top
= (h
- sq_h
) / 2;
1288 tpg
->border
.left
= 0;
1289 tpg
->border
.width
= w
;
1290 tpg
->border
.top
= 0;
1291 tpg
->border
.height
= h
;
1292 switch (tpg
->vid_aspect
) {
1293 case TPG_VIDEO_ASPECT_4X3
:
1294 if (tpg
->pix_aspect
)
1296 if (3 * w
>= 4 * h
) {
1297 tpg
->border
.width
= ((4 * h
) / 3) & ~1;
1298 if (((w
- tpg
->border
.width
) / 2) & ~1)
1299 tpg
->border
.width
-= 2;
1300 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
1303 tpg
->border
.height
= ((3 * w
) / 4) & ~1;
1304 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1306 case TPG_VIDEO_ASPECT_14X9_CENTRE
:
1307 if (tpg
->pix_aspect
) {
1308 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 420 : 506;
1309 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1312 if (9 * w
>= 14 * h
) {
1313 tpg
->border
.width
= ((14 * h
) / 9) & ~1;
1314 if (((w
- tpg
->border
.width
) / 2) & ~1)
1315 tpg
->border
.width
-= 2;
1316 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
1319 tpg
->border
.height
= ((9 * w
) / 14) & ~1;
1320 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1322 case TPG_VIDEO_ASPECT_16X9_CENTRE
:
1323 if (tpg
->pix_aspect
) {
1324 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 368 : 442;
1325 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1328 if (9 * w
>= 16 * h
) {
1329 tpg
->border
.width
= ((16 * h
) / 9) & ~1;
1330 if (((w
- tpg
->border
.width
) / 2) & ~1)
1331 tpg
->border
.width
-= 2;
1332 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
1335 tpg
->border
.height
= ((9 * w
) / 16) & ~1;
1336 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1343 static void tpg_precalculate_line(struct tpg_data
*tpg
)
1345 enum tpg_color contrast
;
1346 u8 pix
[TPG_MAX_PLANES
][8];
1351 switch (tpg
->pattern
) {
1353 contrast
= TPG_COLOR_100_RED
;
1355 case TPG_PAT_CSC_COLORBAR
:
1356 contrast
= TPG_COLOR_CSC_GREEN
;
1359 contrast
= TPG_COLOR_100_GREEN
;
1363 for (pat
= 0; pat
< tpg_get_pat_lines(tpg
); pat
++) {
1364 /* Coarse scaling with Bresenham */
1365 unsigned int_part
= tpg
->src_width
/ tpg
->scaled_width
;
1366 unsigned fract_part
= tpg
->src_width
% tpg
->scaled_width
;
1370 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1371 unsigned real_x
= src_x
;
1372 enum tpg_color color1
, color2
;
1374 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1375 color1
= tpg_get_color(tpg
, pat
, real_x
);
1378 error
+= fract_part
;
1379 if (error
>= tpg
->scaled_width
) {
1380 error
-= tpg
->scaled_width
;
1385 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1386 color2
= tpg_get_color(tpg
, pat
, real_x
);
1389 error
+= fract_part
;
1390 if (error
>= tpg
->scaled_width
) {
1391 error
-= tpg
->scaled_width
;
1395 gen_twopix(tpg
, pix
, tpg
->hflip
? color2
: color1
, 0);
1396 gen_twopix(tpg
, pix
, tpg
->hflip
? color1
: color2
, 1);
1397 for (p
= 0; p
< tpg
->planes
; p
++) {
1398 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1399 unsigned hdiv
= tpg
->hdownsampling
[p
];
1400 u8
*pos
= tpg
->lines
[pat
][p
] + tpg_hdiv(tpg
, p
, x
);
1402 memcpy(pos
, pix
[p
], twopixsize
/ hdiv
);
1407 if (tpg
->vdownsampling
[tpg
->planes
- 1] > 1) {
1408 unsigned pat_lines
= tpg_get_pat_lines(tpg
);
1410 for (pat
= 0; pat
< pat_lines
; pat
++) {
1411 unsigned next_pat
= (pat
+ 1) % pat_lines
;
1413 for (p
= 1; p
< tpg
->planes
; p
++) {
1414 unsigned w
= tpg_hdiv(tpg
, p
, tpg
->scaled_width
* 2);
1415 u8
*pos1
= tpg
->lines
[pat
][p
];
1416 u8
*pos2
= tpg
->lines
[next_pat
][p
];
1417 u8
*dest
= tpg
->downsampled_lines
[pat
][p
];
1419 for (x
= 0; x
< w
; x
++, pos1
++, pos2
++, dest
++)
1420 *dest
= ((u16
)*pos1
+ (u16
)*pos2
) / 2;
1425 gen_twopix(tpg
, pix
, contrast
, 0);
1426 gen_twopix(tpg
, pix
, contrast
, 1);
1427 for (p
= 0; p
< tpg
->planes
; p
++) {
1428 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1429 u8
*pos
= tpg
->contrast_line
[p
];
1431 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2, pos
+= twopixsize
)
1432 memcpy(pos
, pix
[p
], twopixsize
);
1435 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 0);
1436 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 1);
1437 for (p
= 0; p
< tpg
->planes
; p
++) {
1438 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1439 u8
*pos
= tpg
->black_line
[p
];
1441 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2, pos
+= twopixsize
)
1442 memcpy(pos
, pix
[p
], twopixsize
);
1445 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1446 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 0);
1447 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 1);
1448 for (p
= 0; p
< tpg
->planes
; p
++) {
1449 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1450 u8
*pos
= tpg
->random_line
[p
] + x
* twopixsize
/ 2;
1452 memcpy(pos
, pix
[p
], twopixsize
);
1456 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 0);
1457 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 1);
1458 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 0);
1459 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 1);
1462 /* need this to do rgb24 rendering */
1463 typedef struct { u16 __
; u8 _
; } __packed x24
;
1465 void tpg_gen_text(const struct tpg_data
*tpg
, u8
*basep
[TPG_MAX_PLANES
][2],
1466 int y
, int x
, char *text
)
1469 unsigned step
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1470 unsigned div
= step
;
1472 unsigned len
= strlen(text
);
1475 if (font8x16
== NULL
|| basep
== NULL
)
1478 /* Checks if it is possible to show string */
1479 if (y
+ 16 >= tpg
->compose
.height
|| x
+ 8 >= tpg
->compose
.width
)
1482 if (len
> (tpg
->compose
.width
- x
) / 8)
1483 len
= (tpg
->compose
.width
- x
) / 8;
1485 y
= tpg
->compose
.height
- y
- 16;
1487 x
= tpg
->compose
.width
- x
- 8;
1488 y
+= tpg
->compose
.top
;
1489 x
+= tpg
->compose
.left
;
1490 if (tpg
->field
== V4L2_FIELD_BOTTOM
)
1492 else if (tpg
->field
== V4L2_FIELD_SEQ_TB
|| tpg
->field
== V4L2_FIELD_SEQ_BT
)
1495 for (p
= 0; p
< tpg
->planes
; p
++) {
1496 unsigned vdiv
= tpg
->vdownsampling
[p
];
1497 unsigned hdiv
= tpg
->hdownsampling
[p
];
1500 #define PRINTSTR(PIXTYPE) do { \
1503 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1504 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1506 for (line = first; line < 16; line += vdiv * step) { \
1507 int l = tpg->vflip ? 15 - line : line; \
1508 PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \
1509 ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \
1510 (x / hdiv) * sizeof(PIXTYPE)); \
1513 for (s = 0; s < len; s++) { \
1514 u8 chr = font8x16[text[s] * 16 + line]; \
1516 if (hdiv == 2 && tpg->hflip) { \
1517 pos[3] = (chr & (0x01 << 6) ? fg : bg); \
1518 pos[2] = (chr & (0x01 << 4) ? fg : bg); \
1519 pos[1] = (chr & (0x01 << 2) ? fg : bg); \
1520 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1521 } else if (hdiv == 2) { \
1522 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1523 pos[1] = (chr & (0x01 << 5) ? fg : bg); \
1524 pos[2] = (chr & (0x01 << 3) ? fg : bg); \
1525 pos[3] = (chr & (0x01 << 1) ? fg : bg); \
1526 } else if (tpg->hflip) { \
1527 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1528 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1529 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1530 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1531 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1532 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1533 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1534 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1536 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1537 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1538 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1539 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1540 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1541 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1542 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1543 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1546 pos += (tpg->hflip ? -8 : 8) / hdiv; \
1551 switch (tpg
->twopixelsize
[p
]) {
1553 PRINTSTR(u8
); break;
1555 PRINTSTR(u16
); break;
1557 PRINTSTR(x24
); break;
1559 PRINTSTR(u32
); break;
1564 void tpg_update_mv_step(struct tpg_data
*tpg
)
1566 int factor
= tpg
->mv_hor_mode
> TPG_MOVE_NONE
? -1 : 1;
1570 switch (tpg
->mv_hor_mode
) {
1571 case TPG_MOVE_NEG_FAST
:
1572 case TPG_MOVE_POS_FAST
:
1573 tpg
->mv_hor_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1577 tpg
->mv_hor_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1579 case TPG_MOVE_NEG_SLOW
:
1580 case TPG_MOVE_POS_SLOW
:
1581 tpg
->mv_hor_step
= 2;
1584 tpg
->mv_hor_step
= 0;
1588 tpg
->mv_hor_step
= tpg
->src_width
- tpg
->mv_hor_step
;
1590 factor
= tpg
->mv_vert_mode
> TPG_MOVE_NONE
? -1 : 1;
1591 switch (tpg
->mv_vert_mode
) {
1592 case TPG_MOVE_NEG_FAST
:
1593 case TPG_MOVE_POS_FAST
:
1594 tpg
->mv_vert_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1598 tpg
->mv_vert_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1600 case TPG_MOVE_NEG_SLOW
:
1601 case TPG_MOVE_POS_SLOW
:
1602 tpg
->mv_vert_step
= 1;
1605 tpg
->mv_vert_step
= 0;
1609 tpg
->mv_vert_step
= tpg
->src_height
- tpg
->mv_vert_step
;
1612 /* Map the line number relative to the crop rectangle to a frame line number */
1613 static unsigned tpg_calc_frameline(const struct tpg_data
*tpg
, unsigned src_y
,
1617 case V4L2_FIELD_TOP
:
1618 return tpg
->crop
.top
+ src_y
* 2;
1619 case V4L2_FIELD_BOTTOM
:
1620 return tpg
->crop
.top
+ src_y
* 2 + 1;
1622 return src_y
+ tpg
->crop
.top
;
1627 * Map the line number relative to the compose rectangle to a destination
1628 * buffer line number.
1630 static unsigned tpg_calc_buffer_line(const struct tpg_data
*tpg
, unsigned y
,
1633 y
+= tpg
->compose
.top
;
1635 case V4L2_FIELD_SEQ_TB
:
1637 return tpg
->buf_height
/ 2 + y
/ 2;
1639 case V4L2_FIELD_SEQ_BT
:
1642 return tpg
->buf_height
/ 2 + y
/ 2;
1648 static void tpg_recalc(struct tpg_data
*tpg
)
1650 if (tpg
->recalc_colors
) {
1651 tpg
->recalc_colors
= false;
1652 tpg
->recalc_lines
= true;
1653 tpg
->real_ycbcr_enc
= tpg
->ycbcr_enc
;
1654 tpg
->real_quantization
= tpg
->quantization
;
1655 if (tpg
->ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
) {
1656 switch (tpg
->colorspace
) {
1657 case V4L2_COLORSPACE_REC709
:
1658 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_709
;
1660 case V4L2_COLORSPACE_SRGB
:
1661 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SYCC
;
1663 case V4L2_COLORSPACE_BT2020
:
1664 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_BT2020
;
1666 case V4L2_COLORSPACE_SMPTE240M
:
1667 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SMPTE240M
;
1669 case V4L2_COLORSPACE_SMPTE170M
:
1670 case V4L2_COLORSPACE_470_SYSTEM_M
:
1671 case V4L2_COLORSPACE_470_SYSTEM_BG
:
1672 case V4L2_COLORSPACE_ADOBERGB
:
1674 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
1678 if (tpg
->quantization
== V4L2_QUANTIZATION_DEFAULT
) {
1679 tpg
->real_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
1681 switch (tpg
->real_ycbcr_enc
) {
1682 case V4L2_YCBCR_ENC_SYCC
:
1683 case V4L2_YCBCR_ENC_XV601
:
1684 case V4L2_YCBCR_ENC_XV709
:
1687 tpg
->real_quantization
=
1688 V4L2_QUANTIZATION_LIM_RANGE
;
1691 } else if (tpg
->colorspace
== V4L2_COLORSPACE_BT2020
) {
1692 /* R'G'B' BT.2020 is limited range */
1693 tpg
->real_quantization
=
1694 V4L2_QUANTIZATION_LIM_RANGE
;
1697 tpg_precalculate_colors(tpg
);
1699 if (tpg
->recalc_square_border
) {
1700 tpg
->recalc_square_border
= false;
1701 tpg_calculate_square_border(tpg
);
1703 if (tpg
->recalc_lines
) {
1704 tpg
->recalc_lines
= false;
1705 tpg_precalculate_line(tpg
);
1709 void tpg_calc_text_basep(struct tpg_data
*tpg
,
1710 u8
*basep
[TPG_MAX_PLANES
][2], unsigned p
, u8
*vbuf
)
1712 unsigned stride
= tpg
->bytesperline
[p
];
1713 unsigned h
= tpg
->buf_height
;
1719 h
/= tpg
->vdownsampling
[p
];
1720 if (tpg
->field
== V4L2_FIELD_SEQ_TB
)
1721 basep
[p
][1] += h
* stride
/ 2;
1722 else if (tpg
->field
== V4L2_FIELD_SEQ_BT
)
1723 basep
[p
][0] += h
* stride
/ 2;
1724 if (p
== 0 && tpg
->interleaved
)
1725 tpg_calc_text_basep(tpg
, basep
, 1, vbuf
);
1728 static int tpg_pattern_avg(const struct tpg_data
*tpg
,
1729 unsigned pat1
, unsigned pat2
)
1731 unsigned pat_lines
= tpg_get_pat_lines(tpg
);
1733 if (pat1
== (pat2
+ 1) % pat_lines
)
1735 if (pat2
== (pat1
+ 1) % pat_lines
)
1740 void tpg_log_status(struct tpg_data
*tpg
)
1742 pr_info("tpg source WxH: %ux%u (%s)\n",
1743 tpg
->src_width
, tpg
->src_height
,
1744 tpg
->is_yuv
? "YCbCr" : "RGB");
1745 pr_info("tpg field: %u\n", tpg
->field
);
1746 pr_info("tpg crop: %ux%u@%dx%d\n", tpg
->crop
.width
, tpg
->crop
.height
,
1747 tpg
->crop
.left
, tpg
->crop
.top
);
1748 pr_info("tpg compose: %ux%u@%dx%d\n", tpg
->compose
.width
, tpg
->compose
.height
,
1749 tpg
->compose
.left
, tpg
->compose
.top
);
1750 pr_info("tpg colorspace: %d\n", tpg
->colorspace
);
1751 pr_info("tpg Y'CbCr encoding: %d/%d\n", tpg
->ycbcr_enc
, tpg
->real_ycbcr_enc
);
1752 pr_info("tpg quantization: %d/%d\n", tpg
->quantization
, tpg
->real_quantization
);
1753 pr_info("tpg RGB range: %d/%d\n", tpg
->rgb_range
, tpg
->real_rgb_range
);
1757 * This struct contains common parameters used by both the drawing of the
1758 * test pattern and the drawing of the extras (borders, square, etc.)
1760 struct tpg_draw_params
{
1764 unsigned twopixsize
;
1768 unsigned frame_line
;
1769 unsigned frame_line_next
;
1772 unsigned mv_hor_old
;
1773 unsigned mv_hor_new
;
1774 unsigned mv_vert_old
;
1775 unsigned mv_vert_new
;
1779 unsigned wss_random_offset
;
1781 unsigned left_pillar_width
;
1782 unsigned right_pillar_start
;
1785 static void tpg_fill_params_pattern(const struct tpg_data
*tpg
, unsigned p
,
1786 struct tpg_draw_params
*params
)
1788 params
->mv_hor_old
=
1789 tpg_hscale_div(tpg
, p
, tpg
->mv_hor_count
% tpg
->src_width
);
1790 params
->mv_hor_new
=
1791 tpg_hscale_div(tpg
, p
, (tpg
->mv_hor_count
+ tpg
->mv_hor_step
) %
1793 params
->mv_vert_old
= tpg
->mv_vert_count
% tpg
->src_height
;
1794 params
->mv_vert_new
=
1795 (tpg
->mv_vert_count
+ tpg
->mv_vert_step
) % tpg
->src_height
;
1798 static void tpg_fill_params_extras(const struct tpg_data
*tpg
,
1800 struct tpg_draw_params
*params
)
1802 unsigned left_pillar_width
= 0;
1803 unsigned right_pillar_start
= params
->img_width
;
1805 params
->wss_width
= tpg
->crop
.left
< tpg
->src_width
/ 2 ?
1806 tpg
->src_width
/ 2 - tpg
->crop
.left
: 0;
1807 if (params
->wss_width
> tpg
->crop
.width
)
1808 params
->wss_width
= tpg
->crop
.width
;
1809 params
->wss_width
= tpg_hscale_div(tpg
, p
, params
->wss_width
);
1810 params
->wss_random_offset
=
1811 params
->twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1813 if (tpg
->crop
.left
< tpg
->border
.left
) {
1814 left_pillar_width
= tpg
->border
.left
- tpg
->crop
.left
;
1815 if (left_pillar_width
> tpg
->crop
.width
)
1816 left_pillar_width
= tpg
->crop
.width
;
1817 left_pillar_width
= tpg_hscale_div(tpg
, p
, left_pillar_width
);
1819 params
->left_pillar_width
= left_pillar_width
;
1821 if (tpg
->crop
.left
+ tpg
->crop
.width
>
1822 tpg
->border
.left
+ tpg
->border
.width
) {
1823 right_pillar_start
=
1824 tpg
->border
.left
+ tpg
->border
.width
- tpg
->crop
.left
;
1825 right_pillar_start
=
1826 tpg_hscale_div(tpg
, p
, right_pillar_start
);
1827 if (right_pillar_start
> params
->img_width
)
1828 right_pillar_start
= params
->img_width
;
1830 params
->right_pillar_start
= right_pillar_start
;
1832 params
->sav_eav_f
= tpg
->field
==
1833 (params
->is_60hz
? V4L2_FIELD_TOP
: V4L2_FIELD_BOTTOM
);
1836 static void tpg_fill_plane_extras(const struct tpg_data
*tpg
,
1837 const struct tpg_draw_params
*params
,
1838 unsigned p
, unsigned h
, u8
*vbuf
)
1840 unsigned twopixsize
= params
->twopixsize
;
1841 unsigned img_width
= params
->img_width
;
1842 unsigned frame_line
= params
->frame_line
;
1843 const struct v4l2_rect
*sq
= &tpg
->square
;
1844 const struct v4l2_rect
*b
= &tpg
->border
;
1845 const struct v4l2_rect
*c
= &tpg
->crop
;
1847 if (params
->is_tv
&& !params
->is_60hz
&&
1848 frame_line
== 0 && params
->wss_width
) {
1850 * Replace the first half of the top line of a 50 Hz frame
1851 * with random data to simulate a WSS signal.
1853 u8
*wss
= tpg
->random_line
[p
] + params
->wss_random_offset
;
1855 memcpy(vbuf
, wss
, params
->wss_width
);
1858 if (tpg
->show_border
&& frame_line
>= b
->top
&&
1859 frame_line
< b
->top
+ b
->height
) {
1860 unsigned bottom
= b
->top
+ b
->height
- 1;
1861 unsigned left
= params
->left_pillar_width
;
1862 unsigned right
= params
->right_pillar_start
;
1864 if (frame_line
== b
->top
|| frame_line
== b
->top
+ 1 ||
1865 frame_line
== bottom
|| frame_line
== bottom
- 1) {
1866 memcpy(vbuf
+ left
, tpg
->contrast_line
[p
],
1869 if (b
->left
>= c
->left
&&
1870 b
->left
< c
->left
+ c
->width
)
1872 tpg
->contrast_line
[p
], twopixsize
);
1873 if (b
->left
+ b
->width
> c
->left
&&
1874 b
->left
+ b
->width
<= c
->left
+ c
->width
)
1875 memcpy(vbuf
+ right
- twopixsize
,
1876 tpg
->contrast_line
[p
], twopixsize
);
1879 if (tpg
->qual
!= TPG_QUAL_NOISE
&& frame_line
>= b
->top
&&
1880 frame_line
< b
->top
+ b
->height
) {
1881 memcpy(vbuf
, tpg
->black_line
[p
], params
->left_pillar_width
);
1882 memcpy(vbuf
+ params
->right_pillar_start
, tpg
->black_line
[p
],
1883 img_width
- params
->right_pillar_start
);
1885 if (tpg
->show_square
&& frame_line
>= sq
->top
&&
1886 frame_line
< sq
->top
+ sq
->height
&&
1887 sq
->left
< c
->left
+ c
->width
&&
1888 sq
->left
+ sq
->width
>= c
->left
) {
1889 unsigned left
= sq
->left
;
1890 unsigned width
= sq
->width
;
1892 if (c
->left
> left
) {
1893 width
-= c
->left
- left
;
1896 if (c
->left
+ c
->width
< left
+ width
)
1897 width
-= left
+ width
- c
->left
- c
->width
;
1899 left
= tpg_hscale_div(tpg
, p
, left
);
1900 width
= tpg_hscale_div(tpg
, p
, width
);
1901 memcpy(vbuf
+ left
, tpg
->contrast_line
[p
], width
);
1903 if (tpg
->insert_sav
) {
1904 unsigned offset
= tpg_hdiv(tpg
, p
, tpg
->compose
.width
/ 3);
1905 u8
*p
= vbuf
+ offset
;
1906 unsigned vact
= 0, hact
= 0;
1911 p
[3] = 0x80 | (params
->sav_eav_f
<< 6) |
1912 (vact
<< 5) | (hact
<< 4) |
1913 ((hact
^ vact
) << 3) |
1914 ((hact
^ params
->sav_eav_f
) << 2) |
1915 ((params
->sav_eav_f
^ vact
) << 1) |
1916 (hact
^ vact
^ params
->sav_eav_f
);
1918 if (tpg
->insert_eav
) {
1919 unsigned offset
= tpg_hdiv(tpg
, p
, tpg
->compose
.width
* 2 / 3);
1920 u8
*p
= vbuf
+ offset
;
1921 unsigned vact
= 0, hact
= 1;
1926 p
[3] = 0x80 | (params
->sav_eav_f
<< 6) |
1927 (vact
<< 5) | (hact
<< 4) |
1928 ((hact
^ vact
) << 3) |
1929 ((hact
^ params
->sav_eav_f
) << 2) |
1930 ((params
->sav_eav_f
^ vact
) << 1) |
1931 (hact
^ vact
^ params
->sav_eav_f
);
1935 static void tpg_fill_plane_pattern(const struct tpg_data
*tpg
,
1936 const struct tpg_draw_params
*params
,
1937 unsigned p
, unsigned h
, u8
*vbuf
)
1939 unsigned twopixsize
= params
->twopixsize
;
1940 unsigned img_width
= params
->img_width
;
1941 unsigned mv_hor_old
= params
->mv_hor_old
;
1942 unsigned mv_hor_new
= params
->mv_hor_new
;
1943 unsigned mv_vert_old
= params
->mv_vert_old
;
1944 unsigned mv_vert_new
= params
->mv_vert_new
;
1945 unsigned frame_line
= params
->frame_line
;
1946 unsigned frame_line_next
= params
->frame_line_next
;
1947 unsigned line_offset
= tpg_hscale_div(tpg
, p
, tpg
->crop
.left
);
1949 bool fill_blank
= false;
1950 unsigned pat_line_old
;
1951 unsigned pat_line_new
;
1952 u8
*linestart_older
;
1953 u8
*linestart_newer
;
1955 u8
*linestart_bottom
;
1957 even
= !(frame_line
& 1);
1959 if (h
>= params
->hmax
) {
1960 if (params
->hmax
== tpg
->compose
.height
)
1962 if (!tpg
->perc_fill_blank
)
1968 frame_line
= tpg
->src_height
- frame_line
- 1;
1969 frame_line_next
= tpg
->src_height
- frame_line_next
- 1;
1973 linestart_older
= tpg
->contrast_line
[p
];
1974 linestart_newer
= tpg
->contrast_line
[p
];
1975 } else if (tpg
->qual
!= TPG_QUAL_NOISE
&&
1976 (frame_line
< tpg
->border
.top
||
1977 frame_line
>= tpg
->border
.top
+ tpg
->border
.height
)) {
1978 linestart_older
= tpg
->black_line
[p
];
1979 linestart_newer
= tpg
->black_line
[p
];
1980 } else if (tpg
->pattern
== TPG_PAT_NOISE
|| tpg
->qual
== TPG_QUAL_NOISE
) {
1981 linestart_older
= tpg
->random_line
[p
] +
1982 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1983 linestart_newer
= tpg
->random_line
[p
] +
1984 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1986 unsigned frame_line_old
=
1987 (frame_line
+ mv_vert_old
) % tpg
->src_height
;
1988 unsigned frame_line_new
=
1989 (frame_line
+ mv_vert_new
) % tpg
->src_height
;
1990 unsigned pat_line_next_old
;
1991 unsigned pat_line_next_new
;
1993 pat_line_old
= tpg_get_pat_line(tpg
, frame_line_old
);
1994 pat_line_new
= tpg_get_pat_line(tpg
, frame_line_new
);
1995 linestart_older
= tpg
->lines
[pat_line_old
][p
] + mv_hor_old
;
1996 linestart_newer
= tpg
->lines
[pat_line_new
][p
] + mv_hor_new
;
1998 if (tpg
->vdownsampling
[p
] > 1 && frame_line
!= frame_line_next
) {
2002 * Now decide whether we need to use downsampled_lines[].
2003 * That's necessary if the two lines use different patterns.
2005 pat_line_next_old
= tpg_get_pat_line(tpg
,
2006 (frame_line_next
+ mv_vert_old
) % tpg
->src_height
);
2007 pat_line_next_new
= tpg_get_pat_line(tpg
,
2008 (frame_line_next
+ mv_vert_new
) % tpg
->src_height
);
2010 switch (tpg
->field
) {
2011 case V4L2_FIELD_INTERLACED
:
2012 case V4L2_FIELD_INTERLACED_BT
:
2013 case V4L2_FIELD_INTERLACED_TB
:
2014 avg_pat
= tpg_pattern_avg(tpg
, pat_line_old
, pat_line_new
);
2017 linestart_older
= tpg
->downsampled_lines
[avg_pat
][p
] + mv_hor_old
;
2018 linestart_newer
= linestart_older
;
2020 case V4L2_FIELD_NONE
:
2021 case V4L2_FIELD_TOP
:
2022 case V4L2_FIELD_BOTTOM
:
2023 case V4L2_FIELD_SEQ_BT
:
2024 case V4L2_FIELD_SEQ_TB
:
2025 avg_pat
= tpg_pattern_avg(tpg
, pat_line_old
, pat_line_next_old
);
2027 linestart_older
= tpg
->downsampled_lines
[avg_pat
][p
] +
2029 avg_pat
= tpg_pattern_avg(tpg
, pat_line_new
, pat_line_next_new
);
2031 linestart_newer
= tpg
->downsampled_lines
[avg_pat
][p
] +
2036 linestart_older
+= line_offset
;
2037 linestart_newer
+= line_offset
;
2039 if (tpg
->field_alternate
) {
2040 linestart_top
= linestart_bottom
= linestart_older
;
2041 } else if (params
->is_60hz
) {
2042 linestart_top
= linestart_newer
;
2043 linestart_bottom
= linestart_older
;
2045 linestart_top
= linestart_older
;
2046 linestart_bottom
= linestart_newer
;
2049 switch (tpg
->field
) {
2050 case V4L2_FIELD_INTERLACED
:
2051 case V4L2_FIELD_INTERLACED_TB
:
2052 case V4L2_FIELD_SEQ_TB
:
2053 case V4L2_FIELD_SEQ_BT
:
2055 memcpy(vbuf
, linestart_top
, img_width
);
2057 memcpy(vbuf
, linestart_bottom
, img_width
);
2059 case V4L2_FIELD_INTERLACED_BT
:
2061 memcpy(vbuf
, linestart_bottom
, img_width
);
2063 memcpy(vbuf
, linestart_top
, img_width
);
2065 case V4L2_FIELD_TOP
:
2066 memcpy(vbuf
, linestart_top
, img_width
);
2068 case V4L2_FIELD_BOTTOM
:
2069 memcpy(vbuf
, linestart_bottom
, img_width
);
2071 case V4L2_FIELD_NONE
:
2073 memcpy(vbuf
, linestart_older
, img_width
);
2078 void tpg_fill_plane_buffer(struct tpg_data
*tpg
, v4l2_std_id std
,
2079 unsigned p
, u8
*vbuf
)
2081 struct tpg_draw_params params
;
2082 unsigned factor
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
2084 /* Coarse scaling with Bresenham */
2085 unsigned int_part
= (tpg
->crop
.height
/ factor
) / tpg
->compose
.height
;
2086 unsigned fract_part
= (tpg
->crop
.height
/ factor
) % tpg
->compose
.height
;
2094 params
.is_60hz
= std
& V4L2_STD_525_60
;
2095 params
.twopixsize
= tpg
->twopixelsize
[p
];
2096 params
.img_width
= tpg_hdiv(tpg
, p
, tpg
->compose
.width
);
2097 params
.stride
= tpg
->bytesperline
[p
];
2098 params
.hmax
= (tpg
->compose
.height
* tpg
->perc_fill
) / 100;
2100 tpg_fill_params_pattern(tpg
, p
, ¶ms
);
2101 tpg_fill_params_extras(tpg
, p
, ¶ms
);
2103 vbuf
+= tpg_hdiv(tpg
, p
, tpg
->compose
.left
);
2105 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
2108 params
.frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
2109 params
.frame_line_next
= params
.frame_line
;
2110 buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
2112 error
+= fract_part
;
2113 if (error
>= tpg
->compose
.height
) {
2114 error
-= tpg
->compose
.height
;
2119 * For line-interleaved formats determine the 'plane'
2120 * based on the buffer line.
2122 if (tpg_g_interleaved(tpg
))
2123 p
= tpg_g_interleaved_plane(tpg
, buf_line
);
2125 if (tpg
->vdownsampling
[p
] > 1) {
2127 * When doing vertical downsampling the field setting
2128 * matters: for SEQ_BT/TB we downsample each field
2129 * separately (i.e. lines 0+2 are combined, as are
2130 * lines 1+3), for the other field settings we combine
2131 * odd and even lines. Doing that for SEQ_BT/TB would
2134 if (tpg
->field
== V4L2_FIELD_SEQ_BT
||
2135 tpg
->field
== V4L2_FIELD_SEQ_TB
) {
2136 unsigned next_src_y
= src_y
;
2140 next_src_y
+= int_part
;
2141 if (error
+ fract_part
>= tpg
->compose
.height
)
2143 params
.frame_line_next
=
2144 tpg_calc_frameline(tpg
, next_src_y
, tpg
->field
);
2148 params
.frame_line_next
=
2149 tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
2152 buf_line
/= tpg
->vdownsampling
[p
];
2154 tpg_fill_plane_pattern(tpg
, ¶ms
, p
, h
,
2155 vbuf
+ buf_line
* params
.stride
);
2156 tpg_fill_plane_extras(tpg
, ¶ms
, p
, h
,
2157 vbuf
+ buf_line
* params
.stride
);
2161 void tpg_fillbuffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
2163 unsigned offset
= 0;
2166 if (tpg
->buffers
> 1) {
2167 tpg_fill_plane_buffer(tpg
, std
, p
, vbuf
);
2171 for (i
= 0; i
< tpg_g_planes(tpg
); i
++) {
2172 tpg_fill_plane_buffer(tpg
, std
, i
, vbuf
+ offset
);
2173 offset
+= tpg_calc_plane_size(tpg
, i
);