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
? 1 : 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
? 1 : 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
->vdownsampling
[0] = 1;
185 tpg
->hdownsampling
[0] = 1;
188 case V4L2_PIX_FMT_RGB565
:
189 case V4L2_PIX_FMT_RGB565X
:
190 case V4L2_PIX_FMT_RGB555
:
191 case V4L2_PIX_FMT_XRGB555
:
192 case V4L2_PIX_FMT_ARGB555
:
193 case V4L2_PIX_FMT_RGB555X
:
194 case V4L2_PIX_FMT_RGB24
:
195 case V4L2_PIX_FMT_BGR24
:
196 case V4L2_PIX_FMT_RGB32
:
197 case V4L2_PIX_FMT_BGR32
:
198 case V4L2_PIX_FMT_XRGB32
:
199 case V4L2_PIX_FMT_XBGR32
:
200 case V4L2_PIX_FMT_ARGB32
:
201 case V4L2_PIX_FMT_ABGR32
:
204 case V4L2_PIX_FMT_NV16M
:
205 case V4L2_PIX_FMT_NV61M
:
206 tpg
->vdownsampling
[1] = 1;
207 tpg
->hdownsampling
[1] = 1;
211 case V4L2_PIX_FMT_YUYV
:
212 case V4L2_PIX_FMT_UYVY
:
213 case V4L2_PIX_FMT_YVYU
:
214 case V4L2_PIX_FMT_VYUY
:
222 case V4L2_PIX_FMT_RGB565
:
223 case V4L2_PIX_FMT_RGB565X
:
224 case V4L2_PIX_FMT_RGB555
:
225 case V4L2_PIX_FMT_XRGB555
:
226 case V4L2_PIX_FMT_ARGB555
:
227 case V4L2_PIX_FMT_RGB555X
:
228 case V4L2_PIX_FMT_YUYV
:
229 case V4L2_PIX_FMT_UYVY
:
230 case V4L2_PIX_FMT_YVYU
:
231 case V4L2_PIX_FMT_VYUY
:
232 tpg
->twopixelsize
[0] = 2 * 2;
234 case V4L2_PIX_FMT_RGB24
:
235 case V4L2_PIX_FMT_BGR24
:
236 tpg
->twopixelsize
[0] = 2 * 3;
238 case V4L2_PIX_FMT_RGB32
:
239 case V4L2_PIX_FMT_BGR32
:
240 case V4L2_PIX_FMT_XRGB32
:
241 case V4L2_PIX_FMT_XBGR32
:
242 case V4L2_PIX_FMT_ARGB32
:
243 case V4L2_PIX_FMT_ABGR32
:
244 tpg
->twopixelsize
[0] = 2 * 4;
246 case V4L2_PIX_FMT_NV16M
:
247 case V4L2_PIX_FMT_NV61M
:
248 tpg
->twopixelsize
[0] = 2;
249 tpg
->twopixelsize
[1] = 2;
255 void tpg_s_crop_compose(struct tpg_data
*tpg
, const struct v4l2_rect
*crop
,
256 const struct v4l2_rect
*compose
)
259 tpg
->compose
= *compose
;
260 tpg
->scaled_width
= (tpg
->src_width
* tpg
->compose
.width
+
261 tpg
->crop
.width
- 1) / tpg
->crop
.width
;
262 tpg
->scaled_width
&= ~1;
263 if (tpg
->scaled_width
> tpg
->max_line_width
)
264 tpg
->scaled_width
= tpg
->max_line_width
;
265 if (tpg
->scaled_width
< 2)
266 tpg
->scaled_width
= 2;
267 tpg
->recalc_lines
= true;
270 void tpg_reset_source(struct tpg_data
*tpg
, unsigned width
, unsigned height
,
275 tpg
->src_width
= width
;
276 tpg
->src_height
= height
;
278 tpg
->buf_height
= height
;
279 if (V4L2_FIELD_HAS_T_OR_B(field
))
280 tpg
->buf_height
/= 2;
281 tpg
->scaled_width
= width
;
282 tpg
->crop
.top
= tpg
->crop
.left
= 0;
283 tpg
->crop
.width
= width
;
284 tpg
->crop
.height
= height
;
285 tpg
->compose
.top
= tpg
->compose
.left
= 0;
286 tpg
->compose
.width
= width
;
287 tpg
->compose
.height
= tpg
->buf_height
;
288 for (p
= 0; p
< tpg
->planes
; p
++)
289 tpg
->bytesperline
[p
] = (width
* tpg
->twopixelsize
[p
]) /
290 (2 * tpg
->hdownsampling
[p
]);
291 tpg
->recalc_square_border
= true;
294 static enum tpg_color
tpg_get_textbg_color(struct tpg_data
*tpg
)
296 switch (tpg
->pattern
) {
298 return TPG_COLOR_100_WHITE
;
299 case TPG_PAT_CSC_COLORBAR
:
300 return TPG_COLOR_CSC_BLACK
;
302 return TPG_COLOR_100_BLACK
;
306 static enum tpg_color
tpg_get_textfg_color(struct tpg_data
*tpg
)
308 switch (tpg
->pattern
) {
309 case TPG_PAT_75_COLORBAR
:
310 case TPG_PAT_CSC_COLORBAR
:
311 return TPG_COLOR_CSC_WHITE
;
313 return TPG_COLOR_100_BLACK
;
315 return TPG_COLOR_100_WHITE
;
319 static inline int rec709_to_linear(int v
)
321 v
= clamp(v
, 0, 0xff0);
322 return tpg_rec709_to_linear
[v
];
325 static inline int linear_to_rec709(int v
)
327 v
= clamp(v
, 0, 0xff0);
328 return tpg_linear_to_rec709
[v
];
331 static void rgb2ycbcr(const int m
[3][3], int r
, int g
, int b
,
332 int y_offset
, int *y
, int *cb
, int *cr
)
334 *y
= ((m
[0][0] * r
+ m
[0][1] * g
+ m
[0][2] * b
) >> 16) + (y_offset
<< 4);
335 *cb
= ((m
[1][0] * r
+ m
[1][1] * g
+ m
[1][2] * b
) >> 16) + (128 << 4);
336 *cr
= ((m
[2][0] * r
+ m
[2][1] * g
+ m
[2][2] * b
) >> 16) + (128 << 4);
339 static void color_to_ycbcr(struct tpg_data
*tpg
, int r
, int g
, int b
,
340 int *y
, int *cb
, int *cr
)
342 #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
344 static const int bt601
[3][3] = {
345 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
346 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
347 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
349 static const int bt601_full
[3][3] = {
350 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
351 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
352 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
354 static const int rec709
[3][3] = {
355 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
356 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
357 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
359 static const int rec709_full
[3][3] = {
360 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
361 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
362 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
364 static const int smpte240m
[3][3] = {
365 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
366 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
367 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
369 static const int bt2020
[3][3] = {
370 { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
371 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
372 { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
374 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
375 unsigned y_offset
= full
? 0 : 16;
378 switch (tpg
->real_ycbcr_enc
) {
379 case V4L2_YCBCR_ENC_601
:
380 case V4L2_YCBCR_ENC_XV601
:
381 case V4L2_YCBCR_ENC_SYCC
:
382 rgb2ycbcr(full
? bt601_full
: bt601
, r
, g
, b
, y_offset
, y
, cb
, cr
);
384 case V4L2_YCBCR_ENC_BT2020
:
385 rgb2ycbcr(bt2020
, r
, g
, b
, 16, y
, cb
, cr
);
387 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
388 lin_y
= (COEFF(0.2627, 255) * rec709_to_linear(r
) +
389 COEFF(0.6780, 255) * rec709_to_linear(g
) +
390 COEFF(0.0593, 255) * rec709_to_linear(b
)) >> 16;
391 yc
= linear_to_rec709(lin_y
);
392 *y
= (yc
* 219) / 255 + (16 << 4);
394 *cb
= (((b
- yc
) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
396 *cb
= (((b
- yc
) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
398 *cr
= (((r
- yc
) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
400 *cr
= (((r
- yc
) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
402 case V4L2_YCBCR_ENC_SMPTE240M
:
403 rgb2ycbcr(smpte240m
, r
, g
, b
, 16, y
, cb
, cr
);
405 case V4L2_YCBCR_ENC_709
:
406 case V4L2_YCBCR_ENC_XV709
:
408 rgb2ycbcr(full
? rec709_full
: rec709
, r
, g
, b
, y_offset
, y
, cb
, cr
);
413 static void ycbcr2rgb(const int m
[3][3], int y
, int cb
, int cr
,
414 int y_offset
, int *r
, int *g
, int *b
)
419 *r
= m
[0][0] * y
+ m
[0][1] * cb
+ m
[0][2] * cr
;
420 *g
= m
[1][0] * y
+ m
[1][1] * cb
+ m
[1][2] * cr
;
421 *b
= m
[2][0] * y
+ m
[2][1] * cb
+ m
[2][2] * cr
;
422 *r
= clamp(*r
>> 12, 0, 0xff0);
423 *g
= clamp(*g
>> 12, 0, 0xff0);
424 *b
= clamp(*b
>> 12, 0, 0xff0);
427 static void ycbcr_to_color(struct tpg_data
*tpg
, int y
, int cb
, int cr
,
428 int *r
, int *g
, int *b
)
431 #define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
432 static const int bt601
[3][3] = {
433 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
434 { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
435 { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
437 static const int bt601_full
[3][3] = {
438 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
439 { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
440 { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
442 static const int rec709
[3][3] = {
443 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
444 { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
445 { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
447 static const int rec709_full
[3][3] = {
448 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
449 { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
450 { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
452 static const int smpte240m
[3][3] = {
453 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
454 { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
455 { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
457 static const int bt2020
[3][3] = {
458 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
459 { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
460 { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
462 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
463 unsigned y_offset
= full
? 0 : 16;
464 int lin_r
, lin_g
, lin_b
, lin_y
;
466 switch (tpg
->real_ycbcr_enc
) {
467 case V4L2_YCBCR_ENC_601
:
468 case V4L2_YCBCR_ENC_XV601
:
469 case V4L2_YCBCR_ENC_SYCC
:
470 ycbcr2rgb(full
? bt601_full
: bt601
, y
, cb
, cr
, y_offset
, r
, g
, b
);
472 case V4L2_YCBCR_ENC_BT2020
:
473 ycbcr2rgb(bt2020
, y
, cb
, cr
, 16, r
, g
, b
);
475 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
481 *b
= COEFF(1.0, 219) * y
+ COEFF(1.9404, 224) * cb
;
483 *b
= COEFF(1.0, 219) * y
+ COEFF(1.5816, 224) * cb
;
486 *r
= COEFF(1.0, 219) * y
+ COEFF(1.7184, 224) * cr
;
488 *r
= COEFF(1.0, 219) * y
+ COEFF(0.9936, 224) * cr
;
490 lin_r
= rec709_to_linear(*r
);
491 lin_b
= rec709_to_linear(*b
);
492 lin_y
= rec709_to_linear((y
* 255) / 219);
494 lin_g
= COEFF(1.0 / 0.6780, 255) * lin_y
-
495 COEFF(0.2627 / 0.6780, 255) * lin_r
-
496 COEFF(0.0593 / 0.6780, 255) * lin_b
;
497 *g
= linear_to_rec709(lin_g
>> 12);
499 case V4L2_YCBCR_ENC_SMPTE240M
:
500 ycbcr2rgb(smpte240m
, y
, cb
, cr
, 16, r
, g
, b
);
502 case V4L2_YCBCR_ENC_709
:
503 case V4L2_YCBCR_ENC_XV709
:
505 ycbcr2rgb(full
? rec709_full
: rec709
, y
, cb
, cr
, y_offset
, r
, g
, b
);
510 /* precalculate color bar values to speed up rendering */
511 static void precalculate_color(struct tpg_data
*tpg
, int k
)
514 int r
= tpg_colors
[col
].r
;
515 int g
= tpg_colors
[col
].g
;
516 int b
= tpg_colors
[col
].b
;
518 if (k
== TPG_COLOR_TEXTBG
) {
519 col
= tpg_get_textbg_color(tpg
);
521 r
= tpg_colors
[col
].r
;
522 g
= tpg_colors
[col
].g
;
523 b
= tpg_colors
[col
].b
;
524 } else if (k
== TPG_COLOR_TEXTFG
) {
525 col
= tpg_get_textfg_color(tpg
);
527 r
= tpg_colors
[col
].r
;
528 g
= tpg_colors
[col
].g
;
529 b
= tpg_colors
[col
].b
;
530 } else if (tpg
->pattern
== TPG_PAT_NOISE
) {
531 r
= g
= b
= prandom_u32_max(256);
532 } else if (k
== TPG_COLOR_RANDOM
) {
533 r
= g
= b
= tpg
->qual_offset
+ prandom_u32_max(196);
534 } else if (k
>= TPG_COLOR_RAMP
) {
535 r
= g
= b
= k
- TPG_COLOR_RAMP
;
538 if (tpg
->pattern
== TPG_PAT_CSC_COLORBAR
&& col
<= TPG_COLOR_CSC_BLACK
) {
539 r
= tpg_csc_colors
[tpg
->colorspace
][col
].r
;
540 g
= tpg_csc_colors
[tpg
->colorspace
][col
].g
;
541 b
= tpg_csc_colors
[tpg
->colorspace
][col
].b
;
547 if (tpg
->qual
== TPG_QUAL_GRAY
) {
548 /* Rec. 709 Luma function */
549 /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
550 r
= g
= b
= (13879 * r
+ 46688 * g
+ 4713 * b
) >> 16;
554 * The assumption is that the RGB output is always full range,
555 * so only if the rgb_range overrides the 'real' rgb range do
556 * we need to convert the RGB values.
558 * Remember that r, g and b are still in the 0 - 0xff0 range.
560 if (tpg
->real_rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
&&
561 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_FULL
) {
563 * Convert from full range (which is what r, g and b are)
564 * to limited range (which is the 'real' RGB range), which
565 * is then interpreted as full range.
567 r
= (r
* 219) / 255 + (16 << 4);
568 g
= (g
* 219) / 255 + (16 << 4);
569 b
= (b
* 219) / 255 + (16 << 4);
570 } else if (tpg
->real_rgb_range
!= V4L2_DV_RGB_RANGE_LIMITED
&&
571 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
) {
573 * Clamp r, g and b to the limited range and convert to full
574 * range since that's what we deliver.
576 r
= clamp(r
, 16 << 4, 235 << 4);
577 g
= clamp(g
, 16 << 4, 235 << 4);
578 b
= clamp(b
, 16 << 4, 235 << 4);
579 r
= (r
- (16 << 4)) * 255 / 219;
580 g
= (g
- (16 << 4)) * 255 / 219;
581 b
= (b
- (16 << 4)) * 255 / 219;
584 if (tpg
->brightness
!= 128 || tpg
->contrast
!= 128 ||
585 tpg
->saturation
!= 128 || tpg
->hue
) {
586 /* Implement these operations */
590 /* First convert to YCbCr */
592 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
594 y
= (16 << 4) + ((y
- (16 << 4)) * tpg
->contrast
) / 128;
595 y
+= (tpg
->brightness
<< 4) - (128 << 4);
599 tmp_cb
= (cb
* cos(128 + tpg
->hue
)) / 127 + (cr
* sin
[128 + tpg
->hue
]) / 127;
600 tmp_cr
= (cr
* cos(128 + tpg
->hue
)) / 127 - (cb
* sin
[128 + tpg
->hue
]) / 127;
602 cb
= (128 << 4) + (tmp_cb
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
603 cr
= (128 << 4) + (tmp_cr
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
605 tpg
->colors
[k
][0] = clamp(y
>> 4, 1, 254);
606 tpg
->colors
[k
][1] = clamp(cb
>> 4, 1, 254);
607 tpg
->colors
[k
][2] = clamp(cr
>> 4, 1, 254);
610 ycbcr_to_color(tpg
, y
, cb
, cr
, &r
, &g
, &b
);
614 /* Convert to YCbCr */
617 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
619 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
620 y
= clamp(y
, 16 << 4, 235 << 4);
621 cb
= clamp(cb
, 16 << 4, 240 << 4);
622 cr
= clamp(cr
, 16 << 4, 240 << 4);
624 tpg
->colors
[k
][0] = clamp(y
>> 4, 1, 254);
625 tpg
->colors
[k
][1] = clamp(cb
>> 4, 1, 254);
626 tpg
->colors
[k
][2] = clamp(cr
>> 4, 1, 254);
628 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
629 r
= (r
* 219) / 255 + (16 << 4);
630 g
= (g
* 219) / 255 + (16 << 4);
631 b
= (b
* 219) / 255 + (16 << 4);
633 switch (tpg
->fourcc
) {
634 case V4L2_PIX_FMT_RGB565
:
635 case V4L2_PIX_FMT_RGB565X
:
640 case V4L2_PIX_FMT_RGB555
:
641 case V4L2_PIX_FMT_XRGB555
:
642 case V4L2_PIX_FMT_ARGB555
:
643 case V4L2_PIX_FMT_RGB555X
:
655 tpg
->colors
[k
][0] = r
;
656 tpg
->colors
[k
][1] = g
;
657 tpg
->colors
[k
][2] = b
;
661 static void tpg_precalculate_colors(struct tpg_data
*tpg
)
665 for (k
= 0; k
< TPG_COLOR_MAX
; k
++)
666 precalculate_color(tpg
, k
);
669 /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
670 static void gen_twopix(struct tpg_data
*tpg
,
671 u8 buf
[TPG_MAX_PLANES
][8], int color
, bool odd
)
673 unsigned offset
= odd
* tpg
->twopixelsize
[0] / 2;
674 u8 alpha
= tpg
->alpha_component
;
677 if (tpg
->alpha_red_only
&& color
!= TPG_COLOR_CSC_RED
&&
678 color
!= TPG_COLOR_100_RED
&&
679 color
!= TPG_COLOR_75_RED
)
681 if (color
== TPG_COLOR_RANDOM
)
682 precalculate_color(tpg
, color
);
683 r_y
= tpg
->colors
[color
][0]; /* R or precalculated Y */
684 g_u
= tpg
->colors
[color
][1]; /* G or precalculated U */
685 b_v
= tpg
->colors
[color
][2]; /* B or precalculated V */
687 switch (tpg
->fourcc
) {
688 case V4L2_PIX_FMT_NV16M
:
689 buf
[0][offset
] = r_y
;
690 buf
[1][offset
] = odd
? b_v
: g_u
;
692 case V4L2_PIX_FMT_NV61M
:
693 buf
[0][offset
] = r_y
;
694 buf
[1][offset
] = odd
? g_u
: b_v
;
697 case V4L2_PIX_FMT_YUYV
:
698 buf
[0][offset
] = r_y
;
699 buf
[0][offset
+ 1] = odd
? b_v
: g_u
;
701 case V4L2_PIX_FMT_UYVY
:
702 buf
[0][offset
] = odd
? b_v
: g_u
;
703 buf
[0][offset
+ 1] = r_y
;
705 case V4L2_PIX_FMT_YVYU
:
706 buf
[0][offset
] = r_y
;
707 buf
[0][offset
+ 1] = odd
? g_u
: b_v
;
709 case V4L2_PIX_FMT_VYUY
:
710 buf
[0][offset
] = odd
? g_u
: b_v
;
711 buf
[0][offset
+ 1] = r_y
;
713 case V4L2_PIX_FMT_RGB565
:
714 buf
[0][offset
] = (g_u
<< 5) | b_v
;
715 buf
[0][offset
+ 1] = (r_y
<< 3) | (g_u
>> 3);
717 case V4L2_PIX_FMT_RGB565X
:
718 buf
[0][offset
] = (r_y
<< 3) | (g_u
>> 3);
719 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
721 case V4L2_PIX_FMT_RGB555
:
722 case V4L2_PIX_FMT_XRGB555
:
725 case V4L2_PIX_FMT_ARGB555
:
726 buf
[0][offset
] = (g_u
<< 5) | b_v
;
727 buf
[0][offset
+ 1] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
729 case V4L2_PIX_FMT_RGB555X
:
730 buf
[0][offset
] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
731 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
733 case V4L2_PIX_FMT_RGB24
:
734 buf
[0][offset
] = r_y
;
735 buf
[0][offset
+ 1] = g_u
;
736 buf
[0][offset
+ 2] = b_v
;
738 case V4L2_PIX_FMT_BGR24
:
739 buf
[0][offset
] = b_v
;
740 buf
[0][offset
+ 1] = g_u
;
741 buf
[0][offset
+ 2] = r_y
;
743 case V4L2_PIX_FMT_RGB32
:
744 case V4L2_PIX_FMT_XRGB32
:
747 case V4L2_PIX_FMT_ARGB32
:
748 buf
[0][offset
] = alpha
;
749 buf
[0][offset
+ 1] = r_y
;
750 buf
[0][offset
+ 2] = g_u
;
751 buf
[0][offset
+ 3] = b_v
;
753 case V4L2_PIX_FMT_BGR32
:
754 case V4L2_PIX_FMT_XBGR32
:
757 case V4L2_PIX_FMT_ABGR32
:
758 buf
[0][offset
] = b_v
;
759 buf
[0][offset
+ 1] = g_u
;
760 buf
[0][offset
+ 2] = r_y
;
761 buf
[0][offset
+ 3] = alpha
;
766 /* Return how many pattern lines are used by the current pattern. */
767 static unsigned tpg_get_pat_lines(const struct tpg_data
*tpg
)
769 switch (tpg
->pattern
) {
770 case TPG_PAT_CHECKERS_16X16
:
771 case TPG_PAT_CHECKERS_2X2
:
772 case TPG_PAT_CHECKERS_1X1
:
773 case TPG_PAT_COLOR_CHECKERS_2X2
:
774 case TPG_PAT_COLOR_CHECKERS_1X1
:
775 case TPG_PAT_ALTERNATING_HLINES
:
776 case TPG_PAT_CROSS_1_PIXEL
:
777 case TPG_PAT_CROSS_2_PIXELS
:
778 case TPG_PAT_CROSS_10_PIXELS
:
780 case TPG_PAT_100_COLORSQUARES
:
781 case TPG_PAT_100_HCOLORBAR
:
788 /* Which pattern line should be used for the given frame line. */
789 static unsigned tpg_get_pat_line(const struct tpg_data
*tpg
, unsigned line
)
791 switch (tpg
->pattern
) {
792 case TPG_PAT_CHECKERS_16X16
:
793 return (line
>> 4) & 1;
794 case TPG_PAT_CHECKERS_1X1
:
795 case TPG_PAT_COLOR_CHECKERS_1X1
:
796 case TPG_PAT_ALTERNATING_HLINES
:
798 case TPG_PAT_CHECKERS_2X2
:
799 case TPG_PAT_COLOR_CHECKERS_2X2
:
800 return (line
& 2) >> 1;
801 case TPG_PAT_100_COLORSQUARES
:
802 case TPG_PAT_100_HCOLORBAR
:
803 return (line
* 8) / tpg
->src_height
;
804 case TPG_PAT_CROSS_1_PIXEL
:
805 return line
== tpg
->src_height
/ 2;
806 case TPG_PAT_CROSS_2_PIXELS
:
807 return (line
+ 1) / 2 == tpg
->src_height
/ 4;
808 case TPG_PAT_CROSS_10_PIXELS
:
809 return (line
+ 10) / 20 == tpg
->src_height
/ 40;
816 * Which color should be used for the given pattern line and X coordinate.
817 * Note: x is in the range 0 to 2 * tpg->src_width.
819 static enum tpg_color
tpg_get_color(const struct tpg_data
*tpg
,
820 unsigned pat_line
, unsigned x
)
822 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
823 should be modified */
824 static const enum tpg_color bars
[3][8] = {
825 /* Standard ITU-R 75% color bar sequence */
826 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_75_YELLOW
,
827 TPG_COLOR_75_CYAN
, TPG_COLOR_75_GREEN
,
828 TPG_COLOR_75_MAGENTA
, TPG_COLOR_75_RED
,
829 TPG_COLOR_75_BLUE
, TPG_COLOR_100_BLACK
, },
830 /* Standard ITU-R 100% color bar sequence */
831 { TPG_COLOR_100_WHITE
, TPG_COLOR_100_YELLOW
,
832 TPG_COLOR_100_CYAN
, TPG_COLOR_100_GREEN
,
833 TPG_COLOR_100_MAGENTA
, TPG_COLOR_100_RED
,
834 TPG_COLOR_100_BLUE
, TPG_COLOR_100_BLACK
, },
835 /* Color bar sequence suitable to test CSC */
836 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_CSC_YELLOW
,
837 TPG_COLOR_CSC_CYAN
, TPG_COLOR_CSC_GREEN
,
838 TPG_COLOR_CSC_MAGENTA
, TPG_COLOR_CSC_RED
,
839 TPG_COLOR_CSC_BLUE
, TPG_COLOR_CSC_BLACK
, },
842 switch (tpg
->pattern
) {
843 case TPG_PAT_75_COLORBAR
:
844 case TPG_PAT_100_COLORBAR
:
845 case TPG_PAT_CSC_COLORBAR
:
846 return bars
[tpg
->pattern
][((x
* 8) / tpg
->src_width
) % 8];
847 case TPG_PAT_100_COLORSQUARES
:
848 return bars
[1][(pat_line
+ (x
* 8) / tpg
->src_width
) % 8];
849 case TPG_PAT_100_HCOLORBAR
:
850 return bars
[1][pat_line
];
852 return TPG_COLOR_100_BLACK
;
854 return TPG_COLOR_100_WHITE
;
856 return TPG_COLOR_100_RED
;
858 return TPG_COLOR_100_GREEN
;
860 return TPG_COLOR_100_BLUE
;
861 case TPG_PAT_CHECKERS_16X16
:
862 return (((x
>> 4) & 1) ^ (pat_line
& 1)) ?
863 TPG_COLOR_100_BLACK
: TPG_COLOR_100_WHITE
;
864 case TPG_PAT_CHECKERS_1X1
:
865 return ((x
& 1) ^ (pat_line
& 1)) ?
866 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
867 case TPG_PAT_COLOR_CHECKERS_1X1
:
868 return ((x
& 1) ^ (pat_line
& 1)) ?
869 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
870 case TPG_PAT_CHECKERS_2X2
:
871 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
872 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
873 case TPG_PAT_COLOR_CHECKERS_2X2
:
874 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
875 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
876 case TPG_PAT_ALTERNATING_HLINES
:
877 return pat_line
? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
878 case TPG_PAT_ALTERNATING_VLINES
:
879 return (x
& 1) ? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
880 case TPG_PAT_CROSS_1_PIXEL
:
881 if (pat_line
|| (x
% tpg
->src_width
) == tpg
->src_width
/ 2)
882 return TPG_COLOR_100_BLACK
;
883 return TPG_COLOR_100_WHITE
;
884 case TPG_PAT_CROSS_2_PIXELS
:
885 if (pat_line
|| ((x
% tpg
->src_width
) + 1) / 2 == tpg
->src_width
/ 4)
886 return TPG_COLOR_100_BLACK
;
887 return TPG_COLOR_100_WHITE
;
888 case TPG_PAT_CROSS_10_PIXELS
:
889 if (pat_line
|| ((x
% tpg
->src_width
) + 10) / 20 == tpg
->src_width
/ 40)
890 return TPG_COLOR_100_BLACK
;
891 return TPG_COLOR_100_WHITE
;
892 case TPG_PAT_GRAY_RAMP
:
893 return TPG_COLOR_RAMP
+ ((x
% tpg
->src_width
) * 256) / tpg
->src_width
;
895 return TPG_COLOR_100_RED
;
900 * Given the pixel aspect ratio and video aspect ratio calculate the
901 * coordinates of a centered square and the coordinates of the border of
902 * the active video area. The coordinates are relative to the source
905 static void tpg_calculate_square_border(struct tpg_data
*tpg
)
907 unsigned w
= tpg
->src_width
;
908 unsigned h
= tpg
->src_height
;
911 sq_w
= (w
* 2 / 5) & ~1;
912 if (((w
- sq_w
) / 2) & 1)
915 tpg
->square
.width
= sq_w
;
916 if (tpg
->vid_aspect
== TPG_VIDEO_ASPECT_16X9_ANAMORPHIC
) {
917 unsigned ana_sq_w
= (sq_w
/ 4) * 3;
919 if (((w
- ana_sq_w
) / 2) & 1)
921 tpg
->square
.width
= ana_sq_w
;
923 tpg
->square
.left
= (w
- tpg
->square
.width
) / 2;
924 if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
)
925 sq_h
= sq_w
* 10 / 11;
926 else if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_PAL
)
927 sq_h
= sq_w
* 59 / 54;
928 tpg
->square
.height
= sq_h
;
929 tpg
->square
.top
= (h
- sq_h
) / 2;
930 tpg
->border
.left
= 0;
931 tpg
->border
.width
= w
;
933 tpg
->border
.height
= h
;
934 switch (tpg
->vid_aspect
) {
935 case TPG_VIDEO_ASPECT_4X3
:
938 if (3 * w
>= 4 * h
) {
939 tpg
->border
.width
= ((4 * h
) / 3) & ~1;
940 if (((w
- tpg
->border
.width
) / 2) & ~1)
941 tpg
->border
.width
-= 2;
942 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
945 tpg
->border
.height
= ((3 * w
) / 4) & ~1;
946 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
948 case TPG_VIDEO_ASPECT_14X9_CENTRE
:
949 if (tpg
->pix_aspect
) {
950 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 420 : 506;
951 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
954 if (9 * w
>= 14 * h
) {
955 tpg
->border
.width
= ((14 * h
) / 9) & ~1;
956 if (((w
- tpg
->border
.width
) / 2) & ~1)
957 tpg
->border
.width
-= 2;
958 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
961 tpg
->border
.height
= ((9 * w
) / 14) & ~1;
962 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
964 case TPG_VIDEO_ASPECT_16X9_CENTRE
:
965 if (tpg
->pix_aspect
) {
966 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 368 : 442;
967 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
970 if (9 * w
>= 16 * h
) {
971 tpg
->border
.width
= ((16 * h
) / 9) & ~1;
972 if (((w
- tpg
->border
.width
) / 2) & ~1)
973 tpg
->border
.width
-= 2;
974 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
977 tpg
->border
.height
= ((9 * w
) / 16) & ~1;
978 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
985 static void tpg_precalculate_line(struct tpg_data
*tpg
)
987 enum tpg_color contrast
;
992 switch (tpg
->pattern
) {
994 contrast
= TPG_COLOR_100_RED
;
996 case TPG_PAT_CSC_COLORBAR
:
997 contrast
= TPG_COLOR_CSC_GREEN
;
1000 contrast
= TPG_COLOR_100_GREEN
;
1004 for (pat
= 0; pat
< tpg_get_pat_lines(tpg
); pat
++) {
1005 /* Coarse scaling with Bresenham */
1006 unsigned int_part
= tpg
->src_width
/ tpg
->scaled_width
;
1007 unsigned fract_part
= tpg
->src_width
% tpg
->scaled_width
;
1011 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1012 unsigned real_x
= src_x
;
1013 enum tpg_color color1
, color2
;
1014 u8 pix
[TPG_MAX_PLANES
][8];
1016 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1017 color1
= tpg_get_color(tpg
, pat
, real_x
);
1020 error
+= fract_part
;
1021 if (error
>= tpg
->scaled_width
) {
1022 error
-= tpg
->scaled_width
;
1027 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1028 color2
= tpg_get_color(tpg
, pat
, real_x
);
1031 error
+= fract_part
;
1032 if (error
>= tpg
->scaled_width
) {
1033 error
-= tpg
->scaled_width
;
1037 gen_twopix(tpg
, pix
, tpg
->hflip
? color2
: color1
, 0);
1038 gen_twopix(tpg
, pix
, tpg
->hflip
? color1
: color2
, 1);
1039 for (p
= 0; p
< tpg
->planes
; p
++) {
1040 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1041 unsigned hdiv
= tpg
->hdownsampling
[p
];
1042 u8
*pos
= tpg
->lines
[pat
][p
] +
1043 (x
/ hdiv
) * twopixsize
/ 2;
1045 memcpy(pos
, pix
[p
], twopixsize
/ hdiv
);
1050 if (tpg
->vdownsampling
[tpg
->planes
- 1] > 1) {
1051 unsigned pat_lines
= tpg_get_pat_lines(tpg
);
1053 for (pat
= 0; pat
< pat_lines
; pat
++) {
1054 unsigned next_pat
= (pat
+ 1) % pat_lines
;
1056 for (p
= 1; p
< tpg
->planes
; p
++) {
1057 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1058 unsigned hdiv
= tpg
->hdownsampling
[p
];
1060 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1061 unsigned offset
= (x
/ hdiv
) * twopixsize
/ 2;
1062 u8
*pos1
= tpg
->lines
[pat
][p
] + offset
;
1063 u8
*pos2
= tpg
->lines
[next_pat
][p
] + offset
;
1064 u8
*dest
= tpg
->downsampled_lines
[pat
][p
] + offset
;
1067 for (i
= 0; i
< twopixsize
/ hdiv
; i
++, dest
++, pos1
++, pos2
++)
1068 *dest
= ((u16
)*pos1
+ (u16
)*pos2
) / 2;
1074 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1075 u8 pix
[TPG_MAX_PLANES
][8];
1077 gen_twopix(tpg
, pix
, contrast
, 0);
1078 gen_twopix(tpg
, pix
, contrast
, 1);
1079 for (p
= 0; p
< tpg
->planes
; p
++) {
1080 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1081 u8
*pos
= tpg
->contrast_line
[p
] + x
* twopixsize
/ 2;
1083 memcpy(pos
, pix
[p
], twopixsize
);
1086 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1087 u8 pix
[TPG_MAX_PLANES
][8];
1089 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 0);
1090 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 1);
1091 for (p
= 0; p
< tpg
->planes
; p
++) {
1092 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1093 u8
*pos
= tpg
->black_line
[p
] + x
* twopixsize
/ 2;
1095 memcpy(pos
, pix
[p
], twopixsize
);
1098 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1099 u8 pix
[TPG_MAX_PLANES
][8];
1101 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 0);
1102 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 1);
1103 for (p
= 0; p
< tpg
->planes
; p
++) {
1104 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1105 u8
*pos
= tpg
->random_line
[p
] + x
* twopixsize
/ 2;
1107 memcpy(pos
, pix
[p
], twopixsize
);
1110 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 0);
1111 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 1);
1112 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 0);
1113 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 1);
1116 /* need this to do rgb24 rendering */
1117 typedef struct { u16 __
; u8 _
; } __packed x24
;
1119 void tpg_gen_text(struct tpg_data
*tpg
, u8
*basep
[TPG_MAX_PLANES
][2],
1120 int y
, int x
, char *text
)
1123 unsigned step
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1124 unsigned div
= step
;
1126 unsigned len
= strlen(text
);
1129 if (font8x16
== NULL
|| basep
== NULL
)
1132 /* Checks if it is possible to show string */
1133 if (y
+ 16 >= tpg
->compose
.height
|| x
+ 8 >= tpg
->compose
.width
)
1136 if (len
> (tpg
->compose
.width
- x
) / 8)
1137 len
= (tpg
->compose
.width
- x
) / 8;
1139 y
= tpg
->compose
.height
- y
- 16;
1141 x
= tpg
->compose
.width
- x
- 8;
1142 y
+= tpg
->compose
.top
;
1143 x
+= tpg
->compose
.left
;
1144 if (tpg
->field
== V4L2_FIELD_BOTTOM
)
1146 else if (tpg
->field
== V4L2_FIELD_SEQ_TB
|| tpg
->field
== V4L2_FIELD_SEQ_BT
)
1149 for (p
= 0; p
< tpg
->planes
; p
++) {
1150 /* Print stream time */
1151 #define PRINTSTR(PIXTYPE) do { \
1154 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1155 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1157 for (line = first; line < 16; line += step) { \
1158 int l = tpg->vflip ? 15 - line : line; \
1159 PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
1160 ((y * step + l) / div) * tpg->bytesperline[p] + \
1161 x * sizeof(PIXTYPE)); \
1164 for (s = 0; s < len; s++) { \
1165 u8 chr = font8x16[text[s] * 16 + line]; \
1168 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1169 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1170 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1171 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1172 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1173 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1174 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1175 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1177 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1178 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1179 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1180 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1181 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1182 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1183 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1184 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1187 pos += tpg->hflip ? -8 : 8; \
1192 switch (tpg
->twopixelsize
[p
]) {
1194 PRINTSTR(u8
); break;
1196 PRINTSTR(u16
); break;
1198 PRINTSTR(x24
); break;
1200 PRINTSTR(u32
); break;
1205 void tpg_update_mv_step(struct tpg_data
*tpg
)
1207 int factor
= tpg
->mv_hor_mode
> TPG_MOVE_NONE
? -1 : 1;
1211 switch (tpg
->mv_hor_mode
) {
1212 case TPG_MOVE_NEG_FAST
:
1213 case TPG_MOVE_POS_FAST
:
1214 tpg
->mv_hor_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1218 tpg
->mv_hor_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1220 case TPG_MOVE_NEG_SLOW
:
1221 case TPG_MOVE_POS_SLOW
:
1222 tpg
->mv_hor_step
= 2;
1225 tpg
->mv_hor_step
= 0;
1229 tpg
->mv_hor_step
= tpg
->src_width
- tpg
->mv_hor_step
;
1231 factor
= tpg
->mv_vert_mode
> TPG_MOVE_NONE
? -1 : 1;
1232 switch (tpg
->mv_vert_mode
) {
1233 case TPG_MOVE_NEG_FAST
:
1234 case TPG_MOVE_POS_FAST
:
1235 tpg
->mv_vert_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1239 tpg
->mv_vert_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1241 case TPG_MOVE_NEG_SLOW
:
1242 case TPG_MOVE_POS_SLOW
:
1243 tpg
->mv_vert_step
= 1;
1246 tpg
->mv_vert_step
= 0;
1250 tpg
->mv_vert_step
= tpg
->src_height
- tpg
->mv_vert_step
;
1253 /* Map the line number relative to the crop rectangle to a frame line number */
1254 static unsigned tpg_calc_frameline(struct tpg_data
*tpg
, unsigned src_y
,
1258 case V4L2_FIELD_TOP
:
1259 return tpg
->crop
.top
+ src_y
* 2;
1260 case V4L2_FIELD_BOTTOM
:
1261 return tpg
->crop
.top
+ src_y
* 2 + 1;
1263 return src_y
+ tpg
->crop
.top
;
1268 * Map the line number relative to the compose rectangle to a destination
1269 * buffer line number.
1271 static unsigned tpg_calc_buffer_line(struct tpg_data
*tpg
, unsigned y
,
1274 y
+= tpg
->compose
.top
;
1276 case V4L2_FIELD_SEQ_TB
:
1278 return tpg
->buf_height
/ 2 + y
/ 2;
1280 case V4L2_FIELD_SEQ_BT
:
1283 return tpg
->buf_height
/ 2 + y
/ 2;
1289 static void tpg_recalc(struct tpg_data
*tpg
)
1291 if (tpg
->recalc_colors
) {
1292 tpg
->recalc_colors
= false;
1293 tpg
->recalc_lines
= true;
1294 tpg
->real_ycbcr_enc
= tpg
->ycbcr_enc
;
1295 tpg
->real_quantization
= tpg
->quantization
;
1296 if (tpg
->ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
) {
1297 switch (tpg
->colorspace
) {
1298 case V4L2_COLORSPACE_REC709
:
1299 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_709
;
1301 case V4L2_COLORSPACE_SRGB
:
1302 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SYCC
;
1304 case V4L2_COLORSPACE_BT2020
:
1305 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_BT2020
;
1307 case V4L2_COLORSPACE_SMPTE240M
:
1308 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SMPTE240M
;
1310 case V4L2_COLORSPACE_SMPTE170M
:
1311 case V4L2_COLORSPACE_470_SYSTEM_M
:
1312 case V4L2_COLORSPACE_470_SYSTEM_BG
:
1313 case V4L2_COLORSPACE_ADOBERGB
:
1315 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
1319 if (tpg
->quantization
== V4L2_QUANTIZATION_DEFAULT
) {
1320 tpg
->real_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
1322 switch (tpg
->real_ycbcr_enc
) {
1323 case V4L2_YCBCR_ENC_SYCC
:
1324 case V4L2_YCBCR_ENC_XV601
:
1325 case V4L2_YCBCR_ENC_XV709
:
1328 tpg
->real_quantization
=
1329 V4L2_QUANTIZATION_LIM_RANGE
;
1332 } else if (tpg
->colorspace
== V4L2_COLORSPACE_BT2020
) {
1333 /* R'G'B' BT.2020 is limited range */
1334 tpg
->real_quantization
=
1335 V4L2_QUANTIZATION_LIM_RANGE
;
1338 tpg_precalculate_colors(tpg
);
1340 if (tpg
->recalc_square_border
) {
1341 tpg
->recalc_square_border
= false;
1342 tpg_calculate_square_border(tpg
);
1344 if (tpg
->recalc_lines
) {
1345 tpg
->recalc_lines
= false;
1346 tpg_precalculate_line(tpg
);
1350 void tpg_calc_text_basep(struct tpg_data
*tpg
,
1351 u8
*basep
[TPG_MAX_PLANES
][2], unsigned p
, u8
*vbuf
)
1353 unsigned stride
= tpg
->bytesperline
[p
];
1359 if (tpg
->field
== V4L2_FIELD_SEQ_TB
)
1360 basep
[p
][1] += tpg
->buf_height
* stride
/ 2;
1361 else if (tpg
->field
== V4L2_FIELD_SEQ_BT
)
1362 basep
[p
][0] += tpg
->buf_height
* stride
/ 2;
1365 void tpg_fill_plane_buffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
1368 bool is_60hz
= is_tv
&& (std
& V4L2_STD_525_60
);
1369 unsigned mv_hor_old
= tpg
->mv_hor_count
% tpg
->src_width
;
1370 unsigned mv_hor_new
= (tpg
->mv_hor_count
+ tpg
->mv_hor_step
) % tpg
->src_width
;
1371 unsigned mv_vert_old
= tpg
->mv_vert_count
% tpg
->src_height
;
1372 unsigned mv_vert_new
= (tpg
->mv_vert_count
+ tpg
->mv_vert_step
) % tpg
->src_height
;
1375 int hmax
= (tpg
->compose
.height
* tpg
->perc_fill
) / 100;
1377 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1378 unsigned img_width
= tpg
->compose
.width
* twopixsize
/ 2;
1379 unsigned line_offset
;
1380 unsigned left_pillar_width
= 0;
1381 unsigned right_pillar_start
= img_width
;
1382 unsigned stride
= tpg
->bytesperline
[p
];
1383 unsigned factor
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1384 u8
*orig_vbuf
= vbuf
;
1386 /* Coarse scaling with Bresenham */
1387 unsigned int_part
= (tpg
->crop
.height
/ factor
) / tpg
->compose
.height
;
1388 unsigned fract_part
= (tpg
->crop
.height
/ factor
) % tpg
->compose
.height
;
1394 mv_hor_old
= (mv_hor_old
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1395 mv_hor_new
= (mv_hor_new
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1396 wss_width
= tpg
->crop
.left
< tpg
->src_width
/ 2 ?
1397 tpg
->src_width
/ 2 - tpg
->crop
.left
: 0;
1398 if (wss_width
> tpg
->crop
.width
)
1399 wss_width
= tpg
->crop
.width
;
1400 wss_width
= wss_width
* tpg
->scaled_width
/ tpg
->src_width
;
1402 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1403 line_offset
= tpg
->crop
.left
* tpg
->scaled_width
/ tpg
->src_width
;
1404 line_offset
= (line_offset
& ~1) * twopixsize
/ 2;
1405 if (tpg
->crop
.left
< tpg
->border
.left
) {
1406 left_pillar_width
= tpg
->border
.left
- tpg
->crop
.left
;
1407 if (left_pillar_width
> tpg
->crop
.width
)
1408 left_pillar_width
= tpg
->crop
.width
;
1409 left_pillar_width
= (left_pillar_width
* tpg
->scaled_width
) / tpg
->src_width
;
1410 left_pillar_width
= (left_pillar_width
& ~1) * twopixsize
/ 2;
1412 if (tpg
->crop
.left
+ tpg
->crop
.width
> tpg
->border
.left
+ tpg
->border
.width
) {
1413 right_pillar_start
= tpg
->border
.left
+ tpg
->border
.width
- tpg
->crop
.left
;
1414 right_pillar_start
= (right_pillar_start
* tpg
->scaled_width
) / tpg
->src_width
;
1415 right_pillar_start
= (right_pillar_start
& ~1) * twopixsize
/ 2;
1416 if (right_pillar_start
> img_width
)
1417 right_pillar_start
= img_width
;
1420 f
= tpg
->field
== (is_60hz
? V4L2_FIELD_TOP
: V4L2_FIELD_BOTTOM
);
1422 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1424 bool fill_blank
= false;
1425 unsigned frame_line
;
1427 unsigned pat_line_old
;
1428 unsigned pat_line_new
;
1429 u8
*linestart_older
;
1430 u8
*linestart_newer
;
1432 u8
*linestart_bottom
;
1434 frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1435 even
= !(frame_line
& 1);
1436 buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1438 error
+= fract_part
;
1439 if (error
>= tpg
->compose
.height
) {
1440 error
-= tpg
->compose
.height
;
1445 if (hmax
== tpg
->compose
.height
)
1447 if (!tpg
->perc_fill_blank
)
1453 frame_line
= tpg
->src_height
- frame_line
- 1;
1456 linestart_older
= tpg
->contrast_line
[p
];
1457 linestart_newer
= tpg
->contrast_line
[p
];
1458 } else if (tpg
->qual
!= TPG_QUAL_NOISE
&&
1459 (frame_line
< tpg
->border
.top
||
1460 frame_line
>= tpg
->border
.top
+ tpg
->border
.height
)) {
1461 linestart_older
= tpg
->black_line
[p
];
1462 linestart_newer
= tpg
->black_line
[p
];
1463 } else if (tpg
->pattern
== TPG_PAT_NOISE
|| tpg
->qual
== TPG_QUAL_NOISE
) {
1464 linestart_older
= tpg
->random_line
[p
] +
1465 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1466 linestart_newer
= tpg
->random_line
[p
] +
1467 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1469 pat_line_old
= tpg_get_pat_line(tpg
,
1470 (frame_line
+ mv_vert_old
) % tpg
->src_height
);
1471 pat_line_new
= tpg_get_pat_line(tpg
,
1472 (frame_line
+ mv_vert_new
) % tpg
->src_height
);
1473 linestart_older
= tpg
->lines
[pat_line_old
][p
] +
1474 mv_hor_old
* twopixsize
/ 2;
1475 linestart_newer
= tpg
->lines
[pat_line_new
][p
] +
1476 mv_hor_new
* twopixsize
/ 2;
1477 linestart_older
+= line_offset
;
1478 linestart_newer
+= line_offset
;
1480 if (tpg
->field_alternate
) {
1481 linestart_top
= linestart_bottom
= linestart_older
;
1482 } else if (is_60hz
) {
1483 linestart_top
= linestart_newer
;
1484 linestart_bottom
= linestart_older
;
1486 linestart_top
= linestart_older
;
1487 linestart_bottom
= linestart_newer
;
1490 switch (tpg
->field
) {
1491 case V4L2_FIELD_INTERLACED
:
1492 case V4L2_FIELD_INTERLACED_TB
:
1493 case V4L2_FIELD_SEQ_TB
:
1494 case V4L2_FIELD_SEQ_BT
:
1496 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1498 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1500 case V4L2_FIELD_INTERLACED_BT
:
1502 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1504 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1506 case V4L2_FIELD_TOP
:
1507 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1509 case V4L2_FIELD_BOTTOM
:
1510 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1512 case V4L2_FIELD_NONE
:
1514 memcpy(vbuf
+ buf_line
* stride
, linestart_older
, img_width
);
1518 if (is_tv
&& !is_60hz
&& frame_line
== 0 && wss_width
) {
1520 * Replace the first half of the top line of a 50 Hz frame
1521 * with random data to simulate a WSS signal.
1523 u8
*wss
= tpg
->random_line
[p
] +
1524 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1526 memcpy(vbuf
+ buf_line
* stride
, wss
, wss_width
* twopixsize
/ 2);
1531 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1534 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1535 unsigned frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1536 unsigned buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1537 const struct v4l2_rect
*sq
= &tpg
->square
;
1538 const struct v4l2_rect
*b
= &tpg
->border
;
1539 const struct v4l2_rect
*c
= &tpg
->crop
;
1542 error
+= fract_part
;
1543 if (error
>= tpg
->compose
.height
) {
1544 error
-= tpg
->compose
.height
;
1548 if (tpg
->show_border
&& frame_line
>= b
->top
&&
1549 frame_line
< b
->top
+ b
->height
) {
1550 unsigned bottom
= b
->top
+ b
->height
- 1;
1551 unsigned left
= left_pillar_width
;
1552 unsigned right
= right_pillar_start
;
1554 if (frame_line
== b
->top
|| frame_line
== b
->top
+ 1 ||
1555 frame_line
== bottom
|| frame_line
== bottom
- 1) {
1556 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
],
1559 if (b
->left
>= c
->left
&&
1560 b
->left
< c
->left
+ c
->width
)
1561 memcpy(vbuf
+ buf_line
* stride
+ left
,
1562 tpg
->contrast_line
[p
], twopixsize
);
1563 if (b
->left
+ b
->width
> c
->left
&&
1564 b
->left
+ b
->width
<= c
->left
+ c
->width
)
1565 memcpy(vbuf
+ buf_line
* stride
+ right
- twopixsize
,
1566 tpg
->contrast_line
[p
], twopixsize
);
1569 if (tpg
->qual
!= TPG_QUAL_NOISE
&& frame_line
>= b
->top
&&
1570 frame_line
< b
->top
+ b
->height
) {
1571 memcpy(vbuf
+ buf_line
* stride
, tpg
->black_line
[p
], left_pillar_width
);
1572 memcpy(vbuf
+ buf_line
* stride
+ right_pillar_start
, tpg
->black_line
[p
],
1573 img_width
- right_pillar_start
);
1575 if (tpg
->show_square
&& frame_line
>= sq
->top
&&
1576 frame_line
< sq
->top
+ sq
->height
&&
1577 sq
->left
< c
->left
+ c
->width
&&
1578 sq
->left
+ sq
->width
>= c
->left
) {
1579 unsigned left
= sq
->left
;
1580 unsigned width
= sq
->width
;
1582 if (c
->left
> left
) {
1583 width
-= c
->left
- left
;
1586 if (c
->left
+ c
->width
< left
+ width
)
1587 width
-= left
+ width
- c
->left
- c
->width
;
1589 left
= (left
* tpg
->scaled_width
) / tpg
->src_width
;
1590 left
= (left
& ~1) * twopixsize
/ 2;
1591 width
= (width
* tpg
->scaled_width
) / tpg
->src_width
;
1592 width
= (width
& ~1) * twopixsize
/ 2;
1593 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
], width
);
1595 if (tpg
->insert_sav
) {
1596 unsigned offset
= (tpg
->compose
.width
/ 6) * twopixsize
;
1597 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1598 unsigned vact
= 0, hact
= 0;
1603 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1604 ((hact
^ vact
) << 3) |
1609 if (tpg
->insert_eav
) {
1610 unsigned offset
= (tpg
->compose
.width
/ 6) * 2 * twopixsize
;
1611 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1612 unsigned vact
= 0, hact
= 1;
1617 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1618 ((hact
^ vact
) << 3) |
1626 void tpg_fillbuffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
1628 unsigned offset
= 0;
1631 if (tpg
->buffers
> 1) {
1632 tpg_fill_plane_buffer(tpg
, std
, p
, vbuf
);
1636 for (i
= 0; i
< tpg
->planes
; i
++) {
1637 tpg_fill_plane_buffer(tpg
, std
, i
, vbuf
+ offset
);
1638 offset
+= tpg_calc_plane_size(tpg
, i
);