Skip to content

Commit 4abddaf

Browse files
authored
Add EncoderParams to make predictor transform optional (#111)
1 parent c29d6d4 commit 4abddaf

File tree

2 files changed

+79
-25
lines changed

2 files changed

+79
-25
lines changed

src/encoder.rs

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,24 @@ fn write_run<W: Write>(
347347
Ok(())
348348
}
349349

350+
/// Allows fine-tuning some encoder parameters.
351+
///
352+
/// Pass to [`WebPEncoder::set_params()`].
353+
#[non_exhaustive]
354+
#[derive(Clone, Debug)]
355+
pub struct EncoderParams {
356+
/// Use a predictor transform. Enabled by default.
357+
pub use_predictor_transform: bool,
358+
}
359+
360+
impl Default for EncoderParams {
361+
fn default() -> Self {
362+
Self {
363+
use_predictor_transform: true,
364+
}
365+
}
366+
}
367+
350368
/// Encode image data with the indicated color type.
351369
///
352370
/// # Panics
@@ -358,6 +376,7 @@ fn encode_frame<W: Write>(
358376
width: u32,
359377
height: u32,
360378
color: ColorType,
379+
params: EncoderParams,
361380
) -> Result<(), EncodingError> {
362381
let w = &mut BitWriter {
363382
writer,
@@ -392,11 +411,13 @@ fn encode_frame<W: Write>(
392411
w.write_bits(0b101, 3)?;
393412

394413
// predictor transform
395-
w.write_bits(0b111001, 6)?;
396-
w.write_bits(0x0, 1)?; // no color cache
397-
write_single_entry_huffman_tree(w, 2)?;
398-
for _ in 0..4 {
399-
write_single_entry_huffman_tree(w, 0)?;
414+
if params.use_predictor_transform {
415+
w.write_bits(0b111001, 6)?;
416+
w.write_bits(0x0, 1)?; // no color cache
417+
write_single_entry_huffman_tree(w, 2)?;
418+
for _ in 0..4 {
419+
write_single_entry_huffman_tree(w, 0)?;
420+
}
400421
}
401422

402423
// transforms done
@@ -429,18 +450,20 @@ fn encode_frame<W: Write>(
429450
}
430451

431452
// compute predictor transform
432-
let row_bytes = width as usize * 4;
433-
for y in (1..height as usize).rev() {
434-
let (prev, current) =
435-
pixels[(y - 1) * row_bytes..][..row_bytes * 2].split_at_mut(row_bytes);
436-
for (c, p) in current.iter_mut().zip(prev) {
437-
*c = c.wrapping_sub(*p);
453+
if params.use_predictor_transform {
454+
let row_bytes = width as usize * 4;
455+
for y in (1..height as usize).rev() {
456+
let (prev, current) =
457+
pixels[(y - 1) * row_bytes..][..row_bytes * 2].split_at_mut(row_bytes);
458+
for (c, p) in current.iter_mut().zip(prev) {
459+
*c = c.wrapping_sub(*p);
460+
}
438461
}
462+
for i in (4..row_bytes).rev() {
463+
pixels[i] = pixels[i].wrapping_sub(pixels[i - 4]);
464+
}
465+
pixels[3] = pixels[3].wrapping_sub(255);
439466
}
440-
for i in (4..row_bytes).rev() {
441-
pixels[i] = pixels[i].wrapping_sub(pixels[i - 4]);
442-
}
443-
pixels[3] = pixels[3].wrapping_sub(255);
444467

445468
// compute frequencies
446469
let mut frequencies0 = [0u32; 256];
@@ -506,8 +529,10 @@ fn encode_frame<W: Write>(
506529
}
507530
if is_alpha {
508531
write_huffman_tree(w, &frequencies3, &mut lengths3, &mut codes3)?;
509-
} else {
532+
} else if params.use_predictor_transform {
510533
write_single_entry_huffman_tree(w, 0)?;
534+
} else {
535+
write_single_entry_huffman_tree(w, 255)?;
511536
}
512537
write_single_entry_huffman_tree(w, 1)?;
513538

@@ -597,6 +622,7 @@ pub struct WebPEncoder<W> {
597622
icc_profile: Vec<u8>,
598623
exif_metadata: Vec<u8>,
599624
xmp_metadata: Vec<u8>,
625+
params: EncoderParams,
600626
}
601627

602628
impl<W: Write> WebPEncoder<W> {
@@ -609,6 +635,7 @@ impl<W: Write> WebPEncoder<W> {
609635
icc_profile: Vec::new(),
610636
exif_metadata: Vec::new(),
611637
xmp_metadata: Vec::new(),
638+
params: EncoderParams::default(),
612639
}
613640
}
614641

@@ -627,6 +654,11 @@ impl<W: Write> WebPEncoder<W> {
627654
self.xmp_metadata = xmp_metadata;
628655
}
629656

657+
/// Set the `EncoderParams` to use.
658+
pub fn set_params(&mut self, params: EncoderParams) {
659+
self.params = params;
660+
}
661+
630662
/// Encode image data with the indicated color type.
631663
///
632664
/// # Panics
@@ -640,7 +672,7 @@ impl<W: Write> WebPEncoder<W> {
640672
color: ColorType,
641673
) -> Result<(), EncodingError> {
642674
let mut frame = Vec::new();
643-
encode_frame(&mut frame, data, width, height, color)?;
675+
encode_frame(&mut frame, data, width, height, color, self.params)?;
644676

645677
// If the image has no metadata, it can be encoded with the "simple" WebP container format.
646678
if self.icc_profile.is_empty()
@@ -757,45 +789,67 @@ mod tests {
757789

758790
#[test]
759791
fn roundtrip_libwebp() {
792+
roundtrip_libwebp_params(EncoderParams::default());
793+
roundtrip_libwebp_params(EncoderParams {
794+
use_predictor_transform: false,
795+
..Default::default()
796+
});
797+
}
798+
799+
fn roundtrip_libwebp_params(params: EncoderParams) {
800+
println!("Testing {params:?}");
801+
760802
let mut img = vec![0; 256 * 256 * 4];
761803
rand::thread_rng().fill_bytes(&mut img);
762804

763805
let mut output = Vec::new();
764-
WebPEncoder::new(&mut output)
806+
let mut encoder = WebPEncoder::new(&mut output);
807+
encoder.set_params(params.clone());
808+
encoder
765809
.encode(&img[..256 * 256 * 3], 256, 256, crate::ColorType::Rgb8)
766810
.unwrap();
767-
webp::Decoder::new(&output).decode().unwrap();
811+
let decoded = webp::Decoder::new(&output).decode().unwrap();
812+
assert!(&img[..256 * 256 * 3] == &*decoded);
768813

769814
let mut output = Vec::new();
770-
WebPEncoder::new(&mut output)
815+
let mut encoder = WebPEncoder::new(&mut output);
816+
encoder.set_params(params.clone());
817+
encoder
771818
.encode(&img, 256, 256, crate::ColorType::Rgba8)
772819
.unwrap();
773-
webp::Decoder::new(&output).decode().unwrap();
820+
let decoded = webp::Decoder::new(&output).decode().unwrap();
821+
assert!(&img == &*decoded);
774822

775823
let mut output = Vec::new();
776824
let mut encoder = WebPEncoder::new(&mut output);
825+
encoder.set_params(params.clone());
777826
encoder.set_icc_profile(vec![0; 10]);
778827
encoder
779828
.encode(&img, 256, 256, crate::ColorType::Rgba8)
780829
.unwrap();
781-
webp::Decoder::new(&output).decode().unwrap();
830+
let decoded = webp::Decoder::new(&output).decode().unwrap();
831+
assert!(&img == &*decoded);
782832

783833
let mut output = Vec::new();
784834
let mut encoder = WebPEncoder::new(&mut output);
835+
encoder.set_params(params.clone());
785836
encoder.set_exif_metadata(vec![0; 10]);
786837
encoder
787838
.encode(&img, 256, 256, crate::ColorType::Rgba8)
788839
.unwrap();
789-
webp::Decoder::new(&output).decode().unwrap();
840+
let decoded = webp::Decoder::new(&output).decode().unwrap();
841+
assert!(&img == &*decoded);
790842

791843
let mut output = Vec::new();
792844
let mut encoder = WebPEncoder::new(&mut output);
845+
encoder.set_params(params.clone());
793846
encoder.set_xmp_metadata(vec![0; 7]);
794847
encoder.set_icc_profile(vec![0; 8]);
795848
encoder.set_icc_profile(vec![0; 9]);
796849
encoder
797850
.encode(&img, 256, 256, crate::ColorType::Rgba8)
798851
.unwrap();
799-
webp::Decoder::new(&output).decode().unwrap();
852+
let decoded = webp::Decoder::new(&output).decode().unwrap();
853+
assert!(&img == &*decoded);
800854
}
801855
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
extern crate test;
1111

1212
pub use self::decoder::{DecodingError, LoopCount, WebPDecoder};
13-
pub use self::encoder::{ColorType, EncodingError, WebPEncoder};
13+
pub use self::encoder::{ColorType, EncoderParams, EncodingError, WebPEncoder};
1414

1515
mod decoder;
1616
mod encoder;

0 commit comments

Comments
 (0)