| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:math' as math; |
| import 'dart:ui' show Color, lerpDouble, hashValues; |
| |
| import 'package:flutter/foundation.dart'; |
| |
| /// A color represented using [alpha], [hue], [saturation], and [value]. |
| /// |
| /// An [HSVColor] is represented in a parameter space that's motivated by human |
| /// perception. The representation is useful for some color computations (e.g., |
| /// rotating the hue through the colors of the rainbow). |
| @immutable |
| class HSVColor { |
| /// Creates a color. |
| /// |
| /// All the arguments must not be null and be in their respective ranges. See |
| /// the fields for each parameter for a description of their ranges. |
| const HSVColor.fromAHSV(this.alpha, this.hue, this.saturation, this.value) |
| : assert(alpha != null), |
| assert(hue != null), |
| assert(saturation != null), |
| assert(value != null); |
| |
| /// Creates an [HSVColor] from an RGB [Color]. |
| /// |
| /// This constructor does not necessarily round-trip with [toColor] because |
| /// of floating point imprecision. |
| factory HSVColor.fromColor(Color color) { |
| final double alpha = color.alpha / 0xFF; |
| final double red = color.red / 0xFF; |
| final double green = color.green / 0xFF; |
| final double blue = color.blue / 0xFF; |
| |
| final double max = math.max(red, math.max(green, blue)); |
| final double min = math.min(red, math.min(green, blue)); |
| final double delta = max - min; |
| |
| double hue = 0.0; |
| |
| if (max == 0.0) { |
| hue = 0.0; |
| } else if (max == red) { |
| hue = 60.0 * (((green - blue) / delta) % 6); |
| } else if (max == green) { |
| hue = 60.0 * (((blue - red) / delta) + 2); |
| } else if (max == blue) { |
| hue = 60.0 * (((red - green) / delta) + 4); |
| } |
| |
| final double saturation = max == 0.0 ? 0.0 : delta / max; |
| |
| return new HSVColor.fromAHSV(alpha, hue, saturation, max); |
| } |
| |
| /// Alpha, from 0.0 to 1.0. |
| final double alpha; |
| |
| /// Hue, from 0.0 to 360.0. |
| final double hue; |
| |
| /// Saturation, from 0.0 to 1.0. |
| final double saturation; |
| |
| /// Value, from 0.0 to 1.0. |
| final double value; |
| |
| /// Returns a copy of this color with the alpha parameter replaced with the given value. |
| HSVColor withAlpha(double alpha) { |
| return new HSVColor.fromAHSV(alpha, hue, saturation, value); |
| } |
| |
| /// Returns a copy of this color with the hue parameter replaced with the given value. |
| HSVColor withHue(double hue) { |
| return new HSVColor.fromAHSV(alpha, hue, saturation, value); |
| } |
| |
| /// Returns a copy of this color with the saturation parameter replaced with the given value. |
| HSVColor withSaturation(double saturation) { |
| return new HSVColor.fromAHSV(alpha, hue, saturation, value); |
| } |
| |
| /// Returns a copy of this color with the value parameter replaced with the given value. |
| HSVColor withValue(double value) { |
| return new HSVColor.fromAHSV(alpha, hue, saturation, value); |
| } |
| |
| /// Returns this color in RGB. |
| Color toColor() { |
| final double h = hue % 360; |
| final double c = saturation * value; |
| final double x = c * (1 - (((h / 60.0) % 2) - 1).abs()); |
| final double m = value - c; |
| |
| double r; |
| double g; |
| double b; |
| if (h < 60.0) { |
| r = c; |
| g = x; |
| b = 0.0; |
| } else if (h < 120.0) { |
| r = x; |
| g = c; |
| b = 0.0; |
| } else if (h < 180.0) { |
| r = 0.0; |
| g = c; |
| b = x; |
| } else if (h < 240.0) { |
| r = 0.0; |
| g = x; |
| b = c; |
| } else if (h < 300.0) { |
| r = x; |
| g = 0.0; |
| b = c; |
| } else { |
| r = c; |
| g = 0.0; |
| b = x; |
| } |
| return new Color.fromARGB( |
| (alpha * 0xFF).round(), |
| ((r + m) * 0xFF).round(), |
| ((g + m) * 0xFF).round(), |
| ((b + m) * 0xFF).round() |
| ); |
| } |
| |
| HSVColor _scaleAlpha(double factor) { |
| return withAlpha(alpha * factor); |
| } |
| |
| /// Linearly interpolate between two HSVColors. |
| /// |
| /// If either color is null, this function linearly interpolates from a |
| /// transparent instance of the other color. |
| static HSVColor lerp(HSVColor a, HSVColor b, double t) { |
| if (a == null && b == null) |
| return null; |
| if (a == null) |
| return b._scaleAlpha(t); |
| if (b == null) |
| return a._scaleAlpha(1.0 - t); |
| return new HSVColor.fromAHSV( |
| lerpDouble(a.alpha, b.alpha, t), |
| lerpDouble(a.hue, b.hue, t), |
| lerpDouble(a.saturation, b.saturation, t), |
| lerpDouble(a.value, b.value, t) |
| ); |
| } |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (identical(this, other)) |
| return true; |
| if (other is! HSVColor) |
| return false; |
| final HSVColor typedOther = other; |
| return typedOther.alpha == alpha |
| && typedOther.hue == hue |
| && typedOther.saturation == saturation |
| && typedOther.value == value; |
| } |
| |
| @override |
| int get hashCode => hashValues(alpha, hue, saturation, value); |
| |
| @override |
| String toString() => 'HSVColor($alpha, $hue, $saturation, $value)'; |
| } |
| |
| /// A color that has a small table of related colors called a "swatch". |
| /// |
| /// The table is indexed by values of type `T`. |
| /// |
| /// See also: |
| /// |
| /// * [MaterialColor] and [MaterialAccentColor], which define material design |
| /// primary and accent color swatches. |
| /// * [material.Colors], which defines all of the standard material design colors. |
| class ColorSwatch<T> extends Color { |
| /// Creates a color that has a small table of related colors called a "swatch". |
| const ColorSwatch(int primary, this._swatch) : super(primary); |
| |
| @protected |
| final Map<T, Color> _swatch; |
| |
| /// Returns an element of the swatch table. |
| Color operator [](T index) => _swatch[index]; |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (identical(this, other)) |
| return true; |
| if (other.runtimeType != runtimeType) |
| return false; |
| final ColorSwatch<T> typedOther = other; |
| return super==(other) && _swatch == typedOther._swatch; |
| } |
| |
| @override |
| int get hashCode => hashValues(runtimeType, value, _swatch); |
| |
| @override |
| String toString() => '$runtimeType(primary value: ${super.toString()})'; |
| } |