Skip to content

Bevy Colors V2 #10986

@viridia

Description

@viridia

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ColorColor spaces and color mathA-UtilsUtility functions and typesC-FeatureA new feature, making something new possible

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions