Skip to content

feat: Lossless JPEG fast path for orient-only pipelines #6

@lilith

Description

@lilith

Summary

When a pipeline is JPEG→orient→JPEG with no pixel-level operations, zenpipe currently fully decodes to pixels, applies orientation, and re-encodes — introducing generation loss and wasting ~10× the CPU. JPEG orientation can be done losslessly in the DCT coefficient domain via zenjpeg::lossless::transform().

imageflow4's imageflow_core/src/pipeline.rs:1084-1230 has a working implementation (~150 lines).

What to add

Pipeline classification

Before compilation, check if the processing steps can be expressed as a single lossless JPEG transform:

  1. Source must be JPEG — check via zencodecs::from_bytes() format detection
  2. Output must be JPEG — check encode format (explicit JPEG, Keep, or Auto resolving to JPEG)
  3. All steps must map to lossless transforms — FlipH, FlipV, Rotate90/180/270, Transpose, Transverse, AutoOrient, Orient(exif)
  4. If any step is non-lossless (resize, crop, filter, etc.) → fall through to normal pixel pipeline

D4 dihedral group composition

Multiple orientation transforms compose into a single transform via the D4 group (8 elements: 4 rotations × 2 flips). imageflow4 has compose_transforms() using (rotation_steps: 0-3, flip_h: bool) representation.

Execution

let config = zenjpeg::lossless::TransformConfig {
    transform,
    edge_handling: zenjpeg::lossless::EdgeHandling::TrimPartialBlocks,
};
let result = zenjpeg::lossless::transform(source_data, &config, stop)?;

Integration point

Best place is in orchestrate.rs before calling compile() — check if the graph qualifies for lossless, and if so, bypass the entire streaming pipeline. Return the transformed bytes directly as output.

Pull reference

  • Source: imageflow4/imageflow_core/src/pipeline.rs lines 1084-1230
    • try_jpeg_lossless() — main entry point (~60 lines)
    • classify_lossless_steps() — step classification (~30 lines)
    • orientation_to_lossless() — EXIF→LosslessTransform mapping
    • compose_transforms() — D4 group composition via to_d4()/from_d4()
  • Tests: imageflow4 has integration tests for lossless passthrough

Dependency

zenjpeg already has the lossless module with transform(), LosslessTransform enum, TransformConfig, and EdgeHandling. No new dependencies needed.

Priority

High — this is a common real-world pattern (thumbnail services applying EXIF orientation to JPEG uploads) and the speedup is ~10×. Zero generation loss is a correctness improvement, not just performance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions