1use std::collections::BinaryHeap;
3use std::io::{self, Write};
4use std::slice::ChunksExact;
5
6use quick_error::quick_error;
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum ColorType {
15 L8,
17 La8,
19 Rgb8,
21 Rgba8,
23}
24
25quick_error! {
26 #[derive(Debug)]
28 #[non_exhaustive]
29 pub enum EncodingError {
30 IoError(err: io::Error) {
32 from()
33 display("IO error: {}", err)
34 source(err)
35 }
36
37 InvalidDimensions {
39 display("Invalid dimensions")
40 }
41 }
42}
43
44struct BitWriter<W> {
45 writer: W,
46 buffer: u64,
47 nbits: u8,
48}
49
50impl<W: Write> BitWriter<W> {
51 fn write_bits(&mut self, bits: u64, nbits: u8) -> io::Result<()> {
52 debug_assert!(nbits <= 64);
53
54 self.buffer |= bits << self.nbits;
55 self.nbits += nbits;
56
57 if self.nbits >= 64 {
58 self.writer.write_all(&self.buffer.to_le_bytes())?;
59 self.nbits -= 64;
60 self.buffer = bits.checked_shr(u32::from(nbits - self.nbits)).unwrap_or(0);
61 }
62 debug_assert!(self.nbits < 64);
63 Ok(())
64 }
65
66 fn flush(&mut self) -> io::Result<()> {
67 if self.nbits % 8 != 0 {
68 self.write_bits(0, 8 - self.nbits % 8)?;
69 }
70 if self.nbits > 0 {
71 self.writer
72 .write_all(&self.buffer.to_le_bytes()[..self.nbits as usize / 8])
73 .unwrap();
74 self.buffer = 0;
75 self.nbits = 0;
76 }
77 Ok(())
78 }
79}
80
81fn write_single_entry_huffman_tree<W: Write>(w: &mut BitWriter<W>, symbol: u8) -> io::Result<()> {
82 w.write_bits(1, 2)?;
83 if symbol <= 1 {
84 w.write_bits(0, 1)?;
85 w.write_bits(u64::from(symbol), 1)?;
86 } else {
87 w.write_bits(1, 1)?;
88 w.write_bits(u64::from(symbol), 8)?;
89 }
90 Ok(())
91}
92
93fn build_huffman_tree(
94 frequencies: &[u32],
95 lengths: &mut [u8],
96 codes: &mut [u16],
97 length_limit: u8,
98) -> bool {
99 assert_eq!(frequencies.len(), lengths.len());
100 assert_eq!(frequencies.len(), codes.len());
101
102 if frequencies.iter().filter(|&&f| f > 0).count() <= 1 {
103 lengths.fill(0);
104 codes.fill(0);
105 return false;
106 }
107
108 #[derive(Eq, PartialEq, Copy, Clone, Debug)]
109 struct Item(u32, u16);
110 impl Ord for Item {
111 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
112 other.0.cmp(&self.0)
113 }
114 }
115 impl PartialOrd for Item {
116 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
117 Some(self.cmp(other))
118 }
119 }
120
121 let mut internal_nodes = Vec::new();
123 let mut nodes = BinaryHeap::from_iter(
124 frequencies
125 .iter()
126 .enumerate()
127 .filter(|(_, &frequency)| frequency > 0)
128 .map(|(i, &frequency)| Item(frequency, i as u16)),
129 );
130 while nodes.len() > 1 {
131 let Item(frequency1, index1) = nodes.pop().unwrap();
132 let mut root = nodes.peek_mut().unwrap();
133 internal_nodes.push((index1, root.1));
134 *root = Item(
135 frequency1 + root.0,
136 internal_nodes.len() as u16 + frequencies.len() as u16 - 1,
137 );
138 }
139
140 lengths.fill(0);
142 let mut stack = Vec::new();
143 stack.push((nodes.pop().unwrap().1, 0));
144 while let Some((node, depth)) = stack.pop() {
145 let node = node as usize;
146 if node < frequencies.len() {
147 lengths[node] = depth as u8;
148 } else {
149 let (left, right) = internal_nodes[node - frequencies.len()];
150 stack.push((left, depth + 1));
151 stack.push((right, depth + 1));
152 }
153 }
154
155 let mut max_length = 0;
157 for &length in lengths.iter() {
158 max_length = max_length.max(length);
159 }
160 if max_length > length_limit {
161 let mut counts = [0u32; 16];
162 for &length in lengths.iter() {
163 counts[length.min(length_limit) as usize] += 1;
164 }
165
166 let mut total = 0;
167 for (i, count) in counts
168 .iter()
169 .enumerate()
170 .skip(1)
171 .take(length_limit as usize)
172 {
173 total += count << (length_limit as usize - i);
174 }
175
176 while total > 1u32 << length_limit {
177 let mut i = length_limit as usize - 1;
178 while counts[i] == 0 {
179 i -= 1;
180 }
181 counts[i] -= 1;
182 counts[length_limit as usize] -= 1;
183 counts[i + 1] += 2;
184 total -= 1;
185 }
186
187 let mut len = length_limit;
189 let mut indexes = frequencies.iter().copied().enumerate().collect::<Vec<_>>();
190 indexes.sort_unstable_by_key(|&(_, frequency)| frequency);
191 for &(i, frequency) in &indexes {
192 if frequency > 0 {
193 while counts[len as usize] == 0 {
194 len -= 1;
195 }
196 lengths[i] = len;
197 counts[len as usize] -= 1;
198 }
199 }
200 }
201
202 codes.fill(0);
204 let mut code = 0u32;
205 for len in 1..=length_limit {
206 for (i, &length) in lengths.iter().enumerate() {
207 if length == len {
208 codes[i] = (code as u16).reverse_bits() >> (16 - len);
209 code += 1;
210 }
211 }
212 code <<= 1;
213 }
214 assert_eq!(code, 2 << length_limit);
215
216 true
217}
218
219fn write_huffman_tree<W: Write>(
220 w: &mut BitWriter<W>,
221 frequencies: &[u32],
222 lengths: &mut [u8],
223 codes: &mut [u16],
224) -> io::Result<()> {
225 if !build_huffman_tree(frequencies, lengths, codes, 15) {
226 let symbol = frequencies
227 .iter()
228 .position(|&frequency| frequency > 0)
229 .unwrap_or(0);
230 return write_single_entry_huffman_tree(w, symbol as u8);
231 }
232
233 let mut code_length_lengths = [0u8; 16];
234 let mut code_length_codes = [0u16; 16];
235 let mut code_length_frequencies = [0u32; 16];
236 for &length in lengths.iter() {
237 code_length_frequencies[length as usize] += 1;
238 }
239 let single_code_length_length = !build_huffman_tree(
240 &code_length_frequencies,
241 &mut code_length_lengths,
242 &mut code_length_codes,
243 7,
244 );
245
246 const CODE_LENGTH_ORDER: [usize; 19] = [
247 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
248 ];
249
250 w.write_bits(0, 1)?; w.write_bits(19 - 4, 4)?; for i in CODE_LENGTH_ORDER {
255 if i > 15 || code_length_frequencies[i] == 0 {
256 w.write_bits(0, 3)?;
257 } else if single_code_length_length {
258 w.write_bits(1, 3)?;
259 } else {
260 w.write_bits(u64::from(code_length_lengths[i]), 3)?;
261 }
262 }
263
264 match lengths.len() {
265 256 => {
266 w.write_bits(1, 1)?; w.write_bits(3, 3)?; w.write_bits(254, 8)?; }
270 280 => w.write_bits(0, 1)?,
271 _ => unreachable!(),
272 }
273
274 if !single_code_length_length {
276 for &len in lengths.iter() {
277 w.write_bits(
278 u64::from(code_length_codes[len as usize]),
279 code_length_lengths[len as usize],
280 )?;
281 }
282 }
283
284 Ok(())
285}
286
287const fn length_to_symbol(len: u16) -> (u16, u8) {
288 let len = len - 1;
289 let highest_bit = len.ilog2() as u16;
290 let second_highest_bit = (len >> (highest_bit - 1)) & 1;
291 let extra_bits = highest_bit - 1;
292 let symbol = 2 * highest_bit + second_highest_bit;
293 (symbol, extra_bits as u8)
294}
295
296#[inline(always)]
297fn count_run(
298 pixel: &[u8],
299 it: &mut std::iter::Peekable<ChunksExact<u8>>,
300 frequencies1: &mut [u32; 280],
301) {
302 let mut run_length = 0;
303 while run_length < 4096 && it.peek() == Some(&pixel) {
304 run_length += 1;
305 it.next();
306 }
307 if run_length > 0 {
308 if run_length <= 4 {
309 let symbol = 256 + run_length - 1;
310 frequencies1[symbol] += 1;
311 } else {
312 let (symbol, _extra_bits) = length_to_symbol(run_length as u16);
313 frequencies1[256 + symbol as usize] += 1;
314 }
315 }
316}
317
318#[inline(always)]
319fn write_run<W: Write>(
320 w: &mut BitWriter<W>,
321 pixel: &[u8],
322 it: &mut std::iter::Peekable<ChunksExact<u8>>,
323 codes1: &[u16; 280],
324 lengths1: &[u8; 280],
325) -> io::Result<()> {
326 let mut run_length = 0;
327 while run_length < 4096 && it.peek() == Some(&pixel) {
328 run_length += 1;
329 it.next();
330 }
331 if run_length > 0 {
332 if run_length <= 4 {
333 let symbol = 256 + run_length - 1;
334 w.write_bits(u64::from(codes1[symbol]), lengths1[symbol])?;
335 } else {
336 let (symbol, extra_bits) = length_to_symbol(run_length as u16);
337 w.write_bits(
338 u64::from(codes1[256 + symbol as usize]),
339 lengths1[256 + symbol as usize],
340 )?;
341 w.write_bits(
342 (run_length as u64 - 1) & ((1 << extra_bits) - 1),
343 extra_bits,
344 )?;
345 }
346 }
347 Ok(())
348}
349
350#[non_exhaustive]
354#[derive(Clone, Debug)]
355pub struct EncoderParams {
356 pub use_predictor_transform: bool,
358}
359
360impl Default for EncoderParams {
361 fn default() -> Self {
362 Self {
363 use_predictor_transform: true,
364 }
365 }
366}
367
368fn encode_frame<W: Write>(
374 writer: W,
375 data: &[u8],
376 width: u32,
377 height: u32,
378 color: ColorType,
379 params: EncoderParams,
380) -> Result<(), EncodingError> {
381 let w = &mut BitWriter {
382 writer,
383 buffer: 0,
384 nbits: 0,
385 };
386
387 let (is_color, is_alpha, bytes_per_pixel) = match color {
388 ColorType::L8 => (false, false, 1),
389 ColorType::La8 => (false, true, 2),
390 ColorType::Rgb8 => (true, false, 3),
391 ColorType::Rgba8 => (true, true, 4),
392 };
393
394 assert_eq!(
395 (u64::from(width) * u64::from(height)).saturating_mul(bytes_per_pixel),
396 data.len() as u64
397 );
398
399 if width == 0 || width > 16384 || height == 0 || height > 16384 {
400 return Err(EncodingError::InvalidDimensions);
401 }
402
403 w.write_bits(0x2f, 8)?; w.write_bits(u64::from(width) - 1, 14)?;
405 w.write_bits(u64::from(height) - 1, 14)?;
406
407 w.write_bits(u64::from(is_alpha), 1)?; w.write_bits(0x0, 3)?; w.write_bits(0b101, 3)?;
412
413 if params.use_predictor_transform {
415 w.write_bits(0b111001, 6)?;
416 w.write_bits(0x0, 1)?; write_single_entry_huffman_tree(w, 2)?;
418 for _ in 0..4 {
419 write_single_entry_huffman_tree(w, 0)?;
420 }
421 }
422
423 w.write_bits(0x0, 1)?;
425
426 w.write_bits(0x0, 1)?;
428
429 w.write_bits(0x0, 1)?;
431
432 let mut pixels = match color {
434 ColorType::L8 => data.iter().flat_map(|&p| [p, p, p, 255]).collect(),
435 ColorType::La8 => data
436 .chunks_exact(2)
437 .flat_map(|p| [p[0], p[0], p[0], p[1]])
438 .collect(),
439 ColorType::Rgb8 => data
440 .chunks_exact(3)
441 .flat_map(|p| [p[0], p[1], p[2], 255])
442 .collect(),
443 ColorType::Rgba8 => data.to_vec(),
444 };
445
446 for pixel in pixels.chunks_exact_mut(4) {
448 pixel[0] = pixel[0].wrapping_sub(pixel[1]);
449 pixel[2] = pixel[2].wrapping_sub(pixel[1]);
450 }
451
452 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 }
461 }
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);
466 }
467
468 let mut frequencies0 = [0u32; 256];
470 let mut frequencies1 = [0u32; 280];
471 let mut frequencies2 = [0u32; 256];
472 let mut frequencies3 = [0u32; 256];
473 let mut it = pixels.chunks_exact(4).peekable();
474 match color {
475 ColorType::L8 => {
476 frequencies0[0] = 1;
477 frequencies2[0] = 1;
478 frequencies3[0] = 1;
479 while let Some(pixel) = it.next() {
480 frequencies1[pixel[1] as usize] += 1;
481 count_run(pixel, &mut it, &mut frequencies1);
482 }
483 }
484 ColorType::La8 => {
485 frequencies0[0] = 1;
486 frequencies2[0] = 1;
487 while let Some(pixel) = it.next() {
488 frequencies1[pixel[1] as usize] += 1;
489 frequencies3[pixel[3] as usize] += 1;
490 count_run(pixel, &mut it, &mut frequencies1);
491 }
492 }
493 ColorType::Rgb8 => {
494 frequencies3[0] = 1;
495 while let Some(pixel) = it.next() {
496 frequencies0[pixel[0] as usize] += 1;
497 frequencies1[pixel[1] as usize] += 1;
498 frequencies2[pixel[2] as usize] += 1;
499 count_run(pixel, &mut it, &mut frequencies1);
500 }
501 }
502 ColorType::Rgba8 => {
503 while let Some(pixel) = it.next() {
504 frequencies0[pixel[0] as usize] += 1;
505 frequencies1[pixel[1] as usize] += 1;
506 frequencies2[pixel[2] as usize] += 1;
507 frequencies3[pixel[3] as usize] += 1;
508 count_run(pixel, &mut it, &mut frequencies1);
509 }
510 }
511 }
512
513 let mut lengths0 = [0u8; 256];
515 let mut lengths1 = [0u8; 280];
516 let mut lengths2 = [0u8; 256];
517 let mut lengths3 = [0u8; 256];
518 let mut codes0 = [0u16; 256];
519 let mut codes1 = [0u16; 280];
520 let mut codes2 = [0u16; 256];
521 let mut codes3 = [0u16; 256];
522 write_huffman_tree(w, &frequencies1, &mut lengths1, &mut codes1)?;
523 if is_color {
524 write_huffman_tree(w, &frequencies0, &mut lengths0, &mut codes0)?;
525 write_huffman_tree(w, &frequencies2, &mut lengths2, &mut codes2)?;
526 } else {
527 write_single_entry_huffman_tree(w, 0)?;
528 write_single_entry_huffman_tree(w, 0)?;
529 }
530 if is_alpha {
531 write_huffman_tree(w, &frequencies3, &mut lengths3, &mut codes3)?;
532 } else if params.use_predictor_transform {
533 write_single_entry_huffman_tree(w, 0)?;
534 } else {
535 write_single_entry_huffman_tree(w, 255)?;
536 }
537 write_single_entry_huffman_tree(w, 1)?;
538
539 let mut it = pixels.chunks_exact(4).peekable();
541 match color {
542 ColorType::L8 => {
543 while let Some(pixel) = it.next() {
544 w.write_bits(
545 u64::from(codes1[pixel[1] as usize]),
546 lengths1[pixel[1] as usize],
547 )?;
548 write_run(w, pixel, &mut it, &codes1, &lengths1)?;
549 }
550 }
551 ColorType::La8 => {
552 while let Some(pixel) = it.next() {
553 let len1 = lengths1[pixel[1] as usize];
554 let len3 = lengths3[pixel[3] as usize];
555
556 let code = u64::from(codes1[pixel[1] as usize])
557 | (u64::from(codes3[pixel[3] as usize]) << len1);
558
559 w.write_bits(code, len1 + len3)?;
560 write_run(w, pixel, &mut it, &codes1, &lengths1)?;
561 }
562 }
563 ColorType::Rgb8 => {
564 while let Some(pixel) = it.next() {
565 let len1 = lengths1[pixel[1] as usize];
566 let len0 = lengths0[pixel[0] as usize];
567 let len2 = lengths2[pixel[2] as usize];
568
569 let code = u64::from(codes1[pixel[1] as usize])
570 | (u64::from(codes0[pixel[0] as usize]) << len1)
571 | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0));
572
573 w.write_bits(code, len1 + len0 + len2)?;
574 write_run(w, pixel, &mut it, &codes1, &lengths1)?;
575 }
576 }
577 ColorType::Rgba8 => {
578 while let Some(pixel) = it.next() {
579 let len1 = lengths1[pixel[1] as usize];
580 let len0 = lengths0[pixel[0] as usize];
581 let len2 = lengths2[pixel[2] as usize];
582 let len3 = lengths3[pixel[3] as usize];
583
584 let code = u64::from(codes1[pixel[1] as usize])
585 | (u64::from(codes0[pixel[0] as usize]) << len1)
586 | (u64::from(codes2[pixel[2] as usize]) << (len1 + len0))
587 | (u64::from(codes3[pixel[3] as usize]) << (len1 + len0 + len2));
588
589 w.write_bits(code, len1 + len0 + len2 + len3)?;
590 write_run(w, pixel, &mut it, &codes1, &lengths1)?;
591 }
592 }
593 }
594
595 w.flush()?;
596 Ok(())
597}
598
599const fn chunk_size(inner_bytes: usize) -> u32 {
600 if inner_bytes % 2 == 1 {
601 (inner_bytes + 1) as u32 + 8
602 } else {
603 inner_bytes as u32 + 8
604 }
605}
606
607fn write_chunk<W: Write>(mut w: W, name: &[u8], data: &[u8]) -> io::Result<()> {
608 debug_assert!(name.len() == 4);
609
610 w.write_all(name)?;
611 w.write_all(&(data.len() as u32).to_le_bytes())?;
612 w.write_all(data)?;
613 if data.len() % 2 == 1 {
614 w.write_all(&[0])?;
615 }
616 Ok(())
617}
618
619pub struct WebPEncoder<W> {
621 writer: W,
622 icc_profile: Vec<u8>,
623 exif_metadata: Vec<u8>,
624 xmp_metadata: Vec<u8>,
625 params: EncoderParams,
626}
627
628impl<W: Write> WebPEncoder<W> {
629 pub fn new(w: W) -> Self {
633 Self {
634 writer: w,
635 icc_profile: Vec::new(),
636 exif_metadata: Vec::new(),
637 xmp_metadata: Vec::new(),
638 params: EncoderParams::default(),
639 }
640 }
641
642 pub fn set_icc_profile(&mut self, icc_profile: Vec<u8>) {
644 self.icc_profile = icc_profile;
645 }
646
647 pub fn set_exif_metadata(&mut self, exif_metadata: Vec<u8>) {
649 self.exif_metadata = exif_metadata;
650 }
651
652 pub fn set_xmp_metadata(&mut self, xmp_metadata: Vec<u8>) {
654 self.xmp_metadata = xmp_metadata;
655 }
656
657 pub fn set_params(&mut self, params: EncoderParams) {
659 self.params = params;
660 }
661
662 pub fn encode(
668 mut self,
669 data: &[u8],
670 width: u32,
671 height: u32,
672 color: ColorType,
673 ) -> Result<(), EncodingError> {
674 let mut frame = Vec::new();
675 encode_frame(&mut frame, data, width, height, color, self.params)?;
676
677 if self.icc_profile.is_empty()
679 && self.exif_metadata.is_empty()
680 && self.xmp_metadata.is_empty()
681 {
682 self.writer.write_all(b"RIFF")?;
683 self.writer
684 .write_all(&(chunk_size(frame.len()) + 4).to_le_bytes())?;
685 self.writer.write_all(b"WEBP")?;
686 write_chunk(&mut self.writer, b"VP8L", &frame)?;
687 } else {
688 let mut total_bytes = 22 + chunk_size(frame.len());
689 if !self.icc_profile.is_empty() {
690 total_bytes += chunk_size(self.icc_profile.len());
691 }
692 if !self.exif_metadata.is_empty() {
693 total_bytes += chunk_size(self.exif_metadata.len());
694 }
695 if !self.xmp_metadata.is_empty() {
696 total_bytes += chunk_size(self.xmp_metadata.len());
697 }
698
699 let mut flags = 0;
700 if !self.xmp_metadata.is_empty() {
701 flags |= 1 << 2;
702 }
703 if !self.exif_metadata.is_empty() {
704 flags |= 1 << 3;
705 }
706 if let ColorType::La8 | ColorType::Rgba8 = color {
707 flags |= 1 << 4;
708 }
709 if !self.icc_profile.is_empty() {
710 flags |= 1 << 5;
711 }
712
713 self.writer.write_all(b"RIFF")?;
714 self.writer.write_all(&total_bytes.to_le_bytes())?;
715 self.writer.write_all(b"WEBP")?;
716
717 let mut vp8x = Vec::new();
718 vp8x.write_all(&[flags])?; vp8x.write_all(&[0; 3])?; vp8x.write_all(&(width - 1).to_le_bytes()[..3])?; vp8x.write_all(&(height - 1).to_le_bytes()[..3])?; write_chunk(&mut self.writer, b"VP8X", &vp8x)?;
723
724 if !self.icc_profile.is_empty() {
725 write_chunk(&mut self.writer, b"ICCP", &self.icc_profile)?;
726 }
727
728 write_chunk(&mut self.writer, b"VP8L", &frame)?;
729
730 if !self.exif_metadata.is_empty() {
731 write_chunk(&mut self.writer, b"EXIF", &self.exif_metadata)?;
732 }
733
734 if !self.xmp_metadata.is_empty() {
735 write_chunk(&mut self.writer, b"XMP ", &self.xmp_metadata)?;
736 }
737 }
738
739 Ok(())
740 }
741}
742
743#[cfg(test)]
744mod tests {
745 use rand::RngCore;
746
747 use super::*;
748
749 #[test]
750 fn write_webp() {
751 let mut img = vec![0; 256 * 256 * 4];
752 rand::thread_rng().fill_bytes(&mut img);
753
754 let mut output = Vec::new();
755 WebPEncoder::new(&mut output)
756 .encode(&img, 256, 256, crate::ColorType::Rgba8)
757 .unwrap();
758
759 let mut decoder = crate::WebPDecoder::new(std::io::Cursor::new(output)).unwrap();
760 let mut img2 = vec![0; 256 * 256 * 4];
761 decoder.read_image(&mut img2).unwrap();
762 assert_eq!(img, img2);
763 }
764
765 #[test]
766 fn write_webp_exif() {
767 let mut img = vec![0; 256 * 256 * 3];
768 rand::thread_rng().fill_bytes(&mut img);
769
770 let mut exif = vec![0; 10];
771 rand::thread_rng().fill_bytes(&mut exif);
772
773 let mut output = Vec::new();
774 let mut encoder = WebPEncoder::new(&mut output);
775 encoder.set_exif_metadata(exif.clone());
776 encoder
777 .encode(&img, 256, 256, crate::ColorType::Rgb8)
778 .unwrap();
779
780 let mut decoder = crate::WebPDecoder::new(std::io::Cursor::new(output)).unwrap();
781
782 let mut img2 = vec![0; 256 * 256 * 3];
783 decoder.read_image(&mut img2).unwrap();
784 assert_eq!(img, img2);
785
786 let exif2 = decoder.exif_metadata().unwrap();
787 assert_eq!(Some(exif), exif2);
788 }
789
790 #[test]
791 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
802 let mut img = vec![0; 256 * 256 * 4];
803 rand::thread_rng().fill_bytes(&mut img);
804
805 let mut output = Vec::new();
806 let mut encoder = WebPEncoder::new(&mut output);
807 encoder.set_params(params.clone());
808 encoder
809 .encode(&img[..256 * 256 * 3], 256, 256, crate::ColorType::Rgb8)
810 .unwrap();
811 let decoded = webp::Decoder::new(&output).decode().unwrap();
812 assert_eq!(img[..256 * 256 * 3], *decoded);
813
814 let mut output = Vec::new();
815 let mut encoder = WebPEncoder::new(&mut output);
816 encoder.set_params(params.clone());
817 encoder
818 .encode(&img, 256, 256, crate::ColorType::Rgba8)
819 .unwrap();
820 let decoded = webp::Decoder::new(&output).decode().unwrap();
821 assert_eq!(img, *decoded);
822
823 let mut output = Vec::new();
824 let mut encoder = WebPEncoder::new(&mut output);
825 encoder.set_params(params.clone());
826 encoder.set_icc_profile(vec![0; 10]);
827 encoder
828 .encode(&img, 256, 256, crate::ColorType::Rgba8)
829 .unwrap();
830 let decoded = webp::Decoder::new(&output).decode().unwrap();
831 assert_eq!(img, *decoded);
832
833 let mut output = Vec::new();
834 let mut encoder = WebPEncoder::new(&mut output);
835 encoder.set_params(params.clone());
836 encoder.set_exif_metadata(vec![0; 10]);
837 encoder
838 .encode(&img, 256, 256, crate::ColorType::Rgba8)
839 .unwrap();
840 let decoded = webp::Decoder::new(&output).decode().unwrap();
841 assert_eq!(img, *decoded);
842
843 let mut output = Vec::new();
844 let mut encoder = WebPEncoder::new(&mut output);
845 encoder.set_params(params);
846 encoder.set_xmp_metadata(vec![0; 7]);
847 encoder.set_icc_profile(vec![0; 8]);
848 encoder.set_icc_profile(vec![0; 9]);
849 encoder
850 .encode(&img, 256, 256, crate::ColorType::Rgba8)
851 .unwrap();
852 let decoded = webp::Decoder::new(&output).decode().unwrap();
853 assert_eq!(img, *decoded);
854 }
855}