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",
39 "Alternating Hor Lines",
40 "Alternating Vert Lines",
41 "One Pixel Wide Cross",
42 "Two Pixels Wide Cross",
43 "Ten Pixels Wide Cross",
49 /* Must remain in sync with enum tpg_aspect */
50 const char * const tpg_aspect_strings
[] = {
51 "Source Width x Height",
60 * Sine table: sin[0] = 127 * sin(-180 degrees)
61 * sin[128] = 127 * sin(0 degrees)
62 * sin[256] = 127 * sin(180 degrees)
64 static const s8 sin
[257] = {
65 0, -4, -7, -11, -13, -18, -20, -22, -26, -29, -33, -35, -37, -41, -43, -48,
66 -50, -52, -56, -58, -62, -63, -65, -69, -71, -75, -76, -78, -82, -83, -87, -88,
67 -90, -93, -94, -97, -99, -101, -103, -104, -107, -108, -110, -111, -112, -114, -115, -117,
68 -118, -119, -120, -121, -122, -123, -123, -124, -125, -125, -126, -126, -127, -127, -127, -127,
69 -127, -127, -127, -127, -126, -126, -125, -125, -124, -124, -123, -122, -121, -120, -119, -118,
70 -117, -116, -114, -113, -111, -110, -109, -107, -105, -103, -101, -100, -97, -96, -93, -91,
71 -90, -87, -85, -82, -80, -76, -75, -73, -69, -67, -63, -62, -60, -56, -54, -50,
72 -48, -46, -41, -39, -35, -33, -31, -26, -24, -20, -18, -15, -11, -9, -4, -2,
73 0, 2, 4, 9, 11, 15, 18, 20, 24, 26, 31, 33, 35, 39, 41, 46,
74 48, 50, 54, 56, 60, 62, 64, 67, 69, 73, 75, 76, 80, 82, 85, 87,
75 90, 91, 93, 96, 97, 100, 101, 103, 105, 107, 109, 110, 111, 113, 114, 116,
76 117, 118, 119, 120, 121, 122, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127,
77 127, 127, 127, 127, 127, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119,
78 118, 117, 115, 114, 112, 111, 110, 108, 107, 104, 103, 101, 99, 97, 94, 93,
79 90, 88, 87, 83, 82, 78, 76, 75, 71, 69, 65, 64, 62, 58, 56, 52,
80 50, 48, 43, 41, 37, 35, 33, 29, 26, 22, 20, 18, 13, 11, 7, 4,
84 #define cos(idx) sin[((idx) + 64) % sizeof(sin)]
86 /* Global font descriptor */
87 static const u8
*font8x16
;
89 void tpg_set_font(const u8
*f
)
94 void tpg_init(struct tpg_data
*tpg
, unsigned w
, unsigned h
)
96 memset(tpg
, 0, sizeof(*tpg
));
97 tpg
->scaled_width
= tpg
->src_width
= w
;
98 tpg
->src_height
= tpg
->buf_height
= h
;
99 tpg
->crop
.width
= tpg
->compose
.width
= w
;
100 tpg
->crop
.height
= tpg
->compose
.height
= h
;
101 tpg
->recalc_colors
= true;
102 tpg
->recalc_square_border
= true;
103 tpg
->brightness
= 128;
105 tpg
->saturation
= 128;
107 tpg
->mv_hor_mode
= TPG_MOVE_NONE
;
108 tpg
->mv_vert_mode
= TPG_MOVE_NONE
;
109 tpg
->field
= V4L2_FIELD_NONE
;
110 tpg_s_fourcc(tpg
, V4L2_PIX_FMT_RGB24
);
111 tpg
->colorspace
= V4L2_COLORSPACE_SRGB
;
112 tpg
->perc_fill
= 100;
115 int tpg_alloc(struct tpg_data
*tpg
, unsigned max_w
)
120 tpg
->max_line_width
= max_w
;
121 for (pat
= 0; pat
< TPG_MAX_PAT_LINES
; pat
++) {
122 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
123 unsigned pixelsz
= plane
? 1 : 4;
125 tpg
->lines
[pat
][plane
] = vzalloc(max_w
* 2 * pixelsz
);
126 if (!tpg
->lines
[pat
][plane
])
130 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
131 unsigned pixelsz
= plane
? 1 : 4;
133 tpg
->contrast_line
[plane
] = vzalloc(max_w
* pixelsz
);
134 if (!tpg
->contrast_line
[plane
])
136 tpg
->black_line
[plane
] = vzalloc(max_w
* pixelsz
);
137 if (!tpg
->black_line
[plane
])
139 tpg
->random_line
[plane
] = vzalloc(max_w
* 2 * pixelsz
);
140 if (!tpg
->random_line
[plane
])
146 void tpg_free(struct tpg_data
*tpg
)
151 for (pat
= 0; pat
< TPG_MAX_PAT_LINES
; pat
++)
152 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
153 vfree(tpg
->lines
[pat
][plane
]);
154 tpg
->lines
[pat
][plane
] = NULL
;
156 for (plane
= 0; plane
< TPG_MAX_PLANES
; plane
++) {
157 vfree(tpg
->contrast_line
[plane
]);
158 vfree(tpg
->black_line
[plane
]);
159 vfree(tpg
->random_line
[plane
]);
160 tpg
->contrast_line
[plane
] = NULL
;
161 tpg
->black_line
[plane
] = NULL
;
162 tpg
->random_line
[plane
] = NULL
;
166 bool tpg_s_fourcc(struct tpg_data
*tpg
, u32 fourcc
)
168 tpg
->fourcc
= fourcc
;
170 tpg
->recalc_colors
= true;
172 case V4L2_PIX_FMT_RGB565
:
173 case V4L2_PIX_FMT_RGB565X
:
174 case V4L2_PIX_FMT_RGB555
:
175 case V4L2_PIX_FMT_XRGB555
:
176 case V4L2_PIX_FMT_ARGB555
:
177 case V4L2_PIX_FMT_RGB555X
:
178 case V4L2_PIX_FMT_RGB24
:
179 case V4L2_PIX_FMT_BGR24
:
180 case V4L2_PIX_FMT_RGB32
:
181 case V4L2_PIX_FMT_BGR32
:
182 case V4L2_PIX_FMT_XRGB32
:
183 case V4L2_PIX_FMT_XBGR32
:
184 case V4L2_PIX_FMT_ARGB32
:
185 case V4L2_PIX_FMT_ABGR32
:
188 case V4L2_PIX_FMT_NV16M
:
189 case V4L2_PIX_FMT_NV61M
:
192 case V4L2_PIX_FMT_YUYV
:
193 case V4L2_PIX_FMT_UYVY
:
194 case V4L2_PIX_FMT_YVYU
:
195 case V4L2_PIX_FMT_VYUY
:
203 case V4L2_PIX_FMT_RGB565
:
204 case V4L2_PIX_FMT_RGB565X
:
205 case V4L2_PIX_FMT_RGB555
:
206 case V4L2_PIX_FMT_XRGB555
:
207 case V4L2_PIX_FMT_ARGB555
:
208 case V4L2_PIX_FMT_RGB555X
:
209 case V4L2_PIX_FMT_YUYV
:
210 case V4L2_PIX_FMT_UYVY
:
211 case V4L2_PIX_FMT_YVYU
:
212 case V4L2_PIX_FMT_VYUY
:
213 tpg
->twopixelsize
[0] = 2 * 2;
215 case V4L2_PIX_FMT_RGB24
:
216 case V4L2_PIX_FMT_BGR24
:
217 tpg
->twopixelsize
[0] = 2 * 3;
219 case V4L2_PIX_FMT_RGB32
:
220 case V4L2_PIX_FMT_BGR32
:
221 case V4L2_PIX_FMT_XRGB32
:
222 case V4L2_PIX_FMT_XBGR32
:
223 case V4L2_PIX_FMT_ARGB32
:
224 case V4L2_PIX_FMT_ABGR32
:
225 tpg
->twopixelsize
[0] = 2 * 4;
227 case V4L2_PIX_FMT_NV16M
:
228 case V4L2_PIX_FMT_NV61M
:
229 tpg
->twopixelsize
[0] = 2;
230 tpg
->twopixelsize
[1] = 2;
236 void tpg_s_crop_compose(struct tpg_data
*tpg
, const struct v4l2_rect
*crop
,
237 const struct v4l2_rect
*compose
)
240 tpg
->compose
= *compose
;
241 tpg
->scaled_width
= (tpg
->src_width
* tpg
->compose
.width
+
242 tpg
->crop
.width
- 1) / tpg
->crop
.width
;
243 tpg
->scaled_width
&= ~1;
244 if (tpg
->scaled_width
> tpg
->max_line_width
)
245 tpg
->scaled_width
= tpg
->max_line_width
;
246 if (tpg
->scaled_width
< 2)
247 tpg
->scaled_width
= 2;
248 tpg
->recalc_lines
= true;
251 void tpg_reset_source(struct tpg_data
*tpg
, unsigned width
, unsigned height
,
256 tpg
->src_width
= width
;
257 tpg
->src_height
= height
;
259 tpg
->buf_height
= height
;
260 if (V4L2_FIELD_HAS_T_OR_B(field
))
261 tpg
->buf_height
/= 2;
262 tpg
->scaled_width
= width
;
263 tpg
->crop
.top
= tpg
->crop
.left
= 0;
264 tpg
->crop
.width
= width
;
265 tpg
->crop
.height
= height
;
266 tpg
->compose
.top
= tpg
->compose
.left
= 0;
267 tpg
->compose
.width
= width
;
268 tpg
->compose
.height
= tpg
->buf_height
;
269 for (p
= 0; p
< tpg
->planes
; p
++)
270 tpg
->bytesperline
[p
] = width
* tpg
->twopixelsize
[p
] / 2;
271 tpg
->recalc_square_border
= true;
274 static enum tpg_color
tpg_get_textbg_color(struct tpg_data
*tpg
)
276 switch (tpg
->pattern
) {
278 return TPG_COLOR_100_WHITE
;
279 case TPG_PAT_CSC_COLORBAR
:
280 return TPG_COLOR_CSC_BLACK
;
282 return TPG_COLOR_100_BLACK
;
286 static enum tpg_color
tpg_get_textfg_color(struct tpg_data
*tpg
)
288 switch (tpg
->pattern
) {
289 case TPG_PAT_75_COLORBAR
:
290 case TPG_PAT_CSC_COLORBAR
:
291 return TPG_COLOR_CSC_WHITE
;
293 return TPG_COLOR_100_BLACK
;
295 return TPG_COLOR_100_WHITE
;
299 static inline int rec709_to_linear(int v
)
301 v
= clamp(v
, 0, 0xff0);
302 return tpg_rec709_to_linear
[v
];
305 static inline int linear_to_rec709(int v
)
307 v
= clamp(v
, 0, 0xff0);
308 return tpg_linear_to_rec709
[v
];
311 static void rgb2ycbcr(const int m
[3][3], int r
, int g
, int b
,
312 int y_offset
, int *y
, int *cb
, int *cr
)
314 *y
= ((m
[0][0] * r
+ m
[0][1] * g
+ m
[0][2] * b
) >> 16) + (y_offset
<< 4);
315 *cb
= ((m
[1][0] * r
+ m
[1][1] * g
+ m
[1][2] * b
) >> 16) + (128 << 4);
316 *cr
= ((m
[2][0] * r
+ m
[2][1] * g
+ m
[2][2] * b
) >> 16) + (128 << 4);
319 static void color_to_ycbcr(struct tpg_data
*tpg
, int r
, int g
, int b
,
320 int *y
, int *cb
, int *cr
)
322 #define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
324 static const int bt601
[3][3] = {
325 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
326 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
327 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
329 static const int bt601_full
[3][3] = {
330 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
331 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
332 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
334 static const int rec709
[3][3] = {
335 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
336 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
337 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
339 static const int rec709_full
[3][3] = {
340 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
341 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
342 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
344 static const int smpte240m
[3][3] = {
345 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
346 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
347 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
349 static const int bt2020
[3][3] = {
350 { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
351 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
352 { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
354 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
355 unsigned y_offset
= full
? 0 : 16;
358 switch (tpg
->real_ycbcr_enc
) {
359 case V4L2_YCBCR_ENC_601
:
360 case V4L2_YCBCR_ENC_XV601
:
361 case V4L2_YCBCR_ENC_SYCC
:
362 rgb2ycbcr(full
? bt601_full
: bt601
, r
, g
, b
, y_offset
, y
, cb
, cr
);
364 case V4L2_YCBCR_ENC_BT2020
:
365 rgb2ycbcr(bt2020
, r
, g
, b
, 16, y
, cb
, cr
);
367 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
368 lin_y
= (COEFF(0.2627, 255) * rec709_to_linear(r
) +
369 COEFF(0.6780, 255) * rec709_to_linear(g
) +
370 COEFF(0.0593, 255) * rec709_to_linear(b
)) >> 16;
371 yc
= linear_to_rec709(lin_y
);
372 *y
= (yc
* 219) / 255 + (16 << 4);
374 *cb
= (((b
- yc
) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
376 *cb
= (((b
- yc
) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
378 *cr
= (((r
- yc
) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
380 *cr
= (((r
- yc
) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
382 case V4L2_YCBCR_ENC_SMPTE240M
:
383 rgb2ycbcr(smpte240m
, r
, g
, b
, 16, y
, cb
, cr
);
385 case V4L2_YCBCR_ENC_709
:
386 case V4L2_YCBCR_ENC_XV709
:
388 rgb2ycbcr(full
? rec709_full
: rec709
, r
, g
, b
, y_offset
, y
, cb
, cr
);
393 static void ycbcr2rgb(const int m
[3][3], int y
, int cb
, int cr
,
394 int y_offset
, int *r
, int *g
, int *b
)
399 *r
= m
[0][0] * y
+ m
[0][1] * cb
+ m
[0][2] * cr
;
400 *g
= m
[1][0] * y
+ m
[1][1] * cb
+ m
[1][2] * cr
;
401 *b
= m
[2][0] * y
+ m
[2][1] * cb
+ m
[2][2] * cr
;
402 *r
= clamp(*r
>> 12, 0, 0xff0);
403 *g
= clamp(*g
>> 12, 0, 0xff0);
404 *b
= clamp(*b
>> 12, 0, 0xff0);
407 static void ycbcr_to_color(struct tpg_data
*tpg
, int y
, int cb
, int cr
,
408 int *r
, int *g
, int *b
)
411 #define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
412 static const int bt601
[3][3] = {
413 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
414 { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
415 { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
417 static const int bt601_full
[3][3] = {
418 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
419 { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
420 { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
422 static const int rec709
[3][3] = {
423 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
424 { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
425 { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
427 static const int rec709_full
[3][3] = {
428 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
429 { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
430 { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
432 static const int smpte240m
[3][3] = {
433 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
434 { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
435 { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
437 static const int bt2020
[3][3] = {
438 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
439 { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
440 { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
442 bool full
= tpg
->real_quantization
== V4L2_QUANTIZATION_FULL_RANGE
;
443 unsigned y_offset
= full
? 0 : 16;
444 int lin_r
, lin_g
, lin_b
, lin_y
;
446 switch (tpg
->real_ycbcr_enc
) {
447 case V4L2_YCBCR_ENC_601
:
448 case V4L2_YCBCR_ENC_XV601
:
449 case V4L2_YCBCR_ENC_SYCC
:
450 ycbcr2rgb(full
? bt601_full
: bt601
, y
, cb
, cr
, y_offset
, r
, g
, b
);
452 case V4L2_YCBCR_ENC_BT2020
:
453 ycbcr2rgb(bt2020
, y
, cb
, cr
, 16, r
, g
, b
);
455 case V4L2_YCBCR_ENC_BT2020_CONST_LUM
:
461 *b
= COEFF(1.0, 219) * y
+ COEFF(1.9404, 224) * cb
;
463 *b
= COEFF(1.0, 219) * y
+ COEFF(1.5816, 224) * cb
;
466 *r
= COEFF(1.0, 219) * y
+ COEFF(1.7184, 224) * cr
;
468 *r
= COEFF(1.0, 219) * y
+ COEFF(0.9936, 224) * cr
;
470 lin_r
= rec709_to_linear(*r
);
471 lin_b
= rec709_to_linear(*b
);
472 lin_y
= rec709_to_linear((y
* 255) / 219);
474 lin_g
= COEFF(1.0 / 0.6780, 255) * lin_y
-
475 COEFF(0.2627 / 0.6780, 255) * lin_r
-
476 COEFF(0.0593 / 0.6780, 255) * lin_b
;
477 *g
= linear_to_rec709(lin_g
>> 12);
479 case V4L2_YCBCR_ENC_SMPTE240M
:
480 ycbcr2rgb(smpte240m
, y
, cb
, cr
, 16, r
, g
, b
);
482 case V4L2_YCBCR_ENC_709
:
483 case V4L2_YCBCR_ENC_XV709
:
485 ycbcr2rgb(full
? rec709_full
: rec709
, y
, cb
, cr
, y_offset
, r
, g
, b
);
490 /* precalculate color bar values to speed up rendering */
491 static void precalculate_color(struct tpg_data
*tpg
, int k
)
494 int r
= tpg_colors
[col
].r
;
495 int g
= tpg_colors
[col
].g
;
496 int b
= tpg_colors
[col
].b
;
498 if (k
== TPG_COLOR_TEXTBG
) {
499 col
= tpg_get_textbg_color(tpg
);
501 r
= tpg_colors
[col
].r
;
502 g
= tpg_colors
[col
].g
;
503 b
= tpg_colors
[col
].b
;
504 } else if (k
== TPG_COLOR_TEXTFG
) {
505 col
= tpg_get_textfg_color(tpg
);
507 r
= tpg_colors
[col
].r
;
508 g
= tpg_colors
[col
].g
;
509 b
= tpg_colors
[col
].b
;
510 } else if (tpg
->pattern
== TPG_PAT_NOISE
) {
511 r
= g
= b
= prandom_u32_max(256);
512 } else if (k
== TPG_COLOR_RANDOM
) {
513 r
= g
= b
= tpg
->qual_offset
+ prandom_u32_max(196);
514 } else if (k
>= TPG_COLOR_RAMP
) {
515 r
= g
= b
= k
- TPG_COLOR_RAMP
;
518 if (tpg
->pattern
== TPG_PAT_CSC_COLORBAR
&& col
<= TPG_COLOR_CSC_BLACK
) {
519 r
= tpg_csc_colors
[tpg
->colorspace
][col
].r
;
520 g
= tpg_csc_colors
[tpg
->colorspace
][col
].g
;
521 b
= tpg_csc_colors
[tpg
->colorspace
][col
].b
;
527 if (tpg
->qual
== TPG_QUAL_GRAY
) {
528 /* Rec. 709 Luma function */
529 /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
530 r
= g
= b
= ((13879 * r
+ 46688 * g
+ 4713 * b
) >> 16) + (16 << 4);
534 * The assumption is that the RGB output is always full range,
535 * so only if the rgb_range overrides the 'real' rgb range do
536 * we need to convert the RGB values.
538 * Remember that r, g and b are still in the 0 - 0xff0 range.
540 if (tpg
->real_rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
&&
541 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_FULL
) {
543 * Convert from full range (which is what r, g and b are)
544 * to limited range (which is the 'real' RGB range), which
545 * is then interpreted as full range.
547 r
= (r
* 219) / 255 + (16 << 4);
548 g
= (g
* 219) / 255 + (16 << 4);
549 b
= (b
* 219) / 255 + (16 << 4);
550 } else if (tpg
->real_rgb_range
!= V4L2_DV_RGB_RANGE_LIMITED
&&
551 tpg
->rgb_range
== V4L2_DV_RGB_RANGE_LIMITED
) {
553 * Clamp r, g and b to the limited range and convert to full
554 * range since that's what we deliver.
556 r
= clamp(r
, 16 << 4, 235 << 4);
557 g
= clamp(g
, 16 << 4, 235 << 4);
558 b
= clamp(b
, 16 << 4, 235 << 4);
559 r
= (r
- (16 << 4)) * 255 / 219;
560 g
= (g
- (16 << 4)) * 255 / 219;
561 b
= (b
- (16 << 4)) * 255 / 219;
564 if (tpg
->brightness
!= 128 || tpg
->contrast
!= 128 ||
565 tpg
->saturation
!= 128 || tpg
->hue
) {
566 /* Implement these operations */
570 /* First convert to YCbCr */
572 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
574 y
= (16 << 4) + ((y
- (16 << 4)) * tpg
->contrast
) / 128;
575 y
+= (tpg
->brightness
<< 4) - (128 << 4);
579 tmp_cb
= (cb
* cos(128 + tpg
->hue
)) / 127 + (cr
* sin
[128 + tpg
->hue
]) / 127;
580 tmp_cr
= (cr
* cos(128 + tpg
->hue
)) / 127 - (cb
* sin
[128 + tpg
->hue
]) / 127;
582 cb
= (128 << 4) + (tmp_cb
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
583 cr
= (128 << 4) + (tmp_cr
* tpg
->contrast
* tpg
->saturation
) / (128 * 128);
585 tpg
->colors
[k
][0] = clamp(y
>> 4, 1, 254);
586 tpg
->colors
[k
][1] = clamp(cb
>> 4, 1, 254);
587 tpg
->colors
[k
][2] = clamp(cr
>> 4, 1, 254);
590 ycbcr_to_color(tpg
, y
, cb
, cr
, &r
, &g
, &b
);
594 /* Convert to YCbCr */
597 color_to_ycbcr(tpg
, r
, g
, b
, &y
, &cb
, &cr
);
599 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
600 y
= clamp(y
, 16 << 4, 235 << 4);
601 cb
= clamp(cb
, 16 << 4, 240 << 4);
602 cr
= clamp(cr
, 16 << 4, 240 << 4);
604 tpg
->colors
[k
][0] = clamp(y
>> 4, 1, 254);
605 tpg
->colors
[k
][1] = clamp(cb
>> 4, 1, 254);
606 tpg
->colors
[k
][2] = clamp(cr
>> 4, 1, 254);
608 if (tpg
->real_quantization
== V4L2_QUANTIZATION_LIM_RANGE
) {
609 r
= (r
* 219) / 255 + (16 << 4);
610 g
= (g
* 219) / 255 + (16 << 4);
611 b
= (b
* 219) / 255 + (16 << 4);
613 switch (tpg
->fourcc
) {
614 case V4L2_PIX_FMT_RGB565
:
615 case V4L2_PIX_FMT_RGB565X
:
620 case V4L2_PIX_FMT_RGB555
:
621 case V4L2_PIX_FMT_XRGB555
:
622 case V4L2_PIX_FMT_ARGB555
:
623 case V4L2_PIX_FMT_RGB555X
:
635 tpg
->colors
[k
][0] = r
;
636 tpg
->colors
[k
][1] = g
;
637 tpg
->colors
[k
][2] = b
;
641 static void tpg_precalculate_colors(struct tpg_data
*tpg
)
645 for (k
= 0; k
< TPG_COLOR_MAX
; k
++)
646 precalculate_color(tpg
, k
);
649 /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
650 static void gen_twopix(struct tpg_data
*tpg
,
651 u8 buf
[TPG_MAX_PLANES
][8], int color
, bool odd
)
653 unsigned offset
= odd
* tpg
->twopixelsize
[0] / 2;
654 u8 alpha
= tpg
->alpha_component
;
657 if (tpg
->alpha_red_only
&& color
!= TPG_COLOR_CSC_RED
&&
658 color
!= TPG_COLOR_100_RED
&&
659 color
!= TPG_COLOR_75_RED
)
661 if (color
== TPG_COLOR_RANDOM
)
662 precalculate_color(tpg
, color
);
663 r_y
= tpg
->colors
[color
][0]; /* R or precalculated Y */
664 g_u
= tpg
->colors
[color
][1]; /* G or precalculated U */
665 b_v
= tpg
->colors
[color
][2]; /* B or precalculated V */
667 switch (tpg
->fourcc
) {
668 case V4L2_PIX_FMT_NV16M
:
669 buf
[0][offset
] = r_y
;
670 buf
[1][offset
] = odd
? b_v
: g_u
;
672 case V4L2_PIX_FMT_NV61M
:
673 buf
[0][offset
] = r_y
;
674 buf
[1][offset
] = odd
? g_u
: b_v
;
677 case V4L2_PIX_FMT_YUYV
:
678 buf
[0][offset
] = r_y
;
679 buf
[0][offset
+ 1] = odd
? b_v
: g_u
;
681 case V4L2_PIX_FMT_UYVY
:
682 buf
[0][offset
] = odd
? b_v
: g_u
;
683 buf
[0][offset
+ 1] = r_y
;
685 case V4L2_PIX_FMT_YVYU
:
686 buf
[0][offset
] = r_y
;
687 buf
[0][offset
+ 1] = odd
? g_u
: b_v
;
689 case V4L2_PIX_FMT_VYUY
:
690 buf
[0][offset
] = odd
? g_u
: b_v
;
691 buf
[0][offset
+ 1] = r_y
;
693 case V4L2_PIX_FMT_RGB565
:
694 buf
[0][offset
] = (g_u
<< 5) | b_v
;
695 buf
[0][offset
+ 1] = (r_y
<< 3) | (g_u
>> 3);
697 case V4L2_PIX_FMT_RGB565X
:
698 buf
[0][offset
] = (r_y
<< 3) | (g_u
>> 3);
699 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
701 case V4L2_PIX_FMT_RGB555
:
702 case V4L2_PIX_FMT_XRGB555
:
705 case V4L2_PIX_FMT_ARGB555
:
706 buf
[0][offset
] = (g_u
<< 5) | b_v
;
707 buf
[0][offset
+ 1] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
709 case V4L2_PIX_FMT_RGB555X
:
710 buf
[0][offset
] = (alpha
& 0x80) | (r_y
<< 2) | (g_u
>> 3);
711 buf
[0][offset
+ 1] = (g_u
<< 5) | b_v
;
713 case V4L2_PIX_FMT_RGB24
:
714 buf
[0][offset
] = r_y
;
715 buf
[0][offset
+ 1] = g_u
;
716 buf
[0][offset
+ 2] = b_v
;
718 case V4L2_PIX_FMT_BGR24
:
719 buf
[0][offset
] = b_v
;
720 buf
[0][offset
+ 1] = g_u
;
721 buf
[0][offset
+ 2] = r_y
;
723 case V4L2_PIX_FMT_RGB32
:
724 case V4L2_PIX_FMT_XRGB32
:
727 case V4L2_PIX_FMT_ARGB32
:
728 buf
[0][offset
] = alpha
;
729 buf
[0][offset
+ 1] = r_y
;
730 buf
[0][offset
+ 2] = g_u
;
731 buf
[0][offset
+ 3] = b_v
;
733 case V4L2_PIX_FMT_BGR32
:
734 case V4L2_PIX_FMT_XBGR32
:
737 case V4L2_PIX_FMT_ABGR32
:
738 buf
[0][offset
] = b_v
;
739 buf
[0][offset
+ 1] = g_u
;
740 buf
[0][offset
+ 2] = r_y
;
741 buf
[0][offset
+ 3] = alpha
;
746 /* Return how many pattern lines are used by the current pattern. */
747 static unsigned tpg_get_pat_lines(struct tpg_data
*tpg
)
749 switch (tpg
->pattern
) {
750 case TPG_PAT_CHECKERS_16X16
:
751 case TPG_PAT_CHECKERS_1X1
:
752 case TPG_PAT_ALTERNATING_HLINES
:
753 case TPG_PAT_CROSS_1_PIXEL
:
754 case TPG_PAT_CROSS_2_PIXELS
:
755 case TPG_PAT_CROSS_10_PIXELS
:
757 case TPG_PAT_100_COLORSQUARES
:
758 case TPG_PAT_100_HCOLORBAR
:
765 /* Which pattern line should be used for the given frame line. */
766 static unsigned tpg_get_pat_line(struct tpg_data
*tpg
, unsigned line
)
768 switch (tpg
->pattern
) {
769 case TPG_PAT_CHECKERS_16X16
:
770 return (line
>> 4) & 1;
771 case TPG_PAT_CHECKERS_1X1
:
772 case TPG_PAT_ALTERNATING_HLINES
:
774 case TPG_PAT_100_COLORSQUARES
:
775 case TPG_PAT_100_HCOLORBAR
:
776 return (line
* 8) / tpg
->src_height
;
777 case TPG_PAT_CROSS_1_PIXEL
:
778 return line
== tpg
->src_height
/ 2;
779 case TPG_PAT_CROSS_2_PIXELS
:
780 return (line
+ 1) / 2 == tpg
->src_height
/ 4;
781 case TPG_PAT_CROSS_10_PIXELS
:
782 return (line
+ 10) / 20 == tpg
->src_height
/ 40;
789 * Which color should be used for the given pattern line and X coordinate.
790 * Note: x is in the range 0 to 2 * tpg->src_width.
792 static enum tpg_color
tpg_get_color(struct tpg_data
*tpg
, unsigned pat_line
, unsigned x
)
794 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
795 should be modified */
796 static const enum tpg_color bars
[3][8] = {
797 /* Standard ITU-R 75% color bar sequence */
798 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_75_YELLOW
,
799 TPG_COLOR_75_CYAN
, TPG_COLOR_75_GREEN
,
800 TPG_COLOR_75_MAGENTA
, TPG_COLOR_75_RED
,
801 TPG_COLOR_75_BLUE
, TPG_COLOR_100_BLACK
, },
802 /* Standard ITU-R 100% color bar sequence */
803 { TPG_COLOR_100_WHITE
, TPG_COLOR_100_YELLOW
,
804 TPG_COLOR_100_CYAN
, TPG_COLOR_100_GREEN
,
805 TPG_COLOR_100_MAGENTA
, TPG_COLOR_100_RED
,
806 TPG_COLOR_100_BLUE
, TPG_COLOR_100_BLACK
, },
807 /* Color bar sequence suitable to test CSC */
808 { TPG_COLOR_CSC_WHITE
, TPG_COLOR_CSC_YELLOW
,
809 TPG_COLOR_CSC_CYAN
, TPG_COLOR_CSC_GREEN
,
810 TPG_COLOR_CSC_MAGENTA
, TPG_COLOR_CSC_RED
,
811 TPG_COLOR_CSC_BLUE
, TPG_COLOR_CSC_BLACK
, },
814 switch (tpg
->pattern
) {
815 case TPG_PAT_75_COLORBAR
:
816 case TPG_PAT_100_COLORBAR
:
817 case TPG_PAT_CSC_COLORBAR
:
818 return bars
[tpg
->pattern
][((x
* 8) / tpg
->src_width
) % 8];
819 case TPG_PAT_100_COLORSQUARES
:
820 return bars
[1][(pat_line
+ (x
* 8) / tpg
->src_width
) % 8];
821 case TPG_PAT_100_HCOLORBAR
:
822 return bars
[1][pat_line
];
824 return TPG_COLOR_100_BLACK
;
826 return TPG_COLOR_100_WHITE
;
828 return TPG_COLOR_100_RED
;
830 return TPG_COLOR_100_GREEN
;
832 return TPG_COLOR_100_BLUE
;
833 case TPG_PAT_CHECKERS_16X16
:
834 return (((x
>> 4) & 1) ^ (pat_line
& 1)) ?
835 TPG_COLOR_100_BLACK
: TPG_COLOR_100_WHITE
;
836 case TPG_PAT_CHECKERS_1X1
:
837 return ((x
& 1) ^ (pat_line
& 1)) ?
838 TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
839 case TPG_PAT_ALTERNATING_HLINES
:
840 return pat_line
? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
841 case TPG_PAT_ALTERNATING_VLINES
:
842 return (x
& 1) ? TPG_COLOR_100_WHITE
: TPG_COLOR_100_BLACK
;
843 case TPG_PAT_CROSS_1_PIXEL
:
844 if (pat_line
|| (x
% tpg
->src_width
) == tpg
->src_width
/ 2)
845 return TPG_COLOR_100_BLACK
;
846 return TPG_COLOR_100_WHITE
;
847 case TPG_PAT_CROSS_2_PIXELS
:
848 if (pat_line
|| ((x
% tpg
->src_width
) + 1) / 2 == tpg
->src_width
/ 4)
849 return TPG_COLOR_100_BLACK
;
850 return TPG_COLOR_100_WHITE
;
851 case TPG_PAT_CROSS_10_PIXELS
:
852 if (pat_line
|| ((x
% tpg
->src_width
) + 10) / 20 == tpg
->src_width
/ 40)
853 return TPG_COLOR_100_BLACK
;
854 return TPG_COLOR_100_WHITE
;
855 case TPG_PAT_GRAY_RAMP
:
856 return TPG_COLOR_RAMP
+ ((x
% tpg
->src_width
) * 256) / tpg
->src_width
;
858 return TPG_COLOR_100_RED
;
863 * Given the pixel aspect ratio and video aspect ratio calculate the
864 * coordinates of a centered square and the coordinates of the border of
865 * the active video area. The coordinates are relative to the source
868 static void tpg_calculate_square_border(struct tpg_data
*tpg
)
870 unsigned w
= tpg
->src_width
;
871 unsigned h
= tpg
->src_height
;
874 sq_w
= (w
* 2 / 5) & ~1;
875 if (((w
- sq_w
) / 2) & 1)
878 tpg
->square
.width
= sq_w
;
879 if (tpg
->vid_aspect
== TPG_VIDEO_ASPECT_16X9_ANAMORPHIC
) {
880 unsigned ana_sq_w
= (sq_w
/ 4) * 3;
882 if (((w
- ana_sq_w
) / 2) & 1)
884 tpg
->square
.width
= ana_sq_w
;
886 tpg
->square
.left
= (w
- tpg
->square
.width
) / 2;
887 if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
)
888 sq_h
= sq_w
* 10 / 11;
889 else if (tpg
->pix_aspect
== TPG_PIXEL_ASPECT_PAL
)
890 sq_h
= sq_w
* 59 / 54;
891 tpg
->square
.height
= sq_h
;
892 tpg
->square
.top
= (h
- sq_h
) / 2;
893 tpg
->border
.left
= 0;
894 tpg
->border
.width
= w
;
896 tpg
->border
.height
= h
;
897 switch (tpg
->vid_aspect
) {
898 case TPG_VIDEO_ASPECT_4X3
:
901 if (3 * w
>= 4 * h
) {
902 tpg
->border
.width
= ((4 * h
) / 3) & ~1;
903 if (((w
- tpg
->border
.width
) / 2) & ~1)
904 tpg
->border
.width
-= 2;
905 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
908 tpg
->border
.height
= ((3 * w
) / 4) & ~1;
909 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
911 case TPG_VIDEO_ASPECT_14X9_CENTRE
:
912 if (tpg
->pix_aspect
) {
913 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 420 : 506;
914 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
917 if (9 * w
>= 14 * h
) {
918 tpg
->border
.width
= ((14 * h
) / 9) & ~1;
919 if (((w
- tpg
->border
.width
) / 2) & ~1)
920 tpg
->border
.width
-= 2;
921 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
924 tpg
->border
.height
= ((9 * w
) / 14) & ~1;
925 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
927 case TPG_VIDEO_ASPECT_16X9_CENTRE
:
928 if (tpg
->pix_aspect
) {
929 tpg
->border
.height
= tpg
->pix_aspect
== TPG_PIXEL_ASPECT_NTSC
? 368 : 442;
930 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
933 if (9 * w
>= 16 * h
) {
934 tpg
->border
.width
= ((16 * h
) / 9) & ~1;
935 if (((w
- tpg
->border
.width
) / 2) & ~1)
936 tpg
->border
.width
-= 2;
937 tpg
->border
.left
= (w
- tpg
->border
.width
) / 2;
940 tpg
->border
.height
= ((9 * w
) / 16) & ~1;
941 tpg
->border
.top
= (h
- tpg
->border
.height
) / 2;
948 static void tpg_precalculate_line(struct tpg_data
*tpg
)
950 enum tpg_color contrast
;
955 switch (tpg
->pattern
) {
957 contrast
= TPG_COLOR_100_RED
;
959 case TPG_PAT_CSC_COLORBAR
:
960 contrast
= TPG_COLOR_CSC_GREEN
;
963 contrast
= TPG_COLOR_100_GREEN
;
967 for (pat
= 0; pat
< tpg_get_pat_lines(tpg
); pat
++) {
968 /* Coarse scaling with Bresenham */
969 unsigned int_part
= tpg
->src_width
/ tpg
->scaled_width
;
970 unsigned fract_part
= tpg
->src_width
% tpg
->scaled_width
;
974 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
975 unsigned real_x
= src_x
;
976 enum tpg_color color1
, color2
;
977 u8 pix
[TPG_MAX_PLANES
][8];
979 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
980 color1
= tpg_get_color(tpg
, pat
, real_x
);
984 if (error
>= tpg
->scaled_width
) {
985 error
-= tpg
->scaled_width
;
990 real_x
= tpg
->hflip
? tpg
->src_width
* 2 - real_x
- 2 : real_x
;
991 color2
= tpg_get_color(tpg
, pat
, real_x
);
995 if (error
>= tpg
->scaled_width
) {
996 error
-= tpg
->scaled_width
;
1000 gen_twopix(tpg
, pix
, tpg
->hflip
? color2
: color1
, 0);
1001 gen_twopix(tpg
, pix
, tpg
->hflip
? color1
: color2
, 1);
1002 for (p
= 0; p
< tpg
->planes
; p
++) {
1003 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1004 u8
*pos
= tpg
->lines
[pat
][p
] + x
* twopixsize
/ 2;
1006 memcpy(pos
, pix
[p
], twopixsize
);
1010 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1011 u8 pix
[TPG_MAX_PLANES
][8];
1013 gen_twopix(tpg
, pix
, contrast
, 0);
1014 gen_twopix(tpg
, pix
, contrast
, 1);
1015 for (p
= 0; p
< tpg
->planes
; p
++) {
1016 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1017 u8
*pos
= tpg
->contrast_line
[p
] + x
* twopixsize
/ 2;
1019 memcpy(pos
, pix
[p
], twopixsize
);
1022 for (x
= 0; x
< tpg
->scaled_width
; x
+= 2) {
1023 u8 pix
[TPG_MAX_PLANES
][8];
1025 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 0);
1026 gen_twopix(tpg
, pix
, TPG_COLOR_100_BLACK
, 1);
1027 for (p
= 0; p
< tpg
->planes
; p
++) {
1028 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1029 u8
*pos
= tpg
->black_line
[p
] + x
* twopixsize
/ 2;
1031 memcpy(pos
, pix
[p
], twopixsize
);
1034 for (x
= 0; x
< tpg
->scaled_width
* 2; x
+= 2) {
1035 u8 pix
[TPG_MAX_PLANES
][8];
1037 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 0);
1038 gen_twopix(tpg
, pix
, TPG_COLOR_RANDOM
, 1);
1039 for (p
= 0; p
< tpg
->planes
; p
++) {
1040 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1041 u8
*pos
= tpg
->random_line
[p
] + x
* twopixsize
/ 2;
1043 memcpy(pos
, pix
[p
], twopixsize
);
1046 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 0);
1047 gen_twopix(tpg
, tpg
->textbg
, TPG_COLOR_TEXTBG
, 1);
1048 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 0);
1049 gen_twopix(tpg
, tpg
->textfg
, TPG_COLOR_TEXTFG
, 1);
1052 /* need this to do rgb24 rendering */
1053 typedef struct { u16 __
; u8 _
; } __packed x24
;
1055 void tpg_gen_text(struct tpg_data
*tpg
, u8
*basep
[TPG_MAX_PLANES
][2],
1056 int y
, int x
, char *text
)
1059 unsigned step
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1060 unsigned div
= step
;
1062 unsigned len
= strlen(text
);
1065 if (font8x16
== NULL
|| basep
== NULL
)
1068 /* Checks if it is possible to show string */
1069 if (y
+ 16 >= tpg
->compose
.height
|| x
+ 8 >= tpg
->compose
.width
)
1072 if (len
> (tpg
->compose
.width
- x
) / 8)
1073 len
= (tpg
->compose
.width
- x
) / 8;
1075 y
= tpg
->compose
.height
- y
- 16;
1077 x
= tpg
->compose
.width
- x
- 8;
1078 y
+= tpg
->compose
.top
;
1079 x
+= tpg
->compose
.left
;
1080 if (tpg
->field
== V4L2_FIELD_BOTTOM
)
1082 else if (tpg
->field
== V4L2_FIELD_SEQ_TB
|| tpg
->field
== V4L2_FIELD_SEQ_BT
)
1085 for (p
= 0; p
< tpg
->planes
; p
++) {
1086 /* Print stream time */
1087 #define PRINTSTR(PIXTYPE) do { \
1090 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1091 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1093 for (line = first; line < 16; line += step) { \
1094 int l = tpg->vflip ? 15 - line : line; \
1095 PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
1096 ((y * step + l) / div) * tpg->bytesperline[p] + \
1097 x * sizeof(PIXTYPE)); \
1100 for (s = 0; s < len; s++) { \
1101 u8 chr = font8x16[text[s] * 16 + line]; \
1104 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1105 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1106 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1107 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1108 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1109 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1110 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1111 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1113 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1114 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1115 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1116 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1117 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1118 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1119 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1120 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1123 pos += tpg->hflip ? -8 : 8; \
1128 switch (tpg
->twopixelsize
[p
]) {
1130 PRINTSTR(u8
); break;
1132 PRINTSTR(u16
); break;
1134 PRINTSTR(x24
); break;
1136 PRINTSTR(u32
); break;
1141 void tpg_update_mv_step(struct tpg_data
*tpg
)
1143 int factor
= tpg
->mv_hor_mode
> TPG_MOVE_NONE
? -1 : 1;
1147 switch (tpg
->mv_hor_mode
) {
1148 case TPG_MOVE_NEG_FAST
:
1149 case TPG_MOVE_POS_FAST
:
1150 tpg
->mv_hor_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1154 tpg
->mv_hor_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1156 case TPG_MOVE_NEG_SLOW
:
1157 case TPG_MOVE_POS_SLOW
:
1158 tpg
->mv_hor_step
= 2;
1161 tpg
->mv_hor_step
= 0;
1165 tpg
->mv_hor_step
= tpg
->src_width
- tpg
->mv_hor_step
;
1167 factor
= tpg
->mv_vert_mode
> TPG_MOVE_NONE
? -1 : 1;
1168 switch (tpg
->mv_vert_mode
) {
1169 case TPG_MOVE_NEG_FAST
:
1170 case TPG_MOVE_POS_FAST
:
1171 tpg
->mv_vert_step
= ((tpg
->src_width
+ 319) / 320) * 4;
1175 tpg
->mv_vert_step
= ((tpg
->src_width
+ 639) / 640) * 4;
1177 case TPG_MOVE_NEG_SLOW
:
1178 case TPG_MOVE_POS_SLOW
:
1179 tpg
->mv_vert_step
= 1;
1182 tpg
->mv_vert_step
= 0;
1186 tpg
->mv_vert_step
= tpg
->src_height
- tpg
->mv_vert_step
;
1189 /* Map the line number relative to the crop rectangle to a frame line number */
1190 static unsigned tpg_calc_frameline(struct tpg_data
*tpg
, unsigned src_y
,
1194 case V4L2_FIELD_TOP
:
1195 return tpg
->crop
.top
+ src_y
* 2;
1196 case V4L2_FIELD_BOTTOM
:
1197 return tpg
->crop
.top
+ src_y
* 2 + 1;
1199 return src_y
+ tpg
->crop
.top
;
1204 * Map the line number relative to the compose rectangle to a destination
1205 * buffer line number.
1207 static unsigned tpg_calc_buffer_line(struct tpg_data
*tpg
, unsigned y
,
1210 y
+= tpg
->compose
.top
;
1212 case V4L2_FIELD_SEQ_TB
:
1214 return tpg
->buf_height
/ 2 + y
/ 2;
1216 case V4L2_FIELD_SEQ_BT
:
1219 return tpg
->buf_height
/ 2 + y
/ 2;
1225 static void tpg_recalc(struct tpg_data
*tpg
)
1227 if (tpg
->recalc_colors
) {
1228 tpg
->recalc_colors
= false;
1229 tpg
->recalc_lines
= true;
1230 tpg
->real_ycbcr_enc
= tpg
->ycbcr_enc
;
1231 tpg
->real_quantization
= tpg
->quantization
;
1232 if (tpg
->ycbcr_enc
== V4L2_YCBCR_ENC_DEFAULT
) {
1233 switch (tpg
->colorspace
) {
1234 case V4L2_COLORSPACE_REC709
:
1235 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_709
;
1237 case V4L2_COLORSPACE_SRGB
:
1238 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SYCC
;
1240 case V4L2_COLORSPACE_BT2020
:
1241 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_BT2020
;
1243 case V4L2_COLORSPACE_SMPTE240M
:
1244 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_SMPTE240M
;
1246 case V4L2_COLORSPACE_SMPTE170M
:
1247 case V4L2_COLORSPACE_470_SYSTEM_M
:
1248 case V4L2_COLORSPACE_470_SYSTEM_BG
:
1249 case V4L2_COLORSPACE_ADOBERGB
:
1251 tpg
->real_ycbcr_enc
= V4L2_YCBCR_ENC_601
;
1255 if (tpg
->quantization
== V4L2_QUANTIZATION_DEFAULT
) {
1256 tpg
->real_quantization
= V4L2_QUANTIZATION_FULL_RANGE
;
1258 switch (tpg
->real_ycbcr_enc
) {
1259 case V4L2_YCBCR_ENC_SYCC
:
1260 case V4L2_YCBCR_ENC_XV601
:
1261 case V4L2_YCBCR_ENC_XV709
:
1264 tpg
->real_quantization
=
1265 V4L2_QUANTIZATION_LIM_RANGE
;
1270 tpg_precalculate_colors(tpg
);
1272 if (tpg
->recalc_square_border
) {
1273 tpg
->recalc_square_border
= false;
1274 tpg_calculate_square_border(tpg
);
1276 if (tpg
->recalc_lines
) {
1277 tpg
->recalc_lines
= false;
1278 tpg_precalculate_line(tpg
);
1282 void tpg_calc_text_basep(struct tpg_data
*tpg
,
1283 u8
*basep
[TPG_MAX_PLANES
][2], unsigned p
, u8
*vbuf
)
1285 unsigned stride
= tpg
->bytesperline
[p
];
1291 if (tpg
->field
== V4L2_FIELD_SEQ_TB
)
1292 basep
[p
][1] += tpg
->buf_height
* stride
/ 2;
1293 else if (tpg
->field
== V4L2_FIELD_SEQ_BT
)
1294 basep
[p
][0] += tpg
->buf_height
* stride
/ 2;
1297 void tpg_fillbuffer(struct tpg_data
*tpg
, v4l2_std_id std
, unsigned p
, u8
*vbuf
)
1300 bool is_60hz
= is_tv
&& (std
& V4L2_STD_525_60
);
1301 unsigned mv_hor_old
= tpg
->mv_hor_count
% tpg
->src_width
;
1302 unsigned mv_hor_new
= (tpg
->mv_hor_count
+ tpg
->mv_hor_step
) % tpg
->src_width
;
1303 unsigned mv_vert_old
= tpg
->mv_vert_count
% tpg
->src_height
;
1304 unsigned mv_vert_new
= (tpg
->mv_vert_count
+ tpg
->mv_vert_step
) % tpg
->src_height
;
1307 int hmax
= (tpg
->compose
.height
* tpg
->perc_fill
) / 100;
1309 unsigned twopixsize
= tpg
->twopixelsize
[p
];
1310 unsigned img_width
= tpg
->compose
.width
* twopixsize
/ 2;
1311 unsigned line_offset
;
1312 unsigned left_pillar_width
= 0;
1313 unsigned right_pillar_start
= img_width
;
1314 unsigned stride
= tpg
->bytesperline
[p
];
1315 unsigned factor
= V4L2_FIELD_HAS_T_OR_B(tpg
->field
) ? 2 : 1;
1316 u8
*orig_vbuf
= vbuf
;
1318 /* Coarse scaling with Bresenham */
1319 unsigned int_part
= (tpg
->crop
.height
/ factor
) / tpg
->compose
.height
;
1320 unsigned fract_part
= (tpg
->crop
.height
/ factor
) % tpg
->compose
.height
;
1326 mv_hor_old
= (mv_hor_old
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1327 mv_hor_new
= (mv_hor_new
* tpg
->scaled_width
/ tpg
->src_width
) & ~1;
1328 wss_width
= tpg
->crop
.left
< tpg
->src_width
/ 2 ?
1329 tpg
->src_width
/ 2 - tpg
->crop
.left
: 0;
1330 if (wss_width
> tpg
->crop
.width
)
1331 wss_width
= tpg
->crop
.width
;
1332 wss_width
= wss_width
* tpg
->scaled_width
/ tpg
->src_width
;
1334 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1335 line_offset
= tpg
->crop
.left
* tpg
->scaled_width
/ tpg
->src_width
;
1336 line_offset
= (line_offset
& ~1) * twopixsize
/ 2;
1337 if (tpg
->crop
.left
< tpg
->border
.left
) {
1338 left_pillar_width
= tpg
->border
.left
- tpg
->crop
.left
;
1339 if (left_pillar_width
> tpg
->crop
.width
)
1340 left_pillar_width
= tpg
->crop
.width
;
1341 left_pillar_width
= (left_pillar_width
* tpg
->scaled_width
) / tpg
->src_width
;
1342 left_pillar_width
= (left_pillar_width
& ~1) * twopixsize
/ 2;
1344 if (tpg
->crop
.left
+ tpg
->crop
.width
> tpg
->border
.left
+ tpg
->border
.width
) {
1345 right_pillar_start
= tpg
->border
.left
+ tpg
->border
.width
- tpg
->crop
.left
;
1346 right_pillar_start
= (right_pillar_start
* tpg
->scaled_width
) / tpg
->src_width
;
1347 right_pillar_start
= (right_pillar_start
& ~1) * twopixsize
/ 2;
1348 if (right_pillar_start
> img_width
)
1349 right_pillar_start
= img_width
;
1352 f
= tpg
->field
== (is_60hz
? V4L2_FIELD_TOP
: V4L2_FIELD_BOTTOM
);
1354 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1356 bool fill_blank
= false;
1357 unsigned frame_line
;
1359 unsigned pat_line_old
;
1360 unsigned pat_line_new
;
1361 u8
*linestart_older
;
1362 u8
*linestart_newer
;
1364 u8
*linestart_bottom
;
1366 frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1367 even
= !(frame_line
& 1);
1368 buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1370 error
+= fract_part
;
1371 if (error
>= tpg
->compose
.height
) {
1372 error
-= tpg
->compose
.height
;
1377 if (hmax
== tpg
->compose
.height
)
1379 if (!tpg
->perc_fill_blank
)
1385 frame_line
= tpg
->src_height
- frame_line
- 1;
1388 linestart_older
= tpg
->contrast_line
[p
];
1389 linestart_newer
= tpg
->contrast_line
[p
];
1390 } else if (tpg
->qual
!= TPG_QUAL_NOISE
&&
1391 (frame_line
< tpg
->border
.top
||
1392 frame_line
>= tpg
->border
.top
+ tpg
->border
.height
)) {
1393 linestart_older
= tpg
->black_line
[p
];
1394 linestart_newer
= tpg
->black_line
[p
];
1395 } else if (tpg
->pattern
== TPG_PAT_NOISE
|| tpg
->qual
== TPG_QUAL_NOISE
) {
1396 linestart_older
= tpg
->random_line
[p
] +
1397 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1398 linestart_newer
= tpg
->random_line
[p
] +
1399 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1401 pat_line_old
= tpg_get_pat_line(tpg
,
1402 (frame_line
+ mv_vert_old
) % tpg
->src_height
);
1403 pat_line_new
= tpg_get_pat_line(tpg
,
1404 (frame_line
+ mv_vert_new
) % tpg
->src_height
);
1405 linestart_older
= tpg
->lines
[pat_line_old
][p
] +
1406 mv_hor_old
* twopixsize
/ 2;
1407 linestart_newer
= tpg
->lines
[pat_line_new
][p
] +
1408 mv_hor_new
* twopixsize
/ 2;
1409 linestart_older
+= line_offset
;
1410 linestart_newer
+= line_offset
;
1413 linestart_top
= linestart_newer
;
1414 linestart_bottom
= linestart_older
;
1416 linestart_top
= linestart_older
;
1417 linestart_bottom
= linestart_newer
;
1420 switch (tpg
->field
) {
1421 case V4L2_FIELD_INTERLACED
:
1422 case V4L2_FIELD_INTERLACED_TB
:
1423 case V4L2_FIELD_SEQ_TB
:
1424 case V4L2_FIELD_SEQ_BT
:
1426 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1428 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1430 case V4L2_FIELD_INTERLACED_BT
:
1432 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1434 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1436 case V4L2_FIELD_TOP
:
1437 memcpy(vbuf
+ buf_line
* stride
, linestart_top
, img_width
);
1439 case V4L2_FIELD_BOTTOM
:
1440 memcpy(vbuf
+ buf_line
* stride
, linestart_bottom
, img_width
);
1442 case V4L2_FIELD_NONE
:
1444 memcpy(vbuf
+ buf_line
* stride
, linestart_older
, img_width
);
1448 if (is_tv
&& !is_60hz
&& frame_line
== 0 && wss_width
) {
1450 * Replace the first half of the top line of a 50 Hz frame
1451 * with random data to simulate a WSS signal.
1453 u8
*wss
= tpg
->random_line
[p
] +
1454 twopixsize
* prandom_u32_max(tpg
->src_width
/ 2);
1456 memcpy(vbuf
+ buf_line
* stride
, wss
, wss_width
* twopixsize
/ 2);
1461 vbuf
+= tpg
->compose
.left
* twopixsize
/ 2;
1464 for (h
= 0; h
< tpg
->compose
.height
; h
++) {
1465 unsigned frame_line
= tpg_calc_frameline(tpg
, src_y
, tpg
->field
);
1466 unsigned buf_line
= tpg_calc_buffer_line(tpg
, h
, tpg
->field
);
1467 const struct v4l2_rect
*sq
= &tpg
->square
;
1468 const struct v4l2_rect
*b
= &tpg
->border
;
1469 const struct v4l2_rect
*c
= &tpg
->crop
;
1472 error
+= fract_part
;
1473 if (error
>= tpg
->compose
.height
) {
1474 error
-= tpg
->compose
.height
;
1478 if (tpg
->show_border
&& frame_line
>= b
->top
&&
1479 frame_line
< b
->top
+ b
->height
) {
1480 unsigned bottom
= b
->top
+ b
->height
- 1;
1481 unsigned left
= left_pillar_width
;
1482 unsigned right
= right_pillar_start
;
1484 if (frame_line
== b
->top
|| frame_line
== b
->top
+ 1 ||
1485 frame_line
== bottom
|| frame_line
== bottom
- 1) {
1486 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
],
1489 if (b
->left
>= c
->left
&&
1490 b
->left
< c
->left
+ c
->width
)
1491 memcpy(vbuf
+ buf_line
* stride
+ left
,
1492 tpg
->contrast_line
[p
], twopixsize
);
1493 if (b
->left
+ b
->width
> c
->left
&&
1494 b
->left
+ b
->width
<= c
->left
+ c
->width
)
1495 memcpy(vbuf
+ buf_line
* stride
+ right
- twopixsize
,
1496 tpg
->contrast_line
[p
], twopixsize
);
1499 if (tpg
->qual
!= TPG_QUAL_NOISE
&& frame_line
>= b
->top
&&
1500 frame_line
< b
->top
+ b
->height
) {
1501 memcpy(vbuf
+ buf_line
* stride
, tpg
->black_line
[p
], left_pillar_width
);
1502 memcpy(vbuf
+ buf_line
* stride
+ right_pillar_start
, tpg
->black_line
[p
],
1503 img_width
- right_pillar_start
);
1505 if (tpg
->show_square
&& frame_line
>= sq
->top
&&
1506 frame_line
< sq
->top
+ sq
->height
&&
1507 sq
->left
< c
->left
+ c
->width
&&
1508 sq
->left
+ sq
->width
>= c
->left
) {
1509 unsigned left
= sq
->left
;
1510 unsigned width
= sq
->width
;
1512 if (c
->left
> left
) {
1513 width
-= c
->left
- left
;
1516 if (c
->left
+ c
->width
< left
+ width
)
1517 width
-= left
+ width
- c
->left
- c
->width
;
1519 left
= (left
* tpg
->scaled_width
) / tpg
->src_width
;
1520 left
= (left
& ~1) * twopixsize
/ 2;
1521 width
= (width
* tpg
->scaled_width
) / tpg
->src_width
;
1522 width
= (width
& ~1) * twopixsize
/ 2;
1523 memcpy(vbuf
+ buf_line
* stride
+ left
, tpg
->contrast_line
[p
], width
);
1525 if (tpg
->insert_sav
) {
1526 unsigned offset
= (tpg
->compose
.width
/ 6) * twopixsize
;
1527 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1528 unsigned vact
= 0, hact
= 0;
1533 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1534 ((hact
^ vact
) << 3) |
1539 if (tpg
->insert_eav
) {
1540 unsigned offset
= (tpg
->compose
.width
/ 6) * 2 * twopixsize
;
1541 u8
*p
= vbuf
+ buf_line
* stride
+ offset
;
1542 unsigned vact
= 0, hact
= 1;
1547 p
[3] = 0x80 | (f
<< 6) | (vact
<< 5) | (hact
<< 4) |
1548 ((hact
^ vact
) << 3) |