Deterministic, fixed-point color processing for embedded Rust. ph-color
provides typed color values, transfer functions, gamut matrices, per-channel
gain, interpolation, and quantization without a heap or floating-point
dependency on its default target path.
Note
Status: Active. ph-color is published at version 0.1.1, and
ph-color-bake is published at version 0.1.0.
The API is intentionally narrow rather than a comprehensive color library.
| Crate | Use it for | Runtime |
|---|---|---|
ph-color |
Applying baked transforms, transfer tables, gain, interpolation, and quantization in firmware | no_std, no allocation, no unsafe code, no default dependencies |
ph-color-bake |
Deriving matrices and lookup tables and emitting auditable Rust const source |
Host-side std and f64 |
Most applications depend only on ph-color. Run ph-color-bake on a
workstation when you need custom coefficients or tables; do not link the baker
into target firmware.
Add the target crate:
[dependencies]
ph-color = "0.1"Decode an sRGB value to linear light, apply a typed gain, and encode it again:
use ph_color::{Color, Encoded, Gain, Q0_16, Q4_28, SRGB_EOTF, SRGB_OETF, Srgb};
let encoded = Color::<Srgb, Encoded>::new(Q0_16::array_from_raw([
65_535, 32_768, 4_096,
]));
let linear = SRGB_EOTF.decode(encoded);
let dimmed = Gain::<Srgb>::from_q428([
Q4_28::ONE,
Q4_28::ONE,
Q4_28::from_raw(201_326_592), // 0.75 in Q4.28
])
.apply(linear);
let output = SRGB_OETF.encode(dimmed);
assert_eq!(output.ch[0], Q0_16::ONE);The encoding state is part of the type. Matrices and gains accept linear colors, so accidentally applying either operation to an encoded value is a compile-time error.
Runnable examples cover three concrete workflows:
cargo run -p ph-color --example hub75
cargo run -p ph-color --example uv_flood
cargo run -p ph-color --features oklab --example oklab_sketchThese are host-side pipeline sketches. They do not perform hardware I/O or claim validation on a physical display or lamp.
Color<S, E>values with open color-space types and sealedLinearandEncodedstates.Matrix3<Src, Dst>andGain<S>operations for linear colors.- Shipped sRGB EOTF and OETF lookup tables.
InterpLut,Gradient, and typed linear or perceptual interpolation.quantize::<BITS>andexpand::<BITS>with an exact signed-residual seam, plus zero-parameter round-to-nearest throughquantize_round::<BITS>.- Optional fixed-point linear-sRGB to/from Oklab conversion with
oklab. - Optional
ColorF32and supported*_f32operations withf32.
Features are additive and may be combined:
| Feature | Adds | Important limit |
|---|---|---|
| default | Fixed-point color, matrix, gain, LUT, interpolation, and quantization APIs | No Oklab conversion or f32 API |
oklab |
Fixed-point linear-sRGB to/from Oklab | No Oklab f32 entry points |
f32 |
ColorF32 plus supported matrix, gain, LUT, and interpolation operations |
Does not change fixed-point results |
The fixed path uses UQ0.16 channels and Q4.28 coefficients. Matrix and gain operations perform one widening accumulation per output channel, then round-half-away-from-zero and saturate. Public fixed-point results never wrap. For the same API, features, and inputs, results are bit-identical across the checked targets.
The complete compatibility contract—including error bounds, type-state rules,
and the quantization residual identity—is in
docs/NUMERICS.md.
ph-color owns the deterministic step from one color value to another. It
does not own framebuffers, alpha compositing, parameterized dithering policy,
output packing, hardware I/O, animation, or application pipelines. Rounding
to nearest is included only because its half-bin threshold, tie rule, and
top-of-range saturation have no free parameter. Decisions that take a custom
threshold, mask, position, frame index, or accumulator remain out of scope.
ICC, CMYK, appearance models, target-side chromatic adaptation, runtime
transform derivation, and f64 on the target are also outside its current
scope.
Custom matrices, inverse calculations, chromatic adaptation, and lookup-table
generation belong in ph-color-bake; the resulting constants belong in the
target application. New capabilities are admitted only for a current embedded
consumer with testable bounds.
- Minimum supported Rust version: 1.94.0, edition 2024.
- The target crate is
#![no_std], allocation-free, and#![forbid(unsafe_code)]. - The release gate links all feature combinations for
thumbv6m-none-eabi,thumbv8m.main-none-eabihf, andriscv32imc-unknown-none-elf. - Frozen golden vectors, property tests, compile-fail tests, generated-source checks, dependency policy, code-size and static-RAM budgets, panic-symbol checks, and wide-arithmetic checks protect the documented contract.
Run the canonical repository gate from the workspace root:
cargo xtask ciThe command reports passed, failed, and skipped checks separately. Host tests and embedded link checks are software evidence; they are not physical-device, timing, or hardware-qualification evidence.