Where the bytes go
Why these numbers
The math zenpixels-convert does, one step at a time. Anything the crate leaves to you is marked as an illustration.
Same color, other formats
One color, many byte patterns. Select a row to switch.
From a hex code to everything else
A hex code like #FF7A1A is already a pixel format with every choice fixed. Each idea below loosens one choice. "Show me" sets up the explorer to demonstrate it.
Words you will meet
- Channel and sample
- A channel is one ingredient of a pixel (red, alpha, cyan, lightness). A sample is one channel's number in one pixel.
- Channel type
- How each sample is stored:
U8(0–255),U16(0–65535), floating-pointF16/F32, or native types such asI16andU64. The descriptor determines what those numbers mean. - Transfer function
- The curve between stored number and amount of light. sRGB spends more codes on darks, the way eyes do. Linear stores light itself.
- Primaries
- Which exact red, green and blue the numbers mix. Wide gamuts can reach colors outside sRGB. Converting primaries adjusts the numbers to preserve the represented color where possible.
- White point
- The color equal R, G and B make. The known primaries in this demo use D65 (x 0.3127, y 0.3290).
- Gamut
- Every color a set of primaries reaches with values 0 to 1. Outside it you need values below 0 or above 1, which only floats can hold.
- Signal range
- Full uses every code. Narrow (TV) puts black at 16 and white at 235 in 8 bits; 4096 and 60160 in 16 bits.
- Alpha
- Straight: color and opacity apart. Premultiplied: color already times opacity. Opaque: always full. Undefined: a padding byte that means nothing.
- Channel order
- BGRA stores blue first. Still RGB color, just a different order in memory; zenpixels records it in the layout.
- Stride and subimages
- Bytes from one row start to the next. A
PixelSlicecan borrow a cropped rectangle and keep its parent's stride. Padding needs sample alignment, not a whole number of pixels;row(y)returns only the view's active pixels. - Endianness
- Which byte of a multi-byte number comes first. zenpixels uses the CPU's own order; 16-bit PNG files are big-endian, so decoders swap.
- Descriptor
PixelDescriptor=PixelFormat(type and layout) plus curve, primaries, alpha and range. Same bytes, different descriptor: a different color.- Native samples
I16,F64and friends usePixelFormat::Generic. Identity carriage preserves their bits. General numerical conversion refuses. Integer code interpretation is available separately through SampleEncoding; see the plane examples below.- CMYK and ICC
- Printer ink amounts. Turning them into screen color needs that printer's ICC profile and a color management system.
- Oklab
- Perceptual color: L is lightness, a runs green to red, b runs blue to yellow. Edits in Oklab look even.
- PQ and HLG
- HDR curves. PQ codes absolute brightness up to 10,000 nits; conversion from relative light needs a white anchor. Mapping HLG into display light needs explicit rendering conditions.
In Rust
This example allocates zero-filled storage with the selected format and row layout. The borrowed view can cover the whole image or a crop. See a cropped view in the guide.
Converter simulator
Convert the current stored pixels with the real zenpixels-convert WASM build. Source controls are above; destination controls are below. Alpha discard and depth reduction are allowed; the selected luma weights apply. Unsupported routes return the crate's error.
WASM uses native little-endian samples. The source's displayed byte order is a viewing choice. PQ conversion requires an explicit white anchor. This row converter also needs source and target peaks for tone mapping; whole-buffer conversion can measure the source peak. HLG rendering requires explicit display peak, black, effective gamma and SDR white; supported endpoints use full-range BT.2020 RGB/RGBA. Enable it below. The model uses the PQ anchor selected above. CMYK needs an ICC profile, which this simulator does not supply. General signed/wide numerical conversions are refused; exact code reading is shown in the storage examples.
WASM output: all active rows, packed
JavaScript model: first output pixel
Conversion plan and steps
Planes, interleaved components and native codes
PlaneRole::CbCr and CrCb already describe interleaved chroma. CodePlane::component selects one component from interleaved storage, with its own SampleEncoding. Interleaving and RGB10 + alpha1 do not by themselves require a new view type.
Live examples require the WASM assets.
Stored bytes (including row and sample padding)
Existing typed plane → component readers
Converted to canonical RGB8 pixels
Code extraction and full-range numerical widening run together through codes::to_canonical.
Rows here omit trailing padding after the final active row. Each code list is one position, in the displayed component order. These are extracted codes, not normalized intensities or rendered colors.
Scope and remaining limits
Use ordinary typed planes and component readers for interleaved chroma and RGB10 + alpha1. Each reader retains the relevant sample encoding.
CodePlane reads U8/U16 codes. SampleEncoding can describe signed/wide payloads, but this does not add signed/wide code readers or arithmetic. Native PixelFormat::Generic identity carriage remains separate.
Typed multi-byte samples require sample alignment. For RGB565, three CodePlane::new views select different bitfields of the same U16 words. codes::to_canonical can unpack and widen them directly into ordinary RGB pixels.