Skip to content

Commit

Permalink
ambiance/conversion: fix invalid color space conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
arnemolland committed May 30, 2023
1 parent c27db34 commit a0bb081
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 223 deletions.
4 changes: 2 additions & 2 deletions lib/src/ambiance/algorithm/saturate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import 'package:flume/flume.dart';
extension SaturateExtension on Color {
/// Returns a saturated version of this color.
Color saturate(double amount) {
final lch = LCH.fromColor(this);
final lch = LCH.fromCIELAB(CIELAB.fromColor(this));
var c = lch.c;
c += amount * kn;
if (c < 0) c = 0;

return LCH(lch.l, c, lch.h).toColor();
return LCH(lch.l, c, lch.h).toCIELAB().toColor();
}
}
132 changes: 44 additions & 88 deletions lib/src/ambiance/conversion/lab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,118 +2,74 @@ import 'dart:math';
import 'dart:ui';

import 'package:equatable/equatable.dart';
import 'package:flume/flume.dart';
import 'package:flume/src/ambiance/conversion/xyz.dart';

/// {@category Ambiance}
/// Class representing the CIELAB colorspace.
/// A color in the CIELAB color space.
class CIELAB extends Equatable {
/// Perceptual lightness value.
final double l;

/// A value.
/// Red-green value.
final double a;

/// B value.
/// Yellow-blue value.
final double b;

const CIELAB(this.l, this.a, this.b);

/// Converts [Color] to [CIELAB] color.
factory CIELAB.fromColor(Color color) {
return CIELAB.fromRGB(RGB.fromColor(color));
}
/// Converts from [CIEXYZ] to [CIELAB].
static CIELAB fromXYZ(CIEXYZ xyz) {
const double xn = 0.95047;
const double yn = 1.0;
const double zn = 1.08883;

/// Converts [RGB] to [CIELAB] color.
factory CIELAB.fromRGB(RGB color) {
final xyz = color.toCIEXYZ();
final double x = xyz.x.toDouble();
final double y = xyz.y.toDouble();
final double z = xyz.z.toDouble();
num f(double t) {
return t > pow(6 / 29, 3)
? pow(t, 1 / 3.0)
: t / (3 * pow(29 / 6, 2)) + 4 / 29;
}

final double l = 116 * y - 16;
double fx = f(xyz.x / xn).toDouble();
double fy = f(xyz.y / yn).toDouble();
double fz = f(xyz.z / zn).toDouble();

return CIELAB(l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z));
}
double l = 116 * fy - 16;
double a = 500 * (fx - fy);
double b = 200 * (fy - fz);

/// Converts [CIELAB] to [Color].
Color toColor() {
return toRGB().toColor();
return CIELAB(l, a, b);
}

/// Converts [CIELAB] to [RGB].
RGB toRGB() {
return toXYZ().toRGB();
/// Converts from [Color] to [CIELAB].
static CIELAB fromColor(Color color) {
return CIELAB.fromXYZ(CIEXYZ.fromColor(color));
}

/// Converts [CIELAB] to [CIEXYZ].
/// Converts from [CIELAB] to [XYZ].
CIEXYZ toXYZ() {
var xyz = <String, num>{
'x': a / 500 + (l + 16) / 116,
'y': (l + 16) / 116,
'z': (l + 16) / 116 - b / 200
};

xyz.forEach((key, value) {
var cube = pow(value, 3);
if (cube > 0.008856) {
xyz[key] = cube;
} else {
xyz[key] = (value - 16 / 116) / 7.787;
}
xyz[key] = xyz[key]! * CIEXYZ.referenceWhite.toMap()[key]!;
});

return CIEXYZ(xyz['x']!, xyz['y']!, xyz['z']!);
}
const double xn = 0.95047;
const double yn = 1.0;
const double zn = 1.08883;

@override
List<Object> get props => [l, a, b];
}
double fy = (l + 16) / 116;
double fx = fy + a / 500;
double fz = fy - b / 200;

/// {@category Ambiance}
/// Extension that adds [CIELAB] conversion methods to [Color].
extension CIELABExtension on Color {
/// Converts [Color] to [CIELAB] color.
CIELAB toCIELAB() {
return CIELAB.fromColor(this);
}
double xr =
fx * fx * fx > 0.008856 ? fx * fx * fx : (116 * fx - 16) / 903.3;
double yr = l > 7.9996 ? fy * fy * fy : l / 903.3;
double zr =
fz * fz * fz > 0.008856 ? fz * fz * fz : (116 * fz - 16) / 903.3;

/// Converts [CIELAB] color to [Color].
static Color fromCIELAB(CIELAB color) {
return color.toColor();
return CIEXYZ(xr * xn, yr * yn, zr * zn);
}
}

// // Class representing the CIEXYZ coordinates.
// class CIEXYZ extends Equatable {
// /// X value.
// final double x;

// /// Y value.
// final double y;

// /// Z value.
// final double z;

// const CIEXYZ(this.x, this.y, this.z);

// factory CIEXYZ.fromRGB(RGB color) {
// final double r = color.r / 255;
// final double g = color.g / 255;
// final double b = color.b / 255;

// final double x = r * 0.4124 + g * 0.3576 + b * 0.1805;
// final double y = r * 0.2126 + g * 0.7152 + b * 0.0722;
// final double z = r * 0.0193 + g * 0.1192 + b * 0.9505;

// return CIEXYZ(x, y, z);
// }

// /// Converts [Color] to [CIEXYZ] color.
// static CIEXYZ fromColor(Color color) {
// return CIEXYZ.fromRGB(RGB(color.red, color.green, color.blue));
// }
/// Converts from [CIELAB] to [Color].
Color toColor() {
return toXYZ().toColor();
}

// @override
// List<Object> get props => [x, y, z];
// }
@override
List<Object?> get props => [l, a, b];
}
63 changes: 12 additions & 51 deletions lib/src/ambiance/conversion/lch.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import 'dart:math';
import 'dart:ui';
import 'dart:math' as math;

import 'package:equatable/equatable.dart';
import 'package:flume/flume.dart';
import 'package:flume/src/ambiance/conversion/lab.dart';

/// {@category Ambiance}
/// Class representing the LCH colorspace.
Expand All @@ -18,59 +17,21 @@ class LCH extends Equatable {

const LCH(this.l, this.c, this.h);

factory LCH.fromCIELAB(CIELAB lab) {
final double l = lab.l;
final double a = lab.a;
final double b = lab.b;

final double c = sqrt(a * a + b * b);
final double h = (atan2(b, a) * 180 / pi + 360) % 360;

return LCH(l, c, h);
// Conversion from CIELAB to LCH.
static LCH fromCIELAB(CIELAB cielab) {
double c = math.sqrt(cielab.a * cielab.a + cielab.b * cielab.b);
double h = (180 / math.pi) * math.atan2(cielab.b, cielab.a);
if (h < 0) h = h + 360;
return LCH(cielab.l, c, h);
}

/// Converts [RGB] color to [LCH] color.
factory LCH.fromRGB(RGB color) {
return LCH.fromCIELAB(CIELAB.fromRGB(color));
}

/// Converts [Color] to [LCH] color.
factory LCH.fromColor(Color color) {
return LCH.fromRGB(RGB(color.red, color.green, color.blue));
}

/// Converts [LCH] color to [CIELAB] color.
// Conversion from LCH to CIELAB.
CIELAB toCIELAB() {
final double a = c * cos(h * pi / 180);
final double b = c * sin(h * pi / 180);

double a = c * math.cos(h * math.pi / 180);
double b = c * math.sin(h * math.pi / 180);
return CIELAB(l, a, b);
}

/// Converts [LCH] color to [RGB] color.
RGB toRGB() {
return toColor().toRGB();
}

/// Converts [LCH] color to [Color].
Color toColor() {
return toCIELAB().toColor();
}

@override
List<Object> get props => [l, c, h];
}

/// {@category Ambiance}
/// Extension that adds [LCH] conversion methods to [Color].
extension LCHExtension on Color {
/// Converts [Color] to [LCH] color.
LCH toLCH() {
return LCH.fromColor(this);
}

/// Converts [LCH] color to [Color].
static Color fromLCH(LCH color) {
return color.toColor();
}
List<Object?> get props => [l, c, h];
}
13 changes: 7 additions & 6 deletions lib/src/ambiance/conversion/rgb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,22 @@ class RGB extends Equatable {
return p;
}

final r = (hueToRgb(h + 1 / 3) * 255).toInt();
final g = (hueToRgb(h) * 255).toInt();
final b = (hueToRgb(h - 1 / 3) * 255).toInt();
final r = (hueToRgb(h + 1 / 3) * 255).round();
final g = (hueToRgb(h) * 255).round();
final b = (hueToRgb(h - 1 / 3) * 255).round();

return RGB(r, g, b);
}

/// Converts [LCH] color to [RGB] color.
factory RGB.fromLCH(LCH color) {
return color.toRGB();
return color.toCIELAB().toColor().toRGB();
}

/// Converts [CIELAB] color to [RGB] color.
factory RGB.fromCIELAB(CIELAB color) {
return color.toRGB();
final rgb = color.toColor();
return RGB(rgb.red, rgb.green, rgb.blue, rgb.alpha);
}

/// Converts color to hex string.
Expand All @@ -82,7 +83,7 @@ class RGB extends Equatable {
}

/// Converts [RGB] to [CIEXYZ] coordinates.
CIEXYZ toCIEXYZ() {
CIEXYZ toXYZ() {
double singleCoordToLAB(double t) {
if (t > t3) {
return pow(t, 1 / 3).toDouble();
Expand Down
Loading

1 comment on commit a0bb081

@vercel
Copy link

@vercel vercel bot commented on a0bb081 May 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.