d3d11: Update library doc
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / d3d11 / gstd3d11format.cpp
1 /* GStreamer
2  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstd3d11format.h"
25 #include "gstd3d11utils.h"
26 #include "gstd3d11device.h"
27 #include "gstd3d11memory.h"
28 #include "gstd3d11-private.h"
29
30 #include <string.h>
31
32 /**
33  * SECTION:gstd3d11format
34  * @title: GstD3D11Format
35  * @short_description: Bridge of Direct3D11 and GStreamer video format representation
36  *
37  * Since: 1.22
38  */
39
40 #ifndef GST_DISABLE_GST_DEBUG
41 #define GST_CAT_DEFAULT ensure_debug_category()
42 static GstDebugCategory *
43 ensure_debug_category (void)
44 {
45   static GstDebugCategory *cat = nullptr;
46
47   GST_D3D11_CALL_ONCE_BEGIN {
48     cat = _gst_debug_category_new ("d3d11format", 0, "d3d11 specific formats");
49   } GST_D3D11_CALL_ONCE_END;
50
51   return cat;
52 }
53 #else
54 #define ensure_debug_category() /* NOOP */
55 #endif /* GST_DISABLE_GST_DEBUG */
56
57 GType
58 gst_d3d11_format_support_get_type (void)
59 {
60   static GType support_type = 0;
61   static const GFlagsValue support_values[] = {
62     {D3D11_FORMAT_SUPPORT_BUFFER, "BUFFER", "buffer"},
63     {D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER, "IA_VERTEX_BUFFER",
64         "ia-vertex-buffer"},
65     {D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER, "IA_INDEX_BUFFER",
66         "ia-index-buffer"},
67     {D3D11_FORMAT_SUPPORT_SO_BUFFER, "SO_BUFFER", "so-buffer"},
68     {D3D11_FORMAT_SUPPORT_TEXTURE1D, "TEXTURE1D", "texture1d"},
69     {D3D11_FORMAT_SUPPORT_TEXTURE2D, "TEXTURE2D", "texture2d"},
70     {D3D11_FORMAT_SUPPORT_TEXTURE3D, "TEXTURE3D", "texture3d"},
71     {D3D11_FORMAT_SUPPORT_TEXTURECUBE, "TEXTURECUBE", "texturecube"},
72     {D3D11_FORMAT_SUPPORT_SHADER_LOAD, "SHADER_LOAD", "shader-load"},
73     {D3D11_FORMAT_SUPPORT_SHADER_SAMPLE, "SHADER_SAMPLE", "shader-sample"},
74     {D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_COMPARISON, "SHADER_COMPARISION",
75         "shader-comparision"},
76     {D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_MONO_TEXT, "SHADER_SAMPLE_MONO_TEXT",
77         "shader-sample-mono-text"},
78     {D3D11_FORMAT_SUPPORT_MIP, "MIP", "mip"},
79     {D3D11_FORMAT_SUPPORT_MIP_AUTOGEN, "MIP_AUTOGEN", "mip-autogen"},
80     {D3D11_FORMAT_SUPPORT_RENDER_TARGET, "RENDER_TARGET", "render-target"},
81     {D3D11_FORMAT_SUPPORT_BLENDABLE, "BLANDABLE", "blandable"},
82     {D3D11_FORMAT_SUPPORT_DEPTH_STENCIL, "DEPTH_STENCIL", "depth-stencil"},
83     {D3D11_FORMAT_SUPPORT_CPU_LOCKABLE, "CPU_LOCKABLE", "cpu-lockable"},
84     {D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE, "MULTISAMPLE_RESOLVE",
85         "multisample-resolve"},
86     {D3D11_FORMAT_SUPPORT_DISPLAY, "DISPLAY", "display"},
87     {D3D11_FORMAT_SUPPORT_CAST_WITHIN_BIT_LAYOUT, "CAST_WITHIN_BIT_LAYOUT",
88         "cast-within-bit-layout"},
89     {D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET, "MULTISAMPLE_RENDERTARGET",
90         "multisample-rendertarget"},
91     {D3D11_FORMAT_SUPPORT_MULTISAMPLE_LOAD, "MULTISAMPLE_LOAD",
92         "multisample-load"},
93     {D3D11_FORMAT_SUPPORT_SHADER_GATHER, "SHADER_GATHER", "shader-gether"},
94     {D3D11_FORMAT_SUPPORT_BACK_BUFFER_CAST, "BACK_BUFFER_CAST",
95         "back-buffer-cast"},
96     {D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW, "UNORDERED_ACCESS_VIEW",
97         "unordered-access-view"},
98     {D3D11_FORMAT_SUPPORT_SHADER_GATHER_COMPARISON, "SHADER_GATHER_COMPARISON",
99         "shader-gether-comparision"},
100     {D3D11_FORMAT_SUPPORT_DECODER_OUTPUT, "DECODER_OUTPUT", "decoder-output"},
101     {D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT, "VIDEO_PROCESSOR_OUTPUT",
102         "video-processor-output"},
103     {D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT, "VIDEO_PROCESSOR_INPUT",
104         "video-processor-input"},
105     {D3D11_FORMAT_SUPPORT_VIDEO_ENCODER, "VIDEO_ENCODER", "video-encoder"},
106     {0, nullptr, nullptr}
107   };
108
109   GST_D3D11_CALL_ONCE_BEGIN {
110     support_type = g_flags_register_static ("GstD3D11FormatSupport",
111         support_values);
112   } GST_D3D11_CALL_ONCE_END;
113
114   return support_type;
115 }
116
117 /**
118  * gst_d3d11_dxgi_format_get_size:
119  * @format: a DXGI_FORMAT
120  * @width: a texture width
121  * @height: a texture height
122  * @pitch: a pitch of texture
123  * @offset: offset for each plane
124  * @stride: stride for each plane
125  * @size: (out): required memory size for given format
126  *
127  * Calculate required memory size and per plane stride with
128  * based on information
129  *
130  * Returns: %TRUE if @size can be calculated with given information
131  *
132  * Since: 1.22
133  */
134 gboolean
135 gst_d3d11_dxgi_format_get_size (DXGI_FORMAT format, guint width, guint height,
136     guint pitch, gsize offset[GST_VIDEO_MAX_PLANES],
137     gint stride[GST_VIDEO_MAX_PLANES], gsize * size)
138 {
139   g_return_val_if_fail (format != DXGI_FORMAT_UNKNOWN, FALSE);
140
141   switch (format) {
142     case DXGI_FORMAT_B8G8R8A8_UNORM:
143     case DXGI_FORMAT_R8G8B8A8_UNORM:
144     case DXGI_FORMAT_R10G10B10A2_UNORM:
145     case DXGI_FORMAT_AYUV:
146     case DXGI_FORMAT_YUY2:
147     case DXGI_FORMAT_R8_UNORM:
148     case DXGI_FORMAT_R8G8_UNORM:
149     case DXGI_FORMAT_R16_UNORM:
150     case DXGI_FORMAT_R16G16_UNORM:
151     case DXGI_FORMAT_G8R8_G8B8_UNORM:
152     case DXGI_FORMAT_R8G8_B8G8_UNORM:
153     case DXGI_FORMAT_Y210:
154     case DXGI_FORMAT_Y410:
155     case DXGI_FORMAT_R16G16B16A16_UNORM:
156       offset[0] = 0;
157       stride[0] = pitch;
158       *size = pitch * height;
159       break;
160     case DXGI_FORMAT_NV12:
161     case DXGI_FORMAT_P010:
162     case DXGI_FORMAT_P016:
163       offset[0] = 0;
164       stride[0] = pitch;
165       offset[1] = offset[0] + stride[0] * height;
166       stride[1] = pitch;
167       *size = offset[1] + stride[1] * GST_ROUND_UP_2 (height / 2);
168       break;
169     default:
170       return FALSE;
171   }
172
173   GST_LOG ("Calculated buffer size: %" G_GSIZE_FORMAT
174       " (dxgi format:%d, %dx%d, Pitch %d)",
175       *size, format, width, height, pitch);
176
177   return TRUE;
178 }
179
180 /**
181  * gst_d3d11_dxgi_format_to_gst:
182  * @format: a DXGI_FORMAT
183  *
184  * Converts the @format to its #GstVideoFormat representation.
185  *
186  * Returns: a #GstVideoFormat equivalent to @format
187  *
188  * Since: 1.22
189  */
190 GstVideoFormat
191 gst_d3d11_dxgi_format_to_gst (DXGI_FORMAT format)
192 {
193   switch (format) {
194     case DXGI_FORMAT_B8G8R8A8_UNORM:
195       return GST_VIDEO_FORMAT_BGRA;
196     case DXGI_FORMAT_R8G8B8A8_UNORM:
197       return GST_VIDEO_FORMAT_RGBA;
198     case DXGI_FORMAT_R10G10B10A2_UNORM:
199       return GST_VIDEO_FORMAT_RGB10A2_LE;
200     case DXGI_FORMAT_AYUV:
201       return GST_VIDEO_FORMAT_VUYA;
202     case DXGI_FORMAT_YUY2:
203       return GST_VIDEO_FORMAT_YUY2;
204     case DXGI_FORMAT_Y210:
205       return GST_VIDEO_FORMAT_Y210;
206     case DXGI_FORMAT_Y410:
207       return GST_VIDEO_FORMAT_Y410;
208     case DXGI_FORMAT_NV12:
209       return GST_VIDEO_FORMAT_NV12;
210     case DXGI_FORMAT_P010:
211       return GST_VIDEO_FORMAT_P010_10LE;
212     case DXGI_FORMAT_P016:
213       return GST_VIDEO_FORMAT_P016_LE;
214     default:
215       break;
216   }
217
218   return GST_VIDEO_FORMAT_UNKNOWN;
219 }
220
221 /**
222  * gst_d3d11_format_init:
223  * @format: (out caller-allocates): a #GstD3D11Format
224  *
225  * Initialize @format with default values.
226  *
227  * Since: 1.22
228  */
229 void
230 gst_d3d11_format_init (GstD3D11Format * format)
231 {
232   g_return_if_fail (format != nullptr);
233
234   memset (format, 0, sizeof (GstD3D11Format));
235 }
236
237 /**
238  * gst_d3d11_dxgi_format_get_resource_format:
239  * @format: a DXGI_FORMAT
240  * @resource_format: (out caller-allocats): Resource formats for each plane
241  *
242  * Returns: the number of planes for @format
243  *
244  * Since: 1.22
245  */
246 guint
247 gst_d3d11_dxgi_format_get_resource_format (DXGI_FORMAT format,
248     DXGI_FORMAT resource_format[GST_VIDEO_MAX_PLANES])
249 {
250   for (guint i = 0; i < GST_VIDEO_MAX_PLANES; i++)
251     resource_format[i] = DXGI_FORMAT_UNKNOWN;
252
253   if (format == DXGI_FORMAT_UNKNOWN)
254     return 0;
255
256   for (guint i = 0; i < GST_D3D11_N_FORMATS; i++) {
257     const GstD3D11Format *fmt = &_gst_d3d11_default_format_map[i];
258
259     if (fmt->dxgi_format == format) {
260       guint n_planes = 0;
261
262       for (n_planes = 0; n_planes < GST_VIDEO_MAX_PLANES; n_planes++) {
263         if (fmt->resource_format[n_planes] == DXGI_FORMAT_UNKNOWN)
264           break;
265
266         resource_format[n_planes] = fmt->resource_format[n_planes];
267       }
268
269       return n_planes;
270     }
271   }
272
273   resource_format[0] = format;
274   return 1;
275 }
276
277 /**
278  * gst_d3d11_dxgi_format_get_alignment:
279  * @format: a DXGI_FORMAT
280  *
281  * Returns: Width and height Alignment requirement for given @format
282  *
283  * Since: 1.22
284  */
285 guint
286 gst_d3d11_dxgi_format_get_alignment (DXGI_FORMAT format)
287 {
288   switch (format) {
289     case DXGI_FORMAT_NV12:
290     case DXGI_FORMAT_P010:
291     case DXGI_FORMAT_P016:
292       return 2;
293     default:
294       break;
295   }
296
297   return 0;
298 }
299
300 /**
301  * gst_d3d11_dxgi_format_to_string:
302  * @format: a DXGI_FORMAT
303  *
304  * Converts @format enum value to its string representation
305  *
306  * Returns: a string representation of @format
307  *
308  * Since: 1.22
309  */
310 const gchar *
311 gst_d3d11_dxgi_format_to_string (DXGI_FORMAT format)
312 {
313 #define CASE(f) \
314     case DXGI_FORMAT_ ##f: \
315       return G_STRINGIFY (f);
316
317   switch (format) {
318       CASE (UNKNOWN);
319       CASE (R32G32B32A32_TYPELESS);
320       CASE (R32G32B32A32_FLOAT);
321       CASE (R32G32B32A32_UINT);
322       CASE (R32G32B32A32_SINT);
323       CASE (R32G32B32_TYPELESS);
324       CASE (R32G32B32_FLOAT);
325       CASE (R32G32B32_UINT);
326       CASE (R32G32B32_SINT);
327       CASE (R16G16B16A16_TYPELESS);
328       CASE (R16G16B16A16_FLOAT);
329       CASE (R16G16B16A16_UNORM);
330       CASE (R16G16B16A16_UINT);
331       CASE (R16G16B16A16_SNORM);
332       CASE (R16G16B16A16_SINT);
333       CASE (R32G32_TYPELESS);
334       CASE (R32G32_FLOAT);
335       CASE (R32G32_UINT);
336       CASE (R32G32_SINT);
337       CASE (R32G8X24_TYPELESS);
338       CASE (D32_FLOAT_S8X24_UINT);
339       CASE (R32_FLOAT_X8X24_TYPELESS);
340       CASE (X32_TYPELESS_G8X24_UINT);
341       CASE (R10G10B10A2_TYPELESS);
342       CASE (R10G10B10A2_UNORM);
343       CASE (R10G10B10A2_UINT);
344       CASE (R11G11B10_FLOAT);
345       CASE (R8G8B8A8_TYPELESS);
346       CASE (R8G8B8A8_UNORM);
347       CASE (R8G8B8A8_UNORM_SRGB);
348       CASE (R8G8B8A8_UINT);
349       CASE (R8G8B8A8_SNORM);
350       CASE (R8G8B8A8_SINT);
351       CASE (R16G16_TYPELESS);
352       CASE (R16G16_FLOAT);
353       CASE (R16G16_UNORM);
354       CASE (R16G16_UINT);
355       CASE (R16G16_SNORM);
356       CASE (R16G16_SINT);
357       CASE (R32_TYPELESS);
358       CASE (D32_FLOAT);
359       CASE (R32_FLOAT);
360       CASE (R32_UINT);
361       CASE (R32_SINT);
362       CASE (R24G8_TYPELESS);
363       CASE (D24_UNORM_S8_UINT);
364       CASE (R24_UNORM_X8_TYPELESS);
365       CASE (X24_TYPELESS_G8_UINT);
366       CASE (R8G8_TYPELESS);
367       CASE (R8G8_UNORM);
368       CASE (R8G8_UINT);
369       CASE (R8G8_SNORM);
370       CASE (R8G8_SINT);
371       CASE (R16_TYPELESS);
372       CASE (R16_FLOAT);
373       CASE (D16_UNORM);
374       CASE (R16_UNORM);
375       CASE (R16_UINT);
376       CASE (R16_SNORM);
377       CASE (R16_SINT);
378       CASE (R8_TYPELESS);
379       CASE (R8_UNORM);
380       CASE (R8_UINT);
381       CASE (R8_SNORM);
382       CASE (R8_SINT);
383       CASE (A8_UNORM);
384       CASE (R1_UNORM);
385       CASE (R9G9B9E5_SHAREDEXP);
386       CASE (R8G8_B8G8_UNORM);
387       CASE (G8R8_G8B8_UNORM);
388       CASE (BC1_TYPELESS);
389       CASE (BC1_UNORM);
390       CASE (BC1_UNORM_SRGB);
391       CASE (BC2_TYPELESS);
392       CASE (BC2_UNORM);
393       CASE (BC2_UNORM_SRGB);
394       CASE (BC3_TYPELESS);
395       CASE (BC3_UNORM);
396       CASE (BC3_UNORM_SRGB);
397       CASE (BC4_TYPELESS);
398       CASE (BC4_UNORM);
399       CASE (BC4_SNORM);
400       CASE (BC5_TYPELESS);
401       CASE (BC5_UNORM);
402       CASE (BC5_SNORM);
403       CASE (B5G6R5_UNORM);
404       CASE (B5G5R5A1_UNORM);
405       CASE (B8G8R8A8_UNORM);
406       CASE (B8G8R8X8_UNORM);
407       CASE (R10G10B10_XR_BIAS_A2_UNORM);
408       CASE (B8G8R8A8_TYPELESS);
409       CASE (B8G8R8A8_UNORM_SRGB);
410       CASE (B8G8R8X8_TYPELESS);
411       CASE (B8G8R8X8_UNORM_SRGB);
412       CASE (BC6H_TYPELESS);
413       CASE (BC6H_UF16);
414       CASE (BC6H_SF16);
415       CASE (BC7_TYPELESS);
416       CASE (BC7_UNORM);
417       CASE (BC7_UNORM_SRGB);
418       CASE (AYUV);
419       CASE (Y410);
420       CASE (Y416);
421       CASE (NV12);
422       CASE (P010);
423       CASE (P016);
424     case DXGI_FORMAT_420_OPAQUE:
425       return "420_OPAQUE";
426       CASE (YUY2);
427       CASE (Y210);
428       CASE (Y216);
429       CASE (NV11);
430       CASE (AI44);
431       CASE (IA44);
432       CASE (P8);
433       CASE (A8P8);
434       CASE (B4G4R4A4_UNORM);
435       CASE (P208);
436       CASE (V208);
437       CASE (V408);
438     default:
439       break;
440   }
441 #undef CASE
442
443   return "Unknown";
444 }
445
446 /* Some values are not defined in old MinGW toolchain */
447 typedef enum
448 {
449   GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0,
450   GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1,
451   GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2,
452   GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3,
453   GST_DXGI_COLOR_SPACE_RESERVED = 4,
454   GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5,
455   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6,
456   GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7,
457   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8,
458   GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9,
459   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10,
460   GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11,
461   GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12,
462   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13,
463   GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14,
464   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15,
465   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16,
466   GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17,
467   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18,
468   GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19,
469   GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20,
470   GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21,
471   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22,
472   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23,
473   GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24,
474 } GST_DXGI_COLOR_SPACE_TYPE;
475
476 static gboolean
477 rgb_to_colorspace (const GstVideoColorimetry * cinfo,
478     DXGI_COLOR_SPACE_TYPE * color_space)
479 {
480   /* sRGB */
481   GST_DXGI_COLOR_SPACE_TYPE type = GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
482
483   /* Defined DXGI RGB colorspace
484    * 1) DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0 (sRGB)
485    * 2) DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1 (scRGB)
486    * 3) DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2 (BT601/BT709 studio range)
487    * 4) DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3 (BT2020 SDR studio range)
488    * 5) DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12 (HDR10 full range)
489    * 6) DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 13 (HDR10 studio range)
490    * 7) DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17 (BT2020 SDR fullrange)
491    * 8) DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20 (unused)
492    * 9) DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21 (unused)
493    *
494    * Note that GStreamer does not define gamma2.4. So, 8) and 9) are excluded
495    */
496   if (cinfo->transfer == GST_VIDEO_TRANSFER_GAMMA10) {
497     type = GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
498     goto done;
499   }
500
501   /* HLG RGB colorspace is not defined, approximated to HDR10 */
502   if (cinfo->transfer == GST_VIDEO_TRANSFER_SMPTE2084 ||
503       cinfo->transfer == GST_VIDEO_TRANSFER_ARIB_STD_B67) {
504     if (cinfo->range == GST_VIDEO_COLOR_RANGE_16_235) {
505       type = GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020;
506     } else {
507       type = GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
508     }
509     goto done;
510   }
511
512   if (cinfo->primaries == GST_VIDEO_COLOR_PRIMARIES_BT2020) {
513     if (cinfo->range == GST_VIDEO_COLOR_RANGE_16_235) {
514       type = GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020;
515     } else {
516       type = GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020;
517     }
518     goto done;
519   }
520
521   if (cinfo->range == GST_VIDEO_COLOR_RANGE_16_235)
522     type = GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709;
523
524 done:
525   *color_space = (DXGI_COLOR_SPACE_TYPE) type;
526
527   return TRUE;
528 }
529
530 static gboolean
531 yuv_to_colorspace (const GstVideoColorimetry * cinfo,
532     GstVideoChromaSite chroma_site, DXGI_COLOR_SPACE_TYPE * color_space)
533 {
534   /* BT709 */
535   GST_DXGI_COLOR_SPACE_TYPE type =
536       GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
537
538   /* Defined DXGI RGB colorspace
539    * 1) DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5 (common JPG)
540    * 2) DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6 (BT601 studio range)
541    * 3) DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7 (BT601 full range)
542    * 4) DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8 (BT709 studio range)
543    * 5) DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9 (BT709 full range)
544    * 6) DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10 (BT2020 4:2:0 studio range)
545    * 7) DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11 (BT2020 full range)
546    * 8) DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13 (HDR10 4:2:0 studio range)
547    * 9) DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15 (BT2020 4:2:2 or 4:4:4: studio range)
548    * 10) DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16 (HDR10 4:2:2 or 4:4:4 studio range)
549    * 11) DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18 (HLG studio range)
550    * 12) DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19 (HLG full range)
551    * 13) DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22 (unused)
552    * 14) DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23 (unused)
553    * 15) DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24 (unused)
554    *
555    * Note that GStreamer does not define gamma2.4. So, 13) ~ 15) are excluded
556    */
557
558   /* HLG */
559   if (cinfo->transfer == GST_VIDEO_TRANSFER_ARIB_STD_B67) {
560     if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) {
561       type = GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020;
562     } else {
563       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020;
564     }
565     goto done;
566   }
567
568   /* HDR10 */
569   if (cinfo->transfer == GST_VIDEO_TRANSFER_SMPTE2084) {
570     if (chroma_site == GST_VIDEO_CHROMA_SITE_H_COSITED) {
571       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020;
572     } else {
573       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020;
574     }
575     goto done;
576   }
577
578   /* BT2020 */
579   if (cinfo->primaries == GST_VIDEO_COLOR_PRIMARIES_BT2020) {
580     if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) {
581       type = GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020;
582     } else if (chroma_site == GST_VIDEO_CHROMA_SITE_H_COSITED) {
583       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020;
584     } else {
585       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020;
586     }
587     goto done;
588   }
589
590   /* BT601/BT709 primaries are similar. Depends on RGB matrix */
591   if (cinfo->matrix == GST_VIDEO_COLOR_MATRIX_BT601) {
592     if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255) {
593       if (cinfo->primaries == GST_VIDEO_COLOR_PRIMARIES_BT709) {
594         type = GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601;
595       } else {
596         type = GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601;
597       }
598     } else {
599       type = GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601;
600     }
601     goto done;
602   }
603
604   if (cinfo->range == GST_VIDEO_COLOR_RANGE_0_255)
605     type = GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709;
606
607 done:
608   *color_space = (DXGI_COLOR_SPACE_TYPE) type;
609
610   return TRUE;
611 }
612
613 /**
614  * gst_video_info_to_dxgi_color_space:
615  * @info: a #GstVideoInfo
616  * @color_space: (out): DXGI color space
617  *
618  * Derives DXGI_COLOR_SPACE_TYPE from @info
619  *
620  * Returns: %TRUE if successful
621  *
622  * Since: 1.22
623  */
624 gboolean
625 gst_video_info_to_dxgi_color_space (const GstVideoInfo * info,
626     DXGI_COLOR_SPACE_TYPE * color_space)
627 {
628   const GstVideoColorimetry *cinfo;
629   GstVideoColorimetry c;
630
631   g_return_val_if_fail (info != nullptr, FALSE);
632   g_return_val_if_fail (color_space != nullptr, FALSE);
633
634   cinfo = &info->colorimetry;
635
636   if (GST_VIDEO_INFO_IS_RGB (info)) {
637     /* ensure RGB matrix if format is already RGB */
638     c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
639   } else if (GST_VIDEO_INFO_IS_YUV (info) &&
640       cinfo->matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
641     /* Invalid matrix */
642     c.matrix = GST_VIDEO_COLOR_MATRIX_UNKNOWN;
643   } else {
644     c.matrix = cinfo->matrix;
645   }
646
647   switch (cinfo->range) {
648     case GST_VIDEO_COLOR_RANGE_0_255:
649       c.range = GST_VIDEO_COLOR_RANGE_0_255;
650       break;
651     case GST_VIDEO_COLOR_RANGE_16_235:
652       c.range = GST_VIDEO_COLOR_RANGE_16_235;
653       break;
654     default:
655       if (c.matrix == GST_VIDEO_COLOR_MATRIX_RGB)
656         c.range = GST_VIDEO_COLOR_RANGE_0_255;
657       else
658         c.range = GST_VIDEO_COLOR_RANGE_16_235;
659       break;
660   }
661
662   /* DXGI primaries: BT601, BT709, BT2020 */
663   switch (cinfo->primaries) {
664     case GST_VIDEO_COLOR_PRIMARIES_BT2020:
665       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
666       break;
667     case GST_VIDEO_COLOR_PRIMARIES_SMPTE170M:
668     case GST_VIDEO_COLOR_PRIMARIES_SMPTE240M:
669       c.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
670       break;
671     default:
672       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
673       break;
674   }
675
676   /* DXGI gamma functions: linear (RGB only), gamma2.2, PQ, and HLG */
677   switch (cinfo->transfer) {
678     case GST_VIDEO_TRANSFER_SMPTE2084:
679       c.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
680       break;
681     case GST_VIDEO_TRANSFER_ARIB_STD_B67:
682       c.transfer = GST_VIDEO_TRANSFER_ARIB_STD_B67;
683       break;
684     case GST_VIDEO_TRANSFER_GAMMA10:
685       /* Only DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 supports linear gamma */
686       if (c.matrix == GST_VIDEO_COLOR_MATRIX_RGB) {
687         c.transfer = GST_VIDEO_TRANSFER_GAMMA10;
688         c.range = GST_VIDEO_COLOR_RANGE_0_255;
689       } else {
690         c.transfer = GST_VIDEO_TRANSFER_GAMMA22;
691       }
692       break;
693     default:
694       /* Simply map the rest of values to gamma 2.2. We don't have any other
695        * choice */
696       c.transfer = GST_VIDEO_TRANSFER_GAMMA22;
697       break;
698   }
699
700   /* DXGI transform matrix: BT601, BT709, and BT2020 */
701   switch (c.matrix) {
702     case GST_VIDEO_COLOR_MATRIX_RGB:
703       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
704       break;
705     case GST_VIDEO_COLOR_MATRIX_FCC:
706     case GST_VIDEO_COLOR_MATRIX_BT601:
707       c.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
708       break;
709     case GST_VIDEO_COLOR_MATRIX_BT2020:
710       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
711       break;
712     default:
713       c.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
714       break;
715   }
716
717   if (c.matrix == GST_VIDEO_COLOR_MATRIX_RGB)
718     return rgb_to_colorspace (&c, color_space);
719
720   return yuv_to_colorspace (&c, info->chroma_site, color_space);
721 }
722
723 static gboolean
724 dxgi_color_space_is_rgb (GST_DXGI_COLOR_SPACE_TYPE color_space)
725 {
726   switch (color_space) {
727     case GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
728     case GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
729     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709:
730     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020:
731     case GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
732     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020:
733     case GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
734     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709:
735     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020:
736       return TRUE;
737     default:
738       break;
739   }
740
741   return FALSE;
742 }
743
744 /**
745  * gst_video_info_apply_dxgi_color_space:
746  * @color_space: DXGI color space
747  * @info: (inout): a #GstVideoInfo
748  *
749  * Updates color information of @info using @color_space
750  *
751  * Returns: %TRUE if successful
752  *
753  * Since: 1.22
754  */
755 gboolean
756 gst_video_info_apply_dxgi_color_space (DXGI_COLOR_SPACE_TYPE color_space,
757     GstVideoInfo * info)
758 {
759   GST_DXGI_COLOR_SPACE_TYPE type;
760   GstVideoColorimetry c;
761
762   g_return_val_if_fail (info != nullptr, FALSE);
763
764   type = (GST_DXGI_COLOR_SPACE_TYPE) color_space;
765
766   if (GST_VIDEO_INFO_IS_RGB (info) && !dxgi_color_space_is_rgb (type)) {
767     GST_WARNING ("Invalid DXGI color space mapping");
768     return FALSE;
769   }
770
771   switch (type) {
772     case GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
773       c.range = GST_VIDEO_COLOR_RANGE_0_255;
774       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
775       c.transfer = GST_VIDEO_TRANSFER_SRGB;
776       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
777       break;
778     case GST_DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
779       c.range = GST_VIDEO_COLOR_RANGE_0_255;
780       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
781       c.transfer = GST_VIDEO_TRANSFER_GAMMA10;
782       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
783       break;
784     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709:
785     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709:
786       c.range = GST_VIDEO_COLOR_RANGE_16_235;
787       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
788       c.transfer = GST_VIDEO_TRANSFER_BT709;
789       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
790       break;
791     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020:
792     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020:
793       c.range = GST_VIDEO_COLOR_RANGE_16_235;
794       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
795       if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) >= 12)
796         c.transfer = GST_VIDEO_TRANSFER_BT2020_12;
797       else
798         c.transfer = GST_VIDEO_TRANSFER_BT2020_10;
799       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
800       break;
801     case GST_DXGI_COLOR_SPACE_RESERVED:
802       GST_WARNING ("Reserved color space");
803       return FALSE;
804     case GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601:
805       c.range = GST_VIDEO_COLOR_RANGE_0_255;
806       c.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
807       c.transfer = GST_VIDEO_TRANSFER_BT601;
808       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
809       break;
810     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601:
811       c.range = GST_VIDEO_COLOR_RANGE_16_235;
812       c.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
813       c.transfer = GST_VIDEO_TRANSFER_BT601;
814       c.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
815       break;
816     case GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601:
817       c.range = GST_VIDEO_COLOR_RANGE_0_255;
818       c.matrix = GST_VIDEO_COLOR_MATRIX_BT601;
819       c.transfer = GST_VIDEO_TRANSFER_BT601;
820       c.primaries = GST_VIDEO_COLOR_PRIMARIES_SMPTE170M;
821       break;
822     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709:
823     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709:
824       c.range = GST_VIDEO_COLOR_RANGE_16_235;
825       c.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
826       c.transfer = GST_VIDEO_TRANSFER_BT709;
827       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
828       break;
829     case GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709:
830       c.range = GST_VIDEO_COLOR_RANGE_0_255;
831       c.matrix = GST_VIDEO_COLOR_MATRIX_BT709;
832       c.transfer = GST_VIDEO_TRANSFER_BT709;
833       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT709;
834       break;
835     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020:
836     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020:
837       c.range = GST_VIDEO_COLOR_RANGE_16_235;
838       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
839       if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) >= 12)
840         c.transfer = GST_VIDEO_TRANSFER_BT2020_12;
841       else
842         c.transfer = GST_VIDEO_TRANSFER_BT2020_10;
843       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
844       break;
845     case GST_DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020:
846       c.range = GST_VIDEO_COLOR_RANGE_0_255;
847       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
848       if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) >= 12)
849         c.transfer = GST_VIDEO_TRANSFER_BT2020_12;
850       else
851         c.transfer = GST_VIDEO_TRANSFER_BT2020_10;
852       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
853       break;
854     case GST_DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
855       c.range = GST_VIDEO_COLOR_RANGE_0_255;
856       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
857       c.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
858       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
859       break;
860     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020:
861       c.range = GST_VIDEO_COLOR_RANGE_16_235;
862       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
863       c.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
864       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
865       break;
866     case GST_DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020:
867       c.range = GST_VIDEO_COLOR_RANGE_16_235;
868       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
869       c.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
870       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
871       break;
872     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020:
873     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020:
874       c.range = GST_VIDEO_COLOR_RANGE_16_235;
875       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
876       if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) >= 12)
877         c.transfer = GST_VIDEO_TRANSFER_BT2020_12;
878       else
879         c.transfer = GST_VIDEO_TRANSFER_BT2020_10;
880       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
881       break;
882     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020:
883       c.range = GST_VIDEO_COLOR_RANGE_16_235;
884       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
885       c.transfer = GST_VIDEO_TRANSFER_SMPTE2084;
886       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
887       break;
888     case GST_DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
889       c.range = GST_VIDEO_COLOR_RANGE_0_255;
890       c.matrix = GST_VIDEO_COLOR_MATRIX_RGB;
891       if (GST_VIDEO_INFO_COMP_DEPTH (info, 0) >= 12)
892         c.transfer = GST_VIDEO_TRANSFER_BT2020_12;
893       else
894         c.transfer = GST_VIDEO_TRANSFER_BT2020_10;
895       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
896       break;
897     case GST_DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020:
898       c.range = GST_VIDEO_COLOR_RANGE_16_235;
899       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
900       c.transfer = GST_VIDEO_TRANSFER_ARIB_STD_B67;
901       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
902       break;
903     case GST_DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020:
904       c.range = GST_VIDEO_COLOR_RANGE_0_255;
905       c.matrix = GST_VIDEO_COLOR_MATRIX_BT2020;
906       c.transfer = GST_VIDEO_TRANSFER_ARIB_STD_B67;
907       c.primaries = GST_VIDEO_COLOR_PRIMARIES_BT2020;
908       break;
909     default:
910       GST_WARNING ("Unknown DXGI color space %d", type);
911       return FALSE;
912   }
913
914   info->colorimetry = c;
915
916   return TRUE;
917 }
918
919 gchar *
920 gst_d3d11_dump_color_matrix (GstD3D11ColorMatrix * matrix)
921 {
922   /* *INDENT-OFF* */
923   static const gchar format[] =
924       "[MATRIX]\n"
925       "|% .6f, % .6f, % .6f|\n"
926       "|% .6f, % .6f, % .6f|\n"
927       "|% .6f, % .6f, % .6f|\n"
928       "[OFFSET]\n"
929       "|% .6f, % .6f, % .6f|\n"
930       "[MIN]\n"
931       "|% .6f, % .6f, % .6f|\n"
932       "[MAX]\n"
933       "|% .6f, % .6f, % .6f|";
934   /* *INDENT-ON* */
935
936   g_return_val_if_fail (matrix != nullptr, nullptr);
937
938   return g_strdup_printf (format,
939       matrix->matrix[0][0], matrix->matrix[0][1], matrix->matrix[0][2],
940       matrix->matrix[1][0], matrix->matrix[1][1], matrix->matrix[1][2],
941       matrix->matrix[2][0], matrix->matrix[2][1], matrix->matrix[2][2],
942       matrix->offset[0], matrix->offset[1], matrix->offset[2],
943       matrix->min[0], matrix->min[1], matrix->min[2],
944       matrix->max[0], matrix->max[1], matrix->max[2]);
945 }
946
947 static void
948 color_matrix_copy (GstD3D11ColorMatrix * dst, const GstD3D11ColorMatrix * src)
949 {
950   for (guint i = 0; i < 3; i++) {
951     for (guint j = 0; j < 3; j++) {
952       dst->matrix[i][j] = src->matrix[i][j];
953     }
954   }
955 }
956
957 static void
958 color_matrix_multiply (GstD3D11ColorMatrix * dst, GstD3D11ColorMatrix * a,
959     GstD3D11ColorMatrix * b)
960 {
961   GstD3D11ColorMatrix tmp;
962
963   for (guint i = 0; i < 3; i++) {
964     for (guint j = 0; j < 3; j++) {
965       gdouble val = 0;
966       for (guint k = 0; k < 3; k++) {
967         val += a->matrix[i][k] * b->matrix[k][j];
968       }
969
970       tmp.matrix[i][j] = val;
971     }
972   }
973
974   color_matrix_copy (dst, &tmp);
975 }
976
977 static void
978 color_matrix_identity (GstD3D11ColorMatrix * m)
979 {
980   for (guint i = 0; i < 3; i++) {
981     for (guint j = 0; j < 3; j++) {
982       if (i == j)
983         m->matrix[i][j] = 1.0;
984       else
985         m->matrix[i][j] = 0;
986     }
987   }
988 }
989
990 static gboolean
991 color_matrix_invert (GstD3D11ColorMatrix * dst, GstD3D11ColorMatrix * src)
992 {
993   GstD3D11ColorMatrix tmp;
994   gdouble det;
995
996   color_matrix_identity (&tmp);
997   for (guint j = 0; j < 3; j++) {
998     for (guint i = 0; i < 3; i++) {
999       tmp.matrix[j][i] =
1000           src->matrix[(i + 1) % 3][(j + 1) % 3] *
1001           src->matrix[(i + 2) % 3][(j + 2) % 3] -
1002           src->matrix[(i + 1) % 3][(j + 2) % 3] *
1003           src->matrix[(i + 2) % 3][(j + 1) % 3];
1004     }
1005   }
1006
1007   det = tmp.matrix[0][0] * src->matrix[0][0] +
1008       tmp.matrix[0][1] * src->matrix[1][0] +
1009       tmp.matrix[0][2] * src->matrix[2][0];
1010   if (det == 0)
1011     return FALSE;
1012
1013   for (guint j = 0; j < 3; j++) {
1014     for (guint i = 0; i < 3; i++) {
1015       tmp.matrix[i][j] /= det;
1016     }
1017   }
1018
1019   color_matrix_copy (dst, &tmp);
1020
1021   return TRUE;
1022 }
1023
1024 /**
1025  * gst_d3d11_color_range_adjust_matrix_unorm:
1026  * @in_info: a #GstVideoInfo
1027  * @out_info: a #GstVideoInfo
1028  * @matrix: a #GstD3D11ColorMatrix
1029  *
1030  * Calculates matrix for color range adjustment. Both input and output
1031  * signals are in normalized [0.0..1.0] space.
1032  *
1033  * Resulting values can be calculated by
1034  * | Yout |                           | Yin |   | matrix.offset[0] |
1035  * | Uout | = clamp ( matrix.matrix * | Uin | + | matrix.offset[1] |, matrix.min, matrix.max )
1036  * | Vout |                           | Vin |   | matrix.offset[2] |
1037  *
1038  * Returns: %TRUE if successful
1039  */
1040 gboolean
1041 gst_d3d11_color_range_adjust_matrix_unorm (const GstVideoInfo * in_info,
1042     const GstVideoInfo * out_info, GstD3D11ColorMatrix * matrix)
1043 {
1044   gboolean in_rgb, out_rgb;
1045   gint in_offset[GST_VIDEO_MAX_COMPONENTS];
1046   gint in_scale[GST_VIDEO_MAX_COMPONENTS];
1047   gint out_offset[GST_VIDEO_MAX_COMPONENTS];
1048   gint out_scale[GST_VIDEO_MAX_COMPONENTS];
1049   GstVideoColorRange in_range;
1050   GstVideoColorRange out_range;
1051   gdouble src_fullscale, dst_fullscale;
1052
1053   g_return_val_if_fail (in_info != nullptr, FALSE);
1054   g_return_val_if_fail (out_info != nullptr, FALSE);
1055   g_return_val_if_fail (matrix != nullptr, FALSE);
1056
1057   memset (matrix, 0, sizeof (GstD3D11ColorMatrix));
1058   for (guint i = 0; i < 3; i++) {
1059     matrix->matrix[i][i] = 1.0;
1060     matrix->matrix[i][i] = 1.0;
1061     matrix->matrix[i][i] = 1.0;
1062     matrix->max[i] = 1.0;
1063   }
1064
1065   in_rgb = GST_VIDEO_INFO_IS_RGB (in_info);
1066   out_rgb = GST_VIDEO_INFO_IS_RGB (out_info);
1067
1068   if (in_rgb != out_rgb) {
1069     GST_WARNING ("Invalid format conversion");
1070     return FALSE;
1071   }
1072
1073   in_range = in_info->colorimetry.range;
1074   out_range = out_info->colorimetry.range;
1075
1076   if (in_range == GST_VIDEO_COLOR_RANGE_UNKNOWN) {
1077     GST_WARNING ("Unknown input color range");
1078     if (in_rgb || GST_VIDEO_INFO_IS_GRAY (in_info))
1079       in_range = GST_VIDEO_COLOR_RANGE_0_255;
1080     else
1081       in_range = GST_VIDEO_COLOR_RANGE_16_235;
1082   }
1083
1084   if (out_range == GST_VIDEO_COLOR_RANGE_UNKNOWN) {
1085     GST_WARNING ("Unknown output color range");
1086     if (out_rgb || GST_VIDEO_INFO_IS_GRAY (out_info))
1087       out_range = GST_VIDEO_COLOR_RANGE_0_255;
1088     else
1089       out_range = GST_VIDEO_COLOR_RANGE_16_235;
1090   }
1091
1092   src_fullscale = (gdouble) ((1 << in_info->finfo->depth[0]) - 1);
1093   dst_fullscale = (gdouble) ((1 << out_info->finfo->depth[0]) - 1);
1094
1095   gst_video_color_range_offsets (in_range, in_info->finfo, in_offset, in_scale);
1096   gst_video_color_range_offsets (out_range,
1097       out_info->finfo, out_offset, out_scale);
1098
1099   matrix->min[0] = matrix->min[1] = matrix->min[2] =
1100       (gdouble) out_offset[0] / dst_fullscale;
1101
1102   matrix->max[0] = (out_scale[0] + out_offset[0]) / dst_fullscale;
1103   matrix->max[1] = matrix->max[2] =
1104       (out_scale[1] + out_offset[0]) / dst_fullscale;
1105
1106   if (in_info->colorimetry.range == out_info->colorimetry.range) {
1107     GST_DEBUG ("Same color range");
1108     return TRUE;
1109   }
1110
1111   /* Formula
1112    *
1113    * 1) Scales and offset compensates input to [0..1] range
1114    * SRC_NORM[i] = (src[i] * src_fullscale - in_offset[i]) / in_scale[i]
1115    *             = (src[i] * src_fullscale / in_scale[i]) - in_offset[i] / in_scale[i]
1116    *
1117    * 2) Reverse to output UNIT scale
1118    * DST_UINT[i] = SRC_NORM[i] * out_scale[i] + out_offset[i]
1119    *             = src[i] * src_fullscale * out_scale[i] / in_scale[i]
1120    *               - in_offset[i] * out_scale[i] / in_scale[i]
1121    *               + out_offset[i]
1122    *
1123    * 3) Back to [0..1] scale
1124    * dst[i] = DST_UINT[i] / dst_fullscale
1125    *        = COEFF[i] * src[i] + OFF[i]
1126    * where
1127    *             src_fullscale * out_scale[i]
1128    * COEFF[i] = ------------------------------
1129    *             dst_fullscale * in_scale[i]
1130    *
1131    *            out_offset[i]     in_offset[i] * out_scale[i]
1132    * OFF[i] =  -------------- -  ------------------------------
1133    *            dst_fullscale     dst_fullscale * in_scale[i]
1134    */
1135   for (guint i = 0; i < 3; i++) {
1136     matrix->matrix[i][i] = (src_fullscale * out_scale[i]) /
1137         (dst_fullscale * in_scale[i]);
1138     matrix->offset[i] = (out_offset[i] / dst_fullscale) -
1139         ((gdouble) in_offset[i] * out_scale[i] / (dst_fullscale * in_scale[i]));
1140   }
1141
1142   return TRUE;
1143 }
1144
1145 /**
1146  * gst_d3d11_yuv_to_rgb_matrix_unorm:
1147  * @in_yuv_info: a #GstVideoInfo of input YUV signal
1148  * @out_rgb_info: a #GstVideoInfo of output RGB signal
1149  * @matrix: a #GstD3D11ColorMatrix
1150  *
1151  * Calculates transform matrix from YUV to RGB conversion. Both input and output
1152  * signals are in normalized [0.0..1.0] space and additional gamma decoding
1153  * or primary/transfer function transform is not performed by this matrix.
1154  *
1155  * Resulting non-linear RGB values can be calculated by
1156  * | R' |                           | Y' |   | matrix.offset[0] |
1157  * | G' | = clamp ( matrix.matrix * | Cb | + | matrix.offset[1] | matrix.min, matrix.max )
1158  * | B' |                           | Cr |   | matrix.offset[2] |
1159  *
1160  * Returns: %TRUE if successful
1161  */
1162 gboolean
1163 gst_d3d11_yuv_to_rgb_matrix_unorm (const GstVideoInfo * in_yuv_info,
1164     const GstVideoInfo * out_rgb_info, GstD3D11ColorMatrix * matrix)
1165 {
1166   gint offset[4], scale[4];
1167   gdouble Kr, Kb, Kg;
1168
1169   g_return_val_if_fail (in_yuv_info != nullptr, FALSE);
1170   g_return_val_if_fail (out_rgb_info != nullptr, FALSE);
1171   g_return_val_if_fail (matrix != nullptr, FALSE);
1172
1173   /*
1174    * <Formula>
1175    *
1176    * Input: Unsigned normalized Y'CbCr(unorm), [0.0..1.0] range
1177    * Output: Unsigned normalized non-linear R'G'B'(unorm), [0.0..1.0] range
1178    *
1179    * 1) Y'CbCr(unorm) to scaled Y'CbCr
1180    * | Y' |     | Y'(unorm) |
1181    * | Cb | = S | Cb(unorm) |
1182    * | Cb |     | Cr(unorm) |
1183    * where S = (2 ^ bitdepth) - 1
1184    *
1185    * 2) Y'CbCr to YPbPr
1186    * Y  = (Y' - offsetY )    / scaleY
1187    * Pb = [(Cb - offsetCbCr) / scaleCbCr]
1188    * Pr = [(Cr - offsetCrCr) / scaleCrCr]
1189    * =>
1190    * Y  = Y'(unorm) * Sy  + Oy
1191    * Pb = Cb(unorm) * Suv + Ouv
1192    * Pb = Cr(unorm) * Suv + Ouv
1193    * where
1194    * Sy  = S / scaleY
1195    * Suv = S / scaleCbCr
1196    * Oy  = -(offsetY / scaleY)
1197    * Ouv = -(offsetCbCr / scaleCbCr)
1198    *
1199    * 3) YPbPr to R'G'B'
1200    * | R' |      | Y  |
1201    * | G' | = M *| Pb |
1202    * | B' |      | Pr |
1203    * where
1204    *     | vecR |
1205    * M = | vecG |
1206    *     | vecB |
1207    * vecR = | 1,         0           ,       2(1 - Kr)      |
1208    * vecG = | 1, -(Kb/Kg) * 2(1 - Kb), -(Kr/Kg) * 2(1 - Kr) |
1209    * vecB = | 1,       2(1 - Kb)     ,          0           |
1210    * =>
1211    * R' = dot(vecR, (Syuv * Y'CbCr(unorm))) + dot(vecR, Offset)
1212    * G' = dot(vecG, (Svuy * Y'CbCr(unorm))) + dot(vecG, Offset)
1213    * B' = dot(vecB, (Syuv * Y'CbCr(unorm)) + dot(vecB, Offset)
1214    * where
1215    *        | Sy,   0,   0 |
1216    * Syuv = |  0, Suv,   0 |
1217    *        |  0    0, Suv |
1218    *
1219    *          | Oy  |
1220    * Offset = | Ouv |
1221    *          | Ouv |
1222    *
1223    * 4) YUV -> RGB matrix
1224    * | R' |            | Y'(unorm) |   | offsetA |
1225    * | G' | = Matrix * | Cb(unorm) | + | offsetB |
1226    * | B' |            | Cr(unorm) |   | offsetC |
1227    *
1228    * where
1229    *          | vecR |
1230    * Matrix = | vecG | * Syuv
1231    *          | vecB |
1232    *
1233    * offsetA = dot(vecR, Offset)
1234    * offsetB = dot(vecG, Offset)
1235    * offsetC = dot(vecB, Offset)
1236    *
1237    * 4) Consider 16-235 scale RGB
1238    * RGBfull(0..255) -> RGBfull(16..235) matrix is represented by
1239    * | Rs |      | Rf |   | Or |
1240    * | Gs | = Ms | Gf | + | Og |
1241    * | Bs |      | Bf |   | Ob |
1242    *
1243    * Combining all matrix into
1244    * | Rs |                   | Y'(unorm) |   | offsetA |     | Or |
1245    * | Gs | = Ms * ( Matrix * | Cb(unorm) | + | offsetB | ) + | Og |
1246    * | Bs |                   | Cr(unorm) |   | offsetC |     | Ob |
1247    *
1248    *                        | Y'(unorm) |      | offsetA |   | Or |
1249    *        = Ms * Matrix * | Cb(unorm) | + Ms | offsetB | + | Og |
1250    *                        | Cr(unorm) |      | offsetC |   | Ob |
1251    */
1252
1253   memset (matrix, 0, sizeof (GstD3D11ColorMatrix));
1254   for (guint i = 0; i < 3; i++)
1255     matrix->max[i] = 1.0;
1256
1257   gst_video_color_range_offsets (in_yuv_info->colorimetry.range,
1258       in_yuv_info->finfo, offset, scale);
1259
1260   if (gst_video_color_matrix_get_Kr_Kb (in_yuv_info->colorimetry.matrix,
1261           &Kr, &Kb)) {
1262     guint S;
1263     gdouble Sy, Suv;
1264     gdouble Oy, Ouv;
1265     gdouble vecR[3], vecG[3], vecB[3];
1266
1267     Kg = 1.0 - Kr - Kb;
1268
1269     vecR[0] = 1.0;
1270     vecR[1] = 0;
1271     vecR[2] = 2 * (1 - Kr);
1272
1273     vecG[0] = 1.0;
1274     vecG[1] = -(Kb / Kg) * 2 * (1 - Kb);
1275     vecG[2] = -(Kr / Kg) * 2 * (1 - Kr);
1276
1277     vecB[0] = 1.0;
1278     vecB[1] = 2 * (1 - Kb);
1279     vecB[2] = 0;
1280
1281     /* Assume all components has the same bitdepth */
1282     S = (1 << in_yuv_info->finfo->depth[0]) - 1;
1283     Sy = (gdouble) S / scale[0];
1284     Suv = (gdouble) S / scale[1];
1285     Oy = -((gdouble) offset[0] / scale[0]);
1286     Ouv = -((gdouble) offset[1] / scale[1]);
1287
1288     matrix->matrix[0][0] = Sy * vecR[0];
1289     matrix->matrix[1][0] = Sy * vecG[0];
1290     matrix->matrix[2][0] = Sy * vecB[0];
1291
1292     matrix->matrix[0][1] = Suv * vecR[1];
1293     matrix->matrix[1][1] = Suv * vecG[1];
1294     matrix->matrix[2][1] = Suv * vecB[1];
1295
1296     matrix->matrix[0][2] = Suv * vecR[2];
1297     matrix->matrix[1][2] = Suv * vecG[2];
1298     matrix->matrix[2][2] = Suv * vecB[2];
1299
1300     matrix->offset[0] = vecR[0] * Oy + vecR[1] * Ouv + vecR[2] * Ouv;
1301     matrix->offset[1] = vecG[0] * Oy + vecG[1] * Ouv + vecG[2] * Ouv;
1302     matrix->offset[2] = vecB[0] * Oy + vecB[1] * Ouv + vecB[2] * Ouv;
1303
1304     /* Apply RGB range scale matrix */
1305     if (out_rgb_info->colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) {
1306       GstD3D11ColorMatrix scale_matrix, rst;
1307       GstVideoInfo full_rgb = *out_rgb_info;
1308
1309       full_rgb.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
1310
1311       if (gst_d3d11_color_range_adjust_matrix_unorm (&full_rgb,
1312               out_rgb_info, &scale_matrix)) {
1313         /* Ms * Matrix */
1314         color_matrix_multiply (&rst, &scale_matrix, matrix);
1315
1316         /* Ms * transform offsets */
1317         for (guint i = 0; i < 3; i++) {
1318           gdouble val = 0;
1319           for (guint j = 0; j < 3; j++) {
1320             val += scale_matrix.matrix[i][j] * matrix->offset[j];
1321           }
1322           rst.offset[i] = val + scale_matrix.offset[i];
1323         }
1324
1325         /* copy back to output matrix */
1326         for (guint i = 0; i < 3; i++) {
1327           for (guint j = 0; j < 3; j++) {
1328             matrix->matrix[i][j] = rst.matrix[i][j];
1329           }
1330           matrix->offset[i] = rst.offset[i];
1331           matrix->min[i] = scale_matrix.min[i];
1332           matrix->max[i] = scale_matrix.max[i];
1333         }
1334       }
1335     }
1336   } else {
1337     /* Unknown matrix */
1338     matrix->matrix[0][0] = 1.0;
1339     matrix->matrix[1][1] = 1.0;
1340     matrix->matrix[2][2] = 1.0;
1341   }
1342
1343   return TRUE;
1344 }
1345
1346 /**
1347  * gst_d3d11_rgb_to_yuv_matrix_unorm:
1348  * @in_rgb_info: a #GstVideoInfo of input RGB signal
1349  * @out_yuv_info: a #GstVideoInfo of output YUV signal
1350  * @matrix: a #GstD3D11ColorMatrix
1351  *
1352  * Calculates transform matrix from RGB to YUV conversion. Both input and output
1353  * signals are in normalized [0.0..1.0] space and additional gamma decoding
1354  * or primary/transfer function transform is not performed by this matrix.
1355  *
1356  * Resulting RGB values can be calculated by
1357  * | Y' |                           | R' |   | matrix.offset[0] |
1358  * | Cb | = clamp ( matrix.matrix * | G' | + | matrix.offset[1] |, matrix.min, matrix.max )
1359  * | Cr |                           | B' |   | matrix.offset[2] |
1360  *
1361  * Returns: %TRUE if successful
1362  */
1363 gboolean
1364 gst_d3d11_rgb_to_yuv_matrix_unorm (const GstVideoInfo * in_rgb_info,
1365     const GstVideoInfo * out_yuv_info, GstD3D11ColorMatrix * matrix)
1366 {
1367   gint offset[4], scale[4];
1368   gdouble Kr, Kb, Kg;
1369
1370   g_return_val_if_fail (in_rgb_info != nullptr, FALSE);
1371   g_return_val_if_fail (out_yuv_info != nullptr, FALSE);
1372   g_return_val_if_fail (matrix != nullptr, FALSE);
1373
1374   /*
1375    * <Formula>
1376    *
1377    * Input: Unsigned normalized non-linear R'G'B'(unorm), [0.0..1.0] range
1378    * Output: Unsigned normalized Y'CbCr(unorm), [0.0..1.0] range
1379    *
1380    * 1) R'G'B' to YPbPr
1381    * | Y  |      | R' |
1382    * | Pb | = M *| G' |
1383    * | Pr |      | B' |
1384    * where
1385    *     | vecY |
1386    * M = | vecU |
1387    *     | vecV |
1388    * vecY = |       Kr      ,       Kg      ,      Kb       |
1389    * vecU = | -0.5*Kr/(1-Kb), -0.5*Kg/(1-Kb),     0.5       |
1390    * vecV = |      0.5      , -0.5*Kg/(1-Kr), -0.5*Kb(1-Kr) |
1391    *
1392    * 2) YPbPr to Y'CbCr(unorm)
1393    * Y'(unorm) = (Y  * scaleY + offsetY)       / S
1394    * Cb(unorm) = (Pb * scaleCbCr + offsetCbCr) / S
1395    * Cr(unorm) = (Pr * scaleCbCr + offsetCbCr) / S
1396    * =>
1397    * Y'(unorm) = (Y  * scaleY    / S) + (offsetY    / S)
1398    * Cb(unorm) = (Pb * scaleCbCr / S) + (offsetCbCr / S)
1399    * Cr(unorm) = (Pb * scaleCbCr / S) + (offsetCbCr / S)
1400    * where S = (2 ^ bitdepth) - 1
1401    *
1402    * 3) RGB -> YUV matrix
1403    * | Y'(unorm) |            | R' |   | offsetA |
1404    * | Cb(unorm) | = Matrix * | G' | + | offsetB |
1405    * | Cr(unorm) |            | B' |   | offsetC |
1406    *
1407    * where
1408    *          | (scaleY/S)    * vecY |
1409    * Matrix = | (scaleCbCr/S) * vecU |
1410    *          | (scaleCbCr/S) * vecV |
1411    *
1412    * offsetA = offsetY    / S
1413    * offsetB = offsetCbCr / S
1414    * offsetC = offsetCbCr / S
1415    *
1416    * 4) Consider 16-235 scale RGB
1417    * RGBstudio(16..235) -> RGBfull(0..255) matrix is represented by
1418    * | Rf |      | Rs |   | Or |
1419    * | Gf | = Ms | Gs | + | Og |
1420    * | Bf |      | Bs |   | Ob |
1421    *
1422    * Combining all matrix into
1423    * | Y'(unorm) |                 | Rs |   | Or |     | offsetA |
1424    * | Cb(unorm) | = Matrix * ( Ms | Gs | + | Og | ) + | offsetB |
1425    * | Cr(unorm) |                 | Bs |   | Ob |     | offsetC |
1426    *
1427    *                             | Rs |          | Or |   | offsetA |
1428    *               = Matrix * Ms | Gs | + Matrix | Og | + | offsetB |
1429    *                             | Bs |          | Ob |   | offsetB |
1430    */
1431
1432   memset (matrix, 0, sizeof (GstD3D11ColorMatrix));
1433   for (guint i = 0; i < 3; i++)
1434     matrix->max[i] = 1.0;
1435
1436   gst_video_color_range_offsets (out_yuv_info->colorimetry.range,
1437       out_yuv_info->finfo, offset, scale);
1438
1439   if (gst_video_color_matrix_get_Kr_Kb (out_yuv_info->colorimetry.matrix,
1440           &Kr, &Kb)) {
1441     guint S;
1442     gdouble Sy, Suv;
1443     gdouble Oy, Ouv;
1444     gdouble vecY[3], vecU[3], vecV[3];
1445
1446     Kg = 1.0 - Kr - Kb;
1447
1448     vecY[0] = Kr;
1449     vecY[1] = Kg;
1450     vecY[2] = Kb;
1451
1452     vecU[0] = -0.5 * Kr / (1 - Kb);
1453     vecU[1] = -0.5 * Kg / (1 - Kb);
1454     vecU[2] = 0.5;
1455
1456     vecV[0] = 0.5;
1457     vecV[1] = -0.5 * Kg / (1 - Kr);
1458     vecV[2] = -0.5 * Kb / (1 - Kr);
1459
1460     /* Assume all components has the same bitdepth */
1461     S = (1 << out_yuv_info->finfo->depth[0]) - 1;
1462     Sy = (gdouble) scale[0] / S;
1463     Suv = (gdouble) scale[1] / S;
1464     Oy = (gdouble) offset[0] / S;
1465     Ouv = (gdouble) offset[1] / S;
1466
1467     for (guint i = 0; i < 3; i++) {
1468       matrix->matrix[0][i] = Sy * vecY[i];
1469       matrix->matrix[1][i] = Suv * vecU[i];
1470       matrix->matrix[2][i] = Suv * vecV[i];
1471     }
1472
1473     matrix->offset[0] = Oy;
1474     matrix->offset[1] = Ouv;
1475     matrix->offset[2] = Ouv;
1476
1477     matrix->min[0] = Oy;
1478     matrix->min[1] = Oy;
1479     matrix->min[2] = Oy;
1480
1481     matrix->max[0] = ((gdouble) scale[0] + offset[0]) / S;
1482     matrix->max[1] = ((gdouble) scale[1] + offset[0]) / S;
1483     matrix->max[2] = ((gdouble) scale[1] + offset[0]) / S;
1484
1485     /* Apply RGB range scale matrix */
1486     if (in_rgb_info->colorimetry.range == GST_VIDEO_COLOR_RANGE_16_235) {
1487       GstD3D11ColorMatrix scale_matrix, rst;
1488       GstVideoInfo full_rgb = *in_rgb_info;
1489
1490       full_rgb.colorimetry.range = GST_VIDEO_COLOR_RANGE_0_255;
1491
1492       if (gst_d3d11_color_range_adjust_matrix_unorm (in_rgb_info,
1493               &full_rgb, &scale_matrix)) {
1494         /* Matrix * Ms */
1495         color_matrix_multiply (&rst, matrix, &scale_matrix);
1496
1497         /* Matrix * scale offsets */
1498         for (guint i = 0; i < 3; i++) {
1499           gdouble val = 0;
1500           for (guint j = 0; j < 3; j++) {
1501             val += matrix->matrix[i][j] * scale_matrix.offset[j];
1502           }
1503           rst.offset[i] = val + matrix->offset[i];
1504         }
1505
1506         /* copy back to output matrix */
1507         for (guint i = 0; i < 3; i++) {
1508           for (guint j = 0; j < 3; j++) {
1509             matrix->matrix[i][j] = rst.matrix[i][j];
1510           }
1511           matrix->offset[i] = rst.offset[i];
1512         }
1513       }
1514     }
1515   } else {
1516     /* Unknown matrix */
1517     matrix->matrix[0][0] = 1.0;
1518     matrix->matrix[1][1] = 1.0;
1519     matrix->matrix[2][2] = 1.0;
1520   }
1521
1522   return TRUE;
1523 }
1524
1525 static gboolean
1526 rgb_to_xyz_matrix (const GstVideoColorPrimariesInfo * info,
1527     GstD3D11ColorMatrix * matrix)
1528 {
1529   GstD3D11ColorMatrix m, im;
1530   gdouble Sr, Sg, Sb;
1531   gdouble Xw, Yw, Zw;
1532
1533   if (info->Rx == 0 || info->Gx == 0 || info->By == 0 || info->Wy == 0)
1534     return FALSE;
1535
1536   color_matrix_identity (&m);
1537
1538   m.matrix[0][0] = info->Rx / info->Ry;
1539   m.matrix[1][0] = 1.0;
1540   m.matrix[2][0] = (1.0 - info->Rx - info->Ry) / info->Ry;
1541
1542   m.matrix[0][1] = info->Gx / info->Gy;
1543   m.matrix[1][1] = 1.0;
1544   m.matrix[2][1] = (1.0 - info->Gx - info->Gy) / info->Gy;
1545
1546   m.matrix[0][2] = info->Bx / info->By;
1547   m.matrix[1][2] = 1.0;
1548   m.matrix[2][2] = (1.0 - info->Bx - info->By) / info->By;
1549
1550   if (!color_matrix_invert (&im, &m))
1551     return FALSE;
1552
1553   Xw = info->Wx / info->Wy;
1554   Yw = 1.0;
1555   Zw = (1.0 - info->Wx - info->Wy) / info->Wy;
1556
1557   Sr = im.matrix[0][0] * Xw + im.matrix[0][1] * Yw + im.matrix[0][2] * Zw;
1558   Sg = im.matrix[1][0] * Xw + im.matrix[1][1] * Yw + im.matrix[1][2] * Zw;
1559   Sb = im.matrix[2][0] * Xw + im.matrix[2][1] * Yw + im.matrix[2][2] * Zw;
1560
1561   for (guint i = 0; i < 3; i++) {
1562     m.matrix[i][0] *= Sr;
1563     m.matrix[i][1] *= Sg;
1564     m.matrix[i][2] *= Sb;
1565   }
1566
1567   color_matrix_copy (matrix, &m);
1568
1569   return TRUE;
1570 }
1571
1572 /**
1573  * gst_d3d11_color_primaries_matrix_unorm:
1574  * @in_info: a #GstVideoColorPrimariesInfo of input signal
1575  * @out_info: a #GstVideoColorPrimariesInfo of output signal
1576  * @matrix: a #GstD3D11ColorMatrix
1577  *
1578  * Calculates color primaries conversion matrix
1579  *
1580  * Resulting RGB values can be calculated by
1581  * | Rout |                              | Rin |
1582  * | Gout | = saturate ( matrix.matrix * | Gin | )
1583  * | Bout |                              | Bin |
1584  *
1585  * Returns: %TRUE if successful
1586  */
1587 gboolean
1588 gst_d3d11_color_primaries_matrix_unorm (const GstVideoColorPrimariesInfo *
1589     in_info, const GstVideoColorPrimariesInfo * out_info,
1590     GstD3D11ColorMatrix * matrix)
1591 {
1592   GstD3D11ColorMatrix Ms, invMd, ret;
1593
1594   g_return_val_if_fail (in_info != nullptr, FALSE);
1595   g_return_val_if_fail (out_info != nullptr, FALSE);
1596   g_return_val_if_fail (matrix != nullptr, FALSE);
1597
1598   /*
1599    * <Formula>
1600    *
1601    * 1) RGB -> XYZ conversion
1602    * | X |     | R |
1603    * | Y | = M | G |
1604    * | Z |     | B |
1605    * where
1606    *     | SrXr, SgXg, SbXb |
1607    * M = | SrYr, SgYg, SbYb |
1608    *     | SrZr, SgZg, SbZb |
1609    *
1610    * Xr = xr / yr
1611    * Yr = 1
1612    * Zr = (1 - xr - yr) / yr
1613    * xr and yr are xy coordinates of red primary in the CIE 1931 color space.
1614    * And its applied to G and B components
1615    *
1616    * | Sr |        | Xr, Xg, Xb |     | Xw |
1617    * | Sg | = inv( | Yr, Yg, Yb | ) * | Yw |
1618    * | Sb |        | Zr, Zg, Zb |     | Zw |
1619    *
1620    * 2) XYZsrc -> XYZdst conversion
1621    * Apply chromatic adaptation
1622    * | Xdst |      | Xsrc |
1623    * | Ydst | = Mc | Ysrc |
1624    * | Zdst |      | Zsrc |
1625    * where
1626    *      | Xwdst / Xwsrc,       0      ,       0       |
1627    * Mc = |       0      , Ywdst / Ywsrc,       0       |
1628    *      |       0      ,       0      , Zwdst / Zwsrc |
1629    *
1630    * where
1631    *
1632    * 3) Final matrix
1633    * | Rd |                      | Rs |
1634    * | Gd | = inv (Md) * Mc * Ms | Gs |
1635    * | Bd |                      | Bs |
1636    */
1637
1638   memset (matrix, 0, sizeof (GstD3D11ColorMatrix));
1639   for (guint i = 0; i < 3; i++)
1640     matrix->max[i] = 1.0;
1641
1642   if (!rgb_to_xyz_matrix (in_info, &Ms)) {
1643     GST_WARNING ("Failed to get src XYZ matrix");
1644     return FALSE;
1645   }
1646
1647   if (!rgb_to_xyz_matrix (out_info, &invMd) ||
1648       !color_matrix_invert (&invMd, &invMd)) {
1649     GST_WARNING ("Failed to get dst XYZ matrix");
1650     return FALSE;
1651   }
1652
1653   if (in_info->Wx != out_info->Wx || in_info->Wy != out_info->Wy) {
1654     GstD3D11ColorMatrix Mc;
1655
1656     color_matrix_identity (&Mc);
1657     Mc.matrix[0][0] = (out_info->Wx / out_info->Wy) /
1658         (in_info->Wx / in_info->Wy);
1659     /* Yw == 1.0 */
1660     Mc.matrix[2][2] = ((1.0 - out_info->Wx - out_info->Wy) / out_info->Wy) /
1661         ((1.0 - in_info->Wx - in_info->Wy) / in_info->Wy);
1662
1663     color_matrix_multiply (&ret, &Mc, &Ms);
1664   } else {
1665     color_matrix_copy (&ret, &Ms);
1666   }
1667
1668   color_matrix_multiply (&ret, &invMd, &ret);
1669   color_matrix_copy (matrix, &ret);
1670
1671   return TRUE;
1672 }