mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Clamp overflows in Color.lerp. (flutter/engine#4141)
Previously, cases like: ```dart Color.lerp(const Color(0xFF00FF7F), const Color(0xFF00FFFF), 1.1) ``` ...would result in unexpected effects (in this instance, lerping between these colors with a curve that overshoots would take what should be a simple animation from pale green to blue and add some flickering bright green whenever it overshoots).
This commit is contained in:
parent
de14cec698
commit
56b4eb63d3
@ -8,3 +8,6 @@ script:
|
||||
- ./travis/build.sh
|
||||
- ./travis/test.sh
|
||||
- ./travis/format.sh
|
||||
|
||||
# We don't build the engine or run the tests for the engine on Travis
|
||||
# See testing/run_tests.sh if that's what you're looking for though.
|
||||
|
||||
@ -35,7 +35,7 @@ bool _offsetIsValid(Offset offset) {
|
||||
}
|
||||
|
||||
Color _scaleAlpha(Color a, double factor) {
|
||||
return a.withAlpha((a.alpha * factor).round());
|
||||
return a.withAlpha((a.alpha * factor).round().clamp(0, 255));
|
||||
}
|
||||
|
||||
/// An immutable 32 bit color value in ARGB format.
|
||||
@ -100,10 +100,10 @@ class Color {
|
||||
/// See also [fromARGB], which takes the alpha value as a floating point
|
||||
/// value.
|
||||
const Color.fromARGB(int a, int r, int g, int b) :
|
||||
value = ((((a & 0xff) << 24) |
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
((b & 0xff) << 0)) & 0xFFFFFFFF);
|
||||
value = (((a & 0xff) << 24) |
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
((b & 0xff) << 0)) & 0xFFFFFFFF;
|
||||
|
||||
/// Create a color from red, green, blue, and opacity, similar to `rgba()` in CSS.
|
||||
///
|
||||
@ -117,10 +117,10 @@ class Color {
|
||||
///
|
||||
/// See also [fromARGB], which takes the opacity as an integer value.
|
||||
const Color.fromRGBO(int r, int g, int b, double opacity) :
|
||||
value = (((((opacity * 0xff ~/ 1) & 0xff) << 24) |
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
((b & 0xff) << 0)) & 0xFFFFFFFF);
|
||||
value = ((((opacity * 0xff ~/ 1) & 0xff) << 24) |
|
||||
((r & 0xff) << 16) |
|
||||
((g & 0xff) << 8) |
|
||||
((b & 0xff) << 0)) & 0xFFFFFFFF;
|
||||
|
||||
/// A 32 bit value representing this color.
|
||||
///
|
||||
@ -155,31 +155,41 @@ class Color {
|
||||
|
||||
/// Returns a new color that matches this color with the alpha channel
|
||||
/// replaced with `a` (which ranges from 0 to 255).
|
||||
///
|
||||
/// Out of range values will have unexpected effects.
|
||||
Color withAlpha(int a) {
|
||||
return new Color.fromARGB(a, red, green, blue);
|
||||
}
|
||||
|
||||
/// Returns a new color that matches this color with the alpha channel
|
||||
/// replaced with the given `opacity` (which ranges from 0.0 to 1.0).
|
||||
///
|
||||
/// Out of range values will have unexpected effects.
|
||||
Color withOpacity(double opacity) {
|
||||
assert(opacity >= 0.0 && opacity <= 1.0);
|
||||
return withAlpha((255.0 * opacity).round());
|
||||
}
|
||||
|
||||
/// Returns a new color that matches this color with the red channel replaced
|
||||
/// with `r`.
|
||||
/// with `r` (which ranges from 0 to 255).
|
||||
///
|
||||
/// Out of range values will have unexpected effects.
|
||||
Color withRed(int r) {
|
||||
return new Color.fromARGB(alpha, r, green, blue);
|
||||
}
|
||||
|
||||
/// Returns a new color that matches this color with the green channel
|
||||
/// replaced with `g`.
|
||||
/// replaced with `g` (which ranges from 0 to 255).
|
||||
///
|
||||
/// Out of range values will have unexpected effects.
|
||||
Color withGreen(int g) {
|
||||
return new Color.fromARGB(alpha, red, g, blue);
|
||||
}
|
||||
|
||||
/// Returns a new color that matches this color with the blue channel replaced
|
||||
/// with `b`.
|
||||
/// with `b` (which ranges from 0 to 255).
|
||||
///
|
||||
/// Out of range values will have unexpected effects.
|
||||
Color withBlue(int b) {
|
||||
return new Color.fromARGB(alpha, red, green, b);
|
||||
}
|
||||
@ -188,6 +198,12 @@ class Color {
|
||||
///
|
||||
/// If either color is null, this function linearly interpolates from a
|
||||
/// transparent instance of the other color.
|
||||
///
|
||||
/// Values of `t` less that 0.0 or greater than 1.0 are supported. Each
|
||||
/// channel will be clamped to the range 0 to 255.
|
||||
///
|
||||
/// This is intended to be fast but as a result may be ugly. Consider
|
||||
/// [HSVColor] or writing custom logic for interpolating colors.
|
||||
static Color lerp(Color a, Color b, double t) {
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
@ -196,10 +212,10 @@ class Color {
|
||||
if (b == null)
|
||||
return _scaleAlpha(a, 1.0 - t);
|
||||
return new Color.fromARGB(
|
||||
lerpDouble(a.alpha, b.alpha, t).toInt(),
|
||||
lerpDouble(a.red, b.red, t).toInt(),
|
||||
lerpDouble(a.green, b.green, t).toInt(),
|
||||
lerpDouble(a.blue, b.blue, t).toInt()
|
||||
lerpDouble(a.alpha, b.alpha, t).toInt().clamp(0, 255),
|
||||
lerpDouble(a.red, b.red, t).toInt().clamp(0, 255),
|
||||
lerpDouble(a.green, b.green, t).toInt().clamp(0, 255),
|
||||
lerpDouble(a.blue, b.blue, t).toInt().clamp(0, 255),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ class NotAColor extends Color {
|
||||
}
|
||||
|
||||
void main() {
|
||||
test("color accessors should work", () {
|
||||
test('color accessors should work', () {
|
||||
Color foo = const Color(0x12345678);
|
||||
expect(foo.alpha, equals(0x12));
|
||||
expect(foo.red, equals(0x34));
|
||||
@ -19,14 +19,14 @@ void main() {
|
||||
expect(foo.blue, equals(0x78));
|
||||
});
|
||||
|
||||
test("paint set to black", () {
|
||||
test('paint set to black', () {
|
||||
Color c = const Color(0x00000000);
|
||||
Paint p = new Paint();
|
||||
p.color = c;
|
||||
expect(c.toString(), equals('Color(0x00000000)'));
|
||||
});
|
||||
|
||||
test("color created with out of bounds value", () {
|
||||
test('color created with out of bounds value', () {
|
||||
try {
|
||||
Color c = const Color(0x100 << 24);
|
||||
Paint p = new Paint();
|
||||
@ -36,7 +36,7 @@ void main() {
|
||||
}
|
||||
});
|
||||
|
||||
test("color created with wildly out of bounds value", () {
|
||||
test('color created with wildly out of bounds value', () {
|
||||
try {
|
||||
Color c = const Color(1 << 1000000);
|
||||
Paint p = new Paint();
|
||||
@ -46,7 +46,7 @@ void main() {
|
||||
}
|
||||
});
|
||||
|
||||
test("two colors are only == if they have the same runtime type", () {
|
||||
test('two colors are only == if they have the same runtime type', () {
|
||||
expect(const Color(123), equals(const Color(123)));
|
||||
expect(const Color(123), equals(new Color(123)));
|
||||
expect(const Color(123), isNot(equals(const Color(321))));
|
||||
@ -55,4 +55,26 @@ void main() {
|
||||
expect(const NotAColor(123), equals(const NotAColor(123)));
|
||||
});
|
||||
|
||||
test('Color.lerp', () {
|
||||
expect(
|
||||
Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 0.0),
|
||||
const Color(0x00000000),
|
||||
);
|
||||
expect(
|
||||
Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 0.5),
|
||||
const Color(0x7F7F7F7F),
|
||||
);
|
||||
expect(
|
||||
Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 1.0),
|
||||
const Color(0xFFFFFFFF),
|
||||
);
|
||||
expect(
|
||||
Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), -0.1),
|
||||
const Color(0x00000000),
|
||||
);
|
||||
expect(
|
||||
Color.lerp(const Color(0x00000000), const Color(0xFFFFFFFF), 1.1),
|
||||
const Color(0xFFFFFFFF),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user