Skip to main content

Module orient

Module orient 

Source
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/FlipV are pure row copies (memcpy, reordered for FlipV); FlipH/Rotate180 additionally reverse the bpp-sized elements within each row. These are memory-bandwidth bound — a scalar copy_from_slice per 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 in TILE×TILE blocks so each block’s source and destination footprints (TILE*TILE*bpp bytes each) stay resident while we transpose them. The orientation’s reflection (the h-1-sy / w-1-sx terms that turn a bare transpose into a 90°/270° rotation or anti-diagonal flip) is folded into the per-element destination address via forward_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 orientation to src, returning a freshly-allocated buffer with the pixels physically rearranged.
apply_orientation_in_place
Bake orientation into dst in place, reusing its allocation — no second pixel buffer (the transposing orientations would otherwise need a 2× transient).
apply_orientation_into
Apply orientation to src, writing into a caller-provided dstno allocation, so callers can reuse / pool the target across many calls.