osu-framework
osu-framework copied to clipboard
Colour API proposal
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 aroundColour4
. 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 aroundColour4
. 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 asColour4
.Colour4
(noosuTK
dependency, usesSystem.Numerics.Vector4
which should optimise better) should always be preferred overColor4
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 readonlySRGBColour
. - 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 inSRGBColour
. This is also necessary to remove the implicit conversionsColor4
/Colour4
<->SRGBColour
later on. - The change from
Color4
toColour4
insideSRGBColour
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
andColourInfo
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
- Returns a
- [ ] Remove
SRGBColour
operator overloads (*
,/
,+
) (breaking with low impact)- Use
ToLinear()
andLinearColour
instead - Make the expensive sRGB<->linear conversions explicit
- Chaining the
SRGBColour
operators is slow and imprecise and turns the operators into a footgun[^3]
- Use
- [ ] Replace
Color4
andColour4
in public API withSRGBColour
or maybeColour4
instead (breaking with very high impact)- can be done in steps
- Notable:
OsuColour
,Color4Extensions
- [ ] Remove implicit conversions between
Color4
/Colour4
andSRGBColour
(see #5714) (breaking with high impact) - [ ] Remove implicit conversions between
Color4
/Colour4
andColourInfo
(breaking with very high impact)- This is going to be a huge change (
drawable.Colour = Color4.Red
must be replaced withdrawable.Colour = SRGBColour.Red
), but can often be done with search-and-replace. - Benefit: Removes
osuTK
from a lot of places.
- This is going to be a huge change (
- [ ] 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.