There's been some discussion about rethinking the way Bevy represents colors. This ticket is a brief summary of the issues; a formal specification will come later.
Statement of the problem
The current representation of Color is an enum which provides an enumeration of various color spaces (SRGB, HSL, etc.) Unfortunately, this flexibility comes at a cost:
- It's difficult to add additional color spaces without changing a lot of Bevy engine code.
- There are many cases where its more performant to have the color space is known at compile time. For example, when specifying things like lighting colors or shader params, supporting multiple color spaces incurs runtime overhead.
- Some operations are easier to do in some color spaces than others. For example, hue shift is trivial in HSL space, but more complex in RGB. Adding convenience methods for these kinds of operations introduces additional complexity for little apparent value.
- Someone wanting a nice color gradient can easily convert their RGBA colors to Oklab or HSL and then call
mix() on the result.
- Similarly, if you want to do a hue shift, convert to HSL, modify the hue, and convert back. This is better than having Bevy try to implement hue shifts in RGB.
- Users would like color operations such as
lerp or mix which produce different results for different color spaces. Moreover, it either requires that all of the inputs be in the same color space, or it must automatically convert the inputs to a common space. Both of these choices create additional complexity and runtime overhead.
- Having separate types for separate color spaces can help avoid footguns around forgetting to convert to the correct color space.
Proposed solution
A new bevy crate will be introduced, named bevy_color. This will have concrete types for several different color spaces:
LinRgbaColor
LinRgbColor (Note: it's an open question as to whether we need the non-alpha colors).
SRgbaColor
SRgbColor
HslColor
OklabColor
- And possibly others as needed.
- Bikeshedding on the names is permitted :)
The conversion between color spaces will be implemented via the From trait. These conversions will never fail; if a color cannot be represented in the target space, then a suitable, reasonably close value will be chosen. The only methods that return an error result will be deserialization / decoding methods.
In addition, for the Linear and SRGB spaces, there will be a .transmute_xxx() method to deal with the rare case where you have the wrong type encoded - i.e. the image decoder only produces SRGB types, but the actual image file is linear.
There will be traits which add additional color manipulation operations. Not all traits will be defined for every color space:
Mix (linear interpolation)
Add
Multiply
Darken
Lighten
Saturate
Desaturate
HexString (Convert to/from "#nnnnnn")
CssString (Convert to/from "hsl(60, 100%, 50%)" for example)
A11y (e.g. .is_readable(contrasting_color))
Because the existing Color class is used so widely, it will be retained for the next several Bevy releases to ease migration. It may eventually be replaced with a new class which allows multiple bevy_color colors to be represented in an enum.
To ease the pain of transition, API methods that accept a Color can be updated to accept an impl IntoLinRgba trait which automatically converts the argument to LinRgba.
To address the need for named standard colors (like Color::Red), a new namespace bevy_color::names will contain definitions for all the standard colors.
Alternative solutions
There has been proposals to integrate the palette crate, which is a very full-featured library for color representation and manipulation. However, a cursory look at this crate's source code suggests that it is over-engineered for our purpose. Bevy would be better served with a more lightweight solution that only includes the capabilities we are likely to actually need.
See also #1402
There's been some discussion about rethinking the way Bevy represents colors. This ticket is a brief summary of the issues; a formal specification will come later.
Statement of the problem
The current representation of
Coloris an enum which provides an enumeration of various color spaces (SRGB, HSL, etc.) Unfortunately, this flexibility comes at a cost:mix()on the result.lerpormixwhich produce different results for different color spaces. Moreover, it either requires that all of the inputs be in the same color space, or it must automatically convert the inputs to a common space. Both of these choices create additional complexity and runtime overhead.Proposed solution
A new bevy crate will be introduced, named
bevy_color. This will have concrete types for several different color spaces:LinRgbaColorLinRgbColor(Note: it's an open question as to whether we need the non-alpha colors).SRgbaColorSRgbColorHslColorOklabColorThe conversion between color spaces will be implemented via the
Fromtrait. These conversions will never fail; if a color cannot be represented in the target space, then a suitable, reasonably close value will be chosen. The only methods that return an error result will be deserialization / decoding methods.In addition, for the Linear and SRGB spaces, there will be a
.transmute_xxx()method to deal with the rare case where you have the wrong type encoded - i.e. the image decoder only produces SRGB types, but the actual image file is linear.There will be traits which add additional color manipulation operations. Not all traits will be defined for every color space:
Mix(linear interpolation)AddMultiplyDarkenLightenSaturateDesaturateHexString(Convert to/from"#nnnnnn")CssString(Convert to/from"hsl(60, 100%, 50%)"for example)A11y(e.g..is_readable(contrasting_color))Because the existing
Colorclass is used so widely, it will be retained for the next several Bevy releases to ease migration. It may eventually be replaced with a new class which allows multiplebevy_colorcolors to be represented in an enum.To ease the pain of transition, API methods that accept a
Colorcan be updated to accept animpl IntoLinRgbatrait which automatically converts the argument to LinRgba.To address the need for named standard colors (like
Color::Red), a new namespace bevy_color::names will contain definitions for all the standard colors.Alternative solutions
There has been proposals to integrate the
palettecrate, which is a very full-featured library for color representation and manipulation. However, a cursory look at this crate's source code suggests that it is over-engineered for our purpose. Bevy would be better served with a more lightweight solution that only includes the capabilities we are likely to actually need.See also #1402