osu-framework icon indicating copy to clipboard operation
osu-framework copied to clipboard

Colour API proposal

Open FreezyLemon opened this issue 3 months ago • 2 comments

The changes in this PR are the additions needed to implement the proposal as a usable API. More things might need to be added later. Breaking changes are described below but not included in the PR so that reading the code is easier.

Keep in mind that this is a proposal and while the commits in this PR could be merged, they exist more to demonstrate the API as code. I don't mind changing stuff.

API overview

  • ColourInfo: Semantically unchanged. Describes the colours of the four vertices of a quad.
  • SRGBColour: Main single-colour type that framework users interact with. Contains a gamma-corrected sRGB colour. Thin wrapper around Colour4. The type discourages invalid things like gamma-incorrect colour math[^1].
  • LinearColour (new): Secondary colour type for framework users. Contains a linear-light sRGB colour[^2]. Thin wrapper around Colour4. Basically only exists to do colour math.
  • Colour4: Should rarely be used directly. Low level, raw colour values, no inherent colour space. This is where the real math (and optimization) should happen so that all abstractions can benefit.
  • Color4: Semantically the same as Colour4. Colour4 (no osuTK dependency, uses System.Numerics.Vector4 which should optimise better) should always be preferred over Color4 unless interacting with an osuTK API that requires it.

Implementation notes

  • Turning MultiplyAlpha into a pure method is a hidden trap for existing usage ([Pure] should help with this, but not sure it's enough). Renaming it is an alternative, but making it pure is a requirement for a readonly SRGBColour.
  • The colour constants Color4.Red etc. are copies of the CSS <named-color> values and are defined to be in the sRGB colour space, so they should live in SRGBColour. This is also necessary to remove the implicit conversions Color4/Colour4 <-> SRGBColour later on.
  • The change from Color4 to Colour4 inside SRGBColour is most likely going to have performance implications. I am not sure how to test this because colours are used everywhere, and I do not have enough insight into the renderer implementations to know how this impacts them.

Next steps

This list is mostly unordered.

  • [X] Update Colour4 and ColourInfo docs
  • [X] Make SRGBColour readonly and extend API to cover common usage
  • [X] Implement LinearColour
  • [ ] Remove SRGBColour.Linear (breaking with low impact)
    • Returns a Colour4 which loses colour space information
    • ToLinear() should be used instead
  • [ ] Remove SRGBColour operator overloads (*, /, +) (breaking with low impact)
    • Use ToLinear() and LinearColour instead
    • Make the expensive sRGB<->linear conversions explicit
    • Chaining the SRGBColour operators is slow and imprecise and turns the operators into a footgun[^3]
  • [ ] Replace Color4 and Colour4 in public API with SRGBColour or maybe Colour4 instead (breaking with very high impact)
    • can be done in steps
    • Notable: OsuColour, Color4Extensions
  • [ ] Remove implicit conversions between Color4/Colour4 and SRGBColour (see #5714) (breaking with high impact)
  • [ ] Remove implicit conversions between Color4/Colour4 and ColourInfo (breaking with very high impact)
    • This is going to be a huge change (drawable.Colour = Color4.Red must be replaced with drawable.Colour = SRGBColour.Red), but can often be done with search-and-replace.
    • Benefit: Removes osuTK from a lot of places.
  • [ ] Figure out what to do with colour-related Bindable types
  • [ ] Figure out what to do with colour pickers
  • [ ] Maybe move hexcode parsing and related code to SRGBColour (these are also semantically sRGB)
  • [ ] Maybe remove colour constants in Colour4

[^1]: Gamma-incorrect operations could be done with helper functions. [^2]: As described here (search for "srgb-linear"). Can also be called "linear sRGB" or "gamma-expanded sRGB". This is usually called "linear colour"/"linear colour space" in and around o!f, which is why the struct is named LinearColour. [^3]: For example, a * b * c will: Convert a and b to linear. Multiply those two. Convert the result back to sRGB. Convert the first result and c to linear. Multiply those two. Convert that result back to sRGB.

FreezyLemon avatar May 25 '24 21:05 FreezyLemon