Expand description
Physical orientation baking — rotate / flip a whole pixel buffer.
apply_orientation takes a (possibly strided) PixelSlice and an
Orientation and returns a fresh, tightly-allocated PixelBuffer with
the pixels physically rearranged. It is the “bake” half of the zen
orientation model: codecs that decode to a raster buffer and are asked to
resolve orientation (OrientationHint::bakes() is true) call this; the
cheap coordinate math (Orientation::forward_map / output_dimensions)
lives in zenpixels, and this is the buffer operation that consumes it.
§Algorithm
The eight orientations split into two classes:
-
Non-transposing (
Identity,FlipH,FlipV,Rotate180) — the output has the same dimensions, so each output row maps to exactly one input row.Identity/FlipVare pure row copies (memcpy, reordered forFlipV);FlipH/Rotate180additionally reverse thebpp-sized elements within each row. These are memory-bandwidth bound — a scalarcopy_from_sliceper row already runs at copy speed. -
Transposing (
Transpose,Rotate90,Rotate270,Transverse) — width and height swap, and the access pattern is a matrix transpose, which is the cache-hostile case: a naïve element loop strides one of the two buffers by a full row per step and thrashes the cache once the image exceeds L1/L2. We use the standard fix — loop tiling (cache blocking): process the image inTILE×TILEblocks so each block’s source and destination footprints (TILE*TILE*bppbytes each) stay resident while we transpose them. The orientation’s reflection (theh-1-sy/w-1-sxterms that turn a bare transpose into a 90°/270° rotation or anti-diagonal flip) is folded into the per-element destination address viaforward_map, so the whole thing is a single pass with no intermediate buffer.
For 4-byte pixels the per-tile transpose is SIMD on every supported
arch: full 4×4 tiles go through magetypes’ f32x4::transpose_4x4 (the
classic _MM_TRANSPOSE4_PS-shaped shuffle cascade — SSE on x86, NEON on
aarch64, SIMD128 on wasm), generated once via #[magetypes(v3, neon, wasm128, scalar)] and dispatched by incant! at runtime (scalar tier when
no SIMD is available). Each pixel rides as one f32 lane — the kernel only
shuffles whole 32-bit lanes (no float math), so reinterpreting the bytes as
f32 is bit-exact for any 4-byte format, NaN bit patterns included. The
non-multiple-of-4 edge strips and every other element width use the
cache-blocked scalar path, which is also the parity oracle
(simd_transpose_matches_scalar_reference_rgba8). (1- and 2-byte SIMD
transpose — the 16×16 punpck cascade — is a possible follow-up; gray /
16-bit currently go scalar.)
Functions§
- apply_
orientation - Apply
orientationtosrc, returning a freshly-allocated buffer with the pixels physically rearranged. - apply_
orientation_ in_ place - Bake
orientationintodstin place, reusing its allocation — no second pixel buffer (the transposing orientations would otherwise need a 2× transient). - apply_
orientation_ into - Apply
orientationtosrc, writing into a caller-provideddst— no allocation, so callers can reuse / pool the target across many calls.