OutputEncoder dispatches to specialized encoders per format. JPEG: mozjpeg with quality control. PNG: oxipng lossless optimization. WebP: libwebp encoding. AVIF: ravif via image crate. GIF/TIFF: fallback via image crate. Phase 3 complete - 59 tests passing, zero clippy warnings.
89 lines
2.8 KiB
Rust
89 lines
2.8 KiB
Rust
use pixstrip_core::encoder::OutputEncoder;
|
|
use pixstrip_core::types::{ImageFormat, QualityPreset};
|
|
|
|
fn create_test_rgb_image(width: u32, height: u32) -> image::DynamicImage {
|
|
let img = image::RgbImage::from_fn(width, height, |x, y| {
|
|
image::Rgb([(x % 256) as u8, (y % 256) as u8, 128])
|
|
});
|
|
image::DynamicImage::ImageRgb8(img)
|
|
}
|
|
|
|
fn create_test_rgba_image(width: u32, height: u32) -> image::DynamicImage {
|
|
let img = image::RgbaImage::from_fn(width, height, |x, y| {
|
|
image::Rgba([(x % 256) as u8, (y % 256) as u8, 100, 255])
|
|
});
|
|
image::DynamicImage::ImageRgba8(img)
|
|
}
|
|
|
|
#[test]
|
|
fn encode_jpeg() {
|
|
let img = create_test_rgb_image(200, 150);
|
|
let encoder = OutputEncoder::new();
|
|
let bytes = encoder.encode(&img, ImageFormat::Jpeg, Some(85)).unwrap();
|
|
assert!(!bytes.is_empty());
|
|
// JPEG magic bytes
|
|
assert_eq!(bytes[0], 0xFF);
|
|
assert_eq!(bytes[1], 0xD8);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_jpeg_quality_affects_size() {
|
|
let img = create_test_rgb_image(200, 150);
|
|
let encoder = OutputEncoder::new();
|
|
let high = encoder.encode(&img, ImageFormat::Jpeg, Some(95)).unwrap();
|
|
let low = encoder.encode(&img, ImageFormat::Jpeg, Some(50)).unwrap();
|
|
assert!(high.len() > low.len(), "Higher quality should produce larger files");
|
|
}
|
|
|
|
#[test]
|
|
fn encode_png() {
|
|
let img = create_test_rgba_image(200, 150);
|
|
let encoder = OutputEncoder::new();
|
|
let bytes = encoder.encode(&img, ImageFormat::Png, None).unwrap();
|
|
assert!(!bytes.is_empty());
|
|
// PNG magic bytes
|
|
assert_eq!(&bytes[0..4], &[0x89, 0x50, 0x4E, 0x47]);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_webp() {
|
|
let img = create_test_rgb_image(200, 150);
|
|
let encoder = OutputEncoder::new();
|
|
let bytes = encoder.encode(&img, ImageFormat::WebP, Some(80)).unwrap();
|
|
assert!(!bytes.is_empty());
|
|
// RIFF header
|
|
assert_eq!(&bytes[0..4], b"RIFF");
|
|
}
|
|
|
|
#[test]
|
|
fn encode_to_file() {
|
|
let img = create_test_rgb_image(200, 150);
|
|
let encoder = OutputEncoder::new();
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("output.jpg");
|
|
encoder.encode_to_file(&img, &path, ImageFormat::Jpeg, Some(85)).unwrap();
|
|
assert!(path.exists());
|
|
let metadata = std::fs::metadata(&path).unwrap();
|
|
assert!(metadata.len() > 0);
|
|
}
|
|
|
|
#[test]
|
|
fn quality_from_preset() {
|
|
let encoder = OutputEncoder::new();
|
|
let preset = QualityPreset::High;
|
|
let q = encoder.quality_for_format(ImageFormat::Jpeg, &preset);
|
|
assert_eq!(q, 85);
|
|
let q = encoder.quality_for_format(ImageFormat::WebP, &preset);
|
|
assert!((q as f32 - 85.0).abs() < 1.0);
|
|
}
|
|
|
|
#[test]
|
|
fn encode_gif_fallback() {
|
|
let img = create_test_rgba_image(50, 50);
|
|
let encoder = OutputEncoder::new();
|
|
let bytes = encoder.encode(&img, ImageFormat::Gif, None).unwrap();
|
|
assert!(!bytes.is_empty());
|
|
// GIF magic bytes
|
|
assert_eq!(&bytes[0..3], b"GIF");
|
|
}
|