Skip to content

color_operations: add color transform primitives#221

Open
waywardmonkeys wants to merge 1 commit into
linebender:mainfrom
waywardmonkeys:feat/colormatrix
Open

color_operations: add color transform primitives#221
waywardmonkeys wants to merge 1 commit into
linebender:mainfrom
waywardmonkeys:feat/colormatrix

Conversation

@waywardmonkeys
Copy link
Copy Markdown
Collaborator

Introduce the color_operations crate as a small no_std layer for per-pixel color transforms in caller-selected working color spaces.

Add ComponentTransfer and TransferFunction for per-channel identity, table, discrete, linear, and gamma operations. Add ColorMatrix for affine channel-mixing operations, including common constructors for opacity, brightness, contrast, invert, saturate, grayscale, hue rotation, sepia, and luminance-to-alpha.

Add ColorOperation for storing mixed matrix and component-transfer pipelines. Add typed apply support for AlphaColor, PremulColor, and DynamicColor, plus raw straight and premultiplied component helpers. DynamicColor operations preserve the runtime color-space tag and missing-component flags while discarding named-color state.

Operations intentionally do not clip, clamp, gamut-map, pack, quantize, or perform color-space conversion; those remain caller/rendering-boundary responsibilities.

@waywardmonkeys
Copy link
Copy Markdown
Collaborator Author

This was done with the assistance of Codex (GPT 5.5, xhigh).

Introduce the `color_operations` crate as a small `no_std` layer for per-pixel color transforms in caller-selected working color spaces.

Add `ComponentTransfer` and `TransferFunction` for per-channel identity, table, discrete, linear, and gamma operations. Add `ColorMatrix` for affine channel-mixing operations, including common constructors for opacity, brightness, contrast, invert, saturate, grayscale, hue rotation, sepia, and luminance-to-alpha.

Add `ColorOperation` for storing mixed matrix and component-transfer pipelines. Add typed apply support for `AlphaColor`, `PremulColor`, and `DynamicColor`, plus raw straight and premultiplied component helpers. `DynamicColor` operations preserve the runtime color-space tag and missing-component flags while discarding named-color state.

Operations intentionally do not clip, clamp, gamut-map, pack, quantize, or perform color-space conversion; those remain caller/rendering-boundary responsibilities.
Copy link
Copy Markdown
Member

@tomcur tomcur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it would be useful to have some primitives here for implementing these kinds of color filter effects.

I have some observations and unfinished initial thoughts.

The spec limits these filters to sRGB and linear sRGB (in fact, I believe in the spec all filters apply to linear sRGB by default). That is a sensible limitation though also a bit restrictive: these filters are not well-defined at all for e.g. Oklab or HSL, though some filters could make sense for any RGB color model (like Display P3).

Some filter short-hands introduced here, like invert, are a bit antithetical to how color handles gamuts: we allow all component values so any color space can represent any color, but invert is relative to the white point.

Some of the matrix operations have hard-coded coefficients for, e.g., saturation, hue-rotation, and luminance-to-alpha. These are derived for sRGB's chromaticities specifically, and (I believe) are only correct for linear sRGB.

Altogether, we could extract some nice general primitives, but most of the spec is quite opinionated. One thing I think we should not do is to have APIs that implicitly convert to/from (linear) sRGB to do the operations, as that could result in quite bad performance. We could get fancy and limit specific operations to specific color spaces, but that's a serious amount of type system noise for not that much benefit, and would be somewhat hard for users to extend.

There's a discussion in a different issue that touches on some related points: #192 (comment) (specifically, the discussion is about the cost of generics as well as color's inability to operate on slices of colors with a single dynamic color space.) Similarly, most operations introduced here are not truly generic over the color space: some are completely color-space-unaware transforms that simply perform math on the components, and others are very specific to (linear) sRGB (or at the very least, specific to RGB color models).

There may be two concerns in color that intermingle a bit: one is authoring and being clear about the color space, and the other is about bulk operation on color values. One option would be to just provide functions over [f32; 4], and document. The ones depending on linear sRGB's chromaticities and transfer function could be limited to LinearSrgb, but that would introduce some surprising asymmetry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants