Skip to content

Commit 2c92c89

Browse files
authored
[Color 4] Update color.same() and color equality (#2232)
See sass/sass#3852 See sass/sass#3858
1 parent f0dc4cd commit 2c92c89

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

lib/src/functions/color.dart

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,38 @@ final module = BuiltInModule("color", functions: <Callable>[
504504
var color1 = arguments[0].assertColor('color1');
505505
var color2 = arguments[1].assertColor('color2');
506506

507-
// Convert both colors into the same space to compare them. Usually we
508-
// just use color1's space, but since HSL and HWB can't represent
509-
// out-of-gamut colors we use RGB for all legacy color spaces.
510-
var targetSpace = color1.isLegacy ? ColorSpace.rgb : color1.space;
511-
return SassBoolean(
512-
color1.toSpace(targetSpace) == color2.toSpace(targetSpace));
507+
/// Converts [color] to the xyz-d65 space without any mising channels.
508+
SassColor toXyzNoMissing(SassColor color) => switch (color) {
509+
SassColor(space: ColorSpace.xyzD65, hasMissingChannel: false) =>
510+
color,
511+
SassColor(
512+
space: ColorSpace.xyzD65,
513+
:var channel0,
514+
:var channel1,
515+
:var channel2,
516+
:var alpha
517+
) =>
518+
SassColor.xyzD65(channel0, channel1, channel2, alpha),
519+
SassColor(
520+
:var space,
521+
:var channel0,
522+
:var channel1,
523+
:var channel2,
524+
:var alpha
525+
) =>
526+
// Use [ColorSpace.convert] manually so that we can convert missing
527+
// channels to 0 without having to create new intermediate color
528+
// objects.
529+
space.convert(
530+
ColorSpace.xyzD65, channel0, channel1, channel2, alpha)
531+
};
532+
533+
return SassBoolean(color1.space == color2.space
534+
? fuzzyEquals(color1.channel0, color2.channel0) &&
535+
fuzzyEquals(color1.channel1, color2.channel1) &&
536+
fuzzyEquals(color1.channel2, color2.channel2) &&
537+
fuzzyEquals(color1.alpha, color2.alpha)
538+
: toXyzNoMissing(color1) == toXyzNoMissing(color2));
513539
}),
514540

515541
_function(

lib/src/util/number.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ bool fuzzyEquals(num number1, num number2) {
3030
(number2 * _inverseEpsilon).round();
3131
}
3232

33+
/// Like [fuzzyEquals], but allows null values for [number1] and [number2].
34+
///
35+
/// null values are only equal to one another.
36+
bool fuzzyEqualsNullable(num? number1, num? number2) {
37+
if (number1 == number2) return true;
38+
if (number1 == null || number2 == null) return false;
39+
return (number1 - number2).abs() <= _epsilon &&
40+
(number1 * _inverseEpsilon).round() ==
41+
(number2 * _inverseEpsilon).round();
42+
}
43+
3344
/// Returns a hash code for [number] that matches [fuzzyEquals].
3445
int fuzzyHashCode(double number) {
3546
if (!number.isFinite) return number.hashCode;

lib/src/value/color.dart

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,16 @@ class SassColor extends Value {
213213
_ => true
214214
};
215215

216+
/// Whether this color has any missing channels.
217+
///
218+
/// @nodoc
219+
@internal
220+
bool get hasMissingChannel =>
221+
isChannel0Missing ||
222+
isChannel1Missing ||
223+
isChannel2Missing ||
224+
isAlphaMissing;
225+
216226
/// This color's red channel, between `0` and `255`.
217227
///
218228
/// **Note:** This is rounded to the nearest integer, which may be lossy. Use
@@ -953,21 +963,21 @@ class SassColor extends Value {
953963

954964
if (isLegacy) {
955965
if (!other.isLegacy) return false;
956-
if (!fuzzyEquals(alpha, other.alpha)) return false;
957-
if (space == ColorSpace.rgb && other.space == ColorSpace.rgb) {
958-
return fuzzyEquals(channel0, other.channel0) &&
959-
fuzzyEquals(channel1, other.channel1) &&
960-
fuzzyEquals(channel2, other.channel2);
966+
if (!fuzzyEqualsNullable(alphaOrNull, other.alphaOrNull)) return false;
967+
if (space == other.space) {
968+
return fuzzyEqualsNullable(channel0OrNull, other.channel0OrNull) &&
969+
fuzzyEqualsNullable(channel1OrNull, other.channel1OrNull) &&
970+
fuzzyEqualsNullable(channel2OrNull, other.channel2OrNull);
961971
} else {
962972
return toSpace(ColorSpace.rgb) == other.toSpace(ColorSpace.rgb);
963973
}
964974
}
965975

966976
return space == other.space &&
967-
fuzzyEquals(channel0, other.channel0) &&
968-
fuzzyEquals(channel1, other.channel1) &&
969-
fuzzyEquals(channel2, other.channel2) &&
970-
fuzzyEquals(alpha, other.alpha);
977+
fuzzyEqualsNullable(channel0OrNull, other.channel0OrNull) &&
978+
fuzzyEqualsNullable(channel1OrNull, other.channel1OrNull) &&
979+
fuzzyEqualsNullable(channel2OrNull, other.channel2OrNull) &&
980+
fuzzyEqualsNullable(alphaOrNull, other.alphaOrNull);
971981
}
972982

973983
int get hashCode {

0 commit comments

Comments
 (0)