fromXyzInViewingConditions static method

Cam16 fromXyzInViewingConditions(
  1. double x,
  2. double y,
  3. double z,
  4. ViewingConditions viewingConditions,
)

Given color expressed in XYZ and viewed in viewingConditions, convert to CAM16.

Implementation

static Cam16 fromXyzInViewingConditions(
    double x, double y, double z, ViewingConditions viewingConditions) {
  // Transform XYZ to 'cone'/'rgb' responses

  final rC = 0.401288 * x + 0.650173 * y - 0.051461 * z;
  final gC = -0.250268 * x + 1.204414 * y + 0.045854 * z;
  final bC = -0.002079 * x + 0.048952 * y + 0.953127 * z;

  // Discount illuminant
  final rD = viewingConditions.rgbD[0] * rC;
  final gD = viewingConditions.rgbD[1] * gC;
  final bD = viewingConditions.rgbD[2] * bC;

  // chromatic adaptation
  final rAF =
      math.pow(viewingConditions.fl * rD.abs() / 100.0, 0.42).toDouble();
  final gAF =
      math.pow(viewingConditions.fl * gD.abs() / 100.0, 0.42).toDouble();
  final bAF =
      math.pow(viewingConditions.fl * bD.abs() / 100.0, 0.42).toDouble();
  final rA = MathUtils.signum(rD) * 400.0 * rAF / (rAF + 27.13);
  final gA = MathUtils.signum(gD) * 400.0 * gAF / (gAF + 27.13);
  final bA = MathUtils.signum(bD) * 400.0 * bAF / (bAF + 27.13);

  // redness-greenness
  final a = (11.0 * rA + -12.0 * gA + bA) / 11.0;
  // yellowness-blueness
  final b = (rA + gA - 2.0 * bA) / 9.0;

  // auxiliary components
  final u = (20.0 * rA + 20.0 * gA + 21.0 * bA) / 20.0;
  final p2 = (40.0 * rA + 20.0 * gA + bA) / 20.0;

  // hue
  final atan2 = math.atan2(b, a);
  final atanDegrees = atan2 * 180.0 / math.pi;
  final hue = atanDegrees < 0
      ? atanDegrees + 360.0
      : atanDegrees >= 360
          ? atanDegrees - 360
          : atanDegrees;
  final hueRadians = hue * math.pi / 180.0;
  assert(hue >= 0 && hue < 360, 'hue was really $hue');

  // achromatic response to color
  final ac = p2 * viewingConditions.nbb;

  // CAM16 lightness and brightness
  final J = 100.0 *
      math.pow(ac / viewingConditions.aw,
          viewingConditions.c * viewingConditions.z);
  final Q = (4.0 / viewingConditions.c) *
      math.sqrt(J / 100.0) *
      (viewingConditions.aw + 4.0) *
      (viewingConditions.fLRoot);

  final huePrime = (hue < 20.14) ? hue + 360 : hue;
  final eHue =
      (1.0 / 4.0) * (math.cos(huePrime * math.pi / 180.0 + 2.0) + 3.8);
  final p1 =
      50000.0 / 13.0 * eHue * viewingConditions.nC * viewingConditions.ncb;
  final t = p1 * math.sqrt(a * a + b * b) / (u + 0.305);
  final alpha = math.pow(t, 0.9) *
      math.pow(
          1.64 - math.pow(0.29, viewingConditions.backgroundYTowhitePointY),
          0.73);
  // CAM16 chroma, colorfulness, chroma
  final C = alpha * math.sqrt(J / 100.0);
  final M = C * viewingConditions.fLRoot;
  final s = 50.0 *
      math.sqrt((alpha * viewingConditions.c) / (viewingConditions.aw + 4.0));

  // CAM16-UCS components
  final jstar = (1.0 + 100.0 * 0.007) * J / (1.0 + 0.007 * J);
  final mstar = math.log(1.0 + 0.0228 * M) / 0.0228;
  final astar = mstar * math.cos(hueRadians);
  final bstar = mstar * math.sin(hueRadians);
  return Cam16(hue, C, J, Q, M, s, jstar, astar, bstar);
}