Color
Color operations follow the W3C Filter Effects definitions, so they match what a browser's CSS filter does.
photo.Apply(ops => ops
.Brightness(1.05f) // 1 changes nothing
.Contrast(1.1f)
.Saturation(1.2f)
.HueRotate(10), Ct); // these four run as one pass
using Raster bw = photo.Derive(ops => ops.Grayscale(LumaWeights.Rec709), Ct);
using Raster old = photo.Derive(ops => ops.Sepia(0.8f).Opacity(0.9f), Ct);
| Operation | 1 (or 0°) | Range |
|---|---|---|
Brightness(amount) |
No change | 0 gives black; above 1 brightens |
Contrast(amount) |
No change | 0 gives flat gray |
Saturation(amount) |
No change | 0 gives gray; above 1 oversaturates |
HueRotate(degrees) |
No change at 0° and 360° | Any angle |
Sepia(amount) |
Full sepia | 0 to 1 |
Grayscale(weights, amount, colorSpace) |
Full gray | 0 to 1; LumaWeights.Rec709 (default) or Rec601 |
Invert(amount) |
Full inversion | 0 to 1 |
Opacity(amount) |
No change | 0 to 1, multiplies alpha |
Adjacent color steps are multiplied together and run as one pass over the pixels.
Color matrices
// A ColorMatrix is 4 rows of 5 values: each output channel is a weighted sum of R, G, B, A plus an offset.
// Combine the ready-made ones with Then; no clamping happens in between.
ColorMatrix warm = ColorMatrix.Saturation(1.1f).Then(new ColorMatrix(
[
1.05f, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 0.92f, 0, 0,
0, 0, 0, 1, 0,
]));
photo.Apply(ops => ops.ColorMatrix(warm), Ct);
default(ColorMatrix) is the identity, so an uninitialized matrix changes nothing.
Encoded or linear
Grayscale takes a ColorSpace: Encoded (the default) weighs the stored sRGB values as most software does; Linear weighs
light, which keeps the perceived brightness of saturated colors. Converting a color raster to Gray8 with As<Gray8>() uses
Rec. 709 luma in linear light.
Run it
samples/Color is a console app that does what this guide shows on the bundled
images: dotnet run --project samples/Color writes its results to ./output.