blob: ccb4a6be26c1ffaa1c7a7446cc65b76c72d053e0 [file] [log] [blame]
Hans Verkuil63881df2014-08-25 08:02:14 -03001/*
2 * vivid-tpg.c - Test Pattern Generator
3 *
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.
6 *
7 * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
8 *
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.
12 *
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
20 * SOFTWARE.
21 */
22
23#include "vivid-tpg.h"
24
25/* Must remain in sync with enum tpg_pattern */
26const char * const tpg_pattern_strings[] = {
27 "75% Colorbar",
28 "100% Colorbar",
29 "CSC Colorbar",
30 "Horizontal 100% Colorbar",
31 "100% Color Squares",
32 "100% Black",
33 "100% White",
34 "100% Red",
35 "100% Green",
36 "100% Blue",
37 "16x16 Checkers",
Hans Verkuil1a05d312015-03-07 12:49:57 -030038 "2x2 Checkers",
Hans Verkuil63881df2014-08-25 08:02:14 -030039 "1x1 Checkers",
Hans Verkuil1a05d312015-03-07 12:49:57 -030040 "2x2 Red/Green Checkers",
41 "1x1 Red/Green Checkers",
Hans Verkuil63881df2014-08-25 08:02:14 -030042 "Alternating Hor Lines",
43 "Alternating Vert Lines",
44 "One Pixel Wide Cross",
45 "Two Pixels Wide Cross",
46 "Ten Pixels Wide Cross",
47 "Gray Ramp",
48 "Noise",
49 NULL
50};
51
52/* Must remain in sync with enum tpg_aspect */
53const char * const tpg_aspect_strings[] = {
54 "Source Width x Height",
55 "4x3",
56 "14x9",
57 "16x9",
58 "16x9 Anamorphic",
59 NULL
60};
61
62/*
63 * Sine table: sin[0] = 127 * sin(-180 degrees)
64 * sin[128] = 127 * sin(0 degrees)
65 * sin[256] = 127 * sin(180 degrees)
66 */
67static 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,
84 0,
85};
86
87#define cos(idx) sin[((idx) + 64) % sizeof(sin)]
88
89/* Global font descriptor */
90static const u8 *font8x16;
91
92void tpg_set_font(const u8 *f)
93{
94 font8x16 = f;
95}
96
97void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
98{
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;
107 tpg->contrast = 128;
108 tpg->saturation = 128;
109 tpg->hue = 0;
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;
116}
117
118int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
119{
120 unsigned pat;
121 unsigned plane;
122
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;
127
128 tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
129 if (!tpg->lines[pat][plane])
130 return -ENOMEM;
Hans Verkuil5d7c5392015-03-07 14:06:43 -0300131 if (plane == 0)
132 continue;
133 tpg->downsampled_lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
134 if (!tpg->downsampled_lines[pat][plane])
135 return -ENOMEM;
Hans Verkuil63881df2014-08-25 08:02:14 -0300136 }
137 }
138 for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
139 unsigned pixelsz = plane ? 1 : 4;
140
141 tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
142 if (!tpg->contrast_line[plane])
143 return -ENOMEM;
144 tpg->black_line[plane] = vzalloc(max_w * pixelsz);
145 if (!tpg->black_line[plane])
146 return -ENOMEM;
Hans Verkuilc204e1f2014-10-07 08:58:55 -0300147 tpg->random_line[plane] = vzalloc(max_w * 2 * pixelsz);
Hans Verkuil63881df2014-08-25 08:02:14 -0300148 if (!tpg->random_line[plane])
149 return -ENOMEM;
150 }
151 return 0;
152}
153
154void tpg_free(struct tpg_data *tpg)
155{
156 unsigned pat;
157 unsigned plane;
158
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;
Hans Verkuil5d7c5392015-03-07 14:06:43 -0300163 if (plane == 0)
164 continue;
165 vfree(tpg->downsampled_lines[pat][plane]);
166 tpg->downsampled_lines[pat][plane] = NULL;
Hans Verkuil63881df2014-08-25 08:02:14 -0300167 }
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;
175 }
176}
177
178bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
179{
180 tpg->fourcc = fourcc;
181 tpg->planes = 1;
Hans Verkuil06d1f0c2015-03-07 13:01:53 -0300182 tpg->buffers = 1;
Hans Verkuil63881df2014-08-25 08:02:14 -0300183 tpg->recalc_colors = true;
Hans Verkuilba01f672015-03-07 13:57:27 -0300184 tpg->vdownsampling[0] = 1;
185 tpg->hdownsampling[0] = 1;
Hans Verkuil06d1f0c2015-03-07 13:01:53 -0300186
Hans Verkuil63881df2014-08-25 08:02:14 -0300187 switch (fourcc) {
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:
Hans Verkuil51f30962015-03-07 14:57:50 -0300202 case V4L2_PIX_FMT_GREY:
Mauro Carvalho Chehab6c515a42014-09-03 15:53:45 -0300203 tpg->is_yuv = false;
Hans Verkuil63881df2014-08-25 08:02:14 -0300204 break;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300205 case V4L2_PIX_FMT_YUV420M:
206 case V4L2_PIX_FMT_YVU420M:
207 tpg->buffers = 3;
208 /* fall through */
209 case V4L2_PIX_FMT_YUV420:
210 case V4L2_PIX_FMT_YVU420:
211 tpg->vdownsampling[1] = 2;
212 tpg->vdownsampling[2] = 2;
213 tpg->hdownsampling[1] = 2;
214 tpg->hdownsampling[2] = 2;
215 tpg->planes = 3;
216 tpg->is_yuv = true;
217 break;
218 case V4L2_PIX_FMT_YUV422P:
219 tpg->vdownsampling[1] = 1;
220 tpg->vdownsampling[2] = 1;
221 tpg->hdownsampling[1] = 2;
222 tpg->hdownsampling[2] = 2;
223 tpg->planes = 3;
224 tpg->is_yuv = true;
225 break;
Hans Verkuil63881df2014-08-25 08:02:14 -0300226 case V4L2_PIX_FMT_NV16M:
227 case V4L2_PIX_FMT_NV61M:
Hans Verkuil68c90d62015-03-07 14:55:09 -0300228 tpg->buffers = 2;
229 /* fall through */
230 case V4L2_PIX_FMT_NV16:
231 case V4L2_PIX_FMT_NV61:
Hans Verkuilba01f672015-03-07 13:57:27 -0300232 tpg->vdownsampling[1] = 1;
233 tpg->hdownsampling[1] = 1;
Hans Verkuil63881df2014-08-25 08:02:14 -0300234 tpg->planes = 2;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300235 tpg->is_yuv = true;
236 break;
237 case V4L2_PIX_FMT_NV12M:
238 case V4L2_PIX_FMT_NV21M:
239 tpg->buffers = 2;
240 /* fall through */
241 case V4L2_PIX_FMT_NV12:
242 case V4L2_PIX_FMT_NV21:
243 tpg->vdownsampling[1] = 2;
244 tpg->hdownsampling[1] = 1;
245 tpg->planes = 2;
246 tpg->is_yuv = true;
247 break;
Hans Verkuil63881df2014-08-25 08:02:14 -0300248 case V4L2_PIX_FMT_YUYV:
249 case V4L2_PIX_FMT_UYVY:
250 case V4L2_PIX_FMT_YVYU:
251 case V4L2_PIX_FMT_VYUY:
Mauro Carvalho Chehab6c515a42014-09-03 15:53:45 -0300252 tpg->is_yuv = true;
Hans Verkuil63881df2014-08-25 08:02:14 -0300253 break;
254 default:
255 return false;
256 }
257
258 switch (fourcc) {
259 case V4L2_PIX_FMT_RGB565:
260 case V4L2_PIX_FMT_RGB565X:
261 case V4L2_PIX_FMT_RGB555:
262 case V4L2_PIX_FMT_XRGB555:
263 case V4L2_PIX_FMT_ARGB555:
264 case V4L2_PIX_FMT_RGB555X:
265 case V4L2_PIX_FMT_YUYV:
266 case V4L2_PIX_FMT_UYVY:
267 case V4L2_PIX_FMT_YVYU:
268 case V4L2_PIX_FMT_VYUY:
269 tpg->twopixelsize[0] = 2 * 2;
270 break;
271 case V4L2_PIX_FMT_RGB24:
272 case V4L2_PIX_FMT_BGR24:
273 tpg->twopixelsize[0] = 2 * 3;
274 break;
275 case V4L2_PIX_FMT_RGB32:
276 case V4L2_PIX_FMT_BGR32:
277 case V4L2_PIX_FMT_XRGB32:
278 case V4L2_PIX_FMT_XBGR32:
279 case V4L2_PIX_FMT_ARGB32:
280 case V4L2_PIX_FMT_ABGR32:
281 tpg->twopixelsize[0] = 2 * 4;
282 break;
Hans Verkuil51f30962015-03-07 14:57:50 -0300283 case V4L2_PIX_FMT_GREY:
284 tpg->twopixelsize[0] = 2;
285 break;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300286 case V4L2_PIX_FMT_NV12:
287 case V4L2_PIX_FMT_NV21:
288 case V4L2_PIX_FMT_NV12M:
289 case V4L2_PIX_FMT_NV21M:
290 tpg->twopixelsize[0] = 2;
291 tpg->twopixelsize[1] = 2;
292 break;
293 case V4L2_PIX_FMT_NV16:
294 case V4L2_PIX_FMT_NV61:
Hans Verkuil63881df2014-08-25 08:02:14 -0300295 case V4L2_PIX_FMT_NV16M:
296 case V4L2_PIX_FMT_NV61M:
297 tpg->twopixelsize[0] = 2;
298 tpg->twopixelsize[1] = 2;
299 break;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300300 case V4L2_PIX_FMT_YUV422P:
301 case V4L2_PIX_FMT_YUV420:
302 case V4L2_PIX_FMT_YVU420:
303 case V4L2_PIX_FMT_YUV420M:
304 case V4L2_PIX_FMT_YVU420M:
305 tpg->twopixelsize[0] = 2;
306 tpg->twopixelsize[1] = 2;
307 tpg->twopixelsize[2] = 2;
308 break;
Hans Verkuil63881df2014-08-25 08:02:14 -0300309 }
310 return true;
311}
312
313void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
314 const struct v4l2_rect *compose)
315{
316 tpg->crop = *crop;
317 tpg->compose = *compose;
318 tpg->scaled_width = (tpg->src_width * tpg->compose.width +
319 tpg->crop.width - 1) / tpg->crop.width;
320 tpg->scaled_width &= ~1;
321 if (tpg->scaled_width > tpg->max_line_width)
322 tpg->scaled_width = tpg->max_line_width;
323 if (tpg->scaled_width < 2)
324 tpg->scaled_width = 2;
325 tpg->recalc_lines = true;
326}
327
328void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
Hans Verkuil73d81022014-09-03 10:18:57 -0300329 u32 field)
Hans Verkuil63881df2014-08-25 08:02:14 -0300330{
331 unsigned p;
332
333 tpg->src_width = width;
334 tpg->src_height = height;
335 tpg->field = field;
336 tpg->buf_height = height;
337 if (V4L2_FIELD_HAS_T_OR_B(field))
338 tpg->buf_height /= 2;
339 tpg->scaled_width = width;
340 tpg->crop.top = tpg->crop.left = 0;
341 tpg->crop.width = width;
342 tpg->crop.height = height;
343 tpg->compose.top = tpg->compose.left = 0;
344 tpg->compose.width = width;
345 tpg->compose.height = tpg->buf_height;
346 for (p = 0; p < tpg->planes; p++)
Hans Verkuilba01f672015-03-07 13:57:27 -0300347 tpg->bytesperline[p] = (width * tpg->twopixelsize[p]) /
348 (2 * tpg->hdownsampling[p]);
Hans Verkuil63881df2014-08-25 08:02:14 -0300349 tpg->recalc_square_border = true;
350}
351
352static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
353{
354 switch (tpg->pattern) {
355 case TPG_PAT_BLACK:
356 return TPG_COLOR_100_WHITE;
357 case TPG_PAT_CSC_COLORBAR:
358 return TPG_COLOR_CSC_BLACK;
359 default:
360 return TPG_COLOR_100_BLACK;
361 }
362}
363
364static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg)
365{
366 switch (tpg->pattern) {
367 case TPG_PAT_75_COLORBAR:
368 case TPG_PAT_CSC_COLORBAR:
369 return TPG_COLOR_CSC_WHITE;
370 case TPG_PAT_BLACK:
371 return TPG_COLOR_100_BLACK;
372 default:
373 return TPG_COLOR_100_WHITE;
374 }
375}
376
Hans Verkuil481b97a2014-11-17 10:14:32 -0300377static inline int rec709_to_linear(int v)
Hans Verkuil63881df2014-08-25 08:02:14 -0300378{
Hans Verkuil481b97a2014-11-17 10:14:32 -0300379 v = clamp(v, 0, 0xff0);
380 return tpg_rec709_to_linear[v];
381}
382
383static inline int linear_to_rec709(int v)
384{
385 v = clamp(v, 0, 0xff0);
386 return tpg_linear_to_rec709[v];
387}
388
389static void rgb2ycbcr(const int m[3][3], int r, int g, int b,
390 int y_offset, int *y, int *cb, int *cr)
391{
392 *y = ((m[0][0] * r + m[0][1] * g + m[0][2] * b) >> 16) + (y_offset << 4);
393 *cb = ((m[1][0] * r + m[1][1] * g + m[1][2] * b) >> 16) + (128 << 4);
394 *cr = ((m[2][0] * r + m[2][1] * g + m[2][2] * b) >> 16) + (128 << 4);
395}
396
397static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b,
398 int *y, int *cb, int *cr)
399{
400#define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
401
402 static const int bt601[3][3] = {
403 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
404 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
405 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
406 };
407 static const int bt601_full[3][3] = {
408 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
409 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
410 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
411 };
412 static const int rec709[3][3] = {
413 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
414 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
415 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
416 };
417 static const int rec709_full[3][3] = {
418 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
419 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
420 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
421 };
422 static const int smpte240m[3][3] = {
423 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
424 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
425 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
426 };
427 static const int bt2020[3][3] = {
428 { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
429 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
430 { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
431 };
432 bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300433 unsigned y_offset = full ? 0 : 16;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300434 int lin_y, yc;
435
436 switch (tpg->real_ycbcr_enc) {
437 case V4L2_YCBCR_ENC_601:
438 case V4L2_YCBCR_ENC_XV601:
439 case V4L2_YCBCR_ENC_SYCC:
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300440 rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr);
Hans Verkuil481b97a2014-11-17 10:14:32 -0300441 break;
442 case V4L2_YCBCR_ENC_BT2020:
443 rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr);
444 break;
445 case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
446 lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) +
447 COEFF(0.6780, 255) * rec709_to_linear(g) +
448 COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16;
449 yc = linear_to_rec709(lin_y);
450 *y = (yc * 219) / 255 + (16 << 4);
451 if (b <= yc)
452 *cb = (((b - yc) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
453 else
454 *cb = (((b - yc) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
455 if (r <= yc)
456 *cr = (((r - yc) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
457 else
458 *cr = (((r - yc) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
459 break;
460 case V4L2_YCBCR_ENC_SMPTE240M:
461 rgb2ycbcr(smpte240m, r, g, b, 16, y, cb, cr);
462 break;
463 case V4L2_YCBCR_ENC_709:
464 case V4L2_YCBCR_ENC_XV709:
Hans Verkuil63881df2014-08-25 08:02:14 -0300465 default:
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300466 rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr);
Hans Verkuil481b97a2014-11-17 10:14:32 -0300467 break;
Hans Verkuil63881df2014-08-25 08:02:14 -0300468 }
469}
470
Hans Verkuil481b97a2014-11-17 10:14:32 -0300471static void ycbcr2rgb(const int m[3][3], int y, int cb, int cr,
472 int y_offset, int *r, int *g, int *b)
Hans Verkuil63881df2014-08-25 08:02:14 -0300473{
Hans Verkuil481b97a2014-11-17 10:14:32 -0300474 y -= y_offset << 4;
Hans Verkuil63881df2014-08-25 08:02:14 -0300475 cb -= 128 << 4;
476 cr -= 128 << 4;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300477 *r = m[0][0] * y + m[0][1] * cb + m[0][2] * cr;
478 *g = m[1][0] * y + m[1][1] * cb + m[1][2] * cr;
479 *b = m[2][0] * y + m[2][1] * cb + m[2][2] * cr;
480 *r = clamp(*r >> 12, 0, 0xff0);
481 *g = clamp(*g >> 12, 0, 0xff0);
482 *b = clamp(*b >> 12, 0, 0xff0);
Hans Verkuil63881df2014-08-25 08:02:14 -0300483}
484
Hans Verkuil481b97a2014-11-17 10:14:32 -0300485static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
486 int *r, int *g, int *b)
Hans Verkuil63881df2014-08-25 08:02:14 -0300487{
Hans Verkuil481b97a2014-11-17 10:14:32 -0300488#undef COEFF
489#define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
490 static const int bt601[3][3] = {
491 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
492 { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
493 { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
494 };
495 static const int bt601_full[3][3] = {
496 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
497 { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
498 { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
499 };
500 static const int rec709[3][3] = {
501 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
502 { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
503 { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
504 };
505 static const int rec709_full[3][3] = {
506 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
507 { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
508 { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
509 };
510 static const int smpte240m[3][3] = {
511 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
512 { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
513 { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
514 };
515 static const int bt2020[3][3] = {
516 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
517 { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
518 { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
519 };
520 bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300521 unsigned y_offset = full ? 0 : 16;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300522 int lin_r, lin_g, lin_b, lin_y;
Hans Verkuil63881df2014-08-25 08:02:14 -0300523
Hans Verkuil481b97a2014-11-17 10:14:32 -0300524 switch (tpg->real_ycbcr_enc) {
525 case V4L2_YCBCR_ENC_601:
526 case V4L2_YCBCR_ENC_XV601:
527 case V4L2_YCBCR_ENC_SYCC:
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300528 ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b);
Hans Verkuil63881df2014-08-25 08:02:14 -0300529 break;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300530 case V4L2_YCBCR_ENC_BT2020:
531 ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b);
Hans Verkuil63881df2014-08-25 08:02:14 -0300532 break;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300533 case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
534 y -= 16 << 4;
535 cb -= 128 << 4;
536 cr -= 128 << 4;
537
538 if (cb <= 0)
539 *b = COEFF(1.0, 219) * y + COEFF(1.9404, 224) * cb;
540 else
541 *b = COEFF(1.0, 219) * y + COEFF(1.5816, 224) * cb;
542 *b = *b >> 12;
543 if (cr <= 0)
544 *r = COEFF(1.0, 219) * y + COEFF(1.7184, 224) * cr;
545 else
546 *r = COEFF(1.0, 219) * y + COEFF(0.9936, 224) * cr;
547 *r = *r >> 12;
548 lin_r = rec709_to_linear(*r);
549 lin_b = rec709_to_linear(*b);
550 lin_y = rec709_to_linear((y * 255) / 219);
551
552 lin_g = COEFF(1.0 / 0.6780, 255) * lin_y -
553 COEFF(0.2627 / 0.6780, 255) * lin_r -
554 COEFF(0.0593 / 0.6780, 255) * lin_b;
555 *g = linear_to_rec709(lin_g >> 12);
556 break;
557 case V4L2_YCBCR_ENC_SMPTE240M:
558 ycbcr2rgb(smpte240m, y, cb, cr, 16, r, g, b);
559 break;
560 case V4L2_YCBCR_ENC_709:
561 case V4L2_YCBCR_ENC_XV709:
Hans Verkuil63881df2014-08-25 08:02:14 -0300562 default:
Hans Verkuilafad4dd2015-01-27 13:46:17 -0300563 ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b);
Hans Verkuil63881df2014-08-25 08:02:14 -0300564 break;
565 }
Hans Verkuil63881df2014-08-25 08:02:14 -0300566}
567
568/* precalculate color bar values to speed up rendering */
569static void precalculate_color(struct tpg_data *tpg, int k)
570{
571 int col = k;
572 int r = tpg_colors[col].r;
573 int g = tpg_colors[col].g;
574 int b = tpg_colors[col].b;
575
576 if (k == TPG_COLOR_TEXTBG) {
577 col = tpg_get_textbg_color(tpg);
578
579 r = tpg_colors[col].r;
580 g = tpg_colors[col].g;
581 b = tpg_colors[col].b;
582 } else if (k == TPG_COLOR_TEXTFG) {
583 col = tpg_get_textfg_color(tpg);
584
585 r = tpg_colors[col].r;
586 g = tpg_colors[col].g;
587 b = tpg_colors[col].b;
588 } else if (tpg->pattern == TPG_PAT_NOISE) {
589 r = g = b = prandom_u32_max(256);
590 } else if (k == TPG_COLOR_RANDOM) {
591 r = g = b = tpg->qual_offset + prandom_u32_max(196);
592 } else if (k >= TPG_COLOR_RAMP) {
593 r = g = b = k - TPG_COLOR_RAMP;
594 }
595
596 if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
597 r = tpg_csc_colors[tpg->colorspace][col].r;
598 g = tpg_csc_colors[tpg->colorspace][col].g;
599 b = tpg_csc_colors[tpg->colorspace][col].b;
600 } else {
601 r <<= 4;
602 g <<= 4;
603 b <<= 4;
604 }
Hans Verkuil51f30962015-03-07 14:57:50 -0300605 if (tpg->qual == TPG_QUAL_GRAY || tpg->fourcc == V4L2_PIX_FMT_GREY) {
Hans Verkuil481b97a2014-11-17 10:14:32 -0300606 /* Rec. 709 Luma function */
607 /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
Hans Verkuil9c35bd42015-03-07 12:53:39 -0300608 r = g = b = (13879 * r + 46688 * g + 4713 * b) >> 16;
Hans Verkuil481b97a2014-11-17 10:14:32 -0300609 }
Hans Verkuil63881df2014-08-25 08:02:14 -0300610
611 /*
612 * The assumption is that the RGB output is always full range,
613 * so only if the rgb_range overrides the 'real' rgb range do
614 * we need to convert the RGB values.
615 *
Hans Verkuil63881df2014-08-25 08:02:14 -0300616 * Remember that r, g and b are still in the 0 - 0xff0 range.
617 */
618 if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
619 tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
620 /*
621 * Convert from full range (which is what r, g and b are)
622 * to limited range (which is the 'real' RGB range), which
623 * is then interpreted as full range.
624 */
625 r = (r * 219) / 255 + (16 << 4);
626 g = (g * 219) / 255 + (16 << 4);
627 b = (b * 219) / 255 + (16 << 4);
628 } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
629 tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
630 /*
631 * Clamp r, g and b to the limited range and convert to full
632 * range since that's what we deliver.
633 */
634 r = clamp(r, 16 << 4, 235 << 4);
635 g = clamp(g, 16 << 4, 235 << 4);
636 b = clamp(b, 16 << 4, 235 << 4);
637 r = (r - (16 << 4)) * 255 / 219;
638 g = (g - (16 << 4)) * 255 / 219;
639 b = (b - (16 << 4)) * 255 / 219;
640 }
641
642 if (tpg->brightness != 128 || tpg->contrast != 128 ||
643 tpg->saturation != 128 || tpg->hue) {
644 /* Implement these operations */
Hans Verkuil481b97a2014-11-17 10:14:32 -0300645 int y, cb, cr;
646 int tmp_cb, tmp_cr;
Hans Verkuil63881df2014-08-25 08:02:14 -0300647
648 /* First convert to YCbCr */
Hans Verkuil481b97a2014-11-17 10:14:32 -0300649
650 color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
Hans Verkuil63881df2014-08-25 08:02:14 -0300651
652 y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
653 y += (tpg->brightness << 4) - (128 << 4);
654
655 cb -= 128 << 4;
656 cr -= 128 << 4;
657 tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
658 tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
659
660 cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
661 cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
662 if (tpg->is_yuv) {
663 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
664 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
665 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
666 return;
667 }
Hans Verkuil481b97a2014-11-17 10:14:32 -0300668 ycbcr_to_color(tpg, y, cb, cr, &r, &g, &b);
Hans Verkuil63881df2014-08-25 08:02:14 -0300669 }
670
671 if (tpg->is_yuv) {
672 /* Convert to YCbCr */
Hans Verkuil481b97a2014-11-17 10:14:32 -0300673 int y, cb, cr;
Hans Verkuil63881df2014-08-25 08:02:14 -0300674
Hans Verkuil481b97a2014-11-17 10:14:32 -0300675 color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
676
677 if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
678 y = clamp(y, 16 << 4, 235 << 4);
679 cb = clamp(cb, 16 << 4, 240 << 4);
680 cr = clamp(cr, 16 << 4, 240 << 4);
681 }
Hans Verkuil63881df2014-08-25 08:02:14 -0300682 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
683 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
684 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
685 } else {
Hans Verkuil481b97a2014-11-17 10:14:32 -0300686 if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
687 r = (r * 219) / 255 + (16 << 4);
688 g = (g * 219) / 255 + (16 << 4);
689 b = (b * 219) / 255 + (16 << 4);
690 }
Hans Verkuil63881df2014-08-25 08:02:14 -0300691 switch (tpg->fourcc) {
692 case V4L2_PIX_FMT_RGB565:
693 case V4L2_PIX_FMT_RGB565X:
694 r >>= 7;
695 g >>= 6;
696 b >>= 7;
697 break;
698 case V4L2_PIX_FMT_RGB555:
699 case V4L2_PIX_FMT_XRGB555:
700 case V4L2_PIX_FMT_ARGB555:
701 case V4L2_PIX_FMT_RGB555X:
702 r >>= 7;
703 g >>= 7;
704 b >>= 7;
705 break;
706 default:
707 r >>= 4;
708 g >>= 4;
709 b >>= 4;
710 break;
711 }
712
713 tpg->colors[k][0] = r;
714 tpg->colors[k][1] = g;
715 tpg->colors[k][2] = b;
716 }
717}
718
719static void tpg_precalculate_colors(struct tpg_data *tpg)
720{
721 int k;
722
723 for (k = 0; k < TPG_COLOR_MAX; k++)
724 precalculate_color(tpg, k);
725}
726
727/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
728static void gen_twopix(struct tpg_data *tpg,
729 u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
730{
731 unsigned offset = odd * tpg->twopixelsize[0] / 2;
732 u8 alpha = tpg->alpha_component;
733 u8 r_y, g_u, b_v;
734
735 if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
736 color != TPG_COLOR_100_RED &&
737 color != TPG_COLOR_75_RED)
738 alpha = 0;
739 if (color == TPG_COLOR_RANDOM)
740 precalculate_color(tpg, color);
741 r_y = tpg->colors[color][0]; /* R or precalculated Y */
742 g_u = tpg->colors[color][1]; /* G or precalculated U */
743 b_v = tpg->colors[color][2]; /* B or precalculated V */
744
745 switch (tpg->fourcc) {
Hans Verkuil51f30962015-03-07 14:57:50 -0300746 case V4L2_PIX_FMT_GREY:
747 buf[0][offset] = r_y;
748 break;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300749 case V4L2_PIX_FMT_YUV422P:
750 case V4L2_PIX_FMT_YUV420:
751 case V4L2_PIX_FMT_YUV420M:
752 buf[0][offset] = r_y;
753 if (odd) {
754 buf[1][0] = (buf[1][0] + g_u) / 2;
755 buf[2][0] = (buf[2][0] + b_v) / 2;
756 buf[1][1] = buf[1][0];
757 buf[2][1] = buf[2][0];
758 break;
759 }
760 buf[1][0] = g_u;
761 buf[2][0] = b_v;
762 break;
763 case V4L2_PIX_FMT_YVU420:
764 case V4L2_PIX_FMT_YVU420M:
765 buf[0][offset] = r_y;
766 if (odd) {
767 buf[1][0] = (buf[1][0] + b_v) / 2;
768 buf[2][0] = (buf[2][0] + g_u) / 2;
769 buf[1][1] = buf[1][0];
770 buf[2][1] = buf[2][0];
771 break;
772 }
773 buf[1][0] = b_v;
774 buf[2][0] = g_u;
775 break;
776
777 case V4L2_PIX_FMT_NV12:
778 case V4L2_PIX_FMT_NV12M:
779 case V4L2_PIX_FMT_NV16:
Hans Verkuil63881df2014-08-25 08:02:14 -0300780 case V4L2_PIX_FMT_NV16M:
781 buf[0][offset] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300782 if (odd) {
783 buf[1][0] = (buf[1][0] + g_u) / 2;
784 buf[1][1] = (buf[1][1] + b_v) / 2;
785 break;
786 }
787 buf[1][0] = g_u;
788 buf[1][1] = b_v;
Hans Verkuil63881df2014-08-25 08:02:14 -0300789 break;
Hans Verkuil68c90d62015-03-07 14:55:09 -0300790 case V4L2_PIX_FMT_NV21:
791 case V4L2_PIX_FMT_NV21M:
792 case V4L2_PIX_FMT_NV61:
Hans Verkuil63881df2014-08-25 08:02:14 -0300793 case V4L2_PIX_FMT_NV61M:
794 buf[0][offset] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300795 if (odd) {
796 buf[1][0] = (buf[1][0] + b_v) / 2;
797 buf[1][1] = (buf[1][1] + g_u) / 2;
798 break;
799 }
800 buf[1][0] = b_v;
801 buf[1][1] = g_u;
Hans Verkuil63881df2014-08-25 08:02:14 -0300802 break;
803
804 case V4L2_PIX_FMT_YUYV:
805 buf[0][offset] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300806 if (odd) {
807 buf[0][1] = (buf[0][1] + g_u) / 2;
808 buf[0][3] = (buf[0][3] + b_v) / 2;
809 break;
810 }
811 buf[0][1] = g_u;
812 buf[0][3] = b_v;
Hans Verkuil63881df2014-08-25 08:02:14 -0300813 break;
814 case V4L2_PIX_FMT_UYVY:
Hans Verkuil63881df2014-08-25 08:02:14 -0300815 buf[0][offset + 1] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300816 if (odd) {
817 buf[0][0] = (buf[0][0] + g_u) / 2;
818 buf[0][2] = (buf[0][2] + b_v) / 2;
819 break;
820 }
821 buf[0][0] = g_u;
822 buf[0][2] = b_v;
Hans Verkuil63881df2014-08-25 08:02:14 -0300823 break;
824 case V4L2_PIX_FMT_YVYU:
825 buf[0][offset] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300826 if (odd) {
827 buf[0][1] = (buf[0][1] + b_v) / 2;
828 buf[0][3] = (buf[0][3] + g_u) / 2;
829 break;
830 }
831 buf[0][1] = b_v;
832 buf[0][3] = g_u;
Hans Verkuil63881df2014-08-25 08:02:14 -0300833 break;
834 case V4L2_PIX_FMT_VYUY:
Hans Verkuil63881df2014-08-25 08:02:14 -0300835 buf[0][offset + 1] = r_y;
Hans Verkuil1f088dc2015-03-07 14:15:25 -0300836 if (odd) {
837 buf[0][0] = (buf[0][0] + b_v) / 2;
838 buf[0][2] = (buf[0][2] + g_u) / 2;
839 break;
840 }
841 buf[0][0] = b_v;
842 buf[0][2] = g_u;
Hans Verkuil63881df2014-08-25 08:02:14 -0300843 break;
844 case V4L2_PIX_FMT_RGB565:
845 buf[0][offset] = (g_u << 5) | b_v;
846 buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
847 break;
848 case V4L2_PIX_FMT_RGB565X:
849 buf[0][offset] = (r_y << 3) | (g_u >> 3);
850 buf[0][offset + 1] = (g_u << 5) | b_v;
851 break;
852 case V4L2_PIX_FMT_RGB555:
853 case V4L2_PIX_FMT_XRGB555:
854 alpha = 0;
855 /* fall through */
856 case V4L2_PIX_FMT_ARGB555:
857 buf[0][offset] = (g_u << 5) | b_v;
858 buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
859 break;
860 case V4L2_PIX_FMT_RGB555X:
861 buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
862 buf[0][offset + 1] = (g_u << 5) | b_v;
863 break;
864 case V4L2_PIX_FMT_RGB24:
865 buf[0][offset] = r_y;
866 buf[0][offset + 1] = g_u;
867 buf[0][offset + 2] = b_v;
868 break;
869 case V4L2_PIX_FMT_BGR24:
870 buf[0][offset] = b_v;
871 buf[0][offset + 1] = g_u;
872 buf[0][offset + 2] = r_y;
873 break;
874 case V4L2_PIX_FMT_RGB32:
875 case V4L2_PIX_FMT_XRGB32:
876 alpha = 0;
877 /* fall through */
878 case V4L2_PIX_FMT_ARGB32:
879 buf[0][offset] = alpha;
880 buf[0][offset + 1] = r_y;
881 buf[0][offset + 2] = g_u;
882 buf[0][offset + 3] = b_v;
883 break;
884 case V4L2_PIX_FMT_BGR32:
885 case V4L2_PIX_FMT_XBGR32:
886 alpha = 0;
887 /* fall through */
888 case V4L2_PIX_FMT_ABGR32:
889 buf[0][offset] = b_v;
890 buf[0][offset + 1] = g_u;
891 buf[0][offset + 2] = r_y;
892 buf[0][offset + 3] = alpha;
893 break;
894 }
895}
896
897/* Return how many pattern lines are used by the current pattern. */
Hans Verkuil1a05d312015-03-07 12:49:57 -0300898static unsigned tpg_get_pat_lines(const struct tpg_data *tpg)
Hans Verkuil63881df2014-08-25 08:02:14 -0300899{
900 switch (tpg->pattern) {
901 case TPG_PAT_CHECKERS_16X16:
Hans Verkuil1a05d312015-03-07 12:49:57 -0300902 case TPG_PAT_CHECKERS_2X2:
Hans Verkuil63881df2014-08-25 08:02:14 -0300903 case TPG_PAT_CHECKERS_1X1:
Hans Verkuil1a05d312015-03-07 12:49:57 -0300904 case TPG_PAT_COLOR_CHECKERS_2X2:
905 case TPG_PAT_COLOR_CHECKERS_1X1:
Hans Verkuil63881df2014-08-25 08:02:14 -0300906 case TPG_PAT_ALTERNATING_HLINES:
907 case TPG_PAT_CROSS_1_PIXEL:
908 case TPG_PAT_CROSS_2_PIXELS:
909 case TPG_PAT_CROSS_10_PIXELS:
910 return 2;
911 case TPG_PAT_100_COLORSQUARES:
912 case TPG_PAT_100_HCOLORBAR:
913 return 8;
914 default:
915 return 1;
916 }
917}
918
919/* Which pattern line should be used for the given frame line. */
Hans Verkuil1a05d312015-03-07 12:49:57 -0300920static unsigned tpg_get_pat_line(const struct tpg_data *tpg, unsigned line)
Hans Verkuil63881df2014-08-25 08:02:14 -0300921{
922 switch (tpg->pattern) {
923 case TPG_PAT_CHECKERS_16X16:
924 return (line >> 4) & 1;
925 case TPG_PAT_CHECKERS_1X1:
Hans Verkuil1a05d312015-03-07 12:49:57 -0300926 case TPG_PAT_COLOR_CHECKERS_1X1:
Hans Verkuil63881df2014-08-25 08:02:14 -0300927 case TPG_PAT_ALTERNATING_HLINES:
928 return line & 1;
Hans Verkuil1a05d312015-03-07 12:49:57 -0300929 case TPG_PAT_CHECKERS_2X2:
930 case TPG_PAT_COLOR_CHECKERS_2X2:
931 return (line & 2) >> 1;
Hans Verkuil63881df2014-08-25 08:02:14 -0300932 case TPG_PAT_100_COLORSQUARES:
933 case TPG_PAT_100_HCOLORBAR:
934 return (line * 8) / tpg->src_height;
935 case TPG_PAT_CROSS_1_PIXEL:
936 return line == tpg->src_height / 2;
937 case TPG_PAT_CROSS_2_PIXELS:
938 return (line + 1) / 2 == tpg->src_height / 4;
939 case TPG_PAT_CROSS_10_PIXELS:
940 return (line + 10) / 20 == tpg->src_height / 40;
941 default:
942 return 0;
943 }
944}
945
946/*
947 * Which color should be used for the given pattern line and X coordinate.
948 * Note: x is in the range 0 to 2 * tpg->src_width.
949 */
Hans Verkuil1a05d312015-03-07 12:49:57 -0300950static enum tpg_color tpg_get_color(const struct tpg_data *tpg,
951 unsigned pat_line, unsigned x)
Hans Verkuil63881df2014-08-25 08:02:14 -0300952{
953 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
954 should be modified */
955 static const enum tpg_color bars[3][8] = {
956 /* Standard ITU-R 75% color bar sequence */
957 { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW,
958 TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN,
959 TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED,
960 TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, },
961 /* Standard ITU-R 100% color bar sequence */
962 { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW,
963 TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN,
964 TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
965 TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, },
966 /* Color bar sequence suitable to test CSC */
967 { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW,
968 TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN,
969 TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
970 TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, },
971 };
972
973 switch (tpg->pattern) {
974 case TPG_PAT_75_COLORBAR:
975 case TPG_PAT_100_COLORBAR:
976 case TPG_PAT_CSC_COLORBAR:
977 return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
978 case TPG_PAT_100_COLORSQUARES:
979 return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
980 case TPG_PAT_100_HCOLORBAR:
981 return bars[1][pat_line];
982 case TPG_PAT_BLACK:
983 return TPG_COLOR_100_BLACK;
984 case TPG_PAT_WHITE:
985 return TPG_COLOR_100_WHITE;
986 case TPG_PAT_RED:
987 return TPG_COLOR_100_RED;
988 case TPG_PAT_GREEN:
989 return TPG_COLOR_100_GREEN;
990 case TPG_PAT_BLUE:
991 return TPG_COLOR_100_BLUE;
992 case TPG_PAT_CHECKERS_16X16:
993 return (((x >> 4) & 1) ^ (pat_line & 1)) ?
994 TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
995 case TPG_PAT_CHECKERS_1X1:
996 return ((x & 1) ^ (pat_line & 1)) ?
997 TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
Hans Verkuil1a05d312015-03-07 12:49:57 -0300998 case TPG_PAT_COLOR_CHECKERS_1X1:
999 return ((x & 1) ^ (pat_line & 1)) ?
1000 TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
1001 case TPG_PAT_CHECKERS_2X2:
1002 return (((x >> 1) & 1) ^ (pat_line & 1)) ?
1003 TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
1004 case TPG_PAT_COLOR_CHECKERS_2X2:
1005 return (((x >> 1) & 1) ^ (pat_line & 1)) ?
1006 TPG_COLOR_100_RED : TPG_COLOR_100_BLUE;
Hans Verkuil63881df2014-08-25 08:02:14 -03001007 case TPG_PAT_ALTERNATING_HLINES:
1008 return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
1009 case TPG_PAT_ALTERNATING_VLINES:
1010 return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
1011 case TPG_PAT_CROSS_1_PIXEL:
1012 if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
1013 return TPG_COLOR_100_BLACK;
1014 return TPG_COLOR_100_WHITE;
1015 case TPG_PAT_CROSS_2_PIXELS:
1016 if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
1017 return TPG_COLOR_100_BLACK;
1018 return TPG_COLOR_100_WHITE;
1019 case TPG_PAT_CROSS_10_PIXELS:
1020 if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
1021 return TPG_COLOR_100_BLACK;
1022 return TPG_COLOR_100_WHITE;
1023 case TPG_PAT_GRAY_RAMP:
1024 return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
1025 default:
1026 return TPG_COLOR_100_RED;
1027 }
1028}
1029
1030/*
1031 * Given the pixel aspect ratio and video aspect ratio calculate the
1032 * coordinates of a centered square and the coordinates of the border of
1033 * the active video area. The coordinates are relative to the source
1034 * frame rectangle.
1035 */
1036static void tpg_calculate_square_border(struct tpg_data *tpg)
1037{
1038 unsigned w = tpg->src_width;
1039 unsigned h = tpg->src_height;
1040 unsigned sq_w, sq_h;
1041
1042 sq_w = (w * 2 / 5) & ~1;
1043 if (((w - sq_w) / 2) & 1)
1044 sq_w += 2;
1045 sq_h = sq_w;
1046 tpg->square.width = sq_w;
1047 if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
1048 unsigned ana_sq_w = (sq_w / 4) * 3;
1049
1050 if (((w - ana_sq_w) / 2) & 1)
1051 ana_sq_w += 2;
1052 tpg->square.width = ana_sq_w;
1053 }
1054 tpg->square.left = (w - tpg->square.width) / 2;
1055 if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
1056 sq_h = sq_w * 10 / 11;
1057 else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
1058 sq_h = sq_w * 59 / 54;
1059 tpg->square.height = sq_h;
1060 tpg->square.top = (h - sq_h) / 2;
1061 tpg->border.left = 0;
1062 tpg->border.width = w;
1063 tpg->border.top = 0;
1064 tpg->border.height = h;
1065 switch (tpg->vid_aspect) {
1066 case TPG_VIDEO_ASPECT_4X3:
1067 if (tpg->pix_aspect)
1068 return;
1069 if (3 * w >= 4 * h) {
1070 tpg->border.width = ((4 * h) / 3) & ~1;
1071 if (((w - tpg->border.width) / 2) & ~1)
1072 tpg->border.width -= 2;
1073 tpg->border.left = (w - tpg->border.width) / 2;
1074 break;
1075 }
1076 tpg->border.height = ((3 * w) / 4) & ~1;
1077 tpg->border.top = (h - tpg->border.height) / 2;
1078 break;
1079 case TPG_VIDEO_ASPECT_14X9_CENTRE:
1080 if (tpg->pix_aspect) {
1081 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
1082 tpg->border.top = (h - tpg->border.height) / 2;
1083 break;
1084 }
1085 if (9 * w >= 14 * h) {
1086 tpg->border.width = ((14 * h) / 9) & ~1;
1087 if (((w - tpg->border.width) / 2) & ~1)
1088 tpg->border.width -= 2;
1089 tpg->border.left = (w - tpg->border.width) / 2;
1090 break;
1091 }
1092 tpg->border.height = ((9 * w) / 14) & ~1;
1093 tpg->border.top = (h - tpg->border.height) / 2;
1094 break;
1095 case TPG_VIDEO_ASPECT_16X9_CENTRE:
1096 if (tpg->pix_aspect) {
1097 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
1098 tpg->border.top = (h - tpg->border.height) / 2;
1099 break;
1100 }
1101 if (9 * w >= 16 * h) {
1102 tpg->border.width = ((16 * h) / 9) & ~1;
1103 if (((w - tpg->border.width) / 2) & ~1)
1104 tpg->border.width -= 2;
1105 tpg->border.left = (w - tpg->border.width) / 2;
1106 break;
1107 }
1108 tpg->border.height = ((9 * w) / 16) & ~1;
1109 tpg->border.top = (h - tpg->border.height) / 2;
1110 break;
1111 default:
1112 break;
1113 }
1114}
1115
1116static void tpg_precalculate_line(struct tpg_data *tpg)
1117{
1118 enum tpg_color contrast;
1119 unsigned pat;
1120 unsigned p;
1121 unsigned x;
1122
1123 switch (tpg->pattern) {
1124 case TPG_PAT_GREEN:
1125 contrast = TPG_COLOR_100_RED;
1126 break;
1127 case TPG_PAT_CSC_COLORBAR:
1128 contrast = TPG_COLOR_CSC_GREEN;
1129 break;
1130 default:
1131 contrast = TPG_COLOR_100_GREEN;
1132 break;
1133 }
1134
1135 for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
1136 /* Coarse scaling with Bresenham */
1137 unsigned int_part = tpg->src_width / tpg->scaled_width;
1138 unsigned fract_part = tpg->src_width % tpg->scaled_width;
1139 unsigned src_x = 0;
1140 unsigned error = 0;
1141
1142 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
1143 unsigned real_x = src_x;
1144 enum tpg_color color1, color2;
1145 u8 pix[TPG_MAX_PLANES][8];
1146
1147 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
1148 color1 = tpg_get_color(tpg, pat, real_x);
1149
1150 src_x += int_part;
1151 error += fract_part;
1152 if (error >= tpg->scaled_width) {
1153 error -= tpg->scaled_width;
1154 src_x++;
1155 }
1156
1157 real_x = src_x;
1158 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
1159 color2 = tpg_get_color(tpg, pat, real_x);
1160
1161 src_x += int_part;
1162 error += fract_part;
1163 if (error >= tpg->scaled_width) {
1164 error -= tpg->scaled_width;
1165 src_x++;
1166 }
1167
1168 gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
1169 gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
1170 for (p = 0; p < tpg->planes; p++) {
1171 unsigned twopixsize = tpg->twopixelsize[p];
Hans Verkuil5d7c5392015-03-07 14:06:43 -03001172 unsigned hdiv = tpg->hdownsampling[p];
1173 u8 *pos = tpg->lines[pat][p] +
1174 (x / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001175
Hans Verkuil5d7c5392015-03-07 14:06:43 -03001176 memcpy(pos, pix[p], twopixsize / hdiv);
Hans Verkuil63881df2014-08-25 08:02:14 -03001177 }
1178 }
1179 }
Hans Verkuil5d7c5392015-03-07 14:06:43 -03001180
1181 if (tpg->vdownsampling[tpg->planes - 1] > 1) {
1182 unsigned pat_lines = tpg_get_pat_lines(tpg);
1183
1184 for (pat = 0; pat < pat_lines; pat++) {
1185 unsigned next_pat = (pat + 1) % pat_lines;
1186
1187 for (p = 1; p < tpg->planes; p++) {
1188 unsigned twopixsize = tpg->twopixelsize[p];
1189 unsigned hdiv = tpg->hdownsampling[p];
1190
1191 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
1192 unsigned offset = (x / hdiv) * twopixsize / 2;
1193 u8 *pos1 = tpg->lines[pat][p] + offset;
1194 u8 *pos2 = tpg->lines[next_pat][p] + offset;
1195 u8 *dest = tpg->downsampled_lines[pat][p] + offset;
1196 unsigned i;
1197
1198 for (i = 0; i < twopixsize / hdiv; i++, dest++, pos1++, pos2++)
1199 *dest = ((u16)*pos1 + (u16)*pos2) / 2;
1200 }
1201 }
1202 }
1203 }
1204
Hans Verkuil63881df2014-08-25 08:02:14 -03001205 for (x = 0; x < tpg->scaled_width; x += 2) {
1206 u8 pix[TPG_MAX_PLANES][8];
1207
1208 gen_twopix(tpg, pix, contrast, 0);
1209 gen_twopix(tpg, pix, contrast, 1);
1210 for (p = 0; p < tpg->planes; p++) {
1211 unsigned twopixsize = tpg->twopixelsize[p];
1212 u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
1213
1214 memcpy(pos, pix[p], twopixsize);
1215 }
1216 }
1217 for (x = 0; x < tpg->scaled_width; x += 2) {
1218 u8 pix[TPG_MAX_PLANES][8];
1219
1220 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
1221 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
1222 for (p = 0; p < tpg->planes; p++) {
1223 unsigned twopixsize = tpg->twopixelsize[p];
1224 u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
1225
1226 memcpy(pos, pix[p], twopixsize);
1227 }
1228 }
1229 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
1230 u8 pix[TPG_MAX_PLANES][8];
1231
1232 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
1233 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
1234 for (p = 0; p < tpg->planes; p++) {
1235 unsigned twopixsize = tpg->twopixelsize[p];
1236 u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
1237
1238 memcpy(pos, pix[p], twopixsize);
1239 }
1240 }
1241 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
1242 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
1243 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
1244 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
1245}
1246
1247/* need this to do rgb24 rendering */
1248typedef struct { u16 __; u8 _; } __packed x24;
1249
1250void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
1251 int y, int x, char *text)
1252{
1253 int line;
1254 unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
1255 unsigned div = step;
1256 unsigned first = 0;
1257 unsigned len = strlen(text);
1258 unsigned p;
1259
1260 if (font8x16 == NULL || basep == NULL)
1261 return;
1262
1263 /* Checks if it is possible to show string */
1264 if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
1265 return;
1266
1267 if (len > (tpg->compose.width - x) / 8)
1268 len = (tpg->compose.width - x) / 8;
1269 if (tpg->vflip)
1270 y = tpg->compose.height - y - 16;
1271 if (tpg->hflip)
1272 x = tpg->compose.width - x - 8;
1273 y += tpg->compose.top;
1274 x += tpg->compose.left;
1275 if (tpg->field == V4L2_FIELD_BOTTOM)
1276 first = 1;
1277 else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
1278 div = 2;
1279
1280 for (p = 0; p < tpg->planes; p++) {
Hans Verkuil3e14e7a82015-03-07 14:23:16 -03001281 unsigned vdiv = tpg->vdownsampling[p];
1282 unsigned hdiv = tpg->hdownsampling[p];
1283
1284 /* Print text */
Hans Verkuil63881df2014-08-25 08:02:14 -03001285#define PRINTSTR(PIXTYPE) do { \
1286 PIXTYPE fg; \
1287 PIXTYPE bg; \
1288 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1289 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1290 \
Hans Verkuil3e14e7a82015-03-07 14:23:16 -03001291 for (line = first; line < 16; line += vdiv * step) { \
Hans Verkuil63881df2014-08-25 08:02:14 -03001292 int l = tpg->vflip ? 15 - line : line; \
Hans Verkuil3e14e7a82015-03-07 14:23:16 -03001293 PIXTYPE *pos = (PIXTYPE *)(basep[p][(line / vdiv) & 1] + \
1294 ((y * step + l) / (vdiv * div)) * tpg->bytesperline[p] + \
1295 (x / hdiv) * sizeof(PIXTYPE)); \
Hans Verkuil63881df2014-08-25 08:02:14 -03001296 unsigned s; \
1297 \
1298 for (s = 0; s < len; s++) { \
1299 u8 chr = font8x16[text[s] * 16 + line]; \
1300 \
Hans Verkuil3e14e7a82015-03-07 14:23:16 -03001301 if (hdiv == 2 && tpg->hflip) { \
1302 pos[3] = (chr & (0x01 << 6) ? fg : bg); \
1303 pos[2] = (chr & (0x01 << 4) ? fg : bg); \
1304 pos[1] = (chr & (0x01 << 2) ? fg : bg); \
1305 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1306 } else if (hdiv == 2) { \
1307 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1308 pos[1] = (chr & (0x01 << 5) ? fg : bg); \
1309 pos[2] = (chr & (0x01 << 3) ? fg : bg); \
1310 pos[3] = (chr & (0x01 << 1) ? fg : bg); \
1311 } else if (tpg->hflip) { \
Hans Verkuil63881df2014-08-25 08:02:14 -03001312 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1313 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1314 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1315 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1316 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1317 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1318 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1319 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1320 } else { \
1321 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1322 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1323 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1324 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1325 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1326 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1327 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1328 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1329 } \
1330 \
Hans Verkuil3e14e7a82015-03-07 14:23:16 -03001331 pos += (tpg->hflip ? -8 : 8) / hdiv; \
Hans Verkuil63881df2014-08-25 08:02:14 -03001332 } \
1333 } \
1334} while (0)
1335
1336 switch (tpg->twopixelsize[p]) {
1337 case 2:
1338 PRINTSTR(u8); break;
1339 case 4:
1340 PRINTSTR(u16); break;
1341 case 6:
1342 PRINTSTR(x24); break;
1343 case 8:
1344 PRINTSTR(u32); break;
1345 }
1346 }
1347}
1348
1349void tpg_update_mv_step(struct tpg_data *tpg)
1350{
1351 int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
1352
1353 if (tpg->hflip)
1354 factor = -factor;
1355 switch (tpg->mv_hor_mode) {
1356 case TPG_MOVE_NEG_FAST:
1357 case TPG_MOVE_POS_FAST:
1358 tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
1359 break;
1360 case TPG_MOVE_NEG:
1361 case TPG_MOVE_POS:
1362 tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
1363 break;
1364 case TPG_MOVE_NEG_SLOW:
1365 case TPG_MOVE_POS_SLOW:
1366 tpg->mv_hor_step = 2;
1367 break;
1368 case TPG_MOVE_NONE:
1369 tpg->mv_hor_step = 0;
1370 break;
1371 }
1372 if (factor < 0)
1373 tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
1374
1375 factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
1376 switch (tpg->mv_vert_mode) {
1377 case TPG_MOVE_NEG_FAST:
1378 case TPG_MOVE_POS_FAST:
1379 tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
1380 break;
1381 case TPG_MOVE_NEG:
1382 case TPG_MOVE_POS:
1383 tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
1384 break;
1385 case TPG_MOVE_NEG_SLOW:
1386 case TPG_MOVE_POS_SLOW:
1387 tpg->mv_vert_step = 1;
1388 break;
1389 case TPG_MOVE_NONE:
1390 tpg->mv_vert_step = 0;
1391 break;
1392 }
1393 if (factor < 0)
1394 tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
1395}
1396
1397/* Map the line number relative to the crop rectangle to a frame line number */
1398static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
1399 unsigned field)
1400{
1401 switch (field) {
1402 case V4L2_FIELD_TOP:
1403 return tpg->crop.top + src_y * 2;
1404 case V4L2_FIELD_BOTTOM:
1405 return tpg->crop.top + src_y * 2 + 1;
1406 default:
1407 return src_y + tpg->crop.top;
1408 }
1409}
1410
1411/*
1412 * Map the line number relative to the compose rectangle to a destination
1413 * buffer line number.
1414 */
1415static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
1416 unsigned field)
1417{
1418 y += tpg->compose.top;
1419 switch (field) {
1420 case V4L2_FIELD_SEQ_TB:
1421 if (y & 1)
1422 return tpg->buf_height / 2 + y / 2;
1423 return y / 2;
1424 case V4L2_FIELD_SEQ_BT:
1425 if (y & 1)
1426 return y / 2;
1427 return tpg->buf_height / 2 + y / 2;
1428 default:
1429 return y;
1430 }
1431}
1432
1433static void tpg_recalc(struct tpg_data *tpg)
1434{
1435 if (tpg->recalc_colors) {
1436 tpg->recalc_colors = false;
1437 tpg->recalc_lines = true;
Hans Verkuil481b97a2014-11-17 10:14:32 -03001438 tpg->real_ycbcr_enc = tpg->ycbcr_enc;
1439 tpg->real_quantization = tpg->quantization;
1440 if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
1441 switch (tpg->colorspace) {
1442 case V4L2_COLORSPACE_REC709:
1443 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_709;
1444 break;
1445 case V4L2_COLORSPACE_SRGB:
1446 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SYCC;
1447 break;
1448 case V4L2_COLORSPACE_BT2020:
1449 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_BT2020;
1450 break;
1451 case V4L2_COLORSPACE_SMPTE240M:
1452 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SMPTE240M;
1453 break;
1454 case V4L2_COLORSPACE_SMPTE170M:
1455 case V4L2_COLORSPACE_470_SYSTEM_M:
1456 case V4L2_COLORSPACE_470_SYSTEM_BG:
1457 case V4L2_COLORSPACE_ADOBERGB:
1458 default:
1459 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_601;
1460 break;
1461 }
1462 }
1463 if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) {
1464 tpg->real_quantization = V4L2_QUANTIZATION_FULL_RANGE;
1465 if (tpg->is_yuv) {
1466 switch (tpg->real_ycbcr_enc) {
1467 case V4L2_YCBCR_ENC_SYCC:
1468 case V4L2_YCBCR_ENC_XV601:
1469 case V4L2_YCBCR_ENC_XV709:
1470 break;
1471 default:
1472 tpg->real_quantization =
1473 V4L2_QUANTIZATION_LIM_RANGE;
1474 break;
1475 }
Hans Verkuilc0b50d92015-03-08 04:53:33 -03001476 } else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) {
1477 /* R'G'B' BT.2020 is limited range */
1478 tpg->real_quantization =
1479 V4L2_QUANTIZATION_LIM_RANGE;
Hans Verkuil481b97a2014-11-17 10:14:32 -03001480 }
1481 }
Hans Verkuil63881df2014-08-25 08:02:14 -03001482 tpg_precalculate_colors(tpg);
1483 }
1484 if (tpg->recalc_square_border) {
1485 tpg->recalc_square_border = false;
1486 tpg_calculate_square_border(tpg);
1487 }
1488 if (tpg->recalc_lines) {
1489 tpg->recalc_lines = false;
1490 tpg_precalculate_line(tpg);
1491 }
1492}
1493
1494void tpg_calc_text_basep(struct tpg_data *tpg,
1495 u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
1496{
1497 unsigned stride = tpg->bytesperline[p];
Hans Verkuil280abe42015-03-07 14:50:41 -03001498 unsigned h = tpg->buf_height;
Hans Verkuil63881df2014-08-25 08:02:14 -03001499
1500 tpg_recalc(tpg);
1501
1502 basep[p][0] = vbuf;
1503 basep[p][1] = vbuf;
Hans Verkuil280abe42015-03-07 14:50:41 -03001504 h /= tpg->vdownsampling[p];
Hans Verkuil63881df2014-08-25 08:02:14 -03001505 if (tpg->field == V4L2_FIELD_SEQ_TB)
Hans Verkuil280abe42015-03-07 14:50:41 -03001506 basep[p][1] += h * stride / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001507 else if (tpg->field == V4L2_FIELD_SEQ_BT)
Hans Verkuil280abe42015-03-07 14:50:41 -03001508 basep[p][0] += h * stride / 2;
1509}
1510
1511static int tpg_pattern_avg(const struct tpg_data *tpg,
1512 unsigned pat1, unsigned pat2)
1513{
1514 unsigned pat_lines = tpg_get_pat_lines(tpg);
1515
1516 if (pat1 == (pat2 + 1) % pat_lines)
1517 return pat2;
1518 if (pat2 == (pat1 + 1) % pat_lines)
1519 return pat1;
1520 return -1;
Hans Verkuil63881df2014-08-25 08:02:14 -03001521}
1522
Hans Verkuil4db22042015-03-07 13:39:01 -03001523void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
Hans Verkuil63881df2014-08-25 08:02:14 -03001524{
1525 bool is_tv = std;
1526 bool is_60hz = is_tv && (std & V4L2_STD_525_60);
1527 unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
1528 unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
1529 unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
1530 unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
1531 unsigned wss_width;
1532 unsigned f;
1533 int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
1534 int h;
1535 unsigned twopixsize = tpg->twopixelsize[p];
Hans Verkuil280abe42015-03-07 14:50:41 -03001536 unsigned hdiv = tpg->hdownsampling[p];
1537 unsigned vdiv = tpg->vdownsampling[p];
1538 unsigned img_width = (tpg->compose.width / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001539 unsigned line_offset;
1540 unsigned left_pillar_width = 0;
1541 unsigned right_pillar_start = img_width;
1542 unsigned stride = tpg->bytesperline[p];
1543 unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
1544 u8 *orig_vbuf = vbuf;
1545
1546 /* Coarse scaling with Bresenham */
1547 unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
1548 unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
1549 unsigned src_y = 0;
1550 unsigned error = 0;
1551
1552 tpg_recalc(tpg);
1553
1554 mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
1555 mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
1556 wss_width = tpg->crop.left < tpg->src_width / 2 ?
1557 tpg->src_width / 2 - tpg->crop.left : 0;
1558 if (wss_width > tpg->crop.width)
1559 wss_width = tpg->crop.width;
1560 wss_width = wss_width * tpg->scaled_width / tpg->src_width;
1561
1562 vbuf += tpg->compose.left * twopixsize / 2;
1563 line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
Hans Verkuil280abe42015-03-07 14:50:41 -03001564 line_offset = ((line_offset & ~1) / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001565 if (tpg->crop.left < tpg->border.left) {
1566 left_pillar_width = tpg->border.left - tpg->crop.left;
1567 if (left_pillar_width > tpg->crop.width)
1568 left_pillar_width = tpg->crop.width;
1569 left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
Hans Verkuil280abe42015-03-07 14:50:41 -03001570 left_pillar_width = ((left_pillar_width & ~1) / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001571 }
1572 if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
1573 right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
1574 right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
Hans Verkuil280abe42015-03-07 14:50:41 -03001575 right_pillar_start = ((right_pillar_start & ~1) / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001576 if (right_pillar_start > img_width)
1577 right_pillar_start = img_width;
1578 }
1579
1580 f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
1581
1582 for (h = 0; h < tpg->compose.height; h++) {
1583 bool even;
1584 bool fill_blank = false;
1585 unsigned frame_line;
1586 unsigned buf_line;
1587 unsigned pat_line_old;
1588 unsigned pat_line_new;
1589 u8 *linestart_older;
1590 u8 *linestart_newer;
1591 u8 *linestart_top;
1592 u8 *linestart_bottom;
1593
1594 frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1595 even = !(frame_line & 1);
1596 buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1597 src_y += int_part;
1598 error += fract_part;
1599 if (error >= tpg->compose.height) {
1600 error -= tpg->compose.height;
1601 src_y++;
1602 }
1603
Hans Verkuil280abe42015-03-07 14:50:41 -03001604 if (vdiv > 1) {
1605 /*
1606 * When doing vertical downsampling the field setting
1607 * matters: for SEQ_BT/TB we downsample each field
1608 * separately (i.e. lines 0+2 are combined, as are
1609 * lines 1+3), for the other field settings we combine
1610 * odd and even lines. Doing that for SEQ_BT/TB would
1611 * be really weird.
1612 */
1613 if (tpg->field == V4L2_FIELD_SEQ_BT ||
1614 tpg->field == V4L2_FIELD_SEQ_TB) {
1615 if ((h & 3) >= 2)
1616 continue;
1617 } else if (h & 1) {
1618 continue;
1619 }
1620
1621 buf_line /= vdiv;
1622 }
1623
Hans Verkuil63881df2014-08-25 08:02:14 -03001624 if (h >= hmax) {
1625 if (hmax == tpg->compose.height)
1626 continue;
1627 if (!tpg->perc_fill_blank)
1628 continue;
1629 fill_blank = true;
1630 }
1631
1632 if (tpg->vflip)
1633 frame_line = tpg->src_height - frame_line - 1;
1634
1635 if (fill_blank) {
1636 linestart_older = tpg->contrast_line[p];
1637 linestart_newer = tpg->contrast_line[p];
1638 } else if (tpg->qual != TPG_QUAL_NOISE &&
1639 (frame_line < tpg->border.top ||
1640 frame_line >= tpg->border.top + tpg->border.height)) {
1641 linestart_older = tpg->black_line[p];
1642 linestart_newer = tpg->black_line[p];
1643 } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
1644 linestart_older = tpg->random_line[p] +
1645 twopixsize * prandom_u32_max(tpg->src_width / 2);
1646 linestart_newer = tpg->random_line[p] +
1647 twopixsize * prandom_u32_max(tpg->src_width / 2);
1648 } else {
Hans Verkuil280abe42015-03-07 14:50:41 -03001649 unsigned frame_line_old =
1650 (frame_line + mv_vert_old) % tpg->src_height;
1651 unsigned frame_line_new =
1652 (frame_line + mv_vert_new) % tpg->src_height;
1653 unsigned pat_line_next_old;
1654 unsigned pat_line_next_new;
1655
1656 pat_line_old = tpg_get_pat_line(tpg, frame_line_old);
1657 pat_line_new = tpg_get_pat_line(tpg, frame_line_new);
Hans Verkuil63881df2014-08-25 08:02:14 -03001658 linestart_older = tpg->lines[pat_line_old][p] +
Hans Verkuil280abe42015-03-07 14:50:41 -03001659 (mv_hor_old / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001660 linestart_newer = tpg->lines[pat_line_new][p] +
Hans Verkuil280abe42015-03-07 14:50:41 -03001661 (mv_hor_new / hdiv) * twopixsize / 2;
1662
1663 if (vdiv > 1) {
1664 unsigned frame_line_next;
1665 int avg_pat;
1666
1667 /*
1668 * Now decide whether we need to use downsampled_lines[].
1669 * That's necessary if the two lines use different patterns.
1670 */
1671 frame_line_next = tpg_calc_frameline(tpg, src_y, tpg->field);
1672 if (tpg->vflip)
1673 frame_line_next = tpg->src_height - frame_line_next - 1;
1674 pat_line_next_old = tpg_get_pat_line(tpg,
1675 (frame_line_next + mv_vert_old) % tpg->src_height);
1676 pat_line_next_new = tpg_get_pat_line(tpg,
1677 (frame_line_next + mv_vert_new) % tpg->src_height);
1678
1679 switch (tpg->field) {
1680 case V4L2_FIELD_INTERLACED:
1681 case V4L2_FIELD_INTERLACED_BT:
1682 case V4L2_FIELD_INTERLACED_TB:
1683 avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_new);
1684 if (avg_pat < 0)
1685 break;
1686 linestart_older = tpg->downsampled_lines[avg_pat][p] +
1687 (mv_hor_old / hdiv) * twopixsize / 2;
1688 linestart_newer = linestart_older;
1689 break;
1690 case V4L2_FIELD_NONE:
1691 case V4L2_FIELD_TOP:
1692 case V4L2_FIELD_BOTTOM:
1693 case V4L2_FIELD_SEQ_BT:
1694 case V4L2_FIELD_SEQ_TB:
1695 avg_pat = tpg_pattern_avg(tpg, pat_line_old, pat_line_next_old);
1696 if (avg_pat >= 0)
1697 linestart_older = tpg->downsampled_lines[avg_pat][p] +
1698 (mv_hor_old / hdiv) * twopixsize / 2;
1699 avg_pat = tpg_pattern_avg(tpg, pat_line_new, pat_line_next_new);
1700 if (avg_pat >= 0)
1701 linestart_newer = tpg->downsampled_lines[avg_pat][p] +
1702 (mv_hor_new / hdiv) * twopixsize / 2;
1703 break;
1704 }
1705 }
Hans Verkuil63881df2014-08-25 08:02:14 -03001706 linestart_older += line_offset;
1707 linestart_newer += line_offset;
1708 }
Hans Verkuil43047f62015-03-07 12:38:42 -03001709 if (tpg->field_alternate) {
1710 linestart_top = linestart_bottom = linestart_older;
1711 } else if (is_60hz) {
Hans Verkuil63881df2014-08-25 08:02:14 -03001712 linestart_top = linestart_newer;
1713 linestart_bottom = linestart_older;
1714 } else {
1715 linestart_top = linestart_older;
1716 linestart_bottom = linestart_newer;
1717 }
1718
1719 switch (tpg->field) {
1720 case V4L2_FIELD_INTERLACED:
1721 case V4L2_FIELD_INTERLACED_TB:
1722 case V4L2_FIELD_SEQ_TB:
1723 case V4L2_FIELD_SEQ_BT:
1724 if (even)
1725 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1726 else
1727 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1728 break;
1729 case V4L2_FIELD_INTERLACED_BT:
1730 if (even)
1731 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1732 else
1733 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1734 break;
1735 case V4L2_FIELD_TOP:
1736 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1737 break;
1738 case V4L2_FIELD_BOTTOM:
1739 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1740 break;
1741 case V4L2_FIELD_NONE:
1742 default:
1743 memcpy(vbuf + buf_line * stride, linestart_older, img_width);
1744 break;
1745 }
1746
1747 if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
1748 /*
1749 * Replace the first half of the top line of a 50 Hz frame
1750 * with random data to simulate a WSS signal.
1751 */
1752 u8 *wss = tpg->random_line[p] +
1753 twopixsize * prandom_u32_max(tpg->src_width / 2);
1754
Hans Verkuil280abe42015-03-07 14:50:41 -03001755 memcpy(vbuf + buf_line * stride, wss,
1756 (wss_width / hdiv) * twopixsize / 2);
Hans Verkuil63881df2014-08-25 08:02:14 -03001757 }
1758 }
1759
1760 vbuf = orig_vbuf;
Hans Verkuil280abe42015-03-07 14:50:41 -03001761 vbuf += (tpg->compose.left / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001762 src_y = 0;
1763 error = 0;
1764 for (h = 0; h < tpg->compose.height; h++) {
1765 unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1766 unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1767 const struct v4l2_rect *sq = &tpg->square;
1768 const struct v4l2_rect *b = &tpg->border;
1769 const struct v4l2_rect *c = &tpg->crop;
1770
1771 src_y += int_part;
1772 error += fract_part;
1773 if (error >= tpg->compose.height) {
1774 error -= tpg->compose.height;
1775 src_y++;
1776 }
1777
Hans Verkuil280abe42015-03-07 14:50:41 -03001778 if (vdiv > 1) {
1779 if (h & 1)
1780 continue;
1781 buf_line /= vdiv;
1782 }
1783
Hans Verkuil63881df2014-08-25 08:02:14 -03001784 if (tpg->show_border && frame_line >= b->top &&
1785 frame_line < b->top + b->height) {
1786 unsigned bottom = b->top + b->height - 1;
1787 unsigned left = left_pillar_width;
1788 unsigned right = right_pillar_start;
1789
1790 if (frame_line == b->top || frame_line == b->top + 1 ||
1791 frame_line == bottom || frame_line == bottom - 1) {
1792 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
1793 right - left);
1794 } else {
1795 if (b->left >= c->left &&
1796 b->left < c->left + c->width)
1797 memcpy(vbuf + buf_line * stride + left,
1798 tpg->contrast_line[p], twopixsize);
1799 if (b->left + b->width > c->left &&
1800 b->left + b->width <= c->left + c->width)
1801 memcpy(vbuf + buf_line * stride + right - twopixsize,
1802 tpg->contrast_line[p], twopixsize);
1803 }
1804 }
1805 if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
1806 frame_line < b->top + b->height) {
1807 memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
1808 memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
1809 img_width - right_pillar_start);
1810 }
1811 if (tpg->show_square && frame_line >= sq->top &&
1812 frame_line < sq->top + sq->height &&
1813 sq->left < c->left + c->width &&
1814 sq->left + sq->width >= c->left) {
1815 unsigned left = sq->left;
1816 unsigned width = sq->width;
1817
1818 if (c->left > left) {
1819 width -= c->left - left;
1820 left = c->left;
1821 }
1822 if (c->left + c->width < left + width)
1823 width -= left + width - c->left - c->width;
1824 left -= c->left;
1825 left = (left * tpg->scaled_width) / tpg->src_width;
Hans Verkuil280abe42015-03-07 14:50:41 -03001826 left = ((left & ~1) / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001827 width = (width * tpg->scaled_width) / tpg->src_width;
Hans Verkuil280abe42015-03-07 14:50:41 -03001828 width = ((width & ~1) / hdiv) * twopixsize / 2;
Hans Verkuil63881df2014-08-25 08:02:14 -03001829 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
1830 }
1831 if (tpg->insert_sav) {
Hans Verkuil280abe42015-03-07 14:50:41 -03001832 unsigned offset = (tpg->compose.width / (6 * hdiv)) * twopixsize;
Hans Verkuil63881df2014-08-25 08:02:14 -03001833 u8 *p = vbuf + buf_line * stride + offset;
1834 unsigned vact = 0, hact = 0;
1835
1836 p[0] = 0xff;
1837 p[1] = 0;
1838 p[2] = 0;
1839 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1840 ((hact ^ vact) << 3) |
1841 ((hact ^ f) << 2) |
1842 ((f ^ vact) << 1) |
1843 (hact ^ vact ^ f);
1844 }
1845 if (tpg->insert_eav) {
Hans Verkuil280abe42015-03-07 14:50:41 -03001846 unsigned offset = (tpg->compose.width / (6 * hdiv)) * 2 * twopixsize;
Hans Verkuil63881df2014-08-25 08:02:14 -03001847 u8 *p = vbuf + buf_line * stride + offset;
1848 unsigned vact = 0, hact = 1;
1849
1850 p[0] = 0xff;
1851 p[1] = 0;
1852 p[2] = 0;
1853 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1854 ((hact ^ vact) << 3) |
1855 ((hact ^ f) << 2) |
1856 ((f ^ vact) << 1) |
1857 (hact ^ vact ^ f);
1858 }
1859 }
1860}
Hans Verkuil4db22042015-03-07 13:39:01 -03001861
1862void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
1863{
1864 unsigned offset = 0;
1865 unsigned i;
1866
1867 if (tpg->buffers > 1) {
1868 tpg_fill_plane_buffer(tpg, std, p, vbuf);
1869 return;
1870 }
1871
1872 for (i = 0; i < tpg->planes; i++) {
1873 tpg_fill_plane_buffer(tpg, std, i, vbuf + offset);
1874 offset += tpg_calc_plane_size(tpg, i);
1875 }
1876}