Skip to content

feat!: lazy configuration for order-independent API#51

Open
lilith wants to merge 1 commit intoImageOptim:mainfrom
lilith:lazy-config-v2
Open

feat!: lazy configuration for order-independent API#51
lilith wants to merge 1 commit intoImageOptim:mainfrom
lilith:lazy-config-v2

Conversation

@lilith
Copy link
Copy Markdown
Contributor

@lilith lilith commented Feb 5, 2026

Summary

  • All setter methods now buffer to a PendingConfig applied at start_compress() time in the correct order
  • Fixes configuration ordering bugs where set_scan_optimization_mode() and set_fastest_defaults() silently reset other settings via internal jpeg_set_defaults() calls
  • No compilation breakage: all existing methods preserved, deprecated methods emit warnings but still function

New APIs

  • mutate_components_last() - modify components via a callback that runs after all other config
  • mutate_cinfo_last() - access raw cinfo via a callback that runs after all other config
  • set_force_8bit_quantization() - control 8-bit DQT clamping (applies to both quality and custom qtables)
  • write_scanlines_strided() - write scanlines with custom stride for padded pixel data
  • Validation in start_compress() returns io::Error instead of panicking for zero/oversized dimensions and invalid sampling factors

Breaking changes

  • components() and cinfo() now take &mut self (were &self) — they apply pending config before returning so callers always see current state
  • components_mut() → deprecated, use mutate_components_last()
  • cinfo_mut() → deprecated, use mutate_cinfo_last()

Both deprecated methods still work and emit deprecation warnings.

The Bug This Fixes

// BROKEN in 0.10.x: smoothing gets reset to 0 comp.set_smoothing_factor(50); comp.set_scan_optimization_mode(ScanMode::Auto); // In 0.11.0, both orderings produce identical results

set_scan_optimization_mode(Auto) internally calls jpeg_simple_progression() which calls jpeg_set_defaults(), resetting quality, smoothing, subsampling, and more. Same for set_fastest_defaults().

Test plan

  • 71 tests pass (31 unit + 40 integration)
  • Combinatorial ordering tests verify all permutations produce identical output
  • Edge case tests verify validation errors for invalid dimensions/sampling
  • Deprecated API tests verify backward compatibility
  • cargo clippy --all-targets --all-features -- -D warnings clean
@lilith lilith force-pushed the lazy-config-v2 branch 6 times, most recently from f17dc48 to e6af6ef Compare February 5, 2026 03:31
All setter methods now store to a pending configuration that is applied at start_compress() time in the correct order. This fixes configuration ordering bugs where set_scan_optimization_mode() and set_fastest_defaults() would reset other settings via internal jpeg_set_defaults() calls. New APIs: - mutate_components_last() - modify components via callback - mutate_cinfo_last() - access raw cinfo via callback - set_quality_force_8bit() - quality with 8-bit quantization control - write_scanlines_strided() - scanlines with custom stride Deprecated: components_mut(), cinfo_mut() (still functional) Validation in start_compress() returns io::Error for zero dims, oversized dims, and invalid sampling factors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant