Vixen
dd8b0a81
csharp
public readonly record struct UiColorMatrix

The per-pixel colour transform a composited group's filter applies to its surface.

Read the guide page for this →

Remarks

⚠ Three rows and not five, because none of the seven functions this represents touches alpha and none of them reads it. CSS's feColorMatrix is 4×5 — twenty coefficients, an alpha row and an alpha column — and every one of brightness, contrast, grayscale, invert, saturate, sepia and hue-rotate leaves the alpha row at 0 0 0 1 0 and the alpha column at zero. Storing the eight coefficients that are always the same buys nothing and costs thirty-two bytes in a push constant range that has to fit beside the projection.

⚠ Applied to premultiplied colour, with the offset scaled by alpha, and that is what makes the whole feature cost nothing. A colour matrix is defined on un-premultiplied colour: c' = M·(c/a) + o, so c'·a = M·c + o·a. The premultiplied form needs no division and no reconstruction — see Apply — which matters twice over. It means transparent black stays transparent black, so a viewport-sized surface whose group inks a corner of it does not acquire a rectangle of invert(1) white everywhere the group is not; and it means the transform is linear in the sampled value, so it commutes exactly with a bilinear sampler and with the Gaussian in ui-blur.frag. See Filter, which leans on that commutation to run the two filters in whichever order is cheap.

⚠ The arithmetic is in the engine's linear working space, and browsers do it in sRGB. Filter Effects 1 § 8.5 says the shorthand functions run with color-interpolation-filters: sRGB, so a browser's grayscale(1) averages gamma-encoded values. Everything in Vixen is linear from the parser down — StyleValueParser converts on the way in — so matching a browser exactly would mean an encode and a decode per pixel in both executors, to reproduce a rule the spec itself calls a legacy default. What that costs is that a grayscale-50 here is slightly darker than the same class in a browser; what it buys is that the transform stays linear, which is the property the paragraph above spends. Written down rather than discovered.

⚠ default is not the identity, and the nullable is not decoration. An all-zero matrix maps every colour to transparent-adjacent black, so a consumer that read a zeroed field as "no filter" would be right about the intent and wrong about the picture. Both Filter and Filter are UiColorMatrix? for exactly that reason: absence is null, never a struct that happens to be zeroed.

Fields and properties (6)

  • public Vector4 Red

    The red output: three coefficients and an offset.

  • public Vector4 Green

    The green output.

  • public Vector4 Blue

    The blue output.

  • public static readonly Vector3 Luminance

    The luminance weights grayscale() and saturate() are defined with.

  • public static UiColorMatrix Identity

    The transform that changes nothing.

  • public bool IsIdentity

    Whether this is Identity and so worth nothing to apply.

Methods (10)

  • public UiColorMatrix(Vector4 Red, Vector4 Green, Vector4 Blue)

    The per-pixel colour transform a composited group's filter applies to its surface.

  • public Color4 Apply(Color4 colour)

    Transforms one premultiplied colour.

  • public UiColorMatrix Then(in UiColorMatrix next)

    This transform, and then .

  • public static UiColorMatrix Brightness(float amount)

    Lightens or darkens: c' = c · amount.

  • public static UiColorMatrix Contrast(float amount)

    Pushes towards or away from mid grey: c' = c · amount + (0.5 − 0.5 · amount).

  • public static UiColorMatrix Grayscale(float amount)

    Drains colour towards luminance.

  • public static UiColorMatrix Saturate(float amount)

    Scales the distance from luminance.

  • public static UiColorMatrix Sepia(float amount)

    Ages towards a warm monochrome.

  • public static UiColorMatrix Invert(float amount)

    Flips towards the complement: c' = c + amount · (1 − 2c).

  • public static UiColorMatrix HueRotate(float degrees)

    Rotates the hue about the luminance axis.

Used by (13)

  • BackdropFilterTestsVixen.Ui.Controls.Tests
  • DrawCommandVixen.Ui
  • DrawListBuilderVixen.Ui
  • ElementFilterVixen.Ui
  • FrameVixen.Ui.Testing
  • OpeningVixen.Ui
  • UiBackdropVixen.Ui
  • UiCompositingTestsVixen.Graphics.Golden.Tests
  • UiDropShadowVixen.Ui
  • UiLayerVixen.Ui
  • UiRendererVixen.Ui.Renderer
  • EditorUiCompositingDeviceTestsVixen.Editor.App.Tests
  • UtilityFamilySupportTestsVixen.Editor.Ui.Tests