[media] vivid: use TPG_MAX_PLANES instead of hardcoding plane-arrays
[deliverable/linux.git] / drivers / media / platform / vivid / vivid-tpg.c
CommitLineData
63881df9
HV
1/*
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",
38 "1x1 Checkers",
39 "Alternating Hor Lines",
40 "Alternating Vert Lines",
41 "One Pixel Wide Cross",
42 "Two Pixels Wide Cross",
43 "Ten Pixels Wide Cross",
44 "Gray Ramp",
45 "Noise",
46 NULL
47};
48
49/* Must remain in sync with enum tpg_aspect */
50const char * const tpg_aspect_strings[] = {
51 "Source Width x Height",
52 "4x3",
53 "14x9",
54 "16x9",
55 "16x9 Anamorphic",
56 NULL
57};
58
59/*
60 * Sine table: sin[0] = 127 * sin(-180 degrees)
61 * sin[128] = 127 * sin(0 degrees)
62 * sin[256] = 127 * sin(180 degrees)
63 */
64static 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,
81 0,
82};
83
84#define cos(idx) sin[((idx) + 64) % sizeof(sin)]
85
86/* Global font descriptor */
87static const u8 *font8x16;
88
89void tpg_set_font(const u8 *f)
90{
91 font8x16 = f;
92}
93
94void tpg_init(struct tpg_data *tpg, unsigned w, unsigned h)
95{
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;
104 tpg->contrast = 128;
105 tpg->saturation = 128;
106 tpg->hue = 0;
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;
113}
114
115int tpg_alloc(struct tpg_data *tpg, unsigned max_w)
116{
117 unsigned pat;
118 unsigned plane;
119
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;
124
125 tpg->lines[pat][plane] = vzalloc(max_w * 2 * pixelsz);
126 if (!tpg->lines[pat][plane])
127 return -ENOMEM;
128 }
129 }
130 for (plane = 0; plane < TPG_MAX_PLANES; plane++) {
131 unsigned pixelsz = plane ? 1 : 4;
132
133 tpg->contrast_line[plane] = vzalloc(max_w * pixelsz);
134 if (!tpg->contrast_line[plane])
135 return -ENOMEM;
136 tpg->black_line[plane] = vzalloc(max_w * pixelsz);
137 if (!tpg->black_line[plane])
138 return -ENOMEM;
c204e1fa 139 tpg->random_line[plane] = vzalloc(max_w * 2 * pixelsz);
63881df9
HV
140 if (!tpg->random_line[plane])
141 return -ENOMEM;
142 }
143 return 0;
144}
145
146void tpg_free(struct tpg_data *tpg)
147{
148 unsigned pat;
149 unsigned plane;
150
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;
155 }
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;
163 }
164}
165
166bool tpg_s_fourcc(struct tpg_data *tpg, u32 fourcc)
167{
168 tpg->fourcc = fourcc;
169 tpg->planes = 1;
170 tpg->recalc_colors = true;
171 switch (fourcc) {
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:
6c515a44 186 tpg->is_yuv = false;
63881df9
HV
187 break;
188 case V4L2_PIX_FMT_NV16M:
189 case V4L2_PIX_FMT_NV61M:
190 tpg->planes = 2;
191 /* fall-through */
192 case V4L2_PIX_FMT_YUYV:
193 case V4L2_PIX_FMT_UYVY:
194 case V4L2_PIX_FMT_YVYU:
195 case V4L2_PIX_FMT_VYUY:
6c515a44 196 tpg->is_yuv = true;
63881df9
HV
197 break;
198 default:
199 return false;
200 }
201
202 switch (fourcc) {
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;
214 break;
215 case V4L2_PIX_FMT_RGB24:
216 case V4L2_PIX_FMT_BGR24:
217 tpg->twopixelsize[0] = 2 * 3;
218 break;
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;
226 break;
227 case V4L2_PIX_FMT_NV16M:
228 case V4L2_PIX_FMT_NV61M:
229 tpg->twopixelsize[0] = 2;
230 tpg->twopixelsize[1] = 2;
231 break;
232 }
233 return true;
234}
235
236void tpg_s_crop_compose(struct tpg_data *tpg, const struct v4l2_rect *crop,
237 const struct v4l2_rect *compose)
238{
239 tpg->crop = *crop;
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;
249}
250
251void tpg_reset_source(struct tpg_data *tpg, unsigned width, unsigned height,
73d81022 252 u32 field)
63881df9
HV
253{
254 unsigned p;
255
256 tpg->src_width = width;
257 tpg->src_height = height;
258 tpg->field = field;
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;
272}
273
274static enum tpg_color tpg_get_textbg_color(struct tpg_data *tpg)
275{
276 switch (tpg->pattern) {
277 case TPG_PAT_BLACK:
278 return TPG_COLOR_100_WHITE;
279 case TPG_PAT_CSC_COLORBAR:
280 return TPG_COLOR_CSC_BLACK;
281 default:
282 return TPG_COLOR_100_BLACK;
283 }
284}
285
286static enum tpg_color tpg_get_textfg_color(struct tpg_data *tpg)
287{
288 switch (tpg->pattern) {
289 case TPG_PAT_75_COLORBAR:
290 case TPG_PAT_CSC_COLORBAR:
291 return TPG_COLOR_CSC_WHITE;
292 case TPG_PAT_BLACK:
293 return TPG_COLOR_100_BLACK;
294 default:
295 return TPG_COLOR_100_WHITE;
296 }
297}
298
481b97a1 299static inline int rec709_to_linear(int v)
63881df9 300{
481b97a1
HV
301 v = clamp(v, 0, 0xff0);
302 return tpg_rec709_to_linear[v];
63881df9
HV
303}
304
481b97a1 305static inline int linear_to_rec709(int v)
63881df9 306{
481b97a1
HV
307 v = clamp(v, 0, 0xff0);
308 return tpg_linear_to_rec709[v];
63881df9
HV
309}
310
481b97a1
HV
311static void rgb2ycbcr(const int m[3][3], int r, int g, int b,
312 int y_offset, int *y, int *cb, int *cr)
63881df9 313{
481b97a1
HV
314 *y = ((m[0][0] * r + m[0][1] * g + m[0][2] * b) >> 16) + (y_offset << 4);
315 *cb = ((m[1][0] * r + m[1][1] * g + m[1][2] * b) >> 16) + (128 << 4);
316 *cr = ((m[2][0] * r + m[2][1] * g + m[2][2] * b) >> 16) + (128 << 4);
63881df9
HV
317}
318
481b97a1
HV
319static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b,
320 int *y, int *cb, int *cr)
63881df9 321{
481b97a1 322#define COEFF(v, r) ((int)(0.5 + (v) * (r) * 256.0))
63881df9 323
481b97a1
HV
324 static const int bt601[3][3] = {
325 { COEFF(0.299, 219), COEFF(0.587, 219), COEFF(0.114, 219) },
326 { COEFF(-0.169, 224), COEFF(-0.331, 224), COEFF(0.5, 224) },
327 { COEFF(0.5, 224), COEFF(-0.419, 224), COEFF(-0.081, 224) },
328 };
329 static const int bt601_full[3][3] = {
330 { COEFF(0.299, 255), COEFF(0.587, 255), COEFF(0.114, 255) },
331 { COEFF(-0.169, 255), COEFF(-0.331, 255), COEFF(0.5, 255) },
332 { COEFF(0.5, 255), COEFF(-0.419, 255), COEFF(-0.081, 255) },
333 };
334 static const int rec709[3][3] = {
335 { COEFF(0.2126, 219), COEFF(0.7152, 219), COEFF(0.0722, 219) },
336 { COEFF(-0.1146, 224), COEFF(-0.3854, 224), COEFF(0.5, 224) },
337 { COEFF(0.5, 224), COEFF(-0.4542, 224), COEFF(-0.0458, 224) },
338 };
339 static const int rec709_full[3][3] = {
340 { COEFF(0.2126, 255), COEFF(0.7152, 255), COEFF(0.0722, 255) },
341 { COEFF(-0.1146, 255), COEFF(-0.3854, 255), COEFF(0.5, 255) },
342 { COEFF(0.5, 255), COEFF(-0.4542, 255), COEFF(-0.0458, 255) },
343 };
344 static const int smpte240m[3][3] = {
345 { COEFF(0.212, 219), COEFF(0.701, 219), COEFF(0.087, 219) },
346 { COEFF(-0.116, 224), COEFF(-0.384, 224), COEFF(0.5, 224) },
347 { COEFF(0.5, 224), COEFF(-0.445, 224), COEFF(-0.055, 224) },
348 };
349 static const int bt2020[3][3] = {
350 { COEFF(0.2726, 219), COEFF(0.6780, 219), COEFF(0.0593, 219) },
351 { COEFF(-0.1396, 224), COEFF(-0.3604, 224), COEFF(0.5, 224) },
352 { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) },
353 };
354 bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
afad4dd5 355 unsigned y_offset = full ? 0 : 16;
481b97a1
HV
356 int lin_y, yc;
357
358 switch (tpg->real_ycbcr_enc) {
359 case V4L2_YCBCR_ENC_601:
360 case V4L2_YCBCR_ENC_XV601:
361 case V4L2_YCBCR_ENC_SYCC:
afad4dd5 362 rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr);
481b97a1
HV
363 break;
364 case V4L2_YCBCR_ENC_BT2020:
365 rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr);
63881df9 366 break;
481b97a1
HV
367 case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
368 lin_y = (COEFF(0.2627, 255) * rec709_to_linear(r) +
369 COEFF(0.6780, 255) * rec709_to_linear(g) +
370 COEFF(0.0593, 255) * rec709_to_linear(b)) >> 16;
371 yc = linear_to_rec709(lin_y);
372 *y = (yc * 219) / 255 + (16 << 4);
373 if (b <= yc)
374 *cb = (((b - yc) * COEFF(1.0 / 1.9404, 224)) >> 16) + (128 << 4);
375 else
376 *cb = (((b - yc) * COEFF(1.0 / 1.5816, 224)) >> 16) + (128 << 4);
377 if (r <= yc)
378 *cr = (((r - yc) * COEFF(1.0 / 1.7184, 224)) >> 16) + (128 << 4);
379 else
380 *cr = (((r - yc) * COEFF(1.0 / 0.9936, 224)) >> 16) + (128 << 4);
63881df9 381 break;
481b97a1
HV
382 case V4L2_YCBCR_ENC_SMPTE240M:
383 rgb2ycbcr(smpte240m, r, g, b, 16, y, cb, cr);
384 break;
385 case V4L2_YCBCR_ENC_709:
386 case V4L2_YCBCR_ENC_XV709:
63881df9 387 default:
afad4dd5 388 rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr);
63881df9
HV
389 break;
390 }
63881df9
HV
391}
392
481b97a1
HV
393static void ycbcr2rgb(const int m[3][3], int y, int cb, int cr,
394 int y_offset, int *r, int *g, int *b)
63881df9 395{
481b97a1 396 y -= y_offset << 4;
63881df9
HV
397 cb -= 128 << 4;
398 cr -= 128 << 4;
481b97a1
HV
399 *r = m[0][0] * y + m[0][1] * cb + m[0][2] * cr;
400 *g = m[1][0] * y + m[1][1] * cb + m[1][2] * cr;
401 *b = m[2][0] * y + m[2][1] * cb + m[2][2] * cr;
402 *r = clamp(*r >> 12, 0, 0xff0);
403 *g = clamp(*g >> 12, 0, 0xff0);
404 *b = clamp(*b >> 12, 0, 0xff0);
63881df9
HV
405}
406
481b97a1
HV
407static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr,
408 int *r, int *g, int *b)
63881df9 409{
481b97a1
HV
410#undef COEFF
411#define COEFF(v, r) ((int)(0.5 + (v) * ((255.0 * 255.0 * 16.0) / (r))))
412 static const int bt601[3][3] = {
413 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4020, 224) },
414 { COEFF(1, 219), COEFF(-0.3441, 224), COEFF(-0.7141, 224) },
415 { COEFF(1, 219), COEFF(1.7720, 224), COEFF(0, 224) },
416 };
417 static const int bt601_full[3][3] = {
418 { COEFF(1, 255), COEFF(0, 255), COEFF(1.4020, 255) },
419 { COEFF(1, 255), COEFF(-0.3441, 255), COEFF(-0.7141, 255) },
420 { COEFF(1, 255), COEFF(1.7720, 255), COEFF(0, 255) },
421 };
422 static const int rec709[3][3] = {
423 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5748, 224) },
424 { COEFF(1, 219), COEFF(-0.1873, 224), COEFF(-0.4681, 224) },
425 { COEFF(1, 219), COEFF(1.8556, 224), COEFF(0, 224) },
426 };
427 static const int rec709_full[3][3] = {
428 { COEFF(1, 255), COEFF(0, 255), COEFF(1.5748, 255) },
429 { COEFF(1, 255), COEFF(-0.1873, 255), COEFF(-0.4681, 255) },
430 { COEFF(1, 255), COEFF(1.8556, 255), COEFF(0, 255) },
431 };
432 static const int smpte240m[3][3] = {
433 { COEFF(1, 219), COEFF(0, 224), COEFF(1.5756, 224) },
434 { COEFF(1, 219), COEFF(-0.2253, 224), COEFF(-0.4767, 224) },
435 { COEFF(1, 219), COEFF(1.8270, 224), COEFF(0, 224) },
436 };
437 static const int bt2020[3][3] = {
438 { COEFF(1, 219), COEFF(0, 224), COEFF(1.4746, 224) },
439 { COEFF(1, 219), COEFF(-0.1646, 224), COEFF(-0.5714, 224) },
440 { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) },
441 };
442 bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE;
afad4dd5 443 unsigned y_offset = full ? 0 : 16;
481b97a1
HV
444 int lin_r, lin_g, lin_b, lin_y;
445
446 switch (tpg->real_ycbcr_enc) {
447 case V4L2_YCBCR_ENC_601:
448 case V4L2_YCBCR_ENC_XV601:
449 case V4L2_YCBCR_ENC_SYCC:
afad4dd5 450 ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b);
481b97a1
HV
451 break;
452 case V4L2_YCBCR_ENC_BT2020:
453 ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b);
454 break;
455 case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
456 y -= 16 << 4;
457 cb -= 128 << 4;
458 cr -= 128 << 4;
63881df9 459
481b97a1
HV
460 if (cb <= 0)
461 *b = COEFF(1.0, 219) * y + COEFF(1.9404, 224) * cb;
462 else
463 *b = COEFF(1.0, 219) * y + COEFF(1.5816, 224) * cb;
464 *b = *b >> 12;
465 if (cr <= 0)
466 *r = COEFF(1.0, 219) * y + COEFF(1.7184, 224) * cr;
467 else
468 *r = COEFF(1.0, 219) * y + COEFF(0.9936, 224) * cr;
469 *r = *r >> 12;
470 lin_r = rec709_to_linear(*r);
471 lin_b = rec709_to_linear(*b);
472 lin_y = rec709_to_linear((y * 255) / 219);
473
474 lin_g = COEFF(1.0 / 0.6780, 255) * lin_y -
475 COEFF(0.2627 / 0.6780, 255) * lin_r -
476 COEFF(0.0593 / 0.6780, 255) * lin_b;
477 *g = linear_to_rec709(lin_g >> 12);
63881df9 478 break;
481b97a1
HV
479 case V4L2_YCBCR_ENC_SMPTE240M:
480 ycbcr2rgb(smpte240m, y, cb, cr, 16, r, g, b);
63881df9 481 break;
481b97a1
HV
482 case V4L2_YCBCR_ENC_709:
483 case V4L2_YCBCR_ENC_XV709:
63881df9 484 default:
afad4dd5 485 ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b);
63881df9
HV
486 break;
487 }
63881df9
HV
488}
489
490/* precalculate color bar values to speed up rendering */
491static void precalculate_color(struct tpg_data *tpg, int k)
492{
493 int col = k;
494 int r = tpg_colors[col].r;
495 int g = tpg_colors[col].g;
496 int b = tpg_colors[col].b;
497
498 if (k == TPG_COLOR_TEXTBG) {
499 col = tpg_get_textbg_color(tpg);
500
501 r = tpg_colors[col].r;
502 g = tpg_colors[col].g;
503 b = tpg_colors[col].b;
504 } else if (k == TPG_COLOR_TEXTFG) {
505 col = tpg_get_textfg_color(tpg);
506
507 r = tpg_colors[col].r;
508 g = tpg_colors[col].g;
509 b = tpg_colors[col].b;
510 } else if (tpg->pattern == TPG_PAT_NOISE) {
511 r = g = b = prandom_u32_max(256);
512 } else if (k == TPG_COLOR_RANDOM) {
513 r = g = b = tpg->qual_offset + prandom_u32_max(196);
514 } else if (k >= TPG_COLOR_RAMP) {
515 r = g = b = k - TPG_COLOR_RAMP;
516 }
517
518 if (tpg->pattern == TPG_PAT_CSC_COLORBAR && col <= TPG_COLOR_CSC_BLACK) {
519 r = tpg_csc_colors[tpg->colorspace][col].r;
520 g = tpg_csc_colors[tpg->colorspace][col].g;
521 b = tpg_csc_colors[tpg->colorspace][col].b;
522 } else {
523 r <<= 4;
524 g <<= 4;
525 b <<= 4;
526 }
481b97a1
HV
527 if (tpg->qual == TPG_QUAL_GRAY) {
528 /* Rec. 709 Luma function */
529 /* (0.2126, 0.7152, 0.0722) * (255 * 256) */
530 r = g = b = ((13879 * r + 46688 * g + 4713 * b) >> 16) + (16 << 4);
531 }
63881df9
HV
532
533 /*
534 * The assumption is that the RGB output is always full range,
535 * so only if the rgb_range overrides the 'real' rgb range do
536 * we need to convert the RGB values.
537 *
63881df9
HV
538 * Remember that r, g and b are still in the 0 - 0xff0 range.
539 */
540 if (tpg->real_rgb_range == V4L2_DV_RGB_RANGE_LIMITED &&
541 tpg->rgb_range == V4L2_DV_RGB_RANGE_FULL) {
542 /*
543 * Convert from full range (which is what r, g and b are)
544 * to limited range (which is the 'real' RGB range), which
545 * is then interpreted as full range.
546 */
547 r = (r * 219) / 255 + (16 << 4);
548 g = (g * 219) / 255 + (16 << 4);
549 b = (b * 219) / 255 + (16 << 4);
550 } else if (tpg->real_rgb_range != V4L2_DV_RGB_RANGE_LIMITED &&
551 tpg->rgb_range == V4L2_DV_RGB_RANGE_LIMITED) {
552 /*
553 * Clamp r, g and b to the limited range and convert to full
554 * range since that's what we deliver.
555 */
556 r = clamp(r, 16 << 4, 235 << 4);
557 g = clamp(g, 16 << 4, 235 << 4);
558 b = clamp(b, 16 << 4, 235 << 4);
559 r = (r - (16 << 4)) * 255 / 219;
560 g = (g - (16 << 4)) * 255 / 219;
561 b = (b - (16 << 4)) * 255 / 219;
562 }
563
564 if (tpg->brightness != 128 || tpg->contrast != 128 ||
565 tpg->saturation != 128 || tpg->hue) {
566 /* Implement these operations */
481b97a1
HV
567 int y, cb, cr;
568 int tmp_cb, tmp_cr;
63881df9
HV
569
570 /* First convert to YCbCr */
481b97a1
HV
571
572 color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
63881df9
HV
573
574 y = (16 << 4) + ((y - (16 << 4)) * tpg->contrast) / 128;
575 y += (tpg->brightness << 4) - (128 << 4);
576
577 cb -= 128 << 4;
578 cr -= 128 << 4;
579 tmp_cb = (cb * cos(128 + tpg->hue)) / 127 + (cr * sin[128 + tpg->hue]) / 127;
580 tmp_cr = (cr * cos(128 + tpg->hue)) / 127 - (cb * sin[128 + tpg->hue]) / 127;
581
582 cb = (128 << 4) + (tmp_cb * tpg->contrast * tpg->saturation) / (128 * 128);
583 cr = (128 << 4) + (tmp_cr * tpg->contrast * tpg->saturation) / (128 * 128);
584 if (tpg->is_yuv) {
585 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
586 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
587 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
588 return;
589 }
481b97a1 590 ycbcr_to_color(tpg, y, cb, cr, &r, &g, &b);
63881df9
HV
591 }
592
593 if (tpg->is_yuv) {
594 /* Convert to YCbCr */
481b97a1
HV
595 int y, cb, cr;
596
597 color_to_ycbcr(tpg, r, g, b, &y, &cb, &cr);
63881df9 598
481b97a1
HV
599 if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
600 y = clamp(y, 16 << 4, 235 << 4);
601 cb = clamp(cb, 16 << 4, 240 << 4);
602 cr = clamp(cr, 16 << 4, 240 << 4);
603 }
63881df9
HV
604 tpg->colors[k][0] = clamp(y >> 4, 1, 254);
605 tpg->colors[k][1] = clamp(cb >> 4, 1, 254);
606 tpg->colors[k][2] = clamp(cr >> 4, 1, 254);
607 } else {
481b97a1
HV
608 if (tpg->real_quantization == V4L2_QUANTIZATION_LIM_RANGE) {
609 r = (r * 219) / 255 + (16 << 4);
610 g = (g * 219) / 255 + (16 << 4);
611 b = (b * 219) / 255 + (16 << 4);
612 }
63881df9
HV
613 switch (tpg->fourcc) {
614 case V4L2_PIX_FMT_RGB565:
615 case V4L2_PIX_FMT_RGB565X:
616 r >>= 7;
617 g >>= 6;
618 b >>= 7;
619 break;
620 case V4L2_PIX_FMT_RGB555:
621 case V4L2_PIX_FMT_XRGB555:
622 case V4L2_PIX_FMT_ARGB555:
623 case V4L2_PIX_FMT_RGB555X:
624 r >>= 7;
625 g >>= 7;
626 b >>= 7;
627 break;
628 default:
629 r >>= 4;
630 g >>= 4;
631 b >>= 4;
632 break;
633 }
634
635 tpg->colors[k][0] = r;
636 tpg->colors[k][1] = g;
637 tpg->colors[k][2] = b;
638 }
639}
640
641static void tpg_precalculate_colors(struct tpg_data *tpg)
642{
643 int k;
644
645 for (k = 0; k < TPG_COLOR_MAX; k++)
646 precalculate_color(tpg, k);
647}
648
649/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
650static void gen_twopix(struct tpg_data *tpg,
651 u8 buf[TPG_MAX_PLANES][8], int color, bool odd)
652{
653 unsigned offset = odd * tpg->twopixelsize[0] / 2;
654 u8 alpha = tpg->alpha_component;
655 u8 r_y, g_u, b_v;
656
657 if (tpg->alpha_red_only && color != TPG_COLOR_CSC_RED &&
658 color != TPG_COLOR_100_RED &&
659 color != TPG_COLOR_75_RED)
660 alpha = 0;
661 if (color == TPG_COLOR_RANDOM)
662 precalculate_color(tpg, color);
663 r_y = tpg->colors[color][0]; /* R or precalculated Y */
664 g_u = tpg->colors[color][1]; /* G or precalculated U */
665 b_v = tpg->colors[color][2]; /* B or precalculated V */
666
667 switch (tpg->fourcc) {
668 case V4L2_PIX_FMT_NV16M:
669 buf[0][offset] = r_y;
670 buf[1][offset] = odd ? b_v : g_u;
671 break;
672 case V4L2_PIX_FMT_NV61M:
673 buf[0][offset] = r_y;
674 buf[1][offset] = odd ? g_u : b_v;
675 break;
676
677 case V4L2_PIX_FMT_YUYV:
678 buf[0][offset] = r_y;
679 buf[0][offset + 1] = odd ? b_v : g_u;
680 break;
681 case V4L2_PIX_FMT_UYVY:
682 buf[0][offset] = odd ? b_v : g_u;
683 buf[0][offset + 1] = r_y;
684 break;
685 case V4L2_PIX_FMT_YVYU:
686 buf[0][offset] = r_y;
687 buf[0][offset + 1] = odd ? g_u : b_v;
688 break;
689 case V4L2_PIX_FMT_VYUY:
690 buf[0][offset] = odd ? g_u : b_v;
691 buf[0][offset + 1] = r_y;
692 break;
693 case V4L2_PIX_FMT_RGB565:
694 buf[0][offset] = (g_u << 5) | b_v;
695 buf[0][offset + 1] = (r_y << 3) | (g_u >> 3);
696 break;
697 case V4L2_PIX_FMT_RGB565X:
698 buf[0][offset] = (r_y << 3) | (g_u >> 3);
699 buf[0][offset + 1] = (g_u << 5) | b_v;
700 break;
701 case V4L2_PIX_FMT_RGB555:
702 case V4L2_PIX_FMT_XRGB555:
703 alpha = 0;
704 /* fall through */
705 case V4L2_PIX_FMT_ARGB555:
706 buf[0][offset] = (g_u << 5) | b_v;
707 buf[0][offset + 1] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
708 break;
709 case V4L2_PIX_FMT_RGB555X:
710 buf[0][offset] = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
711 buf[0][offset + 1] = (g_u << 5) | b_v;
712 break;
713 case V4L2_PIX_FMT_RGB24:
714 buf[0][offset] = r_y;
715 buf[0][offset + 1] = g_u;
716 buf[0][offset + 2] = b_v;
717 break;
718 case V4L2_PIX_FMT_BGR24:
719 buf[0][offset] = b_v;
720 buf[0][offset + 1] = g_u;
721 buf[0][offset + 2] = r_y;
722 break;
723 case V4L2_PIX_FMT_RGB32:
724 case V4L2_PIX_FMT_XRGB32:
725 alpha = 0;
726 /* fall through */
727 case V4L2_PIX_FMT_ARGB32:
728 buf[0][offset] = alpha;
729 buf[0][offset + 1] = r_y;
730 buf[0][offset + 2] = g_u;
731 buf[0][offset + 3] = b_v;
732 break;
733 case V4L2_PIX_FMT_BGR32:
734 case V4L2_PIX_FMT_XBGR32:
735 alpha = 0;
736 /* fall through */
737 case V4L2_PIX_FMT_ABGR32:
738 buf[0][offset] = b_v;
739 buf[0][offset + 1] = g_u;
740 buf[0][offset + 2] = r_y;
741 buf[0][offset + 3] = alpha;
742 break;
743 }
744}
745
746/* Return how many pattern lines are used by the current pattern. */
747static unsigned tpg_get_pat_lines(struct tpg_data *tpg)
748{
749 switch (tpg->pattern) {
750 case TPG_PAT_CHECKERS_16X16:
751 case TPG_PAT_CHECKERS_1X1:
752 case TPG_PAT_ALTERNATING_HLINES:
753 case TPG_PAT_CROSS_1_PIXEL:
754 case TPG_PAT_CROSS_2_PIXELS:
755 case TPG_PAT_CROSS_10_PIXELS:
756 return 2;
757 case TPG_PAT_100_COLORSQUARES:
758 case TPG_PAT_100_HCOLORBAR:
759 return 8;
760 default:
761 return 1;
762 }
763}
764
765/* Which pattern line should be used for the given frame line. */
766static unsigned tpg_get_pat_line(struct tpg_data *tpg, unsigned line)
767{
768 switch (tpg->pattern) {
769 case TPG_PAT_CHECKERS_16X16:
770 return (line >> 4) & 1;
771 case TPG_PAT_CHECKERS_1X1:
772 case TPG_PAT_ALTERNATING_HLINES:
773 return line & 1;
774 case TPG_PAT_100_COLORSQUARES:
775 case TPG_PAT_100_HCOLORBAR:
776 return (line * 8) / tpg->src_height;
777 case TPG_PAT_CROSS_1_PIXEL:
778 return line == tpg->src_height / 2;
779 case TPG_PAT_CROSS_2_PIXELS:
780 return (line + 1) / 2 == tpg->src_height / 4;
781 case TPG_PAT_CROSS_10_PIXELS:
782 return (line + 10) / 20 == tpg->src_height / 40;
783 default:
784 return 0;
785 }
786}
787
788/*
789 * Which color should be used for the given pattern line and X coordinate.
790 * Note: x is in the range 0 to 2 * tpg->src_width.
791 */
792static enum tpg_color tpg_get_color(struct tpg_data *tpg, unsigned pat_line, unsigned x)
793{
794 /* Maximum number of bars are TPG_COLOR_MAX - otherwise, the input print code
795 should be modified */
796 static const enum tpg_color bars[3][8] = {
797 /* Standard ITU-R 75% color bar sequence */
798 { TPG_COLOR_CSC_WHITE, TPG_COLOR_75_YELLOW,
799 TPG_COLOR_75_CYAN, TPG_COLOR_75_GREEN,
800 TPG_COLOR_75_MAGENTA, TPG_COLOR_75_RED,
801 TPG_COLOR_75_BLUE, TPG_COLOR_100_BLACK, },
802 /* Standard ITU-R 100% color bar sequence */
803 { TPG_COLOR_100_WHITE, TPG_COLOR_100_YELLOW,
804 TPG_COLOR_100_CYAN, TPG_COLOR_100_GREEN,
805 TPG_COLOR_100_MAGENTA, TPG_COLOR_100_RED,
806 TPG_COLOR_100_BLUE, TPG_COLOR_100_BLACK, },
807 /* Color bar sequence suitable to test CSC */
808 { TPG_COLOR_CSC_WHITE, TPG_COLOR_CSC_YELLOW,
809 TPG_COLOR_CSC_CYAN, TPG_COLOR_CSC_GREEN,
810 TPG_COLOR_CSC_MAGENTA, TPG_COLOR_CSC_RED,
811 TPG_COLOR_CSC_BLUE, TPG_COLOR_CSC_BLACK, },
812 };
813
814 switch (tpg->pattern) {
815 case TPG_PAT_75_COLORBAR:
816 case TPG_PAT_100_COLORBAR:
817 case TPG_PAT_CSC_COLORBAR:
818 return bars[tpg->pattern][((x * 8) / tpg->src_width) % 8];
819 case TPG_PAT_100_COLORSQUARES:
820 return bars[1][(pat_line + (x * 8) / tpg->src_width) % 8];
821 case TPG_PAT_100_HCOLORBAR:
822 return bars[1][pat_line];
823 case TPG_PAT_BLACK:
824 return TPG_COLOR_100_BLACK;
825 case TPG_PAT_WHITE:
826 return TPG_COLOR_100_WHITE;
827 case TPG_PAT_RED:
828 return TPG_COLOR_100_RED;
829 case TPG_PAT_GREEN:
830 return TPG_COLOR_100_GREEN;
831 case TPG_PAT_BLUE:
832 return TPG_COLOR_100_BLUE;
833 case TPG_PAT_CHECKERS_16X16:
834 return (((x >> 4) & 1) ^ (pat_line & 1)) ?
835 TPG_COLOR_100_BLACK : TPG_COLOR_100_WHITE;
836 case TPG_PAT_CHECKERS_1X1:
837 return ((x & 1) ^ (pat_line & 1)) ?
838 TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
839 case TPG_PAT_ALTERNATING_HLINES:
840 return pat_line ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
841 case TPG_PAT_ALTERNATING_VLINES:
842 return (x & 1) ? TPG_COLOR_100_WHITE : TPG_COLOR_100_BLACK;
843 case TPG_PAT_CROSS_1_PIXEL:
844 if (pat_line || (x % tpg->src_width) == tpg->src_width / 2)
845 return TPG_COLOR_100_BLACK;
846 return TPG_COLOR_100_WHITE;
847 case TPG_PAT_CROSS_2_PIXELS:
848 if (pat_line || ((x % tpg->src_width) + 1) / 2 == tpg->src_width / 4)
849 return TPG_COLOR_100_BLACK;
850 return TPG_COLOR_100_WHITE;
851 case TPG_PAT_CROSS_10_PIXELS:
852 if (pat_line || ((x % tpg->src_width) + 10) / 20 == tpg->src_width / 40)
853 return TPG_COLOR_100_BLACK;
854 return TPG_COLOR_100_WHITE;
855 case TPG_PAT_GRAY_RAMP:
856 return TPG_COLOR_RAMP + ((x % tpg->src_width) * 256) / tpg->src_width;
857 default:
858 return TPG_COLOR_100_RED;
859 }
860}
861
862/*
863 * Given the pixel aspect ratio and video aspect ratio calculate the
864 * coordinates of a centered square and the coordinates of the border of
865 * the active video area. The coordinates are relative to the source
866 * frame rectangle.
867 */
868static void tpg_calculate_square_border(struct tpg_data *tpg)
869{
870 unsigned w = tpg->src_width;
871 unsigned h = tpg->src_height;
872 unsigned sq_w, sq_h;
873
874 sq_w = (w * 2 / 5) & ~1;
875 if (((w - sq_w) / 2) & 1)
876 sq_w += 2;
877 sq_h = sq_w;
878 tpg->square.width = sq_w;
879 if (tpg->vid_aspect == TPG_VIDEO_ASPECT_16X9_ANAMORPHIC) {
880 unsigned ana_sq_w = (sq_w / 4) * 3;
881
882 if (((w - ana_sq_w) / 2) & 1)
883 ana_sq_w += 2;
884 tpg->square.width = ana_sq_w;
885 }
886 tpg->square.left = (w - tpg->square.width) / 2;
887 if (tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC)
888 sq_h = sq_w * 10 / 11;
889 else if (tpg->pix_aspect == TPG_PIXEL_ASPECT_PAL)
890 sq_h = sq_w * 59 / 54;
891 tpg->square.height = sq_h;
892 tpg->square.top = (h - sq_h) / 2;
893 tpg->border.left = 0;
894 tpg->border.width = w;
895 tpg->border.top = 0;
896 tpg->border.height = h;
897 switch (tpg->vid_aspect) {
898 case TPG_VIDEO_ASPECT_4X3:
899 if (tpg->pix_aspect)
900 return;
901 if (3 * w >= 4 * h) {
902 tpg->border.width = ((4 * h) / 3) & ~1;
903 if (((w - tpg->border.width) / 2) & ~1)
904 tpg->border.width -= 2;
905 tpg->border.left = (w - tpg->border.width) / 2;
906 break;
907 }
908 tpg->border.height = ((3 * w) / 4) & ~1;
909 tpg->border.top = (h - tpg->border.height) / 2;
910 break;
911 case TPG_VIDEO_ASPECT_14X9_CENTRE:
912 if (tpg->pix_aspect) {
913 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 420 : 506;
914 tpg->border.top = (h - tpg->border.height) / 2;
915 break;
916 }
917 if (9 * w >= 14 * h) {
918 tpg->border.width = ((14 * h) / 9) & ~1;
919 if (((w - tpg->border.width) / 2) & ~1)
920 tpg->border.width -= 2;
921 tpg->border.left = (w - tpg->border.width) / 2;
922 break;
923 }
924 tpg->border.height = ((9 * w) / 14) & ~1;
925 tpg->border.top = (h - tpg->border.height) / 2;
926 break;
927 case TPG_VIDEO_ASPECT_16X9_CENTRE:
928 if (tpg->pix_aspect) {
929 tpg->border.height = tpg->pix_aspect == TPG_PIXEL_ASPECT_NTSC ? 368 : 442;
930 tpg->border.top = (h - tpg->border.height) / 2;
931 break;
932 }
933 if (9 * w >= 16 * h) {
934 tpg->border.width = ((16 * h) / 9) & ~1;
935 if (((w - tpg->border.width) / 2) & ~1)
936 tpg->border.width -= 2;
937 tpg->border.left = (w - tpg->border.width) / 2;
938 break;
939 }
940 tpg->border.height = ((9 * w) / 16) & ~1;
941 tpg->border.top = (h - tpg->border.height) / 2;
942 break;
943 default:
944 break;
945 }
946}
947
948static void tpg_precalculate_line(struct tpg_data *tpg)
949{
950 enum tpg_color contrast;
951 unsigned pat;
952 unsigned p;
953 unsigned x;
954
955 switch (tpg->pattern) {
956 case TPG_PAT_GREEN:
957 contrast = TPG_COLOR_100_RED;
958 break;
959 case TPG_PAT_CSC_COLORBAR:
960 contrast = TPG_COLOR_CSC_GREEN;
961 break;
962 default:
963 contrast = TPG_COLOR_100_GREEN;
964 break;
965 }
966
967 for (pat = 0; pat < tpg_get_pat_lines(tpg); pat++) {
968 /* Coarse scaling with Bresenham */
969 unsigned int_part = tpg->src_width / tpg->scaled_width;
970 unsigned fract_part = tpg->src_width % tpg->scaled_width;
971 unsigned src_x = 0;
972 unsigned error = 0;
973
974 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
975 unsigned real_x = src_x;
976 enum tpg_color color1, color2;
977 u8 pix[TPG_MAX_PLANES][8];
978
979 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
980 color1 = tpg_get_color(tpg, pat, real_x);
981
982 src_x += int_part;
983 error += fract_part;
984 if (error >= tpg->scaled_width) {
985 error -= tpg->scaled_width;
986 src_x++;
987 }
988
989 real_x = src_x;
990 real_x = tpg->hflip ? tpg->src_width * 2 - real_x - 2 : real_x;
991 color2 = tpg_get_color(tpg, pat, real_x);
992
993 src_x += int_part;
994 error += fract_part;
995 if (error >= tpg->scaled_width) {
996 error -= tpg->scaled_width;
997 src_x++;
998 }
999
1000 gen_twopix(tpg, pix, tpg->hflip ? color2 : color1, 0);
1001 gen_twopix(tpg, pix, tpg->hflip ? color1 : color2, 1);
1002 for (p = 0; p < tpg->planes; p++) {
1003 unsigned twopixsize = tpg->twopixelsize[p];
1004 u8 *pos = tpg->lines[pat][p] + x * twopixsize / 2;
1005
1006 memcpy(pos, pix[p], twopixsize);
1007 }
1008 }
1009 }
1010 for (x = 0; x < tpg->scaled_width; x += 2) {
1011 u8 pix[TPG_MAX_PLANES][8];
1012
1013 gen_twopix(tpg, pix, contrast, 0);
1014 gen_twopix(tpg, pix, contrast, 1);
1015 for (p = 0; p < tpg->planes; p++) {
1016 unsigned twopixsize = tpg->twopixelsize[p];
1017 u8 *pos = tpg->contrast_line[p] + x * twopixsize / 2;
1018
1019 memcpy(pos, pix[p], twopixsize);
1020 }
1021 }
1022 for (x = 0; x < tpg->scaled_width; x += 2) {
1023 u8 pix[TPG_MAX_PLANES][8];
1024
1025 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 0);
1026 gen_twopix(tpg, pix, TPG_COLOR_100_BLACK, 1);
1027 for (p = 0; p < tpg->planes; p++) {
1028 unsigned twopixsize = tpg->twopixelsize[p];
1029 u8 *pos = tpg->black_line[p] + x * twopixsize / 2;
1030
1031 memcpy(pos, pix[p], twopixsize);
1032 }
1033 }
1034 for (x = 0; x < tpg->scaled_width * 2; x += 2) {
1035 u8 pix[TPG_MAX_PLANES][8];
1036
1037 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 0);
1038 gen_twopix(tpg, pix, TPG_COLOR_RANDOM, 1);
1039 for (p = 0; p < tpg->planes; p++) {
1040 unsigned twopixsize = tpg->twopixelsize[p];
1041 u8 *pos = tpg->random_line[p] + x * twopixsize / 2;
1042
1043 memcpy(pos, pix[p], twopixsize);
1044 }
1045 }
1046 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 0);
1047 gen_twopix(tpg, tpg->textbg, TPG_COLOR_TEXTBG, 1);
1048 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 0);
1049 gen_twopix(tpg, tpg->textfg, TPG_COLOR_TEXTFG, 1);
1050}
1051
1052/* need this to do rgb24 rendering */
1053typedef struct { u16 __; u8 _; } __packed x24;
1054
1055void tpg_gen_text(struct tpg_data *tpg, u8 *basep[TPG_MAX_PLANES][2],
1056 int y, int x, char *text)
1057{
1058 int line;
1059 unsigned step = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
1060 unsigned div = step;
1061 unsigned first = 0;
1062 unsigned len = strlen(text);
1063 unsigned p;
1064
1065 if (font8x16 == NULL || basep == NULL)
1066 return;
1067
1068 /* Checks if it is possible to show string */
1069 if (y + 16 >= tpg->compose.height || x + 8 >= tpg->compose.width)
1070 return;
1071
1072 if (len > (tpg->compose.width - x) / 8)
1073 len = (tpg->compose.width - x) / 8;
1074 if (tpg->vflip)
1075 y = tpg->compose.height - y - 16;
1076 if (tpg->hflip)
1077 x = tpg->compose.width - x - 8;
1078 y += tpg->compose.top;
1079 x += tpg->compose.left;
1080 if (tpg->field == V4L2_FIELD_BOTTOM)
1081 first = 1;
1082 else if (tpg->field == V4L2_FIELD_SEQ_TB || tpg->field == V4L2_FIELD_SEQ_BT)
1083 div = 2;
1084
1085 for (p = 0; p < tpg->planes; p++) {
1086 /* Print stream time */
1087#define PRINTSTR(PIXTYPE) do { \
1088 PIXTYPE fg; \
1089 PIXTYPE bg; \
1090 memcpy(&fg, tpg->textfg[p], sizeof(PIXTYPE)); \
1091 memcpy(&bg, tpg->textbg[p], sizeof(PIXTYPE)); \
1092 \
1093 for (line = first; line < 16; line += step) { \
1094 int l = tpg->vflip ? 15 - line : line; \
1095 PIXTYPE *pos = (PIXTYPE *)(basep[p][line & 1] + \
1096 ((y * step + l) / div) * tpg->bytesperline[p] + \
1097 x * sizeof(PIXTYPE)); \
1098 unsigned s; \
1099 \
1100 for (s = 0; s < len; s++) { \
1101 u8 chr = font8x16[text[s] * 16 + line]; \
1102 \
1103 if (tpg->hflip) { \
1104 pos[7] = (chr & (0x01 << 7) ? fg : bg); \
1105 pos[6] = (chr & (0x01 << 6) ? fg : bg); \
1106 pos[5] = (chr & (0x01 << 5) ? fg : bg); \
1107 pos[4] = (chr & (0x01 << 4) ? fg : bg); \
1108 pos[3] = (chr & (0x01 << 3) ? fg : bg); \
1109 pos[2] = (chr & (0x01 << 2) ? fg : bg); \
1110 pos[1] = (chr & (0x01 << 1) ? fg : bg); \
1111 pos[0] = (chr & (0x01 << 0) ? fg : bg); \
1112 } else { \
1113 pos[0] = (chr & (0x01 << 7) ? fg : bg); \
1114 pos[1] = (chr & (0x01 << 6) ? fg : bg); \
1115 pos[2] = (chr & (0x01 << 5) ? fg : bg); \
1116 pos[3] = (chr & (0x01 << 4) ? fg : bg); \
1117 pos[4] = (chr & (0x01 << 3) ? fg : bg); \
1118 pos[5] = (chr & (0x01 << 2) ? fg : bg); \
1119 pos[6] = (chr & (0x01 << 1) ? fg : bg); \
1120 pos[7] = (chr & (0x01 << 0) ? fg : bg); \
1121 } \
1122 \
1123 pos += tpg->hflip ? -8 : 8; \
1124 } \
1125 } \
1126} while (0)
1127
1128 switch (tpg->twopixelsize[p]) {
1129 case 2:
1130 PRINTSTR(u8); break;
1131 case 4:
1132 PRINTSTR(u16); break;
1133 case 6:
1134 PRINTSTR(x24); break;
1135 case 8:
1136 PRINTSTR(u32); break;
1137 }
1138 }
1139}
1140
1141void tpg_update_mv_step(struct tpg_data *tpg)
1142{
1143 int factor = tpg->mv_hor_mode > TPG_MOVE_NONE ? -1 : 1;
1144
1145 if (tpg->hflip)
1146 factor = -factor;
1147 switch (tpg->mv_hor_mode) {
1148 case TPG_MOVE_NEG_FAST:
1149 case TPG_MOVE_POS_FAST:
1150 tpg->mv_hor_step = ((tpg->src_width + 319) / 320) * 4;
1151 break;
1152 case TPG_MOVE_NEG:
1153 case TPG_MOVE_POS:
1154 tpg->mv_hor_step = ((tpg->src_width + 639) / 640) * 4;
1155 break;
1156 case TPG_MOVE_NEG_SLOW:
1157 case TPG_MOVE_POS_SLOW:
1158 tpg->mv_hor_step = 2;
1159 break;
1160 case TPG_MOVE_NONE:
1161 tpg->mv_hor_step = 0;
1162 break;
1163 }
1164 if (factor < 0)
1165 tpg->mv_hor_step = tpg->src_width - tpg->mv_hor_step;
1166
1167 factor = tpg->mv_vert_mode > TPG_MOVE_NONE ? -1 : 1;
1168 switch (tpg->mv_vert_mode) {
1169 case TPG_MOVE_NEG_FAST:
1170 case TPG_MOVE_POS_FAST:
1171 tpg->mv_vert_step = ((tpg->src_width + 319) / 320) * 4;
1172 break;
1173 case TPG_MOVE_NEG:
1174 case TPG_MOVE_POS:
1175 tpg->mv_vert_step = ((tpg->src_width + 639) / 640) * 4;
1176 break;
1177 case TPG_MOVE_NEG_SLOW:
1178 case TPG_MOVE_POS_SLOW:
1179 tpg->mv_vert_step = 1;
1180 break;
1181 case TPG_MOVE_NONE:
1182 tpg->mv_vert_step = 0;
1183 break;
1184 }
1185 if (factor < 0)
1186 tpg->mv_vert_step = tpg->src_height - tpg->mv_vert_step;
1187}
1188
1189/* Map the line number relative to the crop rectangle to a frame line number */
1190static unsigned tpg_calc_frameline(struct tpg_data *tpg, unsigned src_y,
1191 unsigned field)
1192{
1193 switch (field) {
1194 case V4L2_FIELD_TOP:
1195 return tpg->crop.top + src_y * 2;
1196 case V4L2_FIELD_BOTTOM:
1197 return tpg->crop.top + src_y * 2 + 1;
1198 default:
1199 return src_y + tpg->crop.top;
1200 }
1201}
1202
1203/*
1204 * Map the line number relative to the compose rectangle to a destination
1205 * buffer line number.
1206 */
1207static unsigned tpg_calc_buffer_line(struct tpg_data *tpg, unsigned y,
1208 unsigned field)
1209{
1210 y += tpg->compose.top;
1211 switch (field) {
1212 case V4L2_FIELD_SEQ_TB:
1213 if (y & 1)
1214 return tpg->buf_height / 2 + y / 2;
1215 return y / 2;
1216 case V4L2_FIELD_SEQ_BT:
1217 if (y & 1)
1218 return y / 2;
1219 return tpg->buf_height / 2 + y / 2;
1220 default:
1221 return y;
1222 }
1223}
1224
1225static void tpg_recalc(struct tpg_data *tpg)
1226{
1227 if (tpg->recalc_colors) {
1228 tpg->recalc_colors = false;
1229 tpg->recalc_lines = true;
481b97a1
HV
1230 tpg->real_ycbcr_enc = tpg->ycbcr_enc;
1231 tpg->real_quantization = tpg->quantization;
1232 if (tpg->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
1233 switch (tpg->colorspace) {
1234 case V4L2_COLORSPACE_REC709:
1235 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_709;
1236 break;
1237 case V4L2_COLORSPACE_SRGB:
1238 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SYCC;
1239 break;
1240 case V4L2_COLORSPACE_BT2020:
1241 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_BT2020;
1242 break;
1243 case V4L2_COLORSPACE_SMPTE240M:
1244 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_SMPTE240M;
1245 break;
1246 case V4L2_COLORSPACE_SMPTE170M:
1247 case V4L2_COLORSPACE_470_SYSTEM_M:
1248 case V4L2_COLORSPACE_470_SYSTEM_BG:
1249 case V4L2_COLORSPACE_ADOBERGB:
1250 default:
1251 tpg->real_ycbcr_enc = V4L2_YCBCR_ENC_601;
1252 break;
1253 }
1254 }
1255 if (tpg->quantization == V4L2_QUANTIZATION_DEFAULT) {
1256 tpg->real_quantization = V4L2_QUANTIZATION_FULL_RANGE;
1257 if (tpg->is_yuv) {
1258 switch (tpg->real_ycbcr_enc) {
1259 case V4L2_YCBCR_ENC_SYCC:
1260 case V4L2_YCBCR_ENC_XV601:
1261 case V4L2_YCBCR_ENC_XV709:
1262 break;
1263 default:
1264 tpg->real_quantization =
1265 V4L2_QUANTIZATION_LIM_RANGE;
1266 break;
1267 }
c0b50d95
HV
1268 } else if (tpg->colorspace == V4L2_COLORSPACE_BT2020) {
1269 /* R'G'B' BT.2020 is limited range */
1270 tpg->real_quantization =
1271 V4L2_QUANTIZATION_LIM_RANGE;
481b97a1
HV
1272 }
1273 }
63881df9
HV
1274 tpg_precalculate_colors(tpg);
1275 }
1276 if (tpg->recalc_square_border) {
1277 tpg->recalc_square_border = false;
1278 tpg_calculate_square_border(tpg);
1279 }
1280 if (tpg->recalc_lines) {
1281 tpg->recalc_lines = false;
1282 tpg_precalculate_line(tpg);
1283 }
1284}
1285
1286void tpg_calc_text_basep(struct tpg_data *tpg,
1287 u8 *basep[TPG_MAX_PLANES][2], unsigned p, u8 *vbuf)
1288{
1289 unsigned stride = tpg->bytesperline[p];
1290
1291 tpg_recalc(tpg);
1292
1293 basep[p][0] = vbuf;
1294 basep[p][1] = vbuf;
1295 if (tpg->field == V4L2_FIELD_SEQ_TB)
1296 basep[p][1] += tpg->buf_height * stride / 2;
1297 else if (tpg->field == V4L2_FIELD_SEQ_BT)
1298 basep[p][0] += tpg->buf_height * stride / 2;
1299}
1300
1301void tpg_fillbuffer(struct tpg_data *tpg, v4l2_std_id std, unsigned p, u8 *vbuf)
1302{
1303 bool is_tv = std;
1304 bool is_60hz = is_tv && (std & V4L2_STD_525_60);
1305 unsigned mv_hor_old = tpg->mv_hor_count % tpg->src_width;
1306 unsigned mv_hor_new = (tpg->mv_hor_count + tpg->mv_hor_step) % tpg->src_width;
1307 unsigned mv_vert_old = tpg->mv_vert_count % tpg->src_height;
1308 unsigned mv_vert_new = (tpg->mv_vert_count + tpg->mv_vert_step) % tpg->src_height;
1309 unsigned wss_width;
1310 unsigned f;
1311 int hmax = (tpg->compose.height * tpg->perc_fill) / 100;
1312 int h;
1313 unsigned twopixsize = tpg->twopixelsize[p];
1314 unsigned img_width = tpg->compose.width * twopixsize / 2;
1315 unsigned line_offset;
1316 unsigned left_pillar_width = 0;
1317 unsigned right_pillar_start = img_width;
1318 unsigned stride = tpg->bytesperline[p];
1319 unsigned factor = V4L2_FIELD_HAS_T_OR_B(tpg->field) ? 2 : 1;
1320 u8 *orig_vbuf = vbuf;
1321
1322 /* Coarse scaling with Bresenham */
1323 unsigned int_part = (tpg->crop.height / factor) / tpg->compose.height;
1324 unsigned fract_part = (tpg->crop.height / factor) % tpg->compose.height;
1325 unsigned src_y = 0;
1326 unsigned error = 0;
1327
1328 tpg_recalc(tpg);
1329
1330 mv_hor_old = (mv_hor_old * tpg->scaled_width / tpg->src_width) & ~1;
1331 mv_hor_new = (mv_hor_new * tpg->scaled_width / tpg->src_width) & ~1;
1332 wss_width = tpg->crop.left < tpg->src_width / 2 ?
1333 tpg->src_width / 2 - tpg->crop.left : 0;
1334 if (wss_width > tpg->crop.width)
1335 wss_width = tpg->crop.width;
1336 wss_width = wss_width * tpg->scaled_width / tpg->src_width;
1337
1338 vbuf += tpg->compose.left * twopixsize / 2;
1339 line_offset = tpg->crop.left * tpg->scaled_width / tpg->src_width;
1340 line_offset = (line_offset & ~1) * twopixsize / 2;
1341 if (tpg->crop.left < tpg->border.left) {
1342 left_pillar_width = tpg->border.left - tpg->crop.left;
1343 if (left_pillar_width > tpg->crop.width)
1344 left_pillar_width = tpg->crop.width;
1345 left_pillar_width = (left_pillar_width * tpg->scaled_width) / tpg->src_width;
1346 left_pillar_width = (left_pillar_width & ~1) * twopixsize / 2;
1347 }
1348 if (tpg->crop.left + tpg->crop.width > tpg->border.left + tpg->border.width) {
1349 right_pillar_start = tpg->border.left + tpg->border.width - tpg->crop.left;
1350 right_pillar_start = (right_pillar_start * tpg->scaled_width) / tpg->src_width;
1351 right_pillar_start = (right_pillar_start & ~1) * twopixsize / 2;
1352 if (right_pillar_start > img_width)
1353 right_pillar_start = img_width;
1354 }
1355
1356 f = tpg->field == (is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
1357
1358 for (h = 0; h < tpg->compose.height; h++) {
1359 bool even;
1360 bool fill_blank = false;
1361 unsigned frame_line;
1362 unsigned buf_line;
1363 unsigned pat_line_old;
1364 unsigned pat_line_new;
1365 u8 *linestart_older;
1366 u8 *linestart_newer;
1367 u8 *linestart_top;
1368 u8 *linestart_bottom;
1369
1370 frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1371 even = !(frame_line & 1);
1372 buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1373 src_y += int_part;
1374 error += fract_part;
1375 if (error >= tpg->compose.height) {
1376 error -= tpg->compose.height;
1377 src_y++;
1378 }
1379
1380 if (h >= hmax) {
1381 if (hmax == tpg->compose.height)
1382 continue;
1383 if (!tpg->perc_fill_blank)
1384 continue;
1385 fill_blank = true;
1386 }
1387
1388 if (tpg->vflip)
1389 frame_line = tpg->src_height - frame_line - 1;
1390
1391 if (fill_blank) {
1392 linestart_older = tpg->contrast_line[p];
1393 linestart_newer = tpg->contrast_line[p];
1394 } else if (tpg->qual != TPG_QUAL_NOISE &&
1395 (frame_line < tpg->border.top ||
1396 frame_line >= tpg->border.top + tpg->border.height)) {
1397 linestart_older = tpg->black_line[p];
1398 linestart_newer = tpg->black_line[p];
1399 } else if (tpg->pattern == TPG_PAT_NOISE || tpg->qual == TPG_QUAL_NOISE) {
1400 linestart_older = tpg->random_line[p] +
1401 twopixsize * prandom_u32_max(tpg->src_width / 2);
1402 linestart_newer = tpg->random_line[p] +
1403 twopixsize * prandom_u32_max(tpg->src_width / 2);
1404 } else {
1405 pat_line_old = tpg_get_pat_line(tpg,
1406 (frame_line + mv_vert_old) % tpg->src_height);
1407 pat_line_new = tpg_get_pat_line(tpg,
1408 (frame_line + mv_vert_new) % tpg->src_height);
1409 linestart_older = tpg->lines[pat_line_old][p] +
1410 mv_hor_old * twopixsize / 2;
1411 linestart_newer = tpg->lines[pat_line_new][p] +
1412 mv_hor_new * twopixsize / 2;
1413 linestart_older += line_offset;
1414 linestart_newer += line_offset;
1415 }
1416 if (is_60hz) {
1417 linestart_top = linestart_newer;
1418 linestart_bottom = linestart_older;
1419 } else {
1420 linestart_top = linestart_older;
1421 linestart_bottom = linestart_newer;
1422 }
1423
1424 switch (tpg->field) {
1425 case V4L2_FIELD_INTERLACED:
1426 case V4L2_FIELD_INTERLACED_TB:
1427 case V4L2_FIELD_SEQ_TB:
1428 case V4L2_FIELD_SEQ_BT:
1429 if (even)
1430 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1431 else
1432 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1433 break;
1434 case V4L2_FIELD_INTERLACED_BT:
1435 if (even)
1436 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1437 else
1438 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1439 break;
1440 case V4L2_FIELD_TOP:
1441 memcpy(vbuf + buf_line * stride, linestart_top, img_width);
1442 break;
1443 case V4L2_FIELD_BOTTOM:
1444 memcpy(vbuf + buf_line * stride, linestart_bottom, img_width);
1445 break;
1446 case V4L2_FIELD_NONE:
1447 default:
1448 memcpy(vbuf + buf_line * stride, linestart_older, img_width);
1449 break;
1450 }
1451
1452 if (is_tv && !is_60hz && frame_line == 0 && wss_width) {
1453 /*
1454 * Replace the first half of the top line of a 50 Hz frame
1455 * with random data to simulate a WSS signal.
1456 */
1457 u8 *wss = tpg->random_line[p] +
1458 twopixsize * prandom_u32_max(tpg->src_width / 2);
1459
1460 memcpy(vbuf + buf_line * stride, wss, wss_width * twopixsize / 2);
1461 }
1462 }
1463
1464 vbuf = orig_vbuf;
1465 vbuf += tpg->compose.left * twopixsize / 2;
1466 src_y = 0;
1467 error = 0;
1468 for (h = 0; h < tpg->compose.height; h++) {
1469 unsigned frame_line = tpg_calc_frameline(tpg, src_y, tpg->field);
1470 unsigned buf_line = tpg_calc_buffer_line(tpg, h, tpg->field);
1471 const struct v4l2_rect *sq = &tpg->square;
1472 const struct v4l2_rect *b = &tpg->border;
1473 const struct v4l2_rect *c = &tpg->crop;
1474
1475 src_y += int_part;
1476 error += fract_part;
1477 if (error >= tpg->compose.height) {
1478 error -= tpg->compose.height;
1479 src_y++;
1480 }
1481
1482 if (tpg->show_border && frame_line >= b->top &&
1483 frame_line < b->top + b->height) {
1484 unsigned bottom = b->top + b->height - 1;
1485 unsigned left = left_pillar_width;
1486 unsigned right = right_pillar_start;
1487
1488 if (frame_line == b->top || frame_line == b->top + 1 ||
1489 frame_line == bottom || frame_line == bottom - 1) {
1490 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p],
1491 right - left);
1492 } else {
1493 if (b->left >= c->left &&
1494 b->left < c->left + c->width)
1495 memcpy(vbuf + buf_line * stride + left,
1496 tpg->contrast_line[p], twopixsize);
1497 if (b->left + b->width > c->left &&
1498 b->left + b->width <= c->left + c->width)
1499 memcpy(vbuf + buf_line * stride + right - twopixsize,
1500 tpg->contrast_line[p], twopixsize);
1501 }
1502 }
1503 if (tpg->qual != TPG_QUAL_NOISE && frame_line >= b->top &&
1504 frame_line < b->top + b->height) {
1505 memcpy(vbuf + buf_line * stride, tpg->black_line[p], left_pillar_width);
1506 memcpy(vbuf + buf_line * stride + right_pillar_start, tpg->black_line[p],
1507 img_width - right_pillar_start);
1508 }
1509 if (tpg->show_square && frame_line >= sq->top &&
1510 frame_line < sq->top + sq->height &&
1511 sq->left < c->left + c->width &&
1512 sq->left + sq->width >= c->left) {
1513 unsigned left = sq->left;
1514 unsigned width = sq->width;
1515
1516 if (c->left > left) {
1517 width -= c->left - left;
1518 left = c->left;
1519 }
1520 if (c->left + c->width < left + width)
1521 width -= left + width - c->left - c->width;
1522 left -= c->left;
1523 left = (left * tpg->scaled_width) / tpg->src_width;
1524 left = (left & ~1) * twopixsize / 2;
1525 width = (width * tpg->scaled_width) / tpg->src_width;
1526 width = (width & ~1) * twopixsize / 2;
1527 memcpy(vbuf + buf_line * stride + left, tpg->contrast_line[p], width);
1528 }
1529 if (tpg->insert_sav) {
1530 unsigned offset = (tpg->compose.width / 6) * twopixsize;
1531 u8 *p = vbuf + buf_line * stride + offset;
1532 unsigned vact = 0, hact = 0;
1533
1534 p[0] = 0xff;
1535 p[1] = 0;
1536 p[2] = 0;
1537 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1538 ((hact ^ vact) << 3) |
1539 ((hact ^ f) << 2) |
1540 ((f ^ vact) << 1) |
1541 (hact ^ vact ^ f);
1542 }
1543 if (tpg->insert_eav) {
1544 unsigned offset = (tpg->compose.width / 6) * 2 * twopixsize;
1545 u8 *p = vbuf + buf_line * stride + offset;
1546 unsigned vact = 0, hact = 1;
1547
1548 p[0] = 0xff;
1549 p[1] = 0;
1550 p[2] = 0;
1551 p[3] = 0x80 | (f << 6) | (vact << 5) | (hact << 4) |
1552 ((hact ^ vact) << 3) |
1553 ((hact ^ f) << 2) |
1554 ((f ^ vact) << 1) |
1555 (hact ^ vact ^ f);
1556 }
1557 }
1558}
This page took 0.11765 seconds and 5 git commands to generate.