4e418d6a7cf66ed2b6548756a591458e1482adac
[platform/upstream/gstreamer.git] / gst-libs / gst / video / video-color.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Library       <2002> Ronald Bultje <rbultje@ronald.bitfreak.net>
4  * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <string.h>
27 #include <stdio.h>
28 #include <math.h>
29
30 #include "video-color.h"
31
32 typedef struct
33 {
34   const gchar *name;
35   GstVideoColorimetry color;
36 } ColorimetryInfo;
37
38 #define MAKE_COLORIMETRY(n,r,m,t,p) { GST_VIDEO_COLORIMETRY_ ##n, \
39   { GST_VIDEO_COLOR_RANGE ##r, GST_VIDEO_COLOR_MATRIX_ ##m, \
40   GST_VIDEO_TRANSFER_ ##t, GST_VIDEO_COLOR_PRIMARIES_ ##p } }
41
42 #define GST_VIDEO_COLORIMETRY_NONAME  NULL
43
44 #define DEFAULT_YUV_SD  0
45 #define DEFAULT_YUV_HD  1
46 #define DEFAULT_RGB     3
47 #define DEFAULT_GRAY    4
48 #define DEFAULT_UNKNOWN 5
49
50 static const ColorimetryInfo colorimetry[] = {
51   MAKE_COLORIMETRY (BT601, _16_235, BT601, BT709, BT470M),
52   MAKE_COLORIMETRY (BT709, _16_235, BT709, BT709, BT709),
53   MAKE_COLORIMETRY (SMPTE240M, _16_235, SMPTE240M, SMPTE240M, SMPTE240M),
54   MAKE_COLORIMETRY (NONAME, _0_255, RGB, UNKNOWN, UNKNOWN),
55   MAKE_COLORIMETRY (NONAME, _0_255, BT601, UNKNOWN, UNKNOWN),
56   MAKE_COLORIMETRY (NONAME, _UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN),
57 };
58
59 static const ColorimetryInfo *
60 gst_video_get_colorimetry (const gchar * s)
61 {
62   gint i;
63
64   for (i = 0; colorimetry[i].name; i++) {
65     if (g_str_equal (colorimetry[i].name, s))
66       return &colorimetry[i];
67   }
68   return NULL;
69 }
70
71 #define IS_EQUAL(ci,i) (((ci)->color.range == (i)->range) && \
72                         ((ci)->color.matrix == (i)->matrix) && \
73                         ((ci)->color.transfer == (i)->transfer) && \
74                         ((ci)->color.primaries == (i)->primaries))
75
76 #define IS_UNKNOWN(ci) (IS_EQUAL (&colorimetry[DEFAULT_UNKNOWN], ci))
77
78 /**
79  * gst_video_colorimetry_from_string:
80  * @cinfo: a #GstVideoColorimetry
81  * @color: a colorimetry string
82  *
83  * Parse the colorimetry string and update @cinfo with the parsed
84  * values.
85  *
86  * Returns: #TRUE if @color points to valid colorimetry info.
87  */
88 gboolean
89 gst_video_colorimetry_from_string (GstVideoColorimetry * cinfo,
90     const gchar * color)
91 {
92   const ColorimetryInfo *ci;
93
94   if ((ci = gst_video_get_colorimetry (color))) {
95     *cinfo = ci->color;
96   } else {
97     gint r, m, t, p;
98
99     if (sscanf (color, "%d:%d:%d:%d", &r, &m, &t, &p) == 4) {
100       cinfo->range = r;
101       cinfo->matrix = m;
102       cinfo->transfer = t;
103       cinfo->primaries = p;
104     }
105   }
106   return TRUE;
107 }
108
109 /**
110  * gst_video_colorimetry_to_string:
111  * @cinfo: a #GstVideoColorimetry
112  *
113  * Make a string representation of @cinfo.
114  *
115  * Returns: a string representation of @cinfo.
116  */
117 gchar *
118 gst_video_colorimetry_to_string (GstVideoColorimetry * cinfo)
119 {
120   gint i;
121
122   for (i = 0; colorimetry[i].name; i++) {
123     if (IS_EQUAL (&colorimetry[i], cinfo)) {
124       return g_strdup (colorimetry[i].name);
125     }
126   }
127   if (!IS_UNKNOWN (cinfo)) {
128     return g_strdup_printf ("%d:%d:%d:%d", cinfo->range, cinfo->matrix,
129         cinfo->transfer, cinfo->primaries);
130   }
131   return NULL;
132 }
133
134 /**
135  * gst_video_colorimetry_matches:
136  * @cinfo: a #GstVideoInfo
137  * @color: a colorimetry string
138  *
139  * Check if the colorimetry information in @info matches that of the
140  * string @color.
141  *
142  * Returns: #TRUE if @color conveys the same colorimetry info as the color
143  * information in @info.
144  */
145 gboolean
146 gst_video_colorimetry_matches (GstVideoColorimetry * cinfo, const gchar * color)
147 {
148   const ColorimetryInfo *ci;
149
150   if ((ci = gst_video_get_colorimetry (color)))
151     return IS_EQUAL (ci, cinfo);
152
153   return FALSE;
154 }
155
156 /**
157  * gst_video_color_range_offsets:
158  * @range: a #GstVideoColorRange
159  * @info: a #GstVideoFormatInfo
160  * @offset: (out): output offsets
161  * @scale: (out): output scale
162  *
163  * Compute the offset and scale values for each component of @info. For each
164  * component, (c[i] - offset[i]) / scale[i] will scale the component c[i] to the
165  * range [0.0 .. 1.0].
166  *
167  * The reverse operation (c[i] * scale[i]) + offset[i] can be used to convert
168  * the component values in range [0.0 .. 1.0] back to their representation in
169  * @info and @range.
170  */
171 void
172 gst_video_color_range_offsets (GstVideoColorRange range,
173     const GstVideoFormatInfo * info, gint offset[GST_VIDEO_MAX_COMPONENTS],
174     gint scale[GST_VIDEO_MAX_COMPONENTS])
175 {
176   gboolean yuv;
177
178   yuv = GST_VIDEO_FORMAT_INFO_IS_YUV (info);
179
180   switch (range) {
181     default:
182     case GST_VIDEO_COLOR_RANGE_0_255:
183       offset[0] = 0;
184       if (yuv) {
185         offset[1] = 1 << (info->depth[1] - 1);
186         offset[2] = 1 << (info->depth[2] - 1);
187       } else {
188         offset[1] = 0;
189         offset[2] = 0;
190       }
191       scale[0] = (1 << info->depth[0]) - 1;
192       scale[1] = (1 << info->depth[1]) - 1;
193       scale[2] = (1 << info->depth[2]) - 1;
194       break;
195     case GST_VIDEO_COLOR_RANGE_16_235:
196       offset[0] = 1 << (info->depth[0] - 4);
197       scale[0] = 219 << (info->depth[0] - 8);
198       if (yuv) {
199         offset[1] = 1 << (info->depth[1] - 1);
200         offset[2] = 1 << (info->depth[2] - 1);
201         scale[1] = 224 << (info->depth[1] - 8);
202         scale[2] = 224 << (info->depth[2] - 8);
203       } else {
204         offset[1] = 1 << (info->depth[1] - 4);
205         offset[2] = 1 << (info->depth[2] - 4);
206         scale[1] = 219 << (info->depth[1] - 8);
207         scale[2] = 219 << (info->depth[2] - 8);
208       }
209       break;
210   }
211   /* alpha channel is always full range */
212   offset[3] = 0;
213   scale[3] = (1 << info->depth[3]) - 1;
214
215   GST_DEBUG ("scale: %d %d %d %d", scale[0], scale[1], scale[2], scale[3]);
216   GST_DEBUG ("offset: %d %d %d %d", offset[0], offset[1], offset[2], offset[3]);
217 }
218
219
220 #if 0
221 typedef struct
222 {
223   GstVideoColorPrimaries primaries;
224   gdouble xW, yW;
225   gdouble xR, yR;
226   gdouble xG, yG;
227   gdouble xB, yB;
228 } PrimariesInfo;
229
230 #define WP_C    0.31006, 0.31616
231 #define WP_D65  0.31271, 0.32902
232
233 static const PrimariesInfo primaries[] = {
234   {GST_VIDEO_COLOR_PRIMARIES_UNKNOWN, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
235   {GST_VIDEO_COLOR_PRIMARIES_BT709, WP_D65, 0.64, 0.33, 0.30, 0.60, 0.15, 0.06},
236   {GST_VIDEO_COLOR_PRIMARIES_BT470M, WP_C, 0.67, 0.33, 0.21, 0.71, 0.14, 0.08},
237   {GST_VIDEO_COLOR_PRIMARIES_BT470BG, WP_D65, 0.64, 0.33, 0.29, 0.60, 0.15,
238       0.06},
239   {GST_VIDEO_COLOR_PRIMARIES_SMPTE170M, WP_D65, 0.63, 0.34, 0.31, 0.595, 0.155,
240       0.07},
241   {GST_VIDEO_COLOR_PRIMARIES_SMPTE240M, WP_D65, 0.63, 0.34, 0.31, 0.595, 0.155,
242       0.07},
243   {GST_VIDEO_COLOR_PRIMARIES_FILM, WP_C, 0.681, 0.319, 0.243, 0.692, 0.145,
244       0.049}
245 };
246 #endif
247
248 /**
249  * gst_video_color_matrix_get_Kr_Kb:
250  * @matrix: a #GstVideoColorMatrix
251  * @Kr: result red channel coefficient
252  * @Kb: result blue channel coefficient
253  *
254  * Get the coefficients used to convert between Y'PbPr and R'G'B' using @matrix.
255  *
256  * When:
257  *
258  * |[
259  *   0.0 <= [Y',R',G',B'] <= 1.0)
260  *   (-0.5 <= [Pb,Pr] <= 0.5)
261  * ]|
262  *
263  * the general conversion is given by:
264  *
265  * |[
266  *   Y' = Kr*R' + (1-Kr-Kb)*G' + Kb*B'
267  *   Pb = (B'-Y')/(2*(1-Kb))
268  *   Pr = (R'-Y')/(2*(1-Kr))
269  * ]|
270  *
271  * and the other way around:
272  *
273  * |[
274  *   R' = Y' + Cr*2*(1-Kr)
275  *   G' = Y' - Cb*2*(1-Kb)*Kb/(1-Kr-Kb) - Cr*2*(1-Kr)*Kr/(1-Kr-Kb)
276  *   B' = Y' + Cb*2*(1-Kb)
277  * ]|
278  *
279  * Returns: TRUE if @matrix was a YUV color format and @Kr and @Kb contain valid
280  *    values.
281  *
282  * Since: 1.6
283  */
284 gboolean
285 gst_video_color_matrix_get_Kr_Kb (GstVideoColorMatrix matrix, gdouble * Kr,
286     gdouble * Kb)
287 {
288   gboolean res = TRUE;
289
290   switch (matrix) {
291       /* RGB */
292     default:
293     case GST_VIDEO_COLOR_MATRIX_RGB:
294       res = FALSE;
295       break;
296       /* YUV */
297     case GST_VIDEO_COLOR_MATRIX_FCC:
298       *Kr = 0.30;
299       *Kb = 0.11;
300       break;
301     case GST_VIDEO_COLOR_MATRIX_BT709:
302       *Kr = 0.2126;
303       *Kb = 0.0722;
304       break;
305     case GST_VIDEO_COLOR_MATRIX_BT601:
306       *Kr = 0.2990;
307       *Kb = 0.1140;
308       break;
309     case GST_VIDEO_COLOR_MATRIX_SMPTE240M:
310       *Kr = 0.212;
311       *Kb = 0.087;
312       break;
313   }
314   GST_DEBUG ("matrix: %d, Kr %f, Kb %f", matrix, *Kr, *Kb);
315
316   return res;
317 }
318
319 /**
320  * gst_video_color_transfer_encode:
321  * @func: a #GstVideoTransferFunction
322  * @val: a value
323  *
324  * Convert @val to its gamma encoded value.
325  *
326  * For a linear value L in the range [0..1], conversion to the non-linear
327  * (gamma encoded) L' is in general performed with a power function like:
328  *
329  * |[
330  *    L' = L ^ (1 / gamma)
331  * ]|
332  *
333  * Depending on @func, different formulas might be applied. Some formulas
334  * encode a linear segment in the lower range.
335  *
336  * Returns: the gamme encoded value of @val
337  *
338  * Since: 1.6
339  */
340 gdouble
341 gst_video_color_transfer_encode (GstVideoTransferFunction func, gdouble val)
342 {
343   gdouble res;
344
345   switch (func) {
346     case GST_VIDEO_TRANSFER_UNKNOWN:
347     case GST_VIDEO_TRANSFER_GAMMA10:
348     default:
349       res = val;
350       break;
351     case GST_VIDEO_TRANSFER_GAMMA18:
352       res = pow (val, 1.0 / 1.8);
353       break;
354     case GST_VIDEO_TRANSFER_GAMMA20:
355       res = pow (val, 1.0 / 2.0);
356       break;
357     case GST_VIDEO_TRANSFER_GAMMA22:
358       res = pow (val, 1.0 / 2.2);
359       break;
360     case GST_VIDEO_TRANSFER_BT709:
361       if (val < 0.018)
362         res = 4.5 * val;
363       else
364         res = 1.099 * pow (val, 0.45) - 0.099;
365       break;
366     case GST_VIDEO_TRANSFER_SMPTE240M:
367       if (val < 0.0228)
368         res = val * 4.0;
369       else
370         res = 1.1115 * pow (val, 0.45) - 0.1115;
371       break;
372     case GST_VIDEO_TRANSFER_SRGB:
373       if (val <= 0.0031308)
374         res = 12.92 * val;
375       else
376         res = 1.055 * pow (val, 1.0 / 2.4) - 0.055;
377       break;
378     case GST_VIDEO_TRANSFER_GAMMA28:
379       res = pow (val, 1 / 2.8);
380       break;
381     case GST_VIDEO_TRANSFER_LOG100:
382       if (val < 0.01)
383         res = 0.0;
384       else
385         res = 1.0 + log10 (val) / 2.0;
386       break;
387     case GST_VIDEO_TRANSFER_LOG316:
388       if (val < 0.0031622777)
389         res = 0.0;
390       else
391         res = 1.0 + log10 (val) / 2.5;
392       break;
393   }
394   return res;
395 }
396
397 /**
398  * gst_video_color_transfer_decode:
399  * @func: a #GstVideoTransferFunction
400  * @val: a value
401  *
402  * Convert @val to its gamma decoded value. This is the inverse operation of
403  * @gst_video_color_transfer_encode().
404  *
405  * For a non-linear value L' in the range [0..1], conversion to the linear
406  * L is in general performed with a power function like:
407  *
408  * |[
409  *    L = L' ^ gamma
410  * ]|
411  *
412  * Depending on @func, different formulas might be applied. Some formulas
413  * encode a linear segment in the lower range.
414  *
415  * Returns: the gamme decoded value of @val
416  *
417  * Since: 1.6
418  */
419 gdouble
420 gst_video_color_transfer_decode (GstVideoTransferFunction func, gdouble val)
421 {
422   gdouble res;
423
424   switch (func) {
425     case GST_VIDEO_TRANSFER_UNKNOWN:
426     case GST_VIDEO_TRANSFER_GAMMA10:
427     default:
428       res = val;
429       break;
430     case GST_VIDEO_TRANSFER_GAMMA18:
431       res = pow (val, 1.8);
432       break;
433     case GST_VIDEO_TRANSFER_GAMMA20:
434       res = pow (val, 2.0);
435       break;
436     case GST_VIDEO_TRANSFER_GAMMA22:
437       res = pow (val, 2.2);
438       break;
439     case GST_VIDEO_TRANSFER_BT709:
440       if (val < 0.081)
441         res = val / 4.5;
442       else
443         res = pow ((val + 0.099) / 1.099, 1.0 / 0.45);
444       break;
445     case GST_VIDEO_TRANSFER_SMPTE240M:
446       if (val < 0.0913)
447         res = val / 4.0;
448       else
449         res = pow ((val + 0.1115) / 1.1115, 1.0 / 0.45);
450       break;
451     case GST_VIDEO_TRANSFER_SRGB:
452       if (val <= 0.04045)
453         res = val / 12.92;
454       else
455         res = pow ((val + 0.055) / 1.055, 2.4);
456       break;
457     case GST_VIDEO_TRANSFER_GAMMA28:
458       res = pow (val, 2.8);
459       break;
460     case GST_VIDEO_TRANSFER_LOG100:
461       if (val == 0.0)
462         res = 0.0;
463       else
464         res = pow (10.0, 2.0 * (val - 1.0));
465       break;
466     case GST_VIDEO_TRANSFER_LOG316:
467       if (val == 0.0)
468         res = 0.0;
469       else
470         res = pow (10.0, 2.5 * (val - 1.0));
471       break;
472   }
473   return res;
474 }