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 u16 color_to_y(struct tpg_data *tpg, int r, int g, int b)
301 switch (tpg->colorspace) {
302 case V4L2_COLORSPACE_SMPTE170M:
303 case V4L2_COLORSPACE_470_SYSTEM_M:
304 case V4L2_COLORSPACE_470_SYSTEM_BG:
305 return ((16829 * r + 33039 * g + 6416 * b + 16 * 32768) >> 16) + (16 << 4);
306 case V4L2_COLORSPACE_SMPTE240M:
307 return ((11932 * r + 39455 * g + 4897 * b + 16 * 32768) >> 16) + (16 << 4);
308 case V4L2_COLORSPACE_REC709:
309 case V4L2_COLORSPACE_SRGB:
311 return ((11966 * r + 40254 * g + 4064 * b + 16 * 32768) >> 16) + (16 << 4);
315 static u16 color_to_cb(struct tpg_data *tpg, int r, int g, int b)
317 switch (tpg->colorspace) {
318 case V4L2_COLORSPACE_SMPTE170M:
319 case V4L2_COLORSPACE_470_SYSTEM_M:
320 case V4L2_COLORSPACE_470_SYSTEM_BG:
321 return ((-9714 * r - 19070 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
322 case V4L2_COLORSPACE_SMPTE240M:
323 return ((-6684 * r - 22100 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
324 case V4L2_COLORSPACE_REC709:
325 case V4L2_COLORSPACE_SRGB:
327 return ((-6596 * r - 22189 * g + 28784 * b + 16 * 32768) >> 16) + (128 << 4);
331 static u16 color_to_cr(struct tpg_data *tpg, int r, int g, int b)
333 switch (tpg->colorspace) {
334 case V4L2_COLORSPACE_SMPTE170M:
335 case V4L2_COLORSPACE_470_SYSTEM_M:
336 case V4L2_COLORSPACE_470_SYSTEM_BG:
337 return ((28784 * r - 24103 * g - 4681 * b + 16 * 32768) >> 16) + (128 << 4);
338 case V4L2_COLORSPACE_SMPTE240M:
339 return ((28784 * r - 25606 * g - 3178 * b + 16 * 32768) >> 16) + (128 << 4);
340 case V4L2_COLORSPACE_REC709:
341 case V4L2_COLORSPACE_SRGB:
343 return ((28784 * r - 26145 * g - 2639 * b + 16 * 32768) >> 16) + (128 << 4);
347 static u16 ycbcr_to_r(struct tpg_data *tpg, int y, int cb, int cr)
354 switch (tpg->colorspace) {
355 case V4L2_COLORSPACE_SMPTE170M:
356 case V4L2_COLORSPACE_470_SYSTEM_M:
357 case V4L2_COLORSPACE_470_SYSTEM_BG:
358 r = 4769 * y + 6537 * cr;
360 case V4L2_COLORSPACE_SMPTE240M:
361 r = 4769 * y + 7376 * cr;
363 case V4L2_COLORSPACE_REC709:
364 case V4L2_COLORSPACE_SRGB:
366 r = 4769 * y + 7343 * cr;
369 return clamp(r >> 12, 0, 0xff0);
372 static u16 ycbcr_to_g(struct tpg_data *tpg, int y, int cb, int cr)
379 switch (tpg->colorspace) {
380 case V4L2_COLORSPACE_SMPTE170M:
381 case V4L2_COLORSPACE_470_SYSTEM_M:
382 case V4L2_COLORSPACE_470_SYSTEM_BG:
383 g = 4769 * y - 1605 * cb - 3330 * cr;
385 case V4L2_COLORSPACE_SMPTE240M:
386 g = 4769 * y - 1055 * cb - 2341 * cr;
388 case V4L2_COLORSPACE_REC709:
389 case V4L2_COLORSPACE_SRGB:
391 g = 4769 * y - 873 * cb - 2183 * cr;
394 return clamp(g >> 12, 0, 0xff0);
397 static u16 ycbcr_to_b(struct tpg_data *tpg, int y, int cb, int cr)
404 switch (tpg->colorspace) {
405 case V4L2_COLORSPACE_SMPTE170M:
406 case V4L2_COLORSPACE_470_SYSTEM_M:
407 case V4L2_COLORSPACE_470_SYSTEM_BG:
408 b = 4769 * y + 7343 * cb;
410 case V4L2_COLORSPACE_SMPTE240M:
411 b = 4769 * y + 8552 * cb;
413 case V4L2_COLORSPACE_REC709:
414 case V4L2_COLORSPACE_SRGB:
416 b = 4769 * y + 8652 * cb;
419 return clamp(b >> 12, 0, 0xff0);
422 /* precalculate color bar values to speed up rendering */
423 static void precalculate_color(struct tpg_data *tpg, int k)
426 int r = tpg_colors[col].r;
427 int g = tpg_colors[col].g;
428 int b = tpg_colors[col].b;
430 if (k == TPG_COLOR_TEXTBG) {
431 col = tpg_get_textbg_color(tpg);
433 r = tpg_colors[col].r;
434 g = tpg_colors[col].g;
435 b = tpg_colors[col].b;
436 } else if (k == TPG_COLOR_TEXTFG) {
437 col = tpg_get_textfg_color(tpg);
439 r = tpg_colors[col].r;
440 g = tpg_colors[col].g;
441 b = tpg_colors[col].b;
442 } else if (tpg->pattern == TPG_PAT_NOISE) {
443 r = g = b = prandom_u32_max(256);
444 } else if (k == TPG_COLOR_RANDOM) {
445 r = g = b = tpg->qual_offset + prandom_u32_max(196);
446 } else if (k >= TPG_COLOR_RAMP) {
447 r = g = b = k - TPG_COLOR_RAMP;
450 if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
451 r = tpg_csc_colors[tpg->colorspace][col].r;
452 g = tpg_csc_colors[tpg->colorspace][col].g;
453 b = tpg_csc_colors[tpg->colorspace][col].b;
459 if (tpg->qual == TPG_QUAL_GRAY)
460 r = g = b = color_to_y(tpg, r, g, b);
463 * The assumption is that the RGB output is always full range,
464 * so only if the rgb_range overrides the 'real' rgb range do
465 * we need to convert the RGB values.
467 * Currently there is no way of signalling to userspace if you
468 * are actually giving it limited range RGB (or full range
469 * YUV for that matter).
471 * Remember that r, g and b are still in the 0 - 0xff0 range.
473 if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
474 tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
476 * Convert from full range (which is what r, g and b are)
477 * to limited range (which is the 'real' RGB range), which
478 * is then interpreted as full range.
480 r = (r * 219) / 255 + (16 << 4);
481 g = (g * 219) / 255 + (16 << 4);
482 b = (b * 219) / 255 + (16 << 4);
483 } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
484 tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
486 * Clamp r, g and b to the limited range and convert to full
487 * range since that's what we deliver.
489 r = clamp(r, 16 << 4, 235 << 4);
490 g = clamp(g, 16 << 4, 235 << 4);
491 b = clamp(b, 16 << 4, 235 << 4);
492 r = (r - (16 << 4)) * 255 / 219;
493 g = (g - (16 << 4)) * 255 / 219;
494 b = (b - (16 << 4)) * 255 / 219;
497 if (tpg->brightness != 128 || tpg->contrast != 128 ||
498 tpg->saturation != 128 || tpg->hue) {
499 /* Implement these operations */
501 /* First convert to YCbCr */
502 int y = color_to_y(tpg, r, g, b); /* Luma */
503 int cb = color_to_cb(tpg, r, g, b); /* Cb */
504 int cr = color_to_cr(tpg, r, g, b); /* Cr */
507 y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
508 y += (tpg->brightness << 4) - (128 << 4);
512 tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
513 tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
515 cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
516 cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
518 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
519 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
520 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
523 r = ycbcr_to_r(tpg, y, cb, cr);
524 g = ycbcr_to_g(tpg, y, cb, cr);
525 b = ycbcr_to_b(tpg, y, cb, cr);
529 /* Convert to YCbCr */
530 u16 y = color_to_y(tpg, r, g, b); /* Luma */
531 u16 cb = color_to_cb(tpg, r, g, b); /* Cb */
532 u16 cr = color_to_cr(tpg, r, g, b); /* Cr */
534 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
535 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
536 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
538 switch (tpg->fourcc) {
539 case V4L2_PIX_FMT_RGB565:
540 case V4L2_PIX_FMT_RGB565X:
545 case V4L2_PIX_FMT_RGB555:
546 case V4L2_PIX_FMT_XRGB555:
547 case V4L2_PIX_FMT_ARGB555:
548 case V4L2_PIX_FMT_RGB555X:
560 tpg->colors[k][0] = r;
561 tpg->colors[k][1] = g;
562 tpg->colors[k][2] = b;
566 static void tpg_precalculate_colors(struct tpg_data *tpg)
570 for (k = 0; k < TPG_COLOR_MAX; k++)
571 precalculate_color(tpg, k);
574 /* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
575 static void gen_twopix(struct tpg_data *tpg,
576 u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
578 unsigned offset = odd * tpg->twopixelsize[0] / 2;
579 u8 alpha = tpg->alpha_component;
582 if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
583 color != TPG_COLOR_100_RED &&
584 color != TPG_COLOR_75_RED)
586 if (color == TPG_COLOR_RANDOM)
587 precalculate_color(tpg, color);
588 r_y = tpg->colors[color][0]; /* R or precalculated Y */
589 g_u = tpg->colors[color][1]; /* G or precalculated U */
590 b_v = tpg->colors[color][2]; /* B or precalculated V */
592 switch (tpg->fourcc) {
593 case V4L2_PIX_FMT_NV16M:
594 buf[0][offset] = r_y;
595 buf[1][offset] = odd ? b_v : g_u;
597 case V4L2_PIX_FMT_NV61M:
598 buf[0][offset] = r_y;
599 buf[1][offset] = odd ? g_u : b_v;
602 case V4L2_PIX_FMT_YUYV:
603 buf[0][offset] = r_y;
604 buf[0][offset + 1] = odd ? b_v : g_u;
606 case V4L2_PIX_FMT_UYVY:
607 buf[0][offset] = odd ? b_v : g_u;
608 buf[0][offset + 1] = r_y;
610 case V4L2_PIX_FMT_YVYU:
611 buf[0][offset] = r_y;
612 buf[0][offset + 1] = odd ? g_u : b_v;
614 case V4L2_PIX_FMT_VYUY:
615 buf[0][offset] = odd ? g_u : b_v;
616 buf[0][offset + 1] = r_y;
618 case V4L2_PIX_FMT_RGB565:
619 buf[0][offset] = (g_u << 5) | b_v;
620 buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
622 case V4L2_PIX_FMT_RGB565X:
623 buf[0][offset] = (r_y << 3) | (g_u >> 3);
624 buf[0][offset + 1] = (g_u << 5) | b_v;
626 case V4L2_PIX_FMT_RGB555:
627 case V4L2_PIX_FMT_XRGB555:
630 case V4L2_PIX_FMT_ARGB555:
631 buf[0][offset] = (g_u << 5) | b_v;
632 buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
634 case V4L2_PIX_FMT_RGB555X:
635 buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
636 buf[0][offset + 1] = (g_u << 5) | b_v;
638 case V4L2_PIX_FMT_RGB24:
639 buf[0][offset] = r_y;
640 buf[0][offset + 1] = g_u;
641 buf[0][offset + 2] = b_v;
643 case V4L2_PIX_FMT_BGR24:
644 buf[0][offset] = b_v;
645 buf[0][offset + 1] = g_u;
646 buf[0][offset + 2] = r_y;
648 case V4L2_PIX_FMT_RGB32:
649 case V4L2_PIX_FMT_XRGB32:
652 case V4L2_PIX_FMT_ARGB32:
653 buf[0][offset] = alpha;
654 buf[0][offset + 1] = r_y;
655 buf[0][offset + 2] = g_u;
656 buf[0][offset + 3] = b_v;
658 case V4L2_PIX_FMT_BGR32:
659 case V4L2_PIX_FMT_XBGR32:
662 case V4L2_PIX_FMT_ABGR32:
663 buf[0][offset] = b_v;
664 buf[0][offset + 1] = g_u;
665 buf[0][offset + 2] = r_y;
666 buf[0][offset + 3] = alpha;
671 /* Return how many pattern lines are used by the current pattern. */
672 static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
674 switch (tpg->pattern) {
675 case TPG_PAT_CHECKERS_16X16:
676 case TPG_PAT_CHECKERS_1X1:
677 case TPG_PAT_ALTERNATING_HLINES:
678 case TPG_PAT_CROSS_1_PIXEL:
679 case TPG_PAT_CROSS_2_PIXELS:
680 case TPG_PAT_CROSS_10_PIXELS:
682 case TPG_PAT_100_COLORSQUARES:
683 case TPG_PAT_100_HCOLORBAR:
690 /* Which pattern line should be used for the given frame line. */
691 static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
693 switch (tpg->pattern) {
694 case TPG_PAT_CHECKERS_16X16:
695 return (line >> 4) & 1;
696 case TPG_PAT_CHECKERS_1X1:
697 case TPG_PAT_ALTERNATING_HLINES:
699 case TPG_PAT_100_COLORSQUARES:
700 case TPG_PAT_100_HCOLORBAR:
701 return (line * 8) / tpg->src_height;
702 case TPG_PAT_CROSS_1_PIXEL:
703 return line == tpg->src_height / 2;
704 case TPG_PAT_CROSS_2_PIXELS:
705 return (line + 1) / 2 == tpg->src_height / 4;
706 case TPG_PAT_CROSS_10_PIXELS:
707 return (line + 10) / 20 == tpg->src_height / 40;
714 * Which color should be used for the given pattern line and X coordinate.
715 * Note: x is in the range 0 to 2 * tpg->src_width.
717 static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x)
719 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
720 should be modified */
721 static const enum tpg_color bars[3][8] = {
722 /* Standard ITU-R 75% color bar sequence */
723 { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW,
724 TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN,
725 TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED,
726 TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, },
727 /* Standard ITU-R 100% color bar sequence */
728 { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW,
729 TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN,
730 TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
731 TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, },
732 /* Color bar sequence suitable to test CSC */
733 { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW,
734 TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN,
735 TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
736 TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, },
739 switch (tpg->pattern) {
740 case TPG_PAT_75_COLORBAR:
741 case TPG_PAT_100_COLORBAR:
742 case TPG_PAT_CSC_COLORBAR:
743 return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
744 case TPG_PAT_100_COLORSQUARES:
745 return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
746 case TPG_PAT_100_HCOLORBAR:
747 return bars[1][pat_line];
749 return TPG_COLOR_100_BLACK;
751 return TPG_COLOR_100_WHITE;
753 return TPG_COLOR_100_RED;
755 return TPG_COLOR_100_GREEN;
757 return TPG_COLOR_100_BLUE;
758 case TPG_PAT_CHECKERS_16X16:
759 return (((x >> 4) & 1) ^ (pat_line & 1)) ?
760 TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
761 case TPG_PAT_CHECKERS_1X1:
762 return ((x & 1) ^ (pat_line & 1)) ?
763 TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
764 case TPG_PAT_ALTERNATING_HLINES:
765 return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
766 case TPG_PAT_ALTERNATING_VLINES:
767 return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
768 case TPG_PAT_CROSS_1_PIXEL:
769 if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
770 return TPG_COLOR_100_BLACK;
771 return TPG_COLOR_100_WHITE;
772 case TPG_PAT_CROSS_2_PIXELS:
773 if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
774 return TPG_COLOR_100_BLACK;
775 return TPG_COLOR_100_WHITE;
776 case TPG_PAT_CROSS_10_PIXELS:
777 if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
778 return TPG_COLOR_100_BLACK;
779 return TPG_COLOR_100_WHITE;
780 case TPG_PAT_GRAY_RAMP:
781 return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
783 return TPG_COLOR_100_RED;
788 * Given the pixel aspect ratio and video aspect ratio calculate the
789 * coordinates of a centered square and the coordinates of the border of
790 * the active video area. The coordinates are relative to the source
793 static void tpg_calculate_square_border(struct tpg_data *tpg)
795 unsigned w = tpg->src_width;
796 unsigned h = tpg->src_height;
799 sq_w = (w * 2 / 5) & ~1;
800 if (((w - sq_w) / 2) & 1)
803 tpg->square.width = sq_w;
804 if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
805 unsigned ana_sq_w = (sq_w / 4) * 3;
807 if (((w - ana_sq_w) / 2) & 1)
809 tpg->square.width = ana_sq_w;
811 tpg->square.left = (w - tpg->square.width) / 2;
812 if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
813 sq_h = sq_w * 10 / 11;
814 else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
815 sq_h = sq_w * 59 / 54;
816 tpg->square.height = sq_h;
817 tpg->square.top = (h - sq_h) / 2;
818 tpg->border.left = 0;
819 tpg->border.width = w;
821 tpg->border.height = h;
822 switch (tpg->vid_aspect) {
823 case TPG_VIDEO_ASPECT_4X3:
826 if (3 * w >= 4 * h) {
827 tpg->border.width = ((4 * h) / 3) & ~1;
828 if (((w - tpg->border.width) / 2) & ~1)
829 tpg->border.width -= 2;
830 tpg->border.left = (w - tpg->border.width) / 2;
833 tpg->border.height = ((3 * w) / 4) & ~1;
834 tpg->border.top = (h - tpg->border.height) / 2;
836 case TPG_VIDEO_ASPECT_14X9_CENTRE:
837 if (tpg->pix_aspect) {
838 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
839 tpg->border.top = (h - tpg->border.height) / 2;
842 if (9 * w >= 14 * h) {
843 tpg->border.width = ((14 * h) / 9) & ~1;
844 if (((w - tpg->border.width) / 2) & ~1)
845 tpg->border.width -= 2;
846 tpg->border.left = (w - tpg->border.width) / 2;
849 tpg->border.height = ((9 * w) / 14) & ~1;
850 tpg->border.top = (h - tpg->border.height) / 2;
852 case TPG_VIDEO_ASPECT_16X9_CENTRE:
853 if (tpg->pix_aspect) {
854 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
855 tpg->border.top = (h - tpg->border.height) / 2;
858 if (9 * w >= 16 * h) {
859 tpg->border.width = ((16 * h) / 9) & ~1;
860 if (((w - tpg->border.width) / 2) & ~1)
861 tpg->border.width -= 2;
862 tpg->border.left = (w - tpg->border.width) / 2;
865 tpg->border.height = ((9 * w) / 16) & ~1;
866 tpg->border.top = (h - tpg->border.height) / 2;
873 static void tpg_precalculate_line(struct tpg_data *tpg)
875 enum tpg_color contrast;
880 switch (tpg->pattern) {
882 contrast = TPG_COLOR_100_RED;
884 case TPG_PAT_CSC_COLORBAR:
885 contrast = TPG_COLOR_CSC_GREEN;
888 contrast = TPG_COLOR_100_GREEN;
892 for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
893 /* Coarse scaling with Bresenham */
894 unsigned int_part = tpg->src_width / tpg->scaled_width;
895 unsigned fract_part = tpg->src_width % tpg->scaled_width;
899 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
900 unsigned real_x = src_x;
901 enum tpg_color color1, color2;
902 u8 pix[TPG_MAX_PLANES][8];
904 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
905 color1 = tpg_get_color(tpg, pat, real_x);
909 if (error >= tpg->scaled_width) {
910 error -= tpg->scaled_width;
915 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
916 color2 = tpg_get_color(tpg, pat, real_x);
920 if (error >= tpg->scaled_width) {
921 error -= tpg->scaled_width;
925 gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
926 gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
927 for (p = 0; p < tpg->planes; p++) {
928 unsigned twopixsize = tpg->twopixelsize[p];
929 u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
931 memcpy(pos, pix[p], twopixsize);
935 for (x = 0; x < tpg->scaled_width; x += 2) {
936 u8 pix[TPG_MAX_PLANES][8];
938 gen_twopix(tpg, pix, contrast, 0);
939 gen_twopix(tpg, pix, contrast, 1);
940 for (p = 0; p < tpg->planes; p++) {
941 unsigned twopixsize = tpg->twopixelsize[p];
942 u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
944 memcpy(pos, pix[p], twopixsize);
947 for (x = 0; x < tpg->scaled_width; x += 2) {
948 u8 pix[TPG_MAX_PLANES][8];
950 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
951 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
952 for (p = 0; p < tpg->planes; p++) {
953 unsigned twopixsize = tpg->twopixelsize[p];
954 u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
956 memcpy(pos, pix[p], twopixsize);
959 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
960 u8 pix[TPG_MAX_PLANES][8];
962 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
963 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
964 for (p = 0; p < tpg->planes; p++) {
965 unsigned twopixsize = tpg->twopixelsize[p];
966 u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
968 memcpy(pos, pix[p], twopixsize);
971 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
972 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
973 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
974 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
977 /* need this to do rgb24 rendering */
978 typedef struct { u16 __; u8 _; } __packed x24;
980 void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
981 int y, int x, char *text)
984 unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
987 unsigned len = strlen(text);
990 if (font8x16 == NULL || basep == NULL)
993 /* Checks if it is possible to show string */
994 if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
997 if (len > (tpg->compose.width - x) / 8)
998 len = (tpg->compose.width - x) / 8;
1000 y = tpg->compose.height - y - 16;
1002 x = tpg->compose.width - x - 8;
1003 y += tpg->compose.top;
1004 x += tpg->compose.left;
1005 if (tpg->field == V4L2_FIELD_BOTTOM)
1007 else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
1010 for (p = 0; p < tpg->planes; p++) {
1011 /* Print stream time */
1012 #define PRINTSTR(PIXTYPE) do { \
1015 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1016 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1018 for (line = first; line < 16; line += step) { \
1019 int l = tpg->vflip ? 15 - line : line; \
1020 PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
1021 ((y * step + l) / div) * tpg->bytesperline[p] + \
1022 x * sizeof(PIXTYPE)); \
1025 for (s = 0; s < len; s++) { \
1026 u8 chr = font8x16[text[s] * 16 + line]; \
1029 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1030 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1031 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1032 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1033 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1034 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1035 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1036 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1038 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1039 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1040 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1041 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1042 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1043 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1044 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1045 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1048 pos += tpg->hflip ? -8 : 8; \
1053 switch (tpg->twopixelsize[p]) {
1055 PRINTSTR(u8); break;
1057 PRINTSTR(u16); break;
1059 PRINTSTR(x24); break;
1061 PRINTSTR(u32); break;
1066 void tpg_update_mv_step(struct tpg_data *tpg)
1068 int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
1072 switch (tpg->mv_hor_mode) {
1073 case TPG_MOVE_NEG_FAST:
1074 case TPG_MOVE_POS_FAST:
1075 tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
1079 tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
1081 case TPG_MOVE_NEG_SLOW:
1082 case TPG_MOVE_POS_SLOW:
1083 tpg->mv_hor_step = 2;
1086 tpg->mv_hor_step = 0;
1090 tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
1092 factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
1093 switch (tpg->mv_vert_mode) {
1094 case TPG_MOVE_NEG_FAST:
1095 case TPG_MOVE_POS_FAST:
1096 tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
1100 tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
1102 case TPG_MOVE_NEG_SLOW:
1103 case TPG_MOVE_POS_SLOW:
1104 tpg->mv_vert_step = 1;
1107 tpg->mv_vert_step = 0;
1111 tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
1114 /* Map the line number relative to the crop rectangle to a frame line number */
1115 static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
1119 case V4L2_FIELD_TOP:
1120 return tpg->crop.top + src_y * 2;
1121 case V4L2_FIELD_BOTTOM:
1122 return tpg->crop.top + src_y * 2 + 1;
1124 return src_y + tpg->crop.top;
1129 * Map the line number relative to the compose rectangle to a destination
1130 * buffer line number.
1132 static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
1135 y += tpg->compose.top;
1137 case V4L2_FIELD_SEQ_TB:
1139 return tpg->buf_height / 2 + y / 2;
1141 case V4L2_FIELD_SEQ_BT:
1144 return tpg->buf_height / 2 + y / 2;
1150 static void tpg_recalc(struct tpg_data *tpg)
1152 if (tpg->recalc_colors) {
1153 tpg->recalc_colors = false;
1154 tpg->recalc_lines = true;
1155 tpg_precalculate_colors(tpg);
1157 if (tpg->recalc_square_border) {
1158 tpg->recalc_square_border = false;
1159 tpg_calculate_square_border(tpg);
1161 if (tpg->recalc_lines) {
1162 tpg->recalc_lines = false;
1163 tpg_precalculate_line(tpg);
1167 void tpg_calc_text_basep(struct tpg_data *tpg,
1168 u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
1170 unsigned stride = tpg->bytesperline[p];
1176 if (tpg->field == V4L2_FIELD_SEQ_TB)
1177 basep[p][1] += tpg->buf_height * stride / 2;
1178 else if (tpg->field == V4L2_FIELD_SEQ_BT)
1179 basep[p][0] += tpg->buf_height * stride / 2;
1182 void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
1185 bool is_60hz = is_tv && (std & V4L2_STD_525_60);
1186 unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
1187 unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
1188 unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
1189 unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
1192 int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
1194 unsigned twopixsize = tpg->twopixelsize[p];
1195 unsigned img_width = tpg->compose.width * twopixsize / 2;
1196 unsigned line_offset;
1197 unsigned left_pillar_width = 0;
1198 unsigned right_pillar_start = img_width;
1199 unsigned stride = tpg->bytesperline[p];
1200 unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
1201 u8 *orig_vbuf = vbuf;
1203 /* Coarse scaling with Bresenham */
1204 unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
1205 unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
1211 mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
1212 mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
1213 wss_width = tpg->crop.left < tpg->src_width / 2 ?
1214 tpg->src_width / 2 - tpg->crop.left : 0;
1215 if (wss_width > tpg->crop.width)
1216 wss_width = tpg->crop.width;
1217 wss_width = wss_width * tpg->scaled_width / tpg->src_width;
1219 vbuf += tpg->compose.left * twopixsize / 2;
1220 line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
1221 line_offset = (line_offset & ~1) * twopixsize / 2;
1222 if (tpg->crop.left < tpg->border.left) {
1223 left_pillar_width = tpg->border.left - tpg->crop.left;
1224 if (left_pillar_width > tpg->crop.width)
1225 left_pillar_width = tpg->crop.width;
1226 left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
1227 left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
1229 if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
1230 right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
1231 right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
1232 right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
1233 if (right_pillar_start > img_width)
1234 right_pillar_start = img_width;
1237 f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
1239 for (h = 0; h < tpg->compose.height; h++) {
1241 bool fill_blank = false;
1242 unsigned frame_line;
1244 unsigned pat_line_old;
1245 unsigned pat_line_new;
1246 u8 *linestart_older;
1247 u8 *linestart_newer;
1249 u8 *linestart_bottom;
1251 frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1252 even = !(frame_line & 1);
1253 buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1255 error += fract_part;
1256 if (error >= tpg->compose.height) {
1257 error -= tpg->compose.height;
1262 if (hmax == tpg->compose.height)
1264 if (!tpg->perc_fill_blank)
1270 frame_line = tpg->src_height - frame_line - 1;
1273 linestart_older = tpg->contrast_line[p];
1274 linestart_newer = tpg->contrast_line[p];
1275 } else if (tpg->qual != TPG_QUAL_NOISE &&
1276 (frame_line < tpg->border.top ||
1277 frame_line >= tpg->border.top + tpg->border.height)) {
1278 linestart_older = tpg->black_line[p];
1279 linestart_newer = tpg->black_line[p];
1280 } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
1281 linestart_older = tpg->random_line[p] +
1282 twopixsize * prandom_u32_max(tpg->src_width / 2);
1283 linestart_newer = tpg->random_line[p] +
1284 twopixsize * prandom_u32_max(tpg->src_width / 2);
1286 pat_line_old = tpg_get_pat_line(tpg,
1287 (frame_line + mv_vert_old) % tpg->src_height);
1288 pat_line_new = tpg_get_pat_line(tpg,
1289 (frame_line + mv_vert_new) % tpg->src_height);
1290 linestart_older = tpg->lines[pat_line_old][p] +
1291 mv_hor_old * twopixsize / 2;
1292 linestart_newer = tpg->lines[pat_line_new][p] +
1293 mv_hor_new * twopixsize / 2;
1294 linestart_older += line_offset;
1295 linestart_newer += line_offset;
1298 linestart_top = linestart_newer;
1299 linestart_bottom = linestart_older;
1301 linestart_top = linestart_older;
1302 linestart_bottom = linestart_newer;
1305 switch (tpg->field) {
1306 case V4L2_FIELD_INTERLACED:
1307 case V4L2_FIELD_INTERLACED_TB:
1308 case V4L2_FIELD_SEQ_TB:
1309 case V4L2_FIELD_SEQ_BT:
1311 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1313 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1315 case V4L2_FIELD_INTERLACED_BT:
1317 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1319 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1321 case V4L2_FIELD_TOP:
1322 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1324 case V4L2_FIELD_BOTTOM:
1325 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1327 case V4L2_FIELD_NONE:
1329 memcpy(vbuf + buf_line * stride, linestart_older, img_width);
1333 if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
1335 * Replace the first half of the top line of a 50 Hz frame
1336 * with random data to simulate a WSS signal.
1338 u8 *wss = tpg->random_line[p] +
1339 twopixsize * prandom_u32_max(tpg->src_width / 2);
1341 memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2);
1346 vbuf += tpg->compose.left * twopixsize / 2;
1349 for (h = 0; h < tpg->compose.height; h++) {
1350 unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1351 unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1352 const struct v4l2_rect *sq = &tpg->square;
1353 const struct v4l2_rect *b = &tpg->border;
1354 const struct v4l2_rect *c = &tpg->crop;
1357 error += fract_part;
1358 if (error >= tpg->compose.height) {
1359 error -= tpg->compose.height;
1363 if (tpg->show_border && frame_line >= b->top &&
1364 frame_line < b->top + b->height) {
1365 unsigned bottom = b->top + b->height - 1;
1366 unsigned left = left_pillar_width;
1367 unsigned right = right_pillar_start;
1369 if (frame_line == b->top || frame_line == b->top + 1 ||
1370 frame_line == bottom || frame_line == bottom - 1) {
1371 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
1374 if (b->left >= c->left &&
1375 b->left < c->left + c->width)
1376 memcpy(vbuf + buf_line * stride + left,
1377 tpg->contrast_line[p], twopixsize);
1378 if (b->left + b->width > c->left &&
1379 b->left + b->width <= c->left + c->width)
1380 memcpy(vbuf + buf_line * stride + right - twopixsize,
1381 tpg->contrast_line[p], twopixsize);
1384 if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
1385 frame_line < b->top + b->height) {
1386 memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
1387 memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
1388 img_width - right_pillar_start);
1390 if (tpg->show_square && frame_line >= sq->top &&
1391 frame_line < sq->top + sq->height &&
1392 sq->left < c->left + c->width &&
1393 sq->left + sq->width >= c->left) {
1394 unsigned left = sq->left;
1395 unsigned width = sq->width;
1397 if (c->left > left) {
1398 width -= c->left - left;
1401 if (c->left + c->width < left + width)
1402 width -= left + width - c->left - c->width;
1404 left = (left * tpg->scaled_width) / tpg->src_width;
1405 left = (left & ~1) * twopixsize / 2;
1406 width = (width * tpg->scaled_width) / tpg->src_width;
1407 width = (width & ~1) * twopixsize / 2;
1408 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
1410 if (tpg->insert_sav) {
1411 unsigned offset = (tpg->compose.width / 6) * twopixsize;
1412 u8 *p = vbuf + buf_line * stride + offset;
1413 unsigned vact = 0, hact = 0;
1418 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1419 ((hact ^ vact) << 3) |
1424 if (tpg->insert_eav) {
1425 unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize;
1426 u8 *p = vbuf + buf_line * stride + offset;
1427 unsigned vact = 0, hact = 1;
1432 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1433 ((hact ^ vact) << 3) |