Skip to content

Unified JPEG encoder API surface across mozjpeg-rs and zenjpeg #18

@lilith

Description

@lilith

Summary

Make it trivial to swap between mozjpeg-rs and zenjpeg by unifying the native API surface. Both crates should support a common set of builder methods and encode calls so users change only the use statement. zencodec traits should be implemented directly on the native types (feature-gated), not via adapter wrappers.

Target API (works with either crate)

use mozjpeg_rs::{Encoder, Subsampling};  // OR: use zenjpeg::{Encoder, Subsampling};

let jpeg = Encoder::new()
    .quality(85)
    .progressive(true)
    .subsampling(Subsampling::S420)
    .optimize_huffman(true)
    .deringing(true)
    .icc_profile(icc_bytes)
    .exif_data(exif_bytes)
    .encode_rgb(&pixels, width, height)?;

Encoder::max_compression().encode_rgb(&px, w, h)?;
Encoder::fastest().encode_gray(&gray, w, h)?;

With zencodec feature, the native type IS the zencodec config — no adapter wrapper:

// Encoder directly implements zencodec::EncoderConfig when feature="zencodec"
fn encode_generic<C: zencodec::encode::EncoderConfig>(config: C, pixels: PixelSlice<'_>) -> Vec<u8> {
    config.job().encoder().unwrap().encode(pixels).unwrap().into_vec()
}

Three-way API comparison

Aspect mozjpeg (C wrapper) mozjpeg-rs (current) zenjpeg (current) Unified
Type name Compress Encoder EncoderConfig Encoder
Constructor new(ColorSpace) new(Preset) ycbcr(q, ss) new() + explicit ctors
Quality set_quality(f32) .quality(u8) .quality(impl Into<Quality>) .quality(impl Into<Quality>)
Progressive set_progressive_mode() .progressive(bool) .progressive(impl Into<…>) .progressive(impl Into<…>)
Subsampling tuples Subsampling::S420 ChromaSubsampling::Quarter Subsampling::S420
Deringing N/A .overshoot_deringing(bool) .deringing(bool) .deringing(bool)
Force baseline N/A .force_baseline(bool) .force_baseline() .force_baseline()
ICC post-start .icc_profile(Vec<u8>) builder request().icc_profile(&[u8]) .icc_profile(Vec<u8>) builder
Encode start+write+finish .encode_rgb(&[u8], w, h) .encode_bytes(&[u8], w, h, layout) .encode_rgb(&[u8], w, h)
zencodec N/A Adapter wrapper Adapter wrapper Direct trait impl on native type

Tossups resolved toward zenjpeg's design (more elegant signatures).

Changes needed in zenjpeg

Native API additions

  1. Encoder type aliaspub type Encoder = EncoderConfig; + root re-export from lib.rs
  2. Subsampling re-exportpub use crate::types::Subsampling; from encoder/mod.rs and lib.rs
  3. Default implEncoderConfig::default()ycbcr(85, Quarter).progressive(true).optimize_huffman(true)
  4. new()pub fn new() -> Self { Self::default() }
  5. Preset constructorsmax_compression() and fastest() delegating to OptimizationPreset
  6. .subsampling(Subsampling) — builder method converting via existing From<Subsampling> for ChromaSubsampling
  7. encode_rgb() / encode_gray() — thin wrappers around encode_bytes() with Rgb8Srgb / Gray8Srgb
  8. .icc_profile(Vec<u8>) / .exif_data(Vec<u8>) — metadata fields on EncoderConfig, wired into request() and one-shot encode
  9. Un-hide optimize_huffman() if currently #[doc(hidden)]

zencodec: direct trait impl, remove wrapper

Replace JpegEncoderConfig adapter with impl zencodec::EncoderConfig for EncoderConfig directly (feature-gated). The generic_quality_input: Option<f32> field moves onto EncoderConfig. JpegEncodeJob and JpegEncoder remain as thin feature-gated types (they hold per-operation state). Add pub type JpegEncoderConfig = EncoderConfig; for backwards compat.

Corresponding changes in mozjpeg-rs

Will be tracked in imazen/mozjpeg-rs separately:

  • Encoder::new() no-arg (old new(Preset)with_preset(Preset))
  • Rename overshoot_deringingderinging (deprecated alias)
  • Add no-arg force_baseline()
  • zencodec: impl EncoderConfig for Encoder directly, remove MozjpegEncoderConfig wrapper

Intentional differences (not unified)

mozjpeg-rs zenjpeg
Quality scale mozjpeg u8 jpegli f32 (different tables)
Subsampling::Gray Exists Use encode_gray()
Error type mozjpeg_rs::Error zenjpeg::Error
Extended features C compat, smoothing XYB, AQ, parallel, hybrid trellis, f32/16-bit input

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions