figspec

Unofficial static Figma frame/file viewer available as HTML CustomElement

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
import type * as figma from "../../../figma";

/**
 * Returns whether the given color is a _transparent black_ (value of the `transparent` keyword).
 *
 * https://www.w3.org/TR/css-color-3/#transparent
 */
export function isTransparent({ r, g, b, a }: figma.Color): boolean {
  return !r && !g && !b && !a;
}

// https://drafts.csswg.org/css-color-4/#predefined-sRGB
function toLinearLight(c: number): number {
  const abs = Math.abs(c);

  return abs < 0.04045
    ? c / 12.92
    : (c < 0 ? -1 : 1) * Math.pow((abs + 0.055) / 1.055, 2.4);
}

// https://drafts.csswg.org/css-color-4/#color-conversion-code
function gammaEncode(c: number): number {
  const abs = Math.abs(c);

  return abs > 0.0031308
    ? (c < 0 ? -1 : 1) * (1.055 * Math.pow(abs, 1 / 2.4) - 0.055)
    : 12.92 * c;
}

type Vec3 = readonly [number, number, number];

export function mmul(a: readonly [Vec3, Vec3, Vec3], b: Vec3): Vec3 {
  return [
    a[0][0] * b[0] + a[0][1] * b[1] + a[0][2] * b[2],
    a[1][0] * b[0] + a[1][1] * b[1] + a[1][2] * b[2],
    a[2][0] * b[0] + a[2][1] * b[1] + a[2][2] * b[2],
  ];
}

// https://drafts.csswg.org/css-color-4/#color-conversion-code
function srgbToXYZ(c: figma.Color): Vec3 {
  const r = toLinearLight(c.r);
  const g = toLinearLight(c.g);
  const b = toLinearLight(c.b);

  return mmul(
    [
      [506752 / 1228815, 87881 / 245763, 12673 / 70218],
      [87098 / 409605, 175762 / 245763, 12673 / 175545],
      [7918 / 409605, 87881 / 737289, 1001167 / 1053270],
    ],
    [r, g, b],
  );
}

// https://drafts.csswg.org/css-color-4/#color-conversion-code
function xyzToDisplayP3(xyz: Vec3): figma.Color {
  const [r, g, b] = mmul(
    [
      [446124 / 178915, -333277 / 357830, -72051 / 178915],
      [-14852 / 17905, 63121 / 35810, 423 / 17905],
      [11844 / 330415, -50337 / 660830, 316169 / 330415],
    ],
    xyz,
  );

  return {
    r: gammaEncode(r),
    g: gammaEncode(g),
    b: gammaEncode(b),
    a: 1,
  };
}

export function srgbToDisplayP3(srgb: figma.Color): figma.Color {
  return {
    ...xyzToDisplayP3(srgbToXYZ(srgb)),
    a: srgb.a,
  };
}