Add YCgCo color space support for multiplanar formats.

With multiplanar shared images, yuv/rgb color space conversion is
done through skia instead of through ui/gfx color space conversion
filters with values provided by media.

gfx::ColorSpace has some special ColorSpaces which are not handled by
Skia yet. This change adds support for YCgCo color space conversion
along with their bit depth.

Future follow-ups will add corresponding support for others such as
GBR, FCC, YDZDX etc.

Bug: b/41486014
Change-Id: Ic45d7213031121c5d51b7abd313c701af9f31a0a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/826399
Commit-Queue: Saifuddin Hitawala <hitawala@chromium.org>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/wacky_yuv_formats.cpp b/gm/wacky_yuv_formats.cpp
index 204bd9c..1489d1b 100644
--- a/gm/wacky_yuv_formats.cpp
+++ b/gm/wacky_yuv_formats.cpp
@@ -690,7 +690,9 @@
 static void draw_col_label(SkCanvas* canvas, int x, int yuvColorSpace, bool opaque) {
     static const char* kYUVColorSpaceNames[] = {"JPEG",     "601",      "709F",     "709L",
                                                 "2020_8F",  "2020_8L",  "2020_10F", "2020_10L",
-                                                "2020_12F", "2020_12L", "Identity"};
+                                                "2020_12F", "2020_12L", "YCGCO_8F", "YCGCO_8L",
+                                                "YCGCO_10F", "YCGCO_10L", "YCGCO_12F", "YCGCO_12L",
+                                                "Identity"};
     static_assert(std::size(kYUVColorSpaceNames) == kLastEnum_SkYUVColorSpace + 1);
 
     SkPaint paint;
diff --git a/include/core/SkImageInfo.h b/include/core/SkImageInfo.h
index b8ee488..bb4b3cd 100644
--- a/include/core/SkImageInfo.h
+++ b/include/core/SkImageInfo.h
@@ -76,6 +76,12 @@
     kBT2020_10bit_Limited_SkYUVColorSpace,
     kBT2020_12bit_Full_SkYUVColorSpace,
     kBT2020_12bit_Limited_SkYUVColorSpace,
+    kYCgCo_8bit_Full_SkYUVColorSpace,           //!< describes YCgCo matrix
+    kYCgCo_8bit_Limited_SkYUVColorSpace,
+    kYCgCo_10bit_Full_SkYUVColorSpace,
+    kYCgCo_10bit_Limited_SkYUVColorSpace,
+    kYCgCo_12bit_Full_SkYUVColorSpace,
+    kYCgCo_12bit_Limited_SkYUVColorSpace,
     kIdentity_SkYUVColorSpace,                  //!< maps Y->R, U->G, V->B
 
     kLastEnum_SkYUVColorSpace = kIdentity_SkYUVColorSpace, //!< last valid value
diff --git a/src/core/SkYUVMath.cpp b/src/core/SkYUVMath.cpp
index 8cf06cb..04fb6a2 100644
--- a/src/core/SkYUVMath.cpp
+++ b/src/core/SkYUVMath.cpp
@@ -136,6 +136,78 @@
       1.168664f,  2.149647f, -0.000000f,  0.000000f, -1.148145f,
       0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
 };
+const float YCgCo_8bit_full_rgb_to_yuv[] = {
+      0.250000f,  0.500000f,  0.250000f,  0.000000f,  0.000000f,
+     -0.250000f,  0.500000f, -0.250000f,  0.000000f,  0.501961f,
+      0.500000f,  0.000000f, -0.500000f,  0.000000f,  0.501961f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_8bit_full_yuv_to_rgb[] = {
+      1.000000f, -1.000000f,  1.000000f,  0.000000f,  0.000000f,
+      1.000000f,  1.000000f,  0.000000f,  0.000000f, -0.501961f,
+      1.000000f, -1.000000f, -1.000000f,  0.000000f,  1.003922f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_8bit_limited_rgb_to_yuv[] = {
+      0.214706f,  0.429412f,  0.214706f,  0.000000f,  0.073059f,
+     -0.214706f,  0.429412f, -0.214706f,  0.000000f,  0.504155f,
+      0.429412f,  0.000000f, -0.429412f,  0.000000f,  0.504155f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_8bit_limited_yuv_to_rgb[] = {
+      1.164384f, -1.164384f,  1.164384f,  0.000000f, -0.085069f,
+      1.164384f,  1.164384f,  0.000000f,  0.000000f, -0.672099f,
+      1.164384f, -1.164384f, -1.164384f,  0.000000f,  1.088991f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_10bit_full_rgb_to_yuv[] = {
+      0.250000f,  0.500000f,  0.250000f,  0.000000f,  0.000000f,
+     -0.250000f,  0.500000f, -0.250000f,  0.000000f,  0.500489f,
+      0.500000f,  0.000000f, -0.500000f,  0.000000f,  0.500489f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_10bit_full_yuv_to_rgb[] = {
+      1.000000f, -1.000000f,  1.000000f,  0.000000f,  0.000000f,
+      1.000000f,  1.000000f,  0.000000f,  0.000000f, -0.500489f,
+      1.000000f, -1.000000f, -1.000000f,  0.000000f,  1.000978f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_10bit_limited_rgb_to_yuv[] = {
+      0.214076f,  0.428153f,  0.214076f,  0.000000f,  0.073059f,
+     -0.214076f,  0.428153f, -0.214076f,  0.000000f,  0.501630f,
+      0.428153f,  0.000000f, -0.428153f,  0.000000f,  0.501630f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_10bit_limited_yuv_to_rgb[] = {
+      1.167808f, -1.167808f,  1.167808f,  0.000000f, -0.085319f,
+      1.167808f,  1.167808f,  0.000000f,  0.000000f, -0.671127f,
+      1.167808f, -1.167808f, -1.167808f,  0.000000f,  1.086297f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_12bit_full_rgb_to_yuv[] = {
+      0.250000f,  0.500000f,  0.250000f,  0.000000f,  0.000000f,
+     -0.250000f,  0.500000f, -0.250000f,  0.000000f,  0.500122f,
+      0.500000f,  0.000000f, -0.500000f,  0.000000f,  0.500122f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_12bit_full_yuv_to_rgb[] = {
+      1.000000f, -1.000000f,  1.000000f,  0.000000f,  0.000000f,
+      1.000000f,  1.000000f,  0.000000f,  0.000000f, -0.500122f,
+      1.000000f, -1.000000f, -1.000000f,  0.000000f,  1.000244f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_12bit_limited_rgb_to_yuv[] = {
+      0.213919f,  0.427839f,  0.213919f,  0.000000f,  0.073059f,
+     -0.213919f,  0.427839f, -0.213919f,  0.000000f,  0.501003f,
+      0.427839f,  0.000000f, -0.427839f,  0.000000f,  0.501003f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
+const float YCgCo_12bit_limited_yuv_to_rgb[] = {
+      1.168664f, -1.168664f,  1.168664f,  0.000000f, -0.085382f,
+      1.168664f,  1.168664f,  0.000000f,  0.000000f, -0.670886f,
+      1.168664f, -1.168664f, -1.168664f,  0.000000f,  1.085626f,
+      0.000000f,  0.000000f,  0.000000f,  1.000000f,  0.000000f,
+};
 
 static_assert(kJPEG_Full_SkYUVColorSpace            == 0, "");
 static_assert(kRec601_Limited_SkYUVColorSpace       == 1, "");
@@ -147,6 +219,12 @@
 static_assert(kBT2020_10bit_Limited_SkYUVColorSpace == 7, "");
 static_assert(kBT2020_12bit_Full_SkYUVColorSpace    == 8, "");
 static_assert(kBT2020_12bit_Limited_SkYUVColorSpace == 9, "");
+static_assert(kYCgCo_8bit_Full_SkYUVColorSpace      == 10, "");
+static_assert(kYCgCo_8bit_Limited_SkYUVColorSpace   == 11, "");
+static_assert(kYCgCo_10bit_Full_SkYUVColorSpace     == 12, "");
+static_assert(kYCgCo_10bit_Limited_SkYUVColorSpace  == 13, "");
+static_assert(kYCgCo_12bit_Full_SkYUVColorSpace     == 14, "");
+static_assert(kYCgCo_12bit_Limited_SkYUVColorSpace  == 15, "");
 
 const float* yuv_to_rgb_array[] = {
     JPEG_full_yuv_to_rgb,
@@ -159,6 +237,12 @@
     BT2020_10bit_limited_yuv_to_rgb,
     BT2020_12bit_full_yuv_to_rgb,
     BT2020_12bit_limited_yuv_to_rgb,
+    YCgCo_8bit_full_yuv_to_rgb,
+    YCgCo_8bit_limited_yuv_to_rgb,
+    YCgCo_10bit_full_yuv_to_rgb,
+    YCgCo_10bit_limited_yuv_to_rgb,
+    YCgCo_12bit_full_yuv_to_rgb,
+    YCgCo_12bit_limited_yuv_to_rgb,
 };
 
 const float* rgb_to_yuv_array[] = {
@@ -172,6 +256,12 @@
     BT2020_10bit_limited_rgb_to_yuv,
     BT2020_12bit_full_rgb_to_yuv,
     BT2020_12bit_limited_rgb_to_yuv,
+    YCgCo_8bit_full_rgb_to_yuv,
+    YCgCo_8bit_limited_rgb_to_yuv,
+    YCgCo_10bit_full_rgb_to_yuv,
+    YCgCo_10bit_limited_rgb_to_yuv,
+    YCgCo_12bit_full_rgb_to_yuv,
+    YCgCo_12bit_limited_rgb_to_yuv,
 };
 
 constexpr size_t kSizeOfColorMatrix = 20 * sizeof(float);
@@ -261,7 +351,7 @@
 };
 }  // namespace
 
-static void make_rgb_to_yuv_matrix(float mx[20], const YUVCoeff& c) {
+static void make_rgb_to_yuv_matrix_ycbcr(float mx[20], const YUVCoeff& c) {
     SkASSERT(c.bits >= 8);
     const float Kr = c.Kr;
     const float Kb = c.Kb;
@@ -295,6 +385,60 @@
     scale3(mx + 10, Cb * scaleUV);
 }
 
+static void make_rgb_to_yuv_matrix_ycgco(float mx[20], int bits, Range range) {
+    SkASSERT(bits >= 8);
+    const int shift = bits - 8;
+    const float denom = static_cast<float>((1 << bits) - 1);
+    float scaleY   = 1.0f,
+          addY     = 0.0f,
+          chroma05 = static_cast<float>(1 << (bits - 1)) / denom;
+
+    if (range == kLimited) {
+        scaleY  = (219 << shift) / denom;
+        addY    = (16 << shift) / static_cast<float>(219 << shift);
+    }
+
+    float m[20] = {
+          0.25f,  0.5f,   0.25f,  0.0f,     addY,
+         -0.25f,  0.5f,  -0.25f,  0.0f,    chroma05 * scaleY + addY,
+           0.5f,  0.0f,  -0.5f,   0.0f,    chroma05 * scaleY + addY,
+           0.0f,  0.0f,   0.0f,   1.0f,     0.0f,
+    };
+    memcpy(mx, m, sizeof(m));
+    scale3(mx +  0, scaleY );
+    scale3(mx +  5, scaleY);
+    scale3(mx + 10, scaleY);
+}
+
+static void make_rgb_to_yuv_matrix(float mx[20], SkYUVColorSpace cs) {
+    switch (cs) {
+        case kJPEG_Full_SkYUVColorSpace:
+        case kRec601_Limited_SkYUVColorSpace:
+        case kRec709_Full_SkYUVColorSpace:
+        case kRec709_Limited_SkYUVColorSpace:
+        case kBT2020_8bit_Full_SkYUVColorSpace:
+        case kBT2020_8bit_Limited_SkYUVColorSpace:
+        case kBT2020_10bit_Full_SkYUVColorSpace:
+        case kBT2020_10bit_Limited_SkYUVColorSpace:
+        case kBT2020_12bit_Full_SkYUVColorSpace:
+        case kBT2020_12bit_Limited_SkYUVColorSpace:
+        case kIdentity_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycbcr(mx, gCoeff[(unsigned)cs]);
+        case kYCgCo_8bit_Full_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/8, Range::kFull);
+        case kYCgCo_8bit_Limited_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/8, Range::kLimited);
+        case kYCgCo_10bit_Full_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/10, Range::kFull);
+        case kYCgCo_10bit_Limited_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/10, Range::kLimited);
+        case kYCgCo_12bit_Full_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/12, Range::kFull);
+        case kYCgCo_12bit_Limited_SkYUVColorSpace:
+            return make_rgb_to_yuv_matrix_ycgco(mx, /*bits=*/12, Range::kLimited);
+    }
+}
+
 static void dump(const float m[20], SkYUVColorSpace cs, bool rgb2yuv) {
     const char* names[] = {
         "JPEG_full",
@@ -307,6 +451,12 @@
         "BT2020_10bit_limited",
         "BT2020_12bit_full",
         "BT2020_12bit_limited",
+        "YCgCo_8bit_full",
+        "YCgCo_8bit_limited",
+        "YCgCo_10bit_full",
+        "YCgCo_10bit_limited",
+        "YCgCo_12bit_full",
+        "YCgCo_12bit_limited",
     };
     const char* dirnames[] = {
         "yuv_to_rgb", "rgb_to_yuv",
@@ -328,7 +478,7 @@
     for (int i = 0; i < kLastEnum_SkYUVColorSpace; ++i) {
         SkYUVColorSpace cs = static_cast<SkYUVColorSpace>(i);
         float m[20];
-        make_rgb_to_yuv_matrix(m, gCoeff[(unsigned)cs]);
+        make_rgb_to_yuv_matrix(m, cs);
         dump(m, cs, true);
         SkM44 m44, im44;
         colormatrix_to_matrix44(m, &m44);