• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * vivid-color.c - A table that converts colors to various colorspaces
3  *
4  * The test pattern generator uses the tpg_colors for its test patterns.
5  * For testing colorspaces the first 8 colors of that table need to be
6  * converted to their equivalent in the target colorspace.
7  *
8  * The tpg_csc_colors[] table is the result of that conversion and since
9  * it is precalculated the colorspace conversion is just a simple table
10  * lookup.
11  *
12  * This source also contains the code used to generate the tpg_csc_colors
13  * table. Run the following command to compile it:
14  *
15  *	gcc vivid-colors.c -DCOMPILE_APP -o gen-colors -lm
16  *
17  * and run the utility.
18  *
19  * Note that the converted colors are in the range 0x000-0xff0 (so times 16)
20  * in order to preserve precision.
21  *
22  * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
23  *
24  * This program is free software; you may redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; version 2 of the License.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35  * SOFTWARE.
36  */
37 
38 #include <linux/videodev2.h>
39 
40 #include "vivid-tpg-colors.h"
41 
42 /* sRGB colors with range [0-255] */
43 const struct color tpg_colors[TPG_COLOR_MAX] = {
44 	/*
45 	 * Colors to test colorspace conversion: converting these colors
46 	 * to other colorspaces will never lead to out-of-gamut colors.
47 	 */
48 	{ 191, 191, 191 }, /* TPG_COLOR_CSC_WHITE */
49 	{ 191, 191,  50 }, /* TPG_COLOR_CSC_YELLOW */
50 	{  50, 191, 191 }, /* TPG_COLOR_CSC_CYAN */
51 	{  50, 191,  50 }, /* TPG_COLOR_CSC_GREEN */
52 	{ 191,  50, 191 }, /* TPG_COLOR_CSC_MAGENTA */
53 	{ 191,  50,  50 }, /* TPG_COLOR_CSC_RED */
54 	{  50,  50, 191 }, /* TPG_COLOR_CSC_BLUE */
55 	{  50,  50,  50 }, /* TPG_COLOR_CSC_BLACK */
56 
57 	/* 75% colors */
58 	{ 191, 191,   0 }, /* TPG_COLOR_75_YELLOW */
59 	{   0, 191, 191 }, /* TPG_COLOR_75_CYAN */
60 	{   0, 191,   0 }, /* TPG_COLOR_75_GREEN */
61 	{ 191,   0, 191 }, /* TPG_COLOR_75_MAGENTA */
62 	{ 191,   0,   0 }, /* TPG_COLOR_75_RED */
63 	{   0,   0, 191 }, /* TPG_COLOR_75_BLUE */
64 
65 	/* 100% colors */
66 	{ 255, 255, 255 }, /* TPG_COLOR_100_WHITE */
67 	{ 255, 255,   0 }, /* TPG_COLOR_100_YELLOW */
68 	{   0, 255, 255 }, /* TPG_COLOR_100_CYAN */
69 	{   0, 255,   0 }, /* TPG_COLOR_100_GREEN */
70 	{ 255,   0, 255 }, /* TPG_COLOR_100_MAGENTA */
71 	{ 255,   0,   0 }, /* TPG_COLOR_100_RED */
72 	{   0,   0, 255 }, /* TPG_COLOR_100_BLUE */
73 	{   0,   0,   0 }, /* TPG_COLOR_100_BLACK */
74 
75 	{   0,   0,   0 }, /* TPG_COLOR_RANDOM placeholder */
76 };
77 
78 #ifndef COMPILE_APP
79 
80 /* Generated table */
81 const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {
82 	[V4L2_COLORSPACE_SMPTE170M][0] = { 2953, 2939, 2939 },
83 	[V4L2_COLORSPACE_SMPTE170M][1] = { 2954, 2963, 585 },
84 	[V4L2_COLORSPACE_SMPTE170M][2] = { 84, 2967, 2937 },
85 	[V4L2_COLORSPACE_SMPTE170M][3] = { 93, 2990, 575 },
86 	[V4L2_COLORSPACE_SMPTE170M][4] = { 3030, 259, 2933 },
87 	[V4L2_COLORSPACE_SMPTE170M][5] = { 3031, 406, 557 },
88 	[V4L2_COLORSPACE_SMPTE170M][6] = { 544, 428, 2931 },
89 	[V4L2_COLORSPACE_SMPTE170M][7] = { 551, 547, 547 },
90 	[V4L2_COLORSPACE_SMPTE240M][0] = { 2926, 2926, 2926 },
91 	[V4L2_COLORSPACE_SMPTE240M][1] = { 2926, 2926, 857 },
92 	[V4L2_COLORSPACE_SMPTE240M][2] = { 1594, 2901, 2901 },
93 	[V4L2_COLORSPACE_SMPTE240M][3] = { 1594, 2901, 774 },
94 	[V4L2_COLORSPACE_SMPTE240M][4] = { 2484, 618, 2858 },
95 	[V4L2_COLORSPACE_SMPTE240M][5] = { 2484, 618, 617 },
96 	[V4L2_COLORSPACE_SMPTE240M][6] = { 507, 507, 2832 },
97 	[V4L2_COLORSPACE_SMPTE240M][7] = { 507, 507, 507 },
98 	[V4L2_COLORSPACE_REC709][0] = { 2939, 2939, 2939 },
99 	[V4L2_COLORSPACE_REC709][1] = { 2939, 2939, 547 },
100 	[V4L2_COLORSPACE_REC709][2] = { 547, 2939, 2939 },
101 	[V4L2_COLORSPACE_REC709][3] = { 547, 2939, 547 },
102 	[V4L2_COLORSPACE_REC709][4] = { 2939, 547, 2939 },
103 	[V4L2_COLORSPACE_REC709][5] = { 2939, 547, 547 },
104 	[V4L2_COLORSPACE_REC709][6] = { 547, 547, 2939 },
105 	[V4L2_COLORSPACE_REC709][7] = { 547, 547, 547 },
106 	[V4L2_COLORSPACE_470_SYSTEM_M][0] = { 2894, 2988, 2808 },
107 	[V4L2_COLORSPACE_470_SYSTEM_M][1] = { 2847, 3070, 843 },
108 	[V4L2_COLORSPACE_470_SYSTEM_M][2] = { 1656, 2962, 2783 },
109 	[V4L2_COLORSPACE_470_SYSTEM_M][3] = { 1572, 3045, 763 },
110 	[V4L2_COLORSPACE_470_SYSTEM_M][4] = { 2477, 229, 2743 },
111 	[V4L2_COLORSPACE_470_SYSTEM_M][5] = { 2422, 672, 614 },
112 	[V4L2_COLORSPACE_470_SYSTEM_M][6] = { 725, 63, 2718 },
113 	[V4L2_COLORSPACE_470_SYSTEM_M][7] = { 534, 561, 509 },
114 	[V4L2_COLORSPACE_470_SYSTEM_BG][0] = { 2939, 2939, 2939 },
115 	[V4L2_COLORSPACE_470_SYSTEM_BG][1] = { 2939, 2939, 621 },
116 	[V4L2_COLORSPACE_470_SYSTEM_BG][2] = { 786, 2939, 2939 },
117 	[V4L2_COLORSPACE_470_SYSTEM_BG][3] = { 786, 2939, 621 },
118 	[V4L2_COLORSPACE_470_SYSTEM_BG][4] = { 2879, 547, 2923 },
119 	[V4L2_COLORSPACE_470_SYSTEM_BG][5] = { 2879, 547, 547 },
120 	[V4L2_COLORSPACE_470_SYSTEM_BG][6] = { 547, 547, 2923 },
121 	[V4L2_COLORSPACE_470_SYSTEM_BG][7] = { 547, 547, 547 },
122 	[V4L2_COLORSPACE_SRGB][0] = { 3056, 3056, 3056 },
123 	[V4L2_COLORSPACE_SRGB][1] = { 3056, 3056, 800 },
124 	[V4L2_COLORSPACE_SRGB][2] = { 800, 3056, 3056 },
125 	[V4L2_COLORSPACE_SRGB][3] = { 800, 3056, 800 },
126 	[V4L2_COLORSPACE_SRGB][4] = { 3056, 800, 3056 },
127 	[V4L2_COLORSPACE_SRGB][5] = { 3056, 800, 800 },
128 	[V4L2_COLORSPACE_SRGB][6] = { 800, 800, 3056 },
129 	[V4L2_COLORSPACE_SRGB][7] = { 800, 800, 800 },
130 };
131 
132 #else
133 
134 /* This code generates the table above */
135 
136 #include <math.h>
137 #include <stdio.h>
138 #include <stdlib.h>
139 
140 static const double rec709_to_ntsc1953[3][3] = {
141 	{ 0.6698, 0.2678,  0.0323 },
142 	{ 0.0185, 1.0742, -0.0603 },
143 	{ 0.0162, 0.0432,  0.8551 }
144 };
145 
146 static const double rec709_to_ebu[3][3] = {
147 	{ 0.9578, 0.0422, 0      },
148 	{ 0     , 1     , 0      },
149 	{ 0     , 0.0118, 0.9882 }
150 };
151 
152 static const double rec709_to_170m[3][3] = {
153 	{  1.0654, -0.0554, -0.0010 },
154 	{ -0.0196,  1.0364, -0.0167 },
155 	{  0.0016,  0.0044,  0.9940 }
156 };
157 
158 static const double rec709_to_240m[3][3] = {
159 	{ 0.7151, 0.2849, 0      },
160 	{ 0.0179, 0.9821, 0      },
161 	{ 0.0177, 0.0472, 0.9350 }
162 };
163 
164 
mult_matrix(double * r,double * g,double * b,const double m[3][3])165 static void mult_matrix(double *r, double *g, double *b, const double m[3][3])
166 {
167 	double ir, ig, ib;
168 
169 	ir = m[0][0] * (*r) + m[0][1] * (*g) + m[0][2] * (*b);
170 	ig = m[1][0] * (*r) + m[1][1] * (*g) + m[1][2] * (*b);
171 	ib = m[2][0] * (*r) + m[2][1] * (*g) + m[2][2] * (*b);
172 	*r = ir;
173 	*g = ig;
174 	*b = ib;
175 }
176 
transfer_srgb_to_rgb(double v)177 static double transfer_srgb_to_rgb(double v)
178 {
179 	return (v <= 0.03928) ? v / 12.92 : pow((v + 0.055) / 1.055, 2.4);
180 }
181 
transfer_rgb_to_smpte240m(double v)182 static double transfer_rgb_to_smpte240m(double v)
183 {
184 	return (v <= 0.0228) ? v * 4.0 : 1.1115 * pow(v, 0.45) - 0.1115;
185 }
186 
transfer_rgb_to_rec709(double v)187 static double transfer_rgb_to_rec709(double v)
188 {
189 	return (v < 0.018) ? v * 4.5 : 1.099 * pow(v, 0.45) - 0.099;
190 }
191 
transfer_srgb_to_rec709(double v)192 static double transfer_srgb_to_rec709(double v)
193 {
194 	return transfer_rgb_to_rec709(transfer_srgb_to_rgb(v));
195 }
196 
csc(enum v4l2_colorspace colorspace,double * r,double * g,double * b)197 static void csc(enum v4l2_colorspace colorspace, double *r, double *g, double *b)
198 {
199 	/* Convert the primaries of Rec. 709 Linear RGB */
200 	switch (colorspace) {
201 	case V4L2_COLORSPACE_SMPTE240M:
202 		*r = transfer_srgb_to_rgb(*r);
203 		*g = transfer_srgb_to_rgb(*g);
204 		*b = transfer_srgb_to_rgb(*b);
205 		mult_matrix(r, g, b, rec709_to_240m);
206 		break;
207 	case V4L2_COLORSPACE_SMPTE170M:
208 		*r = transfer_srgb_to_rgb(*r);
209 		*g = transfer_srgb_to_rgb(*g);
210 		*b = transfer_srgb_to_rgb(*b);
211 		mult_matrix(r, g, b, rec709_to_170m);
212 		break;
213 	case V4L2_COLORSPACE_470_SYSTEM_BG:
214 		*r = transfer_srgb_to_rgb(*r);
215 		*g = transfer_srgb_to_rgb(*g);
216 		*b = transfer_srgb_to_rgb(*b);
217 		mult_matrix(r, g, b, rec709_to_ebu);
218 		break;
219 	case V4L2_COLORSPACE_470_SYSTEM_M:
220 		*r = transfer_srgb_to_rgb(*r);
221 		*g = transfer_srgb_to_rgb(*g);
222 		*b = transfer_srgb_to_rgb(*b);
223 		mult_matrix(r, g, b, rec709_to_ntsc1953);
224 		break;
225 	case V4L2_COLORSPACE_SRGB:
226 	case V4L2_COLORSPACE_REC709:
227 	default:
228 		break;
229 	}
230 
231 	*r = ((*r) < 0) ? 0 : (((*r) > 1) ? 1 : (*r));
232 	*g = ((*g) < 0) ? 0 : (((*g) > 1) ? 1 : (*g));
233 	*b = ((*b) < 0) ? 0 : (((*b) > 1) ? 1 : (*b));
234 
235 	/* Encode to gamma corrected colorspace */
236 	switch (colorspace) {
237 	case V4L2_COLORSPACE_SMPTE240M:
238 		*r = transfer_rgb_to_smpte240m(*r);
239 		*g = transfer_rgb_to_smpte240m(*g);
240 		*b = transfer_rgb_to_smpte240m(*b);
241 		break;
242 	case V4L2_COLORSPACE_SMPTE170M:
243 	case V4L2_COLORSPACE_470_SYSTEM_M:
244 	case V4L2_COLORSPACE_470_SYSTEM_BG:
245 		*r = transfer_rgb_to_rec709(*r);
246 		*g = transfer_rgb_to_rec709(*g);
247 		*b = transfer_rgb_to_rec709(*b);
248 		break;
249 	case V4L2_COLORSPACE_SRGB:
250 		break;
251 	case V4L2_COLORSPACE_REC709:
252 	default:
253 		*r = transfer_srgb_to_rec709(*r);
254 		*g = transfer_srgb_to_rec709(*g);
255 		*b = transfer_srgb_to_rec709(*b);
256 		break;
257 	}
258 }
259 
main(int argc,char ** argv)260 int main(int argc, char **argv)
261 {
262 	static const unsigned colorspaces[] = {
263 		0,
264 		V4L2_COLORSPACE_SMPTE170M,
265 		V4L2_COLORSPACE_SMPTE240M,
266 		V4L2_COLORSPACE_REC709,
267 		0,
268 		V4L2_COLORSPACE_470_SYSTEM_M,
269 		V4L2_COLORSPACE_470_SYSTEM_BG,
270 		0,
271 		V4L2_COLORSPACE_SRGB,
272 	};
273 	static const char * const colorspace_names[] = {
274 		"",
275 		"V4L2_COLORSPACE_SMPTE170M",
276 		"V4L2_COLORSPACE_SMPTE240M",
277 		"V4L2_COLORSPACE_REC709",
278 		"",
279 		"V4L2_COLORSPACE_470_SYSTEM_M",
280 		"V4L2_COLORSPACE_470_SYSTEM_BG",
281 		"",
282 		"V4L2_COLORSPACE_SRGB",
283 	};
284 	int i;
285 	int c;
286 
287 	printf("/* Generated table */\n");
288 	printf("const struct color16 tpg_csc_colors[V4L2_COLORSPACE_SRGB + 1][TPG_COLOR_CSC_BLACK + 1] = {\n");
289 	for (c = 0; c <= V4L2_COLORSPACE_SRGB; c++) {
290 		for (i = 0; i <= TPG_COLOR_CSC_BLACK; i++) {
291 			double r, g, b;
292 
293 			if (colorspaces[c] == 0)
294 				continue;
295 
296 			r = tpg_colors[i].r / 255.0;
297 			g = tpg_colors[i].g / 255.0;
298 			b = tpg_colors[i].b / 255.0;
299 
300 			csc(c, &r, &g, &b);
301 
302 			printf("\t[%s][%d] = { %d, %d, %d },\n", colorspace_names[c], i,
303 				(int)(r * 4080), (int)(g * 4080), (int)(b * 4080));
304 		}
305 	}
306 	printf("};\n\n");
307 	return 0;
308 }
309 
310 #endif
311