Repository: electron/electron
Pull request: feat: add `ColorSpace`

#### Description of Change

Add `ColorSpace` structure, split from #46811 

This structure will be added to `paint` event's `textureInfo` when using shared texture OSR, and will be used in import shared texture. We have to keep full color space info. This also get ready for HDR capturing support in the future.

#### Release Notes

Notes: none

Changed files:
filename: docs/api/structures/color-space.md
patch: @@ -0,0 +1,195 @@
+# ColorSpace Object
+
+* `primaries` string - The color primaries of the color space. Can be one of the following values:
+  * `bt709` - BT709 primaries (also used for sRGB)
+  * `bt470m` - BT470M primaries
+  * `bt470bg` - BT470BG primaries
+  * `smpte170m` - SMPTE170M primaries
+  * `smpte240m` - SMPTE240M primaries
+  * `film` - Film primaries
+  * `bt2020` - BT2020 primaries
+  * `smptest428-1` - SMPTEST428-1 primaries
+  * `smptest431-2` - SMPTEST431-2 primaries
+  * `p3` - P3 primaries
+  * `xyz-d50` - XYZ D50 primaries
+  * `adobe-rgb` - Adobe RGB primaries
+  * `apple-generic-rgb` - Apple Generic RGB primaries
+  * `wide-gamut-color-spin` - Wide Gamut Color Spin primaries
+  * `ebu-3213-e` - EBU 3213-E primaries
+  * `custom` - Custom primaries
+  * `invalid` - Invalid primaries
+
+* `transfer` string - The transfer function of the color space. Can be one of the following values:
+  * `bt709` - BT709 transfer function
+  * `bt709-apple` - BT709 Apple transfer function
+  * `gamma18` - Gamma 1.8 transfer function
+  * `gamma22` - Gamma 2.2 transfer function
+  * `gamma24` - Gamma 2.4 transfer function
+  * `gamma28` - Gamma 2.8 transfer function
+  * `smpte170m` - SMPTE170M transfer function
+  * `smpte240m` - SMPTE240M transfer function
+  * `linear` - Linear transfer function
+  * `log` - Log transfer function
+  * `log-sqrt` - Log Square Root transfer function
+  * `iec61966-2-4` - IEC61966-2-4 transfer function
+  * `bt1361-ecg` - BT1361 ECG transfer function
+  * `srgb` - sRGB transfer function
+  * `bt2020-10` - BT2020-10 transfer function
+  * `bt2020-12` - BT2020-12 transfer function
+  * `pq` - PQ (Perceptual Quantizer) transfer function
+  * `smptest428-1` - SMPTEST428-1 transfer function
+  * `hlg` - HLG (Hybrid Log-Gamma) transfer function
+  * `srgb-hdr` - sRGB HDR transfer function
+  * `linear-hdr` - Linear HDR transfer function
+  * `custom` - Custom transfer function
+  * `custom-hdr` - Custom HDR transfer function
+  * `scrgb-linear-80-nits` - scRGB Linear 80 nits transfer function
+  * `invalid` - Invalid transfer function
+
+* `matrix` string - The color matrix of the color space. Can be one of the following values:
+  * `rgb` - RGB matrix
+  * `bt709` - BT709 matrix
+  * `fcc` - FCC matrix
+  * `bt470bg` - BT470BG matrix
+  * `smpte170m` - SMPTE170M matrix
+  * `smpte240m` - SMPTE240M matrix
+  * `ycocg` - YCoCg matrix
+  * `bt2020-ncl` - BT2020 NCL matrix
+  * `ydzdx` - YDzDx matrix
+  * `gbr` - GBR matrix
+  * `invalid` - Invalid matrix
+
+* `range` string - The color range of the color space. Can be one of the following values:
+  * `limited` - Limited color range (RGB values ranging from 16 to 235)
+  * `full` - Full color range (RGB values from 0 to 255)
+  * `derived` - Range defined by the transfer function and matrix
+  * `invalid` - Invalid range
+
+## Common `ColorSpace` definitions
+
+### Standard Color Spaces
+
+**sRGB**:
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'srgb',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**Display P3**:
+
+  ```js
+  const cs = {
+    primaries: 'p3',
+    transfer: 'srgb',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**XYZ D50**:
+
+  ```js
+  const cs = {
+    primaries: 'xyz-d50',
+    transfer: 'linear',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+### HDR Color Spaces
+
+**Extended sRGB** (extends sRGB to all real values):
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'srgb-hdr',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**scRGB Linear** (linear transfer function for all real values):
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'linear-hdr',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**scRGB Linear 80 Nits** (with an SDR white level of 80 nits):
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'scrgb-linear-80-nits',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**HDR10** (BT.2020 primaries with PQ transfer function):
+
+  ```js
+  const cs = {
+    primaries: 'bt2020',
+    transfer: 'pq',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+**HLG** (BT.2020 primaries with HLG transfer function):
+
+  ```js
+  const cs = {
+    primaries: 'bt2020',
+    transfer: 'hlg',
+    matrix: 'rgb',
+    range: 'full'
+  }
+  ```
+
+### Video Color Spaces
+
+**Rec. 601** (SDTV):
+
+  ```js
+  const cs = {
+    primaries: 'smpte170m',
+    transfer: 'smpte170m',
+    matrix: 'smpte170m',
+    range: 'limited'
+  }
+  ```
+
+**Rec. 709** (HDTV):
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'bt709',
+    matrix: 'bt709',
+    range: 'limited'
+  }
+  ```
+
+**JPEG** (typical color space for JPEG images):
+
+  ```js
+  const cs = {
+    primaries: 'bt709',
+    transfer: 'srgb',
+    matrix: 'smpte170m',
+    range: 'full'
+  }
+  ```

filename: filenames.auto.gni
patch: @@ -83,6 +83,7 @@ auto_filenames = {
     "docs/api/structures/browser-window-options.md",
     "docs/api/structures/certificate-principal.md",
     "docs/api/structures/certificate.md",
+    "docs/api/structures/color-space.md",
     "docs/api/structures/cookie.md",
     "docs/api/structures/cpu-usage.md",
     "docs/api/structures/crash-report.md",

filename: shell/common/gin_converters/gfx_converter.cc
patch: @@ -11,6 +11,7 @@
 #include "shell/common/gin_helper/dictionary.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/point_f.h"
@@ -230,4 +231,363 @@ bool Converter<WrappedSkColor>::FromV8(v8::Isolate* isolate,
   return true;
 }
 
+v8::Local<v8::Value> Converter<gfx::ColorSpace>::ToV8(
+    v8::Isolate* isolate,
+    const gfx::ColorSpace& val) {
+  auto dict = gin_helper::Dictionary::CreateEmpty(isolate);
+
+  // Convert primaries to string
+  std::string primaries;
+  switch (val.GetPrimaryID()) {
+    case gfx::ColorSpace::PrimaryID::BT709:
+      primaries = "bt709";
+      break;
+    case gfx::ColorSpace::PrimaryID::BT470M:
+      primaries = "bt470m";
+      break;
+    case gfx::ColorSpace::PrimaryID::BT470BG:
+      primaries = "bt470bg";
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTE170M:
+      primaries = "smpte170m";
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTE240M:
+      primaries = "smpte240m";
+      break;
+    case gfx::ColorSpace::PrimaryID::FILM:
+      primaries = "film";
+      break;
+    case gfx::ColorSpace::PrimaryID::BT2020:
+      primaries = "bt2020";
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTEST428_1:
+      primaries = "smptest428-1";
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTEST431_2:
+      primaries = "smptest431-2";
+      break;
+    case gfx::ColorSpace::PrimaryID::P3:
+      primaries = "p3";
+      break;
+    case gfx::ColorSpace::PrimaryID::XYZ_D50:
+      primaries = "xyz-d50";
+      break;
+    case gfx::ColorSpace::PrimaryID::ADOBE_RGB:
+      primaries = "adobe-rgb";
+      break;
+    case gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB:
+      primaries = "apple-generic-rgb";
+      break;
+    case gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN:
+      primaries = "wide-gamut-color-spin";
+      break;
+    case gfx::ColorSpace::PrimaryID::CUSTOM:
+      primaries = "custom";
+      break;
+    case gfx::ColorSpace::PrimaryID::EBU_3213_E:
+      primaries = "ebu-3213-e";
+      break;
+    case gfx::ColorSpace::PrimaryID::INVALID:
+      primaries = "invalid";
+      break;
+  }
+
+  // Convert transfer function to string
+  std::string transfer;
+  switch (val.GetTransferID()) {
+    case gfx::ColorSpace::TransferID::BT709:
+      transfer = "bt709";
+      break;
+    case gfx::ColorSpace::TransferID::BT709_APPLE:
+      transfer = "bt709-apple";
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA18:
+      transfer = "gamma18";
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA22:
+      transfer = "gamma22";
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA24:
+      transfer = "gamma24";
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA28:
+      transfer = "gamma28";
+      break;
+    case gfx::ColorSpace::TransferID::SMPTE170M:
+      transfer = "smpte170m";
+      break;
+    case gfx::ColorSpace::TransferID::SMPTE240M:
+      transfer = "smpte240m";
+      break;
+    case gfx::ColorSpace::TransferID::LINEAR:
+      transfer = "linear";
+      break;
+    case gfx::ColorSpace::TransferID::LOG:
+      transfer = "log";
+      break;
+    case gfx::ColorSpace::TransferID::LOG_SQRT:
+      transfer = "log-sqrt";
+      break;
+    case gfx::ColorSpace::TransferID::IEC61966_2_4:
+      transfer = "iec61966-2-4";
+      break;
+    case gfx::ColorSpace::TransferID::BT1361_ECG:
+      transfer = "bt1361-ecg";
+      break;
+    case gfx::ColorSpace::TransferID::SRGB:
+      transfer = "srgb";
+      break;
+    case gfx::ColorSpace::TransferID::BT2020_10:
+      transfer = "bt2020-10";
+      break;
+    case gfx::ColorSpace::TransferID::BT2020_12:
+      transfer = "bt2020-12";
+      break;
+    case gfx::ColorSpace::TransferID::PQ:
+      transfer = "pq";
+      break;
+    case gfx::ColorSpace::TransferID::SMPTEST428_1:
+      transfer = "smptest428-1";
+      break;
+    case gfx::ColorSpace::TransferID::HLG:
+      transfer = "hlg";
+      break;
+    case gfx::ColorSpace::TransferID::SRGB_HDR:
+      transfer = "srgb-hdr";
+      break;
+    case gfx::ColorSpace::TransferID::LINEAR_HDR:
+      transfer = "linear-hdr";
+      break;
+    case gfx::ColorSpace::TransferID::CUSTOM:
+      transfer = "custom";
+      break;
+    case gfx::ColorSpace::TransferID::CUSTOM_HDR:
+      transfer = "custom-hdr";
+      break;
+    case gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS:
+      transfer = "scrgb-linear-80-nits";
+      break;
+    case gfx::ColorSpace::TransferID::INVALID:
+      transfer = "invalid";
+      break;
+  }
+
+  // Convert matrix to string
+  std::string matrix;
+  switch (val.GetMatrixID()) {
+    case gfx::ColorSpace::MatrixID::RGB:
+      matrix = "rgb";
+      break;
+    case gfx::ColorSpace::MatrixID::BT709:
+      matrix = "bt709";
+      break;
+    case gfx::ColorSpace::MatrixID::FCC:
+      matrix = "fcc";
+      break;
+    case gfx::ColorSpace::MatrixID::BT470BG:
+      matrix = "bt470bg";
+      break;
+    case gfx::ColorSpace::MatrixID::SMPTE170M:
+      matrix = "smpte170m";
+      break;
+    case gfx::ColorSpace::MatrixID::SMPTE240M:
+      matrix = "smpte240m";
+      break;
+    case gfx::ColorSpace::MatrixID::YCOCG:
+      matrix = "ycocg";
+      break;
+    case gfx::ColorSpace::MatrixID::BT2020_NCL:
+      matrix = "bt2020-ncl";
+      break;
+    case gfx::ColorSpace::MatrixID::YDZDX:
+      matrix = "ydzdx";
+      break;
+    case gfx::ColorSpace::MatrixID::GBR:
+      matrix = "gbr";
+      break;
+    case gfx::ColorSpace::MatrixID::INVALID:
+      matrix = "invalid";
+      break;
+  }
+
+  // Convert range to string
+  std::string range;
+  switch (val.GetRangeID()) {
+    case gfx::ColorSpace::RangeID::LIMITED:
+      range = "limited";
+      break;
+    case gfx::ColorSpace::RangeID::FULL:
+      range = "full";
+      break;
+    case gfx::ColorSpace::RangeID::DERIVED:
+      range = "derived";
+      break;
+    case gfx::ColorSpace::RangeID::INVALID:
+      range = "invalid";
+      break;
+  }
+
+  dict.Set("primaries", primaries);
+  dict.Set("transfer", transfer);
+  dict.Set("matrix", matrix);
+  dict.Set("range", range);
+
+  return dict.GetHandle();
+}
+
+bool Converter<gfx::ColorSpace>::FromV8(v8::Isolate* isolate,
+                                        v8::Local<v8::Value> val,
+                                        gfx::ColorSpace* out) {
+  gin::Dictionary dict(isolate);
+  if (!gin::ConvertFromV8(isolate, val, &dict))
+    return false;
+
+  std::string primaries_str, transfer_str, matrix_str, range_str;
+
+  // Default values if not specified
+  gfx::ColorSpace::PrimaryID primaries = gfx::ColorSpace::PrimaryID::BT709;
+  gfx::ColorSpace::TransferID transfer = gfx::ColorSpace::TransferID::SRGB;
+  gfx::ColorSpace::MatrixID matrix = gfx::ColorSpace::MatrixID::RGB;
+  gfx::ColorSpace::RangeID range = gfx::ColorSpace::RangeID::FULL;
+
+  // Get primaries
+  if (dict.Get("primaries", &primaries_str)) {
+    if (primaries_str == "bt709")
+      primaries = gfx::ColorSpace::PrimaryID::BT709;
+    else if (primaries_str == "bt470m")
+      primaries = gfx::ColorSpace::PrimaryID::BT470M;
+    else if (primaries_str == "bt470bg")
+      primaries = gfx::ColorSpace::PrimaryID::BT470BG;
+    else if (primaries_str == "smpte170m")
+      primaries = gfx::ColorSpace::PrimaryID::SMPTE170M;
+    else if (primaries_str == "smpte240m")
+      primaries = gfx::ColorSpace::PrimaryID::SMPTE240M;
+    else if (primaries_str == "film")
+      primaries = gfx::ColorSpace::PrimaryID::FILM;
+    else if (primaries_str == "bt2020")
+      primaries = gfx::ColorSpace::PrimaryID::BT2020;
+    else if (primaries_str == "smptest428-1")
+      primaries = gfx::ColorSpace::PrimaryID::SMPTEST428_1;
+    else if (primaries_str == "smptest431-2")
+      primaries = gfx::ColorSpace::PrimaryID::SMPTEST431_2;
+    else if (primaries_str == "p3")
+      primaries = gfx::ColorSpace::PrimaryID::P3;
+    else if (primaries_str == "xyz-d50")
+      primaries = gfx::ColorSpace::PrimaryID::XYZ_D50;
+    else if (primaries_str == "adobe-rgb")
+      primaries = gfx::ColorSpace::PrimaryID::ADOBE_RGB;
+    else if (primaries_str == "apple-generic-rgb")
+      primaries = gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB;
+    else if (primaries_str == "wide-gamut-color-spin")
+      primaries = gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN;
+    else if (primaries_str == "ebu-3213-e")
+      primaries = gfx::ColorSpace::PrimaryID::EBU_3213_E;
+
+    if (primaries_str == "custom") {
+      gin_helper::ErrorThrower(isolate).ThrowTypeError(
+          "'custom' not supported.");
+      return false;
+    } else {
+      primaries = gfx::ColorSpace::PrimaryID::INVALID;
+    }
+  }
+
+  // Get transfer
+  if (dict.Get("transfer", &transfer_str)) {
+    if (transfer_str == "bt709")
+      transfer = gfx::ColorSpace::TransferID::BT709;
+    else if (transfer_str == "bt709-apple")
+      transfer = gfx::ColorSpace::TransferID::BT709_APPLE;
+    else if (transfer_str == "gamma18")
+      transfer = gfx::ColorSpace::TransferID::GAMMA18;
+    else if (transfer_str == "gamma22")
+      transfer = gfx::ColorSpace::TransferID::GAMMA22;
+    else if (transfer_str == "gamma24")
+      transfer = gfx::ColorSpace::TransferID::GAMMA24;
+    else if (transfer_str == "gamma28")
+      transfer = gfx::ColorSpace::TransferID::GAMMA28;
+    else if (transfer_str == "smpte170m")
+      transfer = gfx::ColorSpace::TransferID::SMPTE170M;
+    else if (transfer_str == "smpte240m")
+      transfer = gfx::ColorSpace::TransferID::SMPTE240M;
+    else if (transfer_str == "linear")
+      transfer = gfx::ColorSpace::TransferID::LINEAR;
+    else if (transfer_str == "log")
+      transfer = gfx::ColorSpace::TransferID::LOG;
+    else if (transfer_str == "log-sqrt")
+      transfer = gfx::ColorSpace::TransferID::LOG_SQRT;
+    else if (transfer_str == "iec61966-2-4")
+      transfer = gfx::ColorSpace::TransferID::IEC61966_2_4;
+    else if (transfer_str == "bt1361-ecg")
+      transfer = gfx::ColorSpace::TransferID::BT1361_ECG;
+    else if (transfer_str == "srgb")
+      transfer = gfx::ColorSpace::TransferID::SRGB;
+    else if (transfer_str == "bt2020-10")
+      transfer = gfx::ColorSpace::TransferID::BT2020_10;
+    else if (transfer_str == "bt2020-12")
+      transfer = gfx::ColorSpace::TransferID::BT2020_12;
+    else if (transfer_str == "pq")
+      transfer = gfx::ColorSpace::TransferID::PQ;
+    else if (transfer_str == "smptest428-1")
+      transfer = gfx::ColorSpace::TransferID::SMPTEST428_1;
+    else if (transfer_str == "hlg")
+      transfer = gfx::ColorSpace::TransferID::HLG;
+    else if (transfer_str == "srgb-hdr")
+      transfer = gfx::ColorSpace::TransferID::SRGB_HDR;
+    else if (transfer_str == "linear-hdr")
+      transfer = gfx::ColorSpace::TransferID::LINEAR_HDR;
+    else if (transfer_str == "scrgb-linear-80-nits")
+      transfer = gfx::ColorSpace::TransferID::SCRGB_LINEAR_80_NITS;
+
+    if (transfer_str == "custom" || transfer_str == "custom-hdr") {
+      gin_helper::ErrorThrower(isolate).ThrowTypeError(
+          "'custom', 'custom-hdr' not supported.");
+      return false;
+    } else {
+      primaries = gfx::ColorSpace::PrimaryID::INVALID;
+    }
+  }
+
+  // Get matrix
+  if (dict.Get("matrix", &matrix_str)) {
+    if (matrix_str == "rgb")
+      matrix = gfx::ColorSpace::MatrixID::RGB;
+    else if (matrix_str == "bt709")
+      matrix = gfx::ColorSpace::MatrixID::BT709;
+    else if (matrix_str == "fcc")
+      matrix = gfx::ColorSpace::MatrixID::FCC;
+    else if (matrix_str == "bt470bg")
+      matrix = gfx::ColorSpace::MatrixID::BT470BG;
+    else if (matrix_str == "smpte170m")
+      matrix = gfx::ColorSpace::MatrixID::SMPTE170M;
+    else if (matrix_str == "smpte240m")
+      matrix = gfx::ColorSpace::MatrixID::SMPTE240M;
+    else if (matrix_str == "ycocg")
+      matrix = gfx::ColorSpace::MatrixID::YCOCG;
+    else if (matrix_str == "bt2020-ncl")
+      matrix = gfx::ColorSpace::MatrixID::BT2020_NCL;
+    else if (matrix_str == "ydzdx")
+      matrix = gfx::ColorSpace::MatrixID::YDZDX;
+    else if (matrix_str == "gbr")
+      matrix = gfx::ColorSpace::MatrixID::GBR;
+    else
+      matrix = gfx::ColorSpace::MatrixID::INVALID;
+  }
+
+  // Get range
+  if (dict.Get("range", &range_str)) {
+    if (range_str == "limited")
+      range = gfx::ColorSpace::RangeID::LIMITED;
+    else if (range_str == "full")
+      range = gfx::ColorSpace::RangeID::FULL;
+    else if (range_str == "derived")
+      range = gfx::ColorSpace::RangeID::DERIVED;
+    else
+      range = gfx::ColorSpace::RangeID::INVALID;
+  }
+
+  *out = gfx::ColorSpace(primaries, transfer, matrix, range);
+  return true;
+}
+
 }  // namespace gin

filename: shell/common/gin_converters/gfx_converter.h
patch: @@ -18,6 +18,7 @@ class PointF;
 class Size;
 class Rect;
 class Insets;
+class ColorSpace;
 enum class ResizeEdge;
 }  // namespace gfx
 
@@ -87,6 +88,15 @@ struct Converter<WrappedSkColor> {
                      WrappedSkColor* out);
 };
 
+template <>
+struct Converter<gfx::ColorSpace> {
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+                                   const gfx::ColorSpace& val);
+  static bool FromV8(v8::Isolate* isolate,
+                     v8::Local<v8::Value> val,
+                     gfx::ColorSpace* out);
+};
+
 }  // namespace gin
 
 #endif  // ELECTRON_SHELL_COMMON_GIN_CONVERTERS_GFX_CONVERTER_H_