[vlc-commits] d3d11_shaders: add a color primaries conversion in the pixel shader

Steve Lhomme git at videolan.org
Wed Oct 17 16:07:50 CEST 2018


vlc/vlc-3.0 | branch: master | Steve Lhomme <robux4 at ycbcr.xyz> | Fri Oct 12 15:19:14 2018 +0200| [f13e39b3d68ee9317cecaa73bb8e69abd8619124] | committer: Steve Lhomme

d3d11_shaders: add a color primaries conversion in the pixel shader

To convert pixel colors from one colorspace to another.

Using this math http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
And similar to the one from libplacebo:
https://github.com/haasn/libplacebo/blob/b6104e121006290f146177e1847ccd7826d2b6f6/src/colorspace.c#L710

(cherry picked from commit 49eb718bc5c3a61fa0b4cbe203e0d2cd7e24e4ea)

> http://git.videolan.org/gitweb.cgi/vlc/vlc-3.0.git/?a=commit;h=f13e39b3d68ee9317cecaa73bb8e69abd8619124
---

 modules/video_output/win32/d3d11_quad.c    | 209 +++++++++++++++++++++++++++++
 modules/video_output/win32/d3d11_shaders.c |   5 +-
 modules/video_output/win32/d3d11_shaders.h |   3 +-
 3 files changed, 214 insertions(+), 3 deletions(-)

diff --git a/modules/video_output/win32/d3d11_quad.c b/modules/video_output/win32/d3d11_quad.c
index d9eea3a1cc..c3d1369aaa 100644
--- a/modules/video_output/win32/d3d11_quad.c
+++ b/modules/video_output/win32/d3d11_quad.c
@@ -612,6 +612,209 @@ void D3D11_UpdateQuadLuminanceScale(vlc_object_t *o, d3d11_device_t *d3d_dev, d3
         quad->shaderConstants.LuminanceScale = old;
 }
 
+struct xy_primary {
+    double x, y;
+};
+
+struct cie1931_primaries {
+    struct xy_primary red, green, blue, white;
+};
+
+static const struct cie1931_primaries STANDARD_PRIMARIES[] = {
+#define CIE_D65 {0.31271, 0.32902}
+#define CIE_C   {0.31006, 0.31616}
+
+    [COLOR_PRIMARIES_BT601_525] = {
+        .red   = {0.630, 0.340},
+        .green = {0.310, 0.595},
+        .blue  = {0.155, 0.070},
+        .white = CIE_D65
+    },
+    [COLOR_PRIMARIES_BT601_625] = {
+        .red   = {0.640, 0.330},
+        .green = {0.290, 0.600},
+        .blue  = {0.150, 0.060},
+        .white = CIE_D65
+    },
+    [COLOR_PRIMARIES_BT709] = {
+        .red   = {0.640, 0.330},
+        .green = {0.300, 0.600},
+        .blue  = {0.150, 0.060},
+        .white = CIE_D65
+    },
+    [COLOR_PRIMARIES_BT2020] = {
+        .red   = {0.708, 0.292},
+        .green = {0.170, 0.797},
+        .blue  = {0.131, 0.046},
+        .white = CIE_D65
+    },
+    [COLOR_PRIMARIES_DCI_P3] = {
+        .red   = {0.680, 0.320},
+        .green = {0.265, 0.690},
+        .blue  = {0.150, 0.060},
+        .white = CIE_D65
+    },
+    [COLOR_PRIMARIES_FCC1953] = {
+        .red   = {0.670, 0.330},
+        .green = {0.210, 0.710},
+        .blue  = {0.140, 0.080},
+        .white = CIE_C
+    },
+#undef CIE_D65
+#undef CIE_C
+};
+
+static void ChromaticAdaptation(const struct xy_primary *src_white,
+                                const struct xy_primary *dst_white,
+                                double in_out[3 * 3])
+{
+    if (fabs(src_white->x - dst_white->x) < 1e-6 &&
+        fabs(src_white->y - dst_white->y) < 1e-6)
+        return;
+
+    /* TODO, see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html */
+}
+
+static void Float3x3Inverse(double in_out[3 * 3])
+{
+    double m00 = in_out[0 + 0*3], m01 = in_out[1 + 0*3], m02 = in_out[2 + 0*3],
+          m10 = in_out[0 + 1*3], m11 = in_out[1 + 1*3], m12 = in_out[2 + 1*3],
+          m20 = in_out[0 + 2*3], m21 = in_out[1 + 2*3], m22 = in_out[2 + 2*3];
+
+    // calculate the adjoint
+    in_out[0 + 0*3] =  (m11 * m22 - m21 * m12);
+    in_out[1 + 0*3] = -(m01 * m22 - m21 * m02);
+    in_out[2 + 0*3] =  (m01 * m12 - m11 * m02);
+    in_out[0 + 1*3] = -(m10 * m22 - m20 * m12);
+    in_out[1 + 1*3] =  (m00 * m22 - m20 * m02);
+    in_out[2 + 1*3] = -(m00 * m12 - m10 * m02);
+    in_out[0 + 2*3] =  (m10 * m21 - m20 * m11);
+    in_out[1 + 2*3] = -(m00 * m21 - m20 * m01);
+    in_out[2 + 2*3] =  (m00 * m11 - m10 * m01);
+
+    // calculate the determinant (as inverse == 1/det * adjoint,
+    // adjoint * m == identity * det, so this calculates the det)
+    double det = m00 * in_out[0 + 0*3] + m10 * in_out[1 + 0*3] + m20 * in_out[2 + 0*3];
+    det = 1.0f / det;
+
+    for (int i = 0; i < 3; i++) {
+        for (int j = 0; j < 3; j++)
+            in_out[j + i*3] *= det;
+    }
+}
+
+static void Float3x3Multiply(double m1[3 * 3], const double m2[3 * 3])
+{
+    double a00 = m1[0 + 0*3], a01 = m1[1 + 0*3], a02 = m1[2 + 0*3],
+           a10 = m1[0 + 1*3], a11 = m1[1 + 1*3], a12 = m1[2 + 1*3],
+           a20 = m1[0 + 2*3], a21 = m1[1 + 2*3], a22 = m1[2 + 2*3];
+
+    for (int i = 0; i < 3; i++) {
+        m1[i + 0*3] = a00 * m2[i + 0*3] + a01 * m2[i + 1*3] + a02 * m2[i + 2*3];
+        m1[i + 1*3] = a10 * m2[i + 0*3] + a11 * m2[i + 1*3] + a12 * m2[i + 2*3];
+        m1[i + 2*3] = a20 * m2[i + 0*3] + a21 * m2[i + 1*3] + a22 * m2[i + 2*3];
+    }
+}
+
+static void Float3Multiply(const double in[3], const double mult[3 * 3], double out[3])
+{
+    for (size_t i=0; i<3; i++)
+    {
+        out[i] = mult[i + 0*3] * in[0] +
+                 mult[i + 1*3] * in[1] +
+                 mult[i + 2*3] * in[2];
+    }
+}
+
+/* from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html */
+static void GetRGB2XYZMatrix(const struct cie1931_primaries *primaries,
+                             double out[3 * 3])
+{
+#define RED   0
+#define GREEN 1
+#define BLUE  2
+    double X[3], Y[3], Z[3], S[3], W[3];
+    double W_TO_S[3 * 3];
+
+    X[RED  ] = primaries->red.x / primaries->red.y;
+    X[GREEN] = 1;
+    X[BLUE ] = (1 - primaries->red.x - primaries->red.y) / primaries->red.y;
+
+    Y[RED  ] = primaries->green.x / primaries->green.y;
+    Y[GREEN] = 1;
+    Y[BLUE ] = (1 - primaries->green.x - primaries->green.y) / primaries->green.y;
+
+    Z[RED  ] = primaries->blue.x / primaries->blue.y;
+    Z[GREEN] = 1;
+    Z[BLUE ] = (1 - primaries->blue.x - primaries->blue.y) / primaries->blue.y;
+
+    W_TO_S[0 + 0*3] = X[RED  ];
+    W_TO_S[1 + 0*3] = X[GREEN];
+    W_TO_S[2 + 0*3] = X[BLUE ];
+    W_TO_S[0 + 1*3] = Y[RED  ];
+    W_TO_S[1 + 1*3] = Y[GREEN];
+    W_TO_S[2 + 1*3] = Y[BLUE ];
+    W_TO_S[0 + 2*3] = Z[RED  ];
+    W_TO_S[1 + 2*3] = Z[GREEN];
+    W_TO_S[2 + 2*3] = Z[BLUE ];
+
+    Float3x3Inverse(W_TO_S);
+
+    W[0] = primaries->white.x / primaries->white.y; /* Xw */
+    W[1] = 1;                  /* Yw */
+    W[2] = (1 - primaries->white.x - primaries->white.y) / primaries->white.y; /* Yw */
+
+    Float3Multiply(W, W_TO_S, S);
+
+    out[0 + 0*3] = S[RED  ] * X[RED  ];
+    out[1 + 0*3] = S[GREEN] * Y[RED  ];
+    out[2 + 0*3] = S[BLUE ] * Z[RED  ];
+    out[0 + 1*3] = S[RED  ] * X[GREEN];
+    out[1 + 1*3] = S[GREEN] * Y[GREEN];
+    out[2 + 1*3] = S[BLUE ] * Z[GREEN];
+    out[0 + 2*3] = S[RED  ] * X[BLUE ];
+    out[1 + 2*3] = S[GREEN] * Y[BLUE ];
+    out[2 + 2*3] = S[BLUE ] * Z[BLUE ];
+#undef RED
+#undef GREEN
+#undef BLUE
+}
+
+/* from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html */
+static void GetXYZ2RGBMatrix(const struct cie1931_primaries *primaries,
+                             double out[3 * 3])
+{
+    GetRGB2XYZMatrix(primaries, out);
+    Float3x3Inverse(out);
+}
+
+static void GetPrimariesTransform(FLOAT Primaries[4*4], video_color_primaries_t src,
+                                  video_color_primaries_t dst)
+{
+    const struct cie1931_primaries *p_src = &STANDARD_PRIMARIES[src];
+    const struct cie1931_primaries *p_dst = &STANDARD_PRIMARIES[dst];
+    double rgb2xyz[3 * 3], xyz2rgb[3 * 3];
+
+    /* src[RGB] -> src[XYZ] */
+    GetRGB2XYZMatrix(p_src, rgb2xyz);
+
+    /* src[XYZ] -> dst[XYZ] */
+    ChromaticAdaptation(&p_src->white, &p_dst->white, rgb2xyz);
+
+    /* dst[XYZ] -> dst[RGB] */
+    GetXYZ2RGBMatrix(p_dst, xyz2rgb);
+
+    /* src[RGB] -> src[XYZ] -> dst[XYZ] -> dst[RGB] */
+    Float3x3Multiply(xyz2rgb, rgb2xyz);
+
+    for (size_t i=0;i<4; ++i)
+    {
+        for (size_t j=0;j<3; ++j)
+            Primaries[j + i*4] = xyz2rgb[j + i*3];
+        Primaries[3 + i*4] = i == 3;
+    }
+}
+
 #undef D3D11_SetupQuad
 int D3D11_SetupQuad(vlc_object_t *o, d3d11_device_t *d3d_dev, const video_format_t *fmt, d3d_quad_t *quad,
                     const display_info_t *displayFormat, const RECT *output,
@@ -741,6 +944,12 @@ int D3D11_SetupQuad(vlc_object_t *o, d3d11_device_t *d3d_dev, const video_format
 
     memcpy(colorspace.Colorspace, ppColorspace, sizeof(colorspace.Colorspace));
 
+    if (fmt->primaries != displayFormat->colorspace->primaries)
+    {
+        GetPrimariesTransform(colorspace.Primaries, fmt->primaries,
+                              displayFormat->colorspace->primaries);
+    }
+
     constantInit.pSysMem = &colorspace;
 
     static_assert((sizeof(PS_COLOR_TRANSFORM)%16)==0,"Constant buffers require 16-byte alignment");
diff --git a/modules/video_output/win32/d3d11_shaders.c b/modules/video_output/win32/d3d11_shaders.c
index e948aadee2..8b9d197416 100644
--- a/modules/video_output/win32/d3d11_shaders.c
+++ b/modules/video_output/win32/d3d11_shaders.c
@@ -63,7 +63,7 @@ static const char* globPixelShaderDefault = "\
   {\n\
     float4x4 WhitePoint;\n\
     float4x4 Colorspace;\n\
-    float3x3 Primaries;\n\
+    float4x4 Primaries;\n\
   };\n\
   Texture2D%s shaderTexture[" STRINGIZE(D3D11_MAX_SHADER_VIEW) "];\n\
   SamplerState SamplerStates[2];\n\
@@ -353,6 +353,9 @@ HRESULT D3D11_CompilePixelShader(vlc_object_t *o, d3d11_handle_t *hd3d, bool leg
         }
     }
 
+    if (display->colorspace->primaries != primaries)
+        psz_primaries_transform = "return mul(rgb, Primaries)";
+
     int range_adjust = 0;
     if (display->colorspace->b_full_range) {
         if (!src_full_range)
diff --git a/modules/video_output/win32/d3d11_shaders.h b/modules/video_output/win32/d3d11_shaders.h
index fd74b819b3..ce56113034 100644
--- a/modules/video_output/win32/d3d11_shaders.h
+++ b/modules/video_output/win32/d3d11_shaders.h
@@ -61,8 +61,7 @@ typedef struct {
 typedef struct {
     FLOAT WhitePoint[4*4];
     FLOAT Colorspace[4*4];
-    FLOAT Primaries[3*3];
-    FLOAT padding[16 - 3*3];
+    FLOAT Primaries[4*4];
 } PS_COLOR_TRANSFORM;
 
 typedef struct {



More information about the vlc-commits mailing list