xine-lib: xine-lib-1.2.13/src/video_out/color_matrix.c Source File

xine-lib 1.2.13-20230125hg15249

color_matrix.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012-2022 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 */
21
22
23/*
24 TJ. the output color matrix selection feature.
25 Example use:
26*/
27#if 0
28 #define CM_LUT /* recommended optimization */
29
30 typedef struct {
31 ...
32 int cm_state;
33 #ifdef CM_LUT
34 uint8_t cm_lut[32];
35 #endif
36 ...
37 } xxxx_driver_t;
38
39 #define CM_HAVE_YCGCO_SUPPORT /* if you already handle that */
40 #define CM_DRIVER_T xxxx_driver_t
41 #include "color_matrix.c"
42#endif
43/*
44 cm_from_frame () returns current (color_matrix << 1) | color_range control value.
45 Having only 1 var simplifies change event handling and avoids unecessary vo
46 reconfiguration. In the libyuv2rgb case, they are even handled by same code.
47
48 In theory, HD video uses a different YUV->RGB matrix than the rest.
49 It shall come closer to the human eye's brightness feel, and give
50 more shades of green even without higher bit depth.
51
52 I discussed this topic with local TV engineers earlier.
53 They say their studio equipment throws around uncompressed YUV with no
54 extra info attached to it. Anything smaller than 720p is assumed to be
55 ITU-R 601, otherwise ITU-R 709. A rematrix filter applies whenever
56 video is scaled across the above mentioned HD threshold.
57
58 However, the weak point of their argumentation is potentially non-standard
59 input material. Those machines obviously dont verify input data, and
60 ocasionally they dont even respect stream info (tested by comparing TV
61 against retail DVD version of same movie).
62
63 Consumer TV sets handle this fairly inconsistent - stream info, video size,
64 hard-wired matrix, user choice or so-called intelligent picture enhancers
65 that effectively go way off standards.
66 So I decided to provide functionality, and let the user decide if and how
67 to actually use it.
68*/
69
70/* eveybody gets these */
71
72/* user configuration settings */
73#define CM_CONFIG_NAME "video.output.color_matrix"
74#define CM_CONFIG_SIGNAL 0
75#define CM_CONFIG_SIZE 1
76#define CM_CONFIG_SD 2
77#define CM_CONFIG_HD 3
78
79#define CR_CONFIG_NAME "video.output.color_range"
80#define CR_CONFIG_AUTO 0
81#define CR_CONFIG_MPEG 1
82#define CR_CONFIG_FULL 2
83
84 static const char * const cm_names[] = {
85 "RGB",
86 "RGB",
87 "ITU-R 709 / HDTV",
88 "full range ITU-R 709 / HDTV",
89 "undefined",
90 "full range, undefined",
91 "ITU-R 470 BG / SDTV",
92 "full range ITU-R 470 BG / SDTV",
93 "FCC",
94 "full range FCC",
95 "ITU-R 470 BG / SDTV",
96 "full range ITU-R 470 BG / SDTV",
97 "SMPTE 170M",
98 "full range SMPTE 170M",
99 "SMPTE 240M",
100 "full range SMPTE 240M",
101#if defined(CM_HAVE_YCGCO_SUPPORT) || defined(CM_HAVE_BT2020_SUPPORT)
102 "YCgCo",
103 "YCgCo", /* this is always fullrange */
104 "BT.2020 NCL",
105 "fullrange BT.2020 NCL",
106 "BT.2020 CL",
107 "fullrange BT.2020 CL",
108 "#11",
109 "fullrange #11",
110 "#12",
111 "fullrange #12",
112 "#13",
113 "fullrange #13",
114 "#14",
115 "fullrange #14",
116 "#15",
117 "fullrange #15",
118#endif
119};
120
121#ifdef CM_DRIVER_T
122
123/* this is for vo plugins only */
124
125/* the option names */
126static const char * const cm_conf_labels[] = {
127 "Signal", "Signal+Size", "SD", "HD", NULL
128};
129
130static const char * const cr_conf_labels[] = {
131 "Auto", "MPEG", "FULL", NULL
132};
133
134#ifdef CM_HAVE_YCGCO_SUPPORT
135# define CM_G 16
136#else
137# define CM_G 10
138#endif
139
140#ifdef CM_HAVE_BT2020_SUPPORT
141# define CM_2020 18,20
142#else
143# define CM_2020 10,10
144#endif
145
146static
147#ifdef CM_LUT
148const
149#endif
150uint8_t cm_m[] = {
151 10, 2,10, 6, 8,10,12,14,CM_G,CM_2020,10,10,10,10,10, /* SIGNAL */
152 10, 2, 0, 6, 8,10,12,14,CM_G,CM_2020,10,10,10,10,10, /* SIZE */
153 10,10,10,10,10,10,10,10,CM_G, 10, 10,10,10,10,10,10, /* SD */
154 10, 2, 2, 2, 2, 2, 2, 2,CM_G, 2, 2, 2, 2, 2, 2, 2 /* HD */
155};
156
157static void cm_lut_setup (CM_DRIVER_T *this) {
158#ifdef CM_LUT
159 {
160 const uint8_t *a = cm_m + ((this->cm_state >> 2) << 4);
161 uint8_t *d = this->cm_lut, *e = d + 32;
162 while (d < e) {
163 d[0] = d[1] = *a++;
164 d += 2;
165 }
166 }
167 if ((this->cm_state & 3) == CR_CONFIG_AUTO) {
168 /* keep range */
169 int i;
170 for (i = 1; i < 32; i += 2)
171 this->cm_lut[i] |= 1;
172 } else if ((this->cm_state & 3) == CR_CONFIG_FULL) {
173 /* force full range */
174 int i;
175 for (i = 0; i < 32; i += 1)
176 this->cm_lut[i] |= 1;
177 }
178#endif
179}
180
181/* callback when user changes them */
182static void cm_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
183 CM_DRIVER_T *this = (CM_DRIVER_T *)this_gen;
184 this->cm_state = (this->cm_state & 3) | (entry->num_value << 2);
185 cm_lut_setup (this);
186}
187
188static void cr_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
189 CM_DRIVER_T *this = (CM_DRIVER_T *)this_gen;
190 this->cm_state = (this->cm_state & 0x1c) | entry->num_value;
191 cm_lut_setup (this);
192}
193
194static void cm_init (CM_DRIVER_T *this) {
195 /* register configuration */
196 this->cm_state = this->xine->config->register_enum (
197 this->xine->config,
198 CM_CONFIG_NAME,
199 CM_CONFIG_SIZE,
200 (char **)cm_conf_labels,
201 _("Output colour matrix"),
202 _("Tell how output colours should be calculated.\n\n"
203 "Signal: Do as current stream suggests.\n"
204 " This may be wrong sometimes.\n\n"
205 "Signal+Size: Same as above,\n"
206 " but assume HD colour for unmarked HD streams.\n\n"
207 "SD: Force SD video standard ITU-R 470/601.\n"
208 " Try this if you get too little green.\n\n"
209 "HD: Force HD video standard ITU-R 709.\n"
210 " Try when there is too much green coming out.\n\n"),
211 10,
212 cm_cb_config,
213 this
214 ) << 2;
215 this->cm_state |= this->xine->config->register_enum (
216 this->xine->config,
217 CR_CONFIG_NAME,
218 CR_CONFIG_AUTO,
219 (char **)cr_conf_labels,
220 _("Output colour range"),
221 _("Tell how output colours should be ranged.\n\n"
222 "Auto: Do as current stream suggests.\n"
223 " This may be wrong sometimes.\n\n"
224 "MPEG: Force MPEG colour range (16..235) / studio swing / video mode.\n"
225 " Try if image looks dull (no real black or white in it).\n\n"
226 "FULL: Force FULL colour range (0..255) / full swing / PC mode.\n"
227 " Try when flat black and white spots appear.\n\n"),
228 10,
229 cr_cb_config,
230 this
231 );
232 cm_lut_setup (this);
233}
234
235static int cm_from_frame (vo_frame_t *frame) {
236 CM_DRIVER_T *this = (CM_DRIVER_T *)frame->driver;
237 int cm = VO_GET_FLAGS_CM (frame->flags);
238#ifdef CM_LUT
239 cm = this->cm_lut[cm & 31];
240 if (cm & ~1)
241 return cm;
242 return cm | ((frame->height - frame->crop_top - frame->crop_bottom >= 720) ||
243 (frame->width - frame->crop_left - frame->crop_right >= 1280) ? 2 : 10);
244#else
245 static uint8_t cm_r[] = {0, 0, 1, 0}; /* AUTO, MPEG, FULL, safety */
246 int cf = this->cm_state;
247 cm_m[18] = (frame->height - frame->crop_top - frame->crop_bottom >= 720) ||
248 (frame->width - frame->crop_left - frame->crop_right >= 1280) ? 2 : 10;
249 cm_r[0] = cm & 1;
250 return cm_m[((cf >> 2) << 4) | (cm >> 1)] | cm_r[cf & 3];
251#endif
252}
253
254static inline
255void cm_fill_matrix(float *matrix, int color_matrix,
256 float hue, float saturation, float contrast, float brightness) {
257 float uvcos = saturation * cos( hue );
258 float uvsin = saturation * sin( hue );
259 int i;
260
261 if ((color_matrix >> 1) == 8) {
262 /* YCgCo. This is really quite simple. */
263 uvsin *= contrast;
264 uvcos *= contrast;
265 /* matrix[rgb][yuv1] */
266 matrix[1] = -1.0 * uvcos - 1.0 * uvsin;
267 matrix[2] = 1.0 * uvcos - 1.0 * uvsin;
268 matrix[5] = 1.0 * uvcos;
269 matrix[6] = 1.0 * uvsin;
270 matrix[9] = -1.0 * uvcos + 1.0 * uvsin;
271 matrix[10] = -1.0 * uvcos - 1.0 * uvsin;
272 for (i = 0; i < 12; i += 4) {
273 matrix[i] = contrast;
274 matrix[i + 3] = (brightness * contrast - 128.0 * (matrix[i + 1] + matrix[i + 2])) / 255.0;
275 }
276 } else {
277 /* YCbCr */
278 float kb, kr;
279 float vr, vg, ug, ub;
280 float ygain, yoffset;
281
282 switch (color_matrix >> 1) {
283 case 1: kb = 0.0722; kr = 0.2126; break; /* ITU-R 709 */
284 case 4: kb = 0.1100; kr = 0.3000; break; /* FCC */
285 case 7: kb = 0.0870; kr = 0.2120; break; /* SMPTE 240 */
286 case 10:
287 case 9: kb = 0.0593; kr = 0.2627; break; /* BT.2020 */
288 default: kb = 0.1140; kr = 0.2990; /* ITU-R 601 */
289 }
290 vr = 2.0 * (1.0 - kr);
291 vg = -2.0 * kr * (1.0 - kr) / (1.0 - kb - kr);
292 ug = -2.0 * kb * (1.0 - kb) / (1.0 - kb - kr);
293 ub = 2.0 * (1.0 - kb);
294
295 if (color_matrix & 1) {
296 /* fullrange mode */
297 yoffset = brightness;
298 ygain = contrast;
299 uvcos *= contrast * 255.0 / 254.0;
300 uvsin *= contrast * 255.0 / 254.0;
301 } else {
302 /* mpeg range */
303 yoffset = brightness - 16.0;
304 ygain = contrast * 255.0 / 219.0;
305 uvcos *= contrast * 255.0 / 224.0;
306 uvsin *= contrast * 255.0 / 224.0;
307 }
308
309 /* matrix[rgb][yuv1] */
310 matrix[1] = -uvsin * vr;
311 matrix[2] = uvcos * vr;
312 matrix[5] = uvcos * ug - uvsin * vg;
313 matrix[6] = uvcos * vg + uvsin * ug;
314 matrix[9] = uvcos * ub;
315 matrix[10] = uvsin * ub;
316 for (i = 0; i < 12; i += 4) {
317 matrix[i] = ygain;
318 matrix[i + 3] = (yoffset * ygain - 128.0 * (matrix[i + 1] + matrix[i + 2])) / 255.0;
319 }
320 }
321}
322
323static void cm_close (CM_DRIVER_T *this) {
324 /* dont know whether this is really necessary */
325 this->xine->config->unregister_callbacks (this->xine->config, NULL, NULL, this, sizeof (*this));
326}
327
328#endif /* defined CM_DRIVER_T */
color_matrix.c
CR_CONFIG_AUTO
#define CR_CONFIG_AUTO
Definition color_matrix.c:80
CR_CONFIG_NAME
#define CR_CONFIG_NAME
Definition color_matrix.c:79
cm_names
static const char *const cm_names[]
Definition color_matrix.c:84
CM_CONFIG_SIZE
#define CM_CONFIG_SIZE
Definition color_matrix.c:75
CM_CONFIG_NAME
#define CM_CONFIG_NAME
Definition color_matrix.c:73
CR_CONFIG_FULL
#define CR_CONFIG_FULL
Definition color_matrix.c:82
contrast
contrast
Definition eq.c:160
VO_GET_FLAGS_CM
#define VO_GET_FLAGS_CM(flags)
Definition video_out.h:317
vo_frame_s
Definition video_out.h:70
vo_frame_s::driver
vo_driver_t * driver
Definition video_out.h:162
xine_cfg_entry_s
Definition xine.h:1649
xine_cfg_entry_s::num_value
int num_value
Definition xine.h:1670
_
#define _(String)
Definition vcdplayer.h:39
CM_DRIVER_T
#define CM_DRIVER_T
Definition video_out_opengl.c:250
NULL
NULL
Definition xine_plugin.c:78