-
Notifications
You must be signed in to change notification settings - Fork 6k
Update Color to do all calculations with floating point components #54981
Changes from all commits
36ca48a
66f8393
138f388
7493253
745a8a7
94e4b23
2aebceb
26595ac
aa290db
4466d89
e45bb0c
dadd6c9
72dca37
19adc88
512d0a1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,8 +48,8 @@ bool _radiusIsValid(Radius radius) { | |
| return true; | ||
| } | ||
|
|
||
| Color _scaleAlpha(Color a, double factor) { | ||
| return a.withAlpha((a.alpha * factor).round().clamp(0, 255)); | ||
| Color _scaleAlpha(Color x, double factor) { | ||
| return x.withValues(alpha: clampDouble(x.a * factor, 0, 1)); | ||
| } | ||
|
|
||
| /// An immutable 32 bit color value in ARGB format. | ||
|
|
@@ -310,10 +310,11 @@ class Color { | |
| /// | ||
| /// See <https://en.wikipedia.org/wiki/Relative_luminance>. | ||
| double computeLuminance() { | ||
| assert(colorSpace != ColorSpace.extendedSRGB); | ||
| // See <https://www.w3.org/TR/WCAG20/#relativeluminancedef> | ||
| final double R = _linearizeColorComponent(red / 0xFF); | ||
| final double G = _linearizeColorComponent(green / 0xFF); | ||
| final double B = _linearizeColorComponent(blue / 0xFF); | ||
| final double R = _linearizeColorComponent(r); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that we have colorspaces, should these be taught to clamp or convert to a non-extended CS as well?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an assert that extended srgb is in't used. I don't think it will make sense with that. |
||
| final double G = _linearizeColorComponent(g); | ||
| final double B = _linearizeColorComponent(b); | ||
| return 0.2126 * R + 0.7152 * G + 0.0722 * B; | ||
| } | ||
|
|
||
|
|
@@ -339,28 +340,26 @@ class Color { | |
| /// | ||
| /// Values for `t` are usually obtained from an [Animation<double>], such as | ||
| /// an [AnimationController]. | ||
| static Color? lerp(Color? a, Color? b, double t) { | ||
| // TODO(gaaclarke): Update math to use floats. This was already attempted | ||
| // but it leads to subtle changes that change test results. | ||
| assert(a?.colorSpace != ColorSpace.extendedSRGB); | ||
| assert(b?.colorSpace != ColorSpace.extendedSRGB); | ||
| if (b == null) { | ||
| if (a == null) { | ||
| static Color? lerp(Color? x, Color? y, double t) { | ||
| assert(x?.colorSpace != ColorSpace.extendedSRGB); | ||
| assert(y?.colorSpace != ColorSpace.extendedSRGB); | ||
| if (y == null) { | ||
| if (x == null) { | ||
| return null; | ||
| } else { | ||
| return _scaleAlpha(a, 1.0 - t); | ||
| return _scaleAlpha(x, 1.0 - t); | ||
| } | ||
| } else { | ||
| if (a == null) { | ||
| return _scaleAlpha(b, t); | ||
| if (x == null) { | ||
| return _scaleAlpha(y, t); | ||
| } else { | ||
| assert(a.colorSpace == b.colorSpace); | ||
| return Color._fromARGBC( | ||
| _clampInt(_lerpInt(a.alpha, b.alpha, t).toInt(), 0, 255), | ||
| _clampInt(_lerpInt(a.red, b.red, t).toInt(), 0, 255), | ||
| _clampInt(_lerpInt(a.green, b.green, t).toInt(), 0, 255), | ||
| _clampInt(_lerpInt(a.blue, b.blue, t).toInt(), 0, 255), | ||
| a.colorSpace, | ||
| assert(x.colorSpace == y.colorSpace); | ||
| return Color.from( | ||
| alpha: clampDouble(_lerpDouble(x.a, y.a, t), 0, 1), | ||
| red: clampDouble(_lerpDouble(x.r, y.r, t), 0, 1), | ||
| green: clampDouble(_lerpDouble(x.g, y.g, t), 0, 1), | ||
| blue: clampDouble(_lerpDouble(x.b, y.b, t), 0, 1), | ||
| colorSpace: x.colorSpace, | ||
| ); | ||
| } | ||
| } | ||
|
|
@@ -377,32 +376,30 @@ class Color { | |
| static Color alphaBlend(Color foreground, Color background) { | ||
| assert(foreground.colorSpace == background.colorSpace); | ||
| assert(foreground.colorSpace != ColorSpace.extendedSRGB); | ||
| // TODO(gaaclarke): Update math to use floats. This was already attempted | ||
| // but it leads to subtle changes that change test results. | ||
| final int alpha = foreground.alpha; | ||
| if (alpha == 0x00) { // Foreground completely transparent. | ||
| final double alpha = foreground.a; | ||
| if (alpha == 0) { // Foreground completely transparent. | ||
| return background; | ||
| } | ||
| final int invAlpha = 0xff - alpha; | ||
| int backAlpha = background.alpha; | ||
| if (backAlpha == 0xff) { // Opaque background case | ||
| return Color._fromARGBC( | ||
| 0xff, | ||
| (alpha * foreground.red + invAlpha * background.red) ~/ 0xff, | ||
| (alpha * foreground.green + invAlpha * background.green) ~/ 0xff, | ||
| (alpha * foreground.blue + invAlpha * background.blue) ~/ 0xff, | ||
| foreground.colorSpace, | ||
| final double invAlpha = 1 - alpha; | ||
| double backAlpha = background.a; | ||
| if (backAlpha == 1) { // Opaque background case | ||
| return Color.from( | ||
| alpha: 1, | ||
| red: alpha * foreground.r + invAlpha * background.r, | ||
| green: alpha * foreground.g + invAlpha * background.g, | ||
| blue: alpha * foreground.b + invAlpha * background.b, | ||
| colorSpace: foreground.colorSpace, | ||
| ); | ||
| } else { // General case | ||
| backAlpha = (backAlpha * invAlpha) ~/ 0xff; | ||
| final int outAlpha = alpha + backAlpha; | ||
| assert(outAlpha != 0x00); | ||
| return Color._fromARGBC( | ||
| outAlpha, | ||
| (foreground.red * alpha + background.red * backAlpha) ~/ outAlpha, | ||
| (foreground.green * alpha + background.green * backAlpha) ~/ outAlpha, | ||
| (foreground.blue * alpha + background.blue * backAlpha) ~/ outAlpha, | ||
| foreground.colorSpace, | ||
| backAlpha = backAlpha * invAlpha; | ||
| final double outAlpha = alpha + backAlpha; | ||
| assert(outAlpha != 0); | ||
| return Color.from( | ||
| alpha: outAlpha, | ||
| red: (foreground.r * alpha + background.r * backAlpha) / outAlpha, | ||
| green: (foreground.g * alpha + background.g * backAlpha) / outAlpha, | ||
| blue: (foreground.b * alpha + background.b * backAlpha) / outAlpha, | ||
| colorSpace: foreground.colorSpace, | ||
| ); | ||
| } | ||
| } | ||
|
|
@@ -423,16 +420,19 @@ class Color { | |
| return false; | ||
| } | ||
| return other is Color && | ||
| other.value == value && | ||
| other.a == a && | ||
| other.r == r && | ||
| other.g == g && | ||
| other.b == b && | ||
| other.colorSpace == colorSpace; | ||
| } | ||
|
|
||
| @override | ||
| int get hashCode => Object.hash(value, colorSpace); | ||
| int get hashCode => Object.hash(a, r, g, b, colorSpace); | ||
|
|
||
| // TODO(gaaclarke): Make toString() print out float values. | ||
| @override | ||
| String toString() => 'Color(0x${value.toRadixString(16).padLeft(8, '0')})'; | ||
| String toString() => | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4 sig figs was chosen since hdmi 1.3 can support 12 bits per color component and 1/2^12 = 0.00024
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scientific notation could have been another option if we wanted ( 2.4e-4 ). |
||
| 'Color(alpha: ${a.toStringAsFixed(4)}, red: ${r.toStringAsFixed(4)}, green: ${g.toStringAsFixed(4)}, blue: ${b.toStringAsFixed(4)}, colorSpace: $colorSpace)'; | ||
| } | ||
|
|
||
| /// Algorithms to use when painting on the canvas. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typically I'd call
Color.toString()here but I'm unable to importdart:uihere for some reason.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a VM test, so that is why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it's weird that I'm asserting behavior for
dart:ui, so it's there, it's just not visible to me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its basically a compiler test though