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
;
691 buf
[1][0] = (buf
[1][0] + g_u
) / 2;
692 buf
[1][1] = (buf
[1][1] + b_v
) / 2;
698 case V4L2_PIX_FMT_NV61M
:
699 buf
[0][offset
] = r_y
;
701 buf
[1][0] = (buf
[1][0] + b_v
) / 2;
702 buf
[1][1] = (buf
[1][1] + g_u
) / 2;
709 case V4L2_PIX_FMT_YUYV
:
710 buf
[0][offset
] = r_y
;
712 buf
[0][1] = (buf
[0][1] + g_u
) / 2;
713 buf
[0][3] = (buf
[0][3] + b_v
) / 2;
719 case V4L2_PIX_FMT_UYVY
:
720 buf
[0][offset
+ 1] = r_y
;
722 buf
[0][0] = (buf
[0][0] + g_u
) / 2;
723 buf
[0][2] = (buf
[0][2] + b_v
) / 2;
729 case V4L2_PIX_FMT_YVYU
:
730 buf
[0][offset
] = r_y
;
732 buf
[0][1] = (buf
[0][1] + b_v
) / 2;
733 buf
[0][3] = (buf
[0][3] + g_u
) / 2;
739 case V4L2_PIX_FMT_VYUY
:
740 buf
[0][offset
+ 1] = r_y
;
742 buf
[0][0] = (buf
[0][0] + b_v
) / 2;
743 buf
[0][2] = (buf
[0][2] + g_u
) / 2;
749 case V4L2_PIX_FMT_RGB565
:
750 buf
[0][offset
] = (g_u
<< 5) | b_v
;
751 buf
[0][offset
+ 1] = (r_y
<< 3) | (g_u
>> 3);
753 case V4L2_PIX_FMT_RGB565X
:
754 buf
[0][offset
] = (r_y
<< 3) | (g_u
>> 3);
755 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
757 case V4L2_PIX_FMT_RGB555
:
758 case V4L2_PIX_FMT_XRGB555
:
761 case V4L2_PIX_FMT_ARGB555
:
762 buf
[0][offset
] = (g_u
<< 5) | b_v
;
763 buf
[0][offset
+ 1] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
765 case V4L2_PIX_FMT_RGB555X
:
766 buf
[0][offset
] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
767 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
769 case V4L2_PIX_FMT_RGB24
:
770 buf
[0][offset
] = r_y
;
771 buf
[0][offset
+ 1] = g_u
;
772 buf
[0][offset
+ 2] = b_v
;
774 case V4L2_PIX_FMT_BGR24
:
775 buf
[0][offset
] = b_v
;
776 buf
[0][offset
+ 1] = g_u
;
777 buf
[0][offset
+ 2] = r_y
;
779 case V4L2_PIX_FMT_RGB32
:
780 case V4L2_PIX_FMT_XRGB32
:
783 case V4L2_PIX_FMT_ARGB32
:
784 buf
[0][offset
] = alpha
;
785 buf
[0][offset
+ 1] = r_y
;
786 buf
[0][offset
+ 2] = g_u
;
787 buf
[0][offset
+ 3] = b_v
;
789 case V4L2_PIX_FMT_BGR32
:
790 case V4L2_PIX_FMT_XBGR32
:
793 case V4L2_PIX_FMT_ABGR32
:
794 buf
[0][offset
] = b_v
;
795 buf
[0][offset
+ 1] = g_u
;
796 buf
[0][offset
+ 2] = r_y
;
797 buf
[0][offset
+ 3] = alpha
;
802 /* Return how many pattern lines are used by the current pattern. */
803 static unsigned tpg_get_pat_lines(const struct tpg_data
*tpg
)
805 switch (tpg
->pattern
) {
806 case TPG_PAT_CHECKERS_16X16
:
807 case TPG_PAT_CHECKERS_2X2
:
808 case TPG_PAT_CHECKERS_1X1
:
809 case TPG_PAT_COLOR_CHECKERS_2X2
:
810 case TPG_PAT_COLOR_CHECKERS_1X1
:
811 case TPG_PAT_ALTERNATING_HLINES
:
812 case TPG_PAT_CROSS_1_PIXEL
:
813 case TPG_PAT_CROSS_2_PIXELS
:
814 case TPG_PAT_CROSS_10_PIXELS
:
816 case TPG_PAT_100_COLORSQUARES
:
817 case TPG_PAT_100_HCOLORBAR
:
824 /* Which pattern line should be used for the given frame line. */
825 static unsigned tpg_get_pat_line(const struct tpg_data
*tpg
, unsigned line
)
827 switch (tpg
->pattern
) {
828 case TPG_PAT_CHECKERS_16X16
:
829 return (line
>> 4) & 1;
830 case TPG_PAT_CHECKERS_1X1
:
831 case TPG_PAT_COLOR_CHECKERS_1X1
:
832 case TPG_PAT_ALTERNATING_HLINES
:
834 case TPG_PAT_CHECKERS_2X2
:
835 case TPG_PAT_COLOR_CHECKERS_2X2
:
836 return (line
& 2) >> 1;
837 case TPG_PAT_100_COLORSQUARES
:
838 case TPG_PAT_100_HCOLORBAR
:
839 return (line
* 8) / tpg
->src_height
;
840 case TPG_PAT_CROSS_1_PIXEL
:
841 return line
== tpg
->src_height
/ 2;
842 case TPG_PAT_CROSS_2_PIXELS
:
843 return (line
+ 1) / 2 == tpg
->src_height
/ 4;
844 case TPG_PAT_CROSS_10_PIXELS
:
845 return (line
+ 10) / 20 == tpg
->src_height
/ 40;
852 * Which color should be used for the given pattern line and X coordinate.
853 * Note: x is in the range 0 to 2 * tpg->src_width.
855 static enum tpg_color
tpg_get_color(const struct tpg_data
*tpg
,
856 unsigned pat_line
, unsigned x
)
858 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
859 should be modified */
860 static const enum tpg_color bars
[3][8] = {
861 /* Standard ITU-R 75% color bar sequence */
862 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_75_YELLOW
,
863 TPG_COLOR_75_CYAN
, TPG_COLOR_75_GREEN
,
864 TPG_COLOR_75_MAGENTA
, TPG_COLOR_75_RED
,
865 TPG_COLOR_75_BLUE
, TPG_COLOR_100_BLACK
, },
866 /* Standard ITU-R 100% color bar sequence */
867 { TPG_COLOR_100_WHITE
, TPG_COLOR_100_YELLOW
,
868 TPG_COLOR_100_CYAN
, TPG_COLOR_100_GREEN
,
869 TPG_COLOR_100_MAGENTA
, TPG_COLOR_100_RED
,
870 TPG_COLOR_100_BLUE
, TPG_COLOR_100_BLACK
, },
871 /* Color bar sequence suitable to test CSC */
872 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_CSC_YELLOW
,
873 TPG_COLOR_CSC_CYAN
, TPG_COLOR_CSC_GREEN
,
874 TPG_COLOR_CSC_MAGENTA
, TPG_COLOR_CSC_RED
,
875 TPG_COLOR_CSC_BLUE
, TPG_COLOR_CSC_BLACK
, },
878 switch (tpg
->pattern
) {
879 case TPG_PAT_75_COLORBAR
:
880 case TPG_PAT_100_COLORBAR
:
881 case TPG_PAT_CSC_COLORBAR
:
882 return bars
[tpg
->pattern
][((x
* 8) / tpg
->src_width
) % 8];
883 case TPG_PAT_100_COLORSQUARES
:
884 return bars
[1][(pat_line
+ (x
* 8) / tpg
->src_width
) % 8];
885 case TPG_PAT_100_HCOLORBAR
:
886 return bars
[1][pat_line
];
888 return TPG_COLOR_100_BLACK
;
890 return TPG_COLOR_100_WHITE
;
892 return TPG_COLOR_100_RED
;
894 return TPG_COLOR_100_GREEN
;
896 return TPG_COLOR_100_BLUE
;
897 case TPG_PAT_CHECKERS_16X16
:
898 return (((x
>> 4) & 1) ^ (pat_line
& 1)) ?
899 TPG_COLOR_100_BLACK
: TPG_COLOR_100_WHITE
;
900 case TPG_PAT_CHECKERS_1X1
:
901 return ((x
& 1) ^ (pat_line
& 1)) ?
902 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
903 case TPG_PAT_COLOR_CHECKERS_1X1
:
904 return ((x
& 1) ^ (pat_line
& 1)) ?
905 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
906 case TPG_PAT_CHECKERS_2X2
:
907 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
908 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
909 case TPG_PAT_COLOR_CHECKERS_2X2
:
910 return (((x
>> 1) & 1) ^ (pat_line
& 1)) ?
911 TPG_COLOR_100_RED
: TPG_COLOR_100_BLUE
;
912 case TPG_PAT_ALTERNATING_HLINES
:
913 return pat_line
? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
914 case TPG_PAT_ALTERNATING_VLINES
:
915 return (x
& 1) ? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
916 case TPG_PAT_CROSS_1_PIXEL
:
917 if (pat_line
|| (x
% tpg
->src_width
) == tpg
->src_width
/ 2)
918 return TPG_COLOR_100_BLACK
;
919 return TPG_COLOR_100_WHITE
;
920 case TPG_PAT_CROSS_2_PIXELS
:
921 if (pat_line
|| ((x
% tpg
->src_width
) + 1) / 2 == tpg
->src_width
/ 4)
922 return TPG_COLOR_100_BLACK
;
923 return TPG_COLOR_100_WHITE
;
924 case TPG_PAT_CROSS_10_PIXELS
:
925 if (pat_line
|| ((x
% tpg
->src_width
) + 10) / 20 == tpg
->src_width
/ 40)
926 return TPG_COLOR_100_BLACK
;
927 return TPG_COLOR_100_WHITE
;
928 case TPG_PAT_GRAY_RAMP
:
929 return TPG_COLOR_RAMP
+ ((x
% tpg
->src_width
) * 256) / tpg
->src_width
;
931 return TPG_COLOR_100_RED
;
936 * Given the pixel aspect ratio and video aspect ratio calculate the
937 * coordinates of a centered square and the coordinates of the border of
938 * the active video area. The coordinates are relative to the source
941 static void tpg_calculate_square_border(struct tpg_data
*tpg
)
943 unsigned w
= tpg
->src_width
;
944 unsigned h
= tpg
->src_height
;
947 sq_w
= (w
* 2 / 5) & ~1;
948 if (((w
- sq_w
) / 2) & 1)
951 tpg
->square
.width
= sq_w
;
952 if (tpg
->vid_aspect
== TPG_VIDEO_ASPECT_16X9_ANAMORPHIC
) {
953 unsigned ana_sq_w
= (sq_w
/ 4) * 3;
955 if (((w
- ana_sq_w
) / 2) & 1)
957 tpg
->square
.width
= ana_sq_w
;
959 tpg
->square
.left
= (w
- tpg
->square
.width
) / 2;
960 if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
)
961 sq_h
= sq_w
* 10 / 11;
962 else if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_PAL
)
963 sq_h
= sq_w
* 59 / 54;
964 tpg
->square
.height
= sq_h
;
965 tpg
->square
.top
= (h
- sq_h
) / 2;
966 tpg
->border
.left
= 0;
967 tpg
->border
.width
= w
;
969 tpg
->border
.height
= h
;
970 switch (tpg
->vid_aspect
) {
971 case TPG_VIDEO_ASPECT_4X3
:
974 if (3 * w
>= 4 * h
) {
975 tpg
->border
.width
= ((4 * h
) / 3) & ~1;
976 if (((w
- tpg
->border
.width
) / 2) & ~1)
977 tpg
->border
.width
-= 2;
978 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
981 tpg
->border
.height
= ((3 * w
) / 4) & ~1;
982 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
984 case TPG_VIDEO_ASPECT_14X9_CENTRE
:
985 if (tpg
->pix_aspect
) {
986 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 420 : 506;
987 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
990 if (9 * w
>= 14 * h
) {
991 tpg
->border
.width
= ((14 * h
) / 9) & ~1;
992 if (((w
- tpg
->border
.width
) / 2) & ~1)
993 tpg
->border
.width
-= 2;
994 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
997 tpg
->border
.height
= ((9 * w
) / 14) & ~1;
998 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1000 case TPG_VIDEO_ASPECT_16X9_CENTRE
:
1001 if (tpg
->pix_aspect
) {
1002 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 368 : 442;
1003 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1006 if (9 * w
>= 16 * h
) {
1007 tpg
->border
.width
= ((16 * h
) / 9) & ~1;
1008 if (((w
- tpg
->border
.width
) / 2) & ~1)
1009 tpg
->border
.width
-= 2;
1010 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
1013 tpg
->border
.height
= ((9 * w
) / 16) & ~1;
1014 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
1021 static void tpg_precalculate_line(struct tpg_data
*tpg
)
1023 enum tpg_color contrast
;
1028 switch (tpg
->pattern
) {
1030 contrast
= TPG_COLOR_100_RED
;
1032 case TPG_PAT_CSC_COLORBAR
:
1033 contrast
= TPG_COLOR_CSC_GREEN
;
1036 contrast
= TPG_COLOR_100_GREEN
;
1040 for (pat
= 0; pat
< tpg_get_pat_lines(tpg
); pat
++) {
1041 /* Coarse scaling with Bresenham */
1042 unsigned int_part
= tpg
->src_width
/ tpg
->scaled_width
;
1043 unsigned fract_part
= tpg
->src_width
% tpg
->scaled_width
;
1047 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1048 unsigned real_x
= src_x
;
1049 enum tpg_color color1
, color2
;
1050 u8 pix
[TPG_MAX_PLANES
][8];
1052 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1053 color1
= tpg_get_color(tpg
, pat
, real_x
);
1056 error
+= fract_part
;
1057 if (error
>= tpg
->scaled_width
) {
1058 error
-= tpg
->scaled_width
;
1063 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
1064 color2
= tpg_get_color(tpg
, pat
, real_x
);
1067 error
+= fract_part
;
1068 if (error
>= tpg
->scaled_width
) {
1069 error
-= tpg
->scaled_width
;
1073 gen_twopix(tpg
, pix
, tpg
->hflip
? color2
: color1
, 0);
1074 gen_twopix(tpg
, pix
, tpg
->hflip
? color1
: color2
, 1);
1075 for (p
= 0; p
< tpg
->planes
; p
++) {
1076 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1077 unsigned hdiv
= tpg
->hdownsampling
[p
];
1078 u8
*pos
= tpg
->lines
[pat
][p
] +
1079 (x
/ hdiv
) * twopixsize
/ 2;
1081 memcpy(pos
, pix
[p
], twopixsize
/ hdiv
);
1086 if (tpg
->vdownsampling
[tpg
->planes
- 1] > 1) {
1087 unsigned pat_lines
= tpg_get_pat_lines(tpg
);
1089 for (pat
= 0; pat
< pat_lines
; pat
++) {
1090 unsigned next_pat
= (pat
+ 1) % pat_lines
;
1092 for (p
= 1; p
< tpg
->planes
; p
++) {
1093 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1094 unsigned hdiv
= tpg
->hdownsampling
[p
];
1096 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1097 unsigned offset
= (x
/ hdiv
) * twopixsize
/ 2;
1098 u8
*pos1
= tpg
->lines
[pat
][p
] + offset
;
1099 u8
*pos2
= tpg
->lines
[next_pat
][p
] + offset
;
1100 u8
*dest
= tpg
->downsampled_lines
[pat
][p
] + offset
;
1103 for (i
= 0; i
< twopixsize
/ hdiv
; i
++, dest
++, pos1
++, pos2
++)
1104 *dest
= ((u16
)*pos1
+ (u16
)*pos2
) / 2;
1110 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1111 u8 pix
[TPG_MAX_PLANES
][8];
1113 gen_twopix(tpg
, pix
, contrast
, 0);
1114 gen_twopix(tpg
, pix
, contrast
, 1);
1115 for (p
= 0; p
< tpg
->planes
; p
++) {
1116 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1117 u8
*pos
= tpg
->contrast_line
[p
] + x
* twopixsize
/ 2;
1119 memcpy(pos
, pix
[p
], twopixsize
);
1122 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1123 u8 pix
[TPG_MAX_PLANES
][8];
1125 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 0);
1126 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 1);
1127 for (p
= 0; p
< tpg
->planes
; p
++) {
1128 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1129 u8
*pos
= tpg
->black_line
[p
] + x
* twopixsize
/ 2;
1131 memcpy(pos
, pix
[p
], twopixsize
);
1134 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1135 u8 pix
[TPG_MAX_PLANES
][8];
1137 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 0);
1138 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 1);
1139 for (p
= 0; p
< tpg
->planes
; p
++) {
1140 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1141 u8
*pos
= tpg
->random_line
[p
] + x
* twopixsize
/ 2;
1143 memcpy(pos
, pix
[p
], twopixsize
);
1146 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 0);
1147 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 1);
1148 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 0);
1149 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 1);
1152 /* need this to do rgb24 rendering */
1153 typedef struct { u16 __
; u8 _
; } __packed x24
;
1155 void tpg_gen_text(struct tpg_data
*tpg
, u8
*basep
[TPG_MAX_PLANES
][2],
1156 int y
, int x
, char *text
)
1159 unsigned step
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1160 unsigned div
= step
;
1162 unsigned len
= strlen(text
);
1165 if (font8x16
== NULL
|| basep
== NULL
)
1168 /* Checks if it is possible to show string */
1169 if (y
+ 16 >= tpg
->compose
.height
|| x
+ 8 >= tpg
->compose
.width
)
1172 if (len
> (tpg
->compose
.width
- x
) / 8)
1173 len
= (tpg
->compose
.width
- x
) / 8;
1175 y
= tpg
->compose
.height
- y
- 16;
1177 x
= tpg
->compose
.width
- x
- 8;
1178 y
+= tpg
->compose
.top
;
1179 x
+= tpg
->compose
.left
;
1180 if (tpg
->field
== V4L2_FIELD_BOTTOM
)
1182 else if (tpg
->field
== V4L2_FIELD_SEQ_TB
|| tpg
->field
== V4L2_FIELD_SEQ_BT
)
1185 for (p
= 0; p
< tpg
->planes
; p
++) {
1186 /* Print stream time */
1187 #define PRINTSTR(PIXTYPE) do { \
1190 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1191 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1193 for (line = first; line < 16; line += step) { \
1194 int l = tpg->vflip ? 15 - line : line; \
1195 PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
1196 ((y * step + l) / div) * tpg->bytesperline[p] + \
1197 x * sizeof(PIXTYPE)); \
1200 for (s = 0; s < len; s++) { \
1201 u8 chr = font8x16[text[s] * 16 + line]; \
1204 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1205 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1206 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1207 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1208 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1209 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1210 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1211 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1213 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1214 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1215 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1216 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1217 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1218 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1219 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1220 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1223 pos += tpg->hflip ? -8 : 8; \
1228 switch (tpg
->twopixelsize
[p
]) {
1230 PRINTSTR(u8
); break;
1232 PRINTSTR(u16
); break;
1234 PRINTSTR(x24
); break;
1236 PRINTSTR(u32
); break;
1241 void tpg_update_mv_step(struct tpg_data
*tpg
)
1243 int factor
= tpg
->mv_hor_mode
> TPG_MOVE_NONE
? -1 : 1;
1247 switch (tpg
->mv_hor_mode
) {
1248 case TPG_MOVE_NEG_FAST
:
1249 case TPG_MOVE_POS_FAST
:
1250 tpg
->mv_hor_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1254 tpg
->mv_hor_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1256 case TPG_MOVE_NEG_SLOW
:
1257 case TPG_MOVE_POS_SLOW
:
1258 tpg
->mv_hor_step
= 2;
1261 tpg
->mv_hor_step
= 0;
1265 tpg
->mv_hor_step
= tpg
->src_width
- tpg
->mv_hor_step
;
1267 factor
= tpg
->mv_vert_mode
> TPG_MOVE_NONE
? -1 : 1;
1268 switch (tpg
->mv_vert_mode
) {
1269 case TPG_MOVE_NEG_FAST
:
1270 case TPG_MOVE_POS_FAST
:
1271 tpg
->mv_vert_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1275 tpg
->mv_vert_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1277 case TPG_MOVE_NEG_SLOW
:
1278 case TPG_MOVE_POS_SLOW
:
1279 tpg
->mv_vert_step
= 1;
1282 tpg
->mv_vert_step
= 0;
1286 tpg
->mv_vert_step
= tpg
->src_height
- tpg
->mv_vert_step
;
1289 /* Map the line number relative to the crop rectangle to a frame line number */
1290 static unsigned tpg_calc_frameline(struct tpg_data
*tpg
, unsigned src_y
,
1294 case V4L2_FIELD_TOP
:
1295 return tpg
->crop
.top
+ src_y
* 2;
1296 case V4L2_FIELD_BOTTOM
:
1297 return tpg
->crop
.top
+ src_y
* 2 + 1;
1299 return src_y
+ tpg
->crop
.top
;
1304 * Map the line number relative to the compose rectangle to a destination
1305 * buffer line number.
1307 static unsigned tpg_calc_buffer_line(struct tpg_data
*tpg
, unsigned y
,
1310 y
+= tpg
->compose
.top
;
1312 case V4L2_FIELD_SEQ_TB
:
1314 return tpg
->buf_height
/ 2 + y
/ 2;
1316 case V4L2_FIELD_SEQ_BT
:
1319 return tpg
->buf_height
/ 2 + y
/ 2;
1325 static void tpg_recalc(struct tpg_data
*tpg
)
1327 if (tpg
->recalc_colors
) {
1328 tpg
->recalc_colors
= false;
1329 tpg
->recalc_lines
= true;
1330 tpg
->real_ycbcr_enc
= tpg
->ycbcr_enc
;
1331 tpg
->real_quantization
= tpg
->quantization
;
1332 if (tpg
->ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
) {
1333 switch (tpg
->colorspace
) {
1334 case V4L2_COLORSPACE_REC709
:
1335 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_709
;
1337 case V4L2_COLORSPACE_SRGB
:
1338 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SYCC
;
1340 case V4L2_COLORSPACE_BT2020
:
1341 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_BT2020
;
1343 case V4L2_COLORSPACE_SMPTE240M
:
1344 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SMPTE240M
;
1346 case V4L2_COLORSPACE_SMPTE170M
:
1347 case V4L2_COLORSPACE_470_SYSTEM_M
:
1348 case V4L2_COLORSPACE_470_SYSTEM_BG
:
1349 case V4L2_COLORSPACE_ADOBERGB
:
1351 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
1355 if (tpg
->quantization
== V4L2_QUANTIZATION_DEFAULT
) {
1356 tpg
->real_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
1358 switch (tpg
->real_ycbcr_enc
) {
1359 case V4L2_YCBCR_ENC_SYCC
:
1360 case V4L2_YCBCR_ENC_XV601
:
1361 case V4L2_YCBCR_ENC_XV709
:
1364 tpg
->real_quantization
=
1365 V4L2_QUANTIZATION_LIM_RANGE
;
1368 } else if (tpg
->colorspace
== V4L2_COLORSPACE_BT2020
) {
1369 /* R'G'B' BT.2020 is limited range */
1370 tpg
->real_quantization
=
1371 V4L2_QUANTIZATION_LIM_RANGE
;
1374 tpg_precalculate_colors(tpg
);
1376 if (tpg
->recalc_square_border
) {
1377 tpg
->recalc_square_border
= false;
1378 tpg_calculate_square_border(tpg
);
1380 if (tpg
->recalc_lines
) {
1381 tpg
->recalc_lines
= false;
1382 tpg_precalculate_line(tpg
);
1386 void tpg_calc_text_basep(struct tpg_data
*tpg
,
1387 u8
*basep
[TPG_MAX_PLANES
][2], unsigned p
, u8
*vbuf
)
1389 unsigned stride
= tpg
->bytesperline
[p
];
1395 if (tpg
->field
== V4L2_FIELD_SEQ_TB
)
1396 basep
[p
][1] += tpg
->buf_height
* stride
/ 2;
1397 else if (tpg
->field
== V4L2_FIELD_SEQ_BT
)
1398 basep
[p
][0] += tpg
->buf_height
* stride
/ 2;
1401 void tpg_fill_plane_buffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
1404 bool is_60hz
= is_tv
&& (std
& V4L2_STD_525_60
);
1405 unsigned mv_hor_old
= tpg
->mv_hor_count
% tpg
->src_width
;
1406 unsigned mv_hor_new
= (tpg
->mv_hor_count
+ tpg
->mv_hor_step
) % tpg
->src_width
;
1407 unsigned mv_vert_old
= tpg
->mv_vert_count
% tpg
->src_height
;
1408 unsigned mv_vert_new
= (tpg
->mv_vert_count
+ tpg
->mv_vert_step
) % tpg
->src_height
;
1411 int hmax
= (tpg
->compose
.height
* tpg
->perc_fill
) / 100;
1413 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1414 unsigned img_width
= tpg
->compose
.width
* twopixsize
/ 2;
1415 unsigned line_offset
;
1416 unsigned left_pillar_width
= 0;
1417 unsigned right_pillar_start
= img_width
;
1418 unsigned stride
= tpg
->bytesperline
[p
];
1419 unsigned factor
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1420 u8
*orig_vbuf
= vbuf
;
1422 /* Coarse scaling with Bresenham */
1423 unsigned int_part
= (tpg
->crop
.height
/ factor
) / tpg
->compose
.height
;
1424 unsigned fract_part
= (tpg
->crop
.height
/ factor
) % tpg
->compose
.height
;
1430 mv_hor_old
= (mv_hor_old
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1431 mv_hor_new
= (mv_hor_new
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1432 wss_width
= tpg
->crop
.left
< tpg
->src_width
/ 2 ?
1433 tpg
->src_width
/ 2 - tpg
->crop
.left
: 0;
1434 if (wss_width
> tpg
->crop
.width
)
1435 wss_width
= tpg
->crop
.width
;
1436 wss_width
= wss_width
* tpg
->scaled_width
/ tpg
->src_width
;
1438 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1439 line_offset
= tpg
->crop
.left
* tpg
->scaled_width
/ tpg
->src_width
;
1440 line_offset
= (line_offset
& ~1) * twopixsize
/ 2;
1441 if (tpg
->crop
.left
< tpg
->border
.left
) {
1442 left_pillar_width
= tpg
->border
.left
- tpg
->crop
.left
;
1443 if (left_pillar_width
> tpg
->crop
.width
)
1444 left_pillar_width
= tpg
->crop
.width
;
1445 left_pillar_width
= (left_pillar_width
* tpg
->scaled_width
) / tpg
->src_width
;
1446 left_pillar_width
= (left_pillar_width
& ~1) * twopixsize
/ 2;
1448 if (tpg
->crop
.left
+ tpg
->crop
.width
> tpg
->border
.left
+ tpg
->border
.width
) {
1449 right_pillar_start
= tpg
->border
.left
+ tpg
->border
.width
- tpg
->crop
.left
;
1450 right_pillar_start
= (right_pillar_start
* tpg
->scaled_width
) / tpg
->src_width
;
1451 right_pillar_start
= (right_pillar_start
& ~1) * twopixsize
/ 2;
1452 if (right_pillar_start
> img_width
)
1453 right_pillar_start
= img_width
;
1456 f
= tpg
->field
== (is_60hz
? V4L2_FIELD_TOP
: V4L2_FIELD_BOTTOM
);
1458 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1460 bool fill_blank
= false;
1461 unsigned frame_line
;
1463 unsigned pat_line_old
;
1464 unsigned pat_line_new
;
1465 u8
*linestart_older
;
1466 u8
*linestart_newer
;
1468 u8
*linestart_bottom
;
1470 frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1471 even
= !(frame_line
& 1);
1472 buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1474 error
+= fract_part
;
1475 if (error
>= tpg
->compose
.height
) {
1476 error
-= tpg
->compose
.height
;
1481 if (hmax
== tpg
->compose
.height
)
1483 if (!tpg
->perc_fill_blank
)
1489 frame_line
= tpg
->src_height
- frame_line
- 1;
1492 linestart_older
= tpg
->contrast_line
[p
];
1493 linestart_newer
= tpg
->contrast_line
[p
];
1494 } else if (tpg
->qual
!= TPG_QUAL_NOISE
&&
1495 (frame_line
< tpg
->border
.top
||
1496 frame_line
>= tpg
->border
.top
+ tpg
->border
.height
)) {
1497 linestart_older
= tpg
->black_line
[p
];
1498 linestart_newer
= tpg
->black_line
[p
];
1499 } else if (tpg
->pattern
== TPG_PAT_NOISE
|| tpg
->qual
== TPG_QUAL_NOISE
) {
1500 linestart_older
= tpg
->random_line
[p
] +
1501 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1502 linestart_newer
= tpg
->random_line
[p
] +
1503 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1505 pat_line_old
= tpg_get_pat_line(tpg
,
1506 (frame_line
+ mv_vert_old
) % tpg
->src_height
);
1507 pat_line_new
= tpg_get_pat_line(tpg
,
1508 (frame_line
+ mv_vert_new
) % tpg
->src_height
);
1509 linestart_older
= tpg
->lines
[pat_line_old
][p
] +
1510 mv_hor_old
* twopixsize
/ 2;
1511 linestart_newer
= tpg
->lines
[pat_line_new
][p
] +
1512 mv_hor_new
* twopixsize
/ 2;
1513 linestart_older
+= line_offset
;
1514 linestart_newer
+= line_offset
;
1516 if (tpg
->field_alternate
) {
1517 linestart_top
= linestart_bottom
= linestart_older
;
1518 } else if (is_60hz
) {
1519 linestart_top
= linestart_newer
;
1520 linestart_bottom
= linestart_older
;
1522 linestart_top
= linestart_older
;
1523 linestart_bottom
= linestart_newer
;
1526 switch (tpg
->field
) {
1527 case V4L2_FIELD_INTERLACED
:
1528 case V4L2_FIELD_INTERLACED_TB
:
1529 case V4L2_FIELD_SEQ_TB
:
1530 case V4L2_FIELD_SEQ_BT
:
1532 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1534 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1536 case V4L2_FIELD_INTERLACED_BT
:
1538 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1540 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1542 case V4L2_FIELD_TOP
:
1543 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1545 case V4L2_FIELD_BOTTOM
:
1546 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1548 case V4L2_FIELD_NONE
:
1550 memcpy(vbuf
+ buf_line
* stride
, linestart_older
, img_width
);
1554 if (is_tv
&& !is_60hz
&& frame_line
== 0 && wss_width
) {
1556 * Replace the first half of the top line of a 50 Hz frame
1557 * with random data to simulate a WSS signal.
1559 u8
*wss
= tpg
->random_line
[p
] +
1560 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1562 memcpy(vbuf
+ buf_line
* stride
, wss
, wss_width
* twopixsize
/ 2);
1567 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1570 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1571 unsigned frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1572 unsigned buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1573 const struct v4l2_rect
*sq
= &tpg
->square
;
1574 const struct v4l2_rect
*b
= &tpg
->border
;
1575 const struct v4l2_rect
*c
= &tpg
->crop
;
1578 error
+= fract_part
;
1579 if (error
>= tpg
->compose
.height
) {
1580 error
-= tpg
->compose
.height
;
1584 if (tpg
->show_border
&& frame_line
>= b
->top
&&
1585 frame_line
< b
->top
+ b
->height
) {
1586 unsigned bottom
= b
->top
+ b
->height
- 1;
1587 unsigned left
= left_pillar_width
;
1588 unsigned right
= right_pillar_start
;
1590 if (frame_line
== b
->top
|| frame_line
== b
->top
+ 1 ||
1591 frame_line
== bottom
|| frame_line
== bottom
- 1) {
1592 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
],
1595 if (b
->left
>= c
->left
&&
1596 b
->left
< c
->left
+ c
->width
)
1597 memcpy(vbuf
+ buf_line
* stride
+ left
,
1598 tpg
->contrast_line
[p
], twopixsize
);
1599 if (b
->left
+ b
->width
> c
->left
&&
1600 b
->left
+ b
->width
<= c
->left
+ c
->width
)
1601 memcpy(vbuf
+ buf_line
* stride
+ right
- twopixsize
,
1602 tpg
->contrast_line
[p
], twopixsize
);
1605 if (tpg
->qual
!= TPG_QUAL_NOISE
&& frame_line
>= b
->top
&&
1606 frame_line
< b
->top
+ b
->height
) {
1607 memcpy(vbuf
+ buf_line
* stride
, tpg
->black_line
[p
], left_pillar_width
);
1608 memcpy(vbuf
+ buf_line
* stride
+ right_pillar_start
, tpg
->black_line
[p
],
1609 img_width
- right_pillar_start
);
1611 if (tpg
->show_square
&& frame_line
>= sq
->top
&&
1612 frame_line
< sq
->top
+ sq
->height
&&
1613 sq
->left
< c
->left
+ c
->width
&&
1614 sq
->left
+ sq
->width
>= c
->left
) {
1615 unsigned left
= sq
->left
;
1616 unsigned width
= sq
->width
;
1618 if (c
->left
> left
) {
1619 width
-= c
->left
- left
;
1622 if (c
->left
+ c
->width
< left
+ width
)
1623 width
-= left
+ width
- c
->left
- c
->width
;
1625 left
= (left
* tpg
->scaled_width
) / tpg
->src_width
;
1626 left
= (left
& ~1) * twopixsize
/ 2;
1627 width
= (width
* tpg
->scaled_width
) / tpg
->src_width
;
1628 width
= (width
& ~1) * twopixsize
/ 2;
1629 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
], width
);
1631 if (tpg
->insert_sav
) {
1632 unsigned offset
= (tpg
->compose
.width
/ 6) * twopixsize
;
1633 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1634 unsigned vact
= 0, hact
= 0;
1639 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1640 ((hact
^ vact
) << 3) |
1645 if (tpg
->insert_eav
) {
1646 unsigned offset
= (tpg
->compose
.width
/ 6) * 2 * twopixsize
;
1647 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1648 unsigned vact
= 0, hact
= 1;
1653 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1654 ((hact
^ vact
) << 3) |
1662 void tpg_fillbuffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
1664 unsigned offset
= 0;
1667 if (tpg
->buffers
> 1) {
1668 tpg_fill_plane_buffer(tpg
, std
, p
, vbuf
);
1672 for (i
= 0; i
< tpg
->planes
; i
++) {
1673 tpg_fill_plane_buffer(tpg
, std
, i
, vbuf
+ offset
);
1674 offset
+= tpg_calc_plane_size(tpg
, i
);