image_meta/loader/
webp.rs1use std::io::{BufRead, Read, Seek};
2
3use byteorder::{LittleEndian, ReadBytesExt};
4
5use crate::errors::{ImageError, ImageResult};
6use crate::loader::riff::{Chunk, RiffReader};
7use crate::types::{Color, ColorMode, Dimensions, Format, ImageMeta};
8
9pub struct WebpReader<T: BufRead + Seek> {
10 animation_frames: usize,
11 dimensions: Option<Dimensions>,
12 riff: RiffReader<T>,
13}
14
15pub fn load<R: ?Sized + BufRead + Seek>(image: &mut R) -> ImageResult<ImageMeta> {
16 let mut reader = WebpReader::new(RiffReader::open(image)?);
17 reader.read()?;
18
19 let dimensions = reader
20 .dimensions
21 .ok_or_else(|| ImageError::CorruptImage("VP8? chunk not found".into()))?;
22 let animation_frames = if 0 < reader.animation_frames {
23 Some(reader.animation_frames)
24 } else {
25 None
26 };
27 let color = Color {
28 alpha_channel: true,
29 mode: ColorMode::Rgb,
30 resolution: 8,
31 };
32
33 Ok(ImageMeta {
34 animation_frames,
35 color,
36 dimensions,
37 format: Format::Webp,
38 })
39}
40
41impl<T: BufRead + Seek> WebpReader<T> {
42 pub fn new(riff: RiffReader<T>) -> Self {
43 Self {
44 animation_frames: 0,
45 dimensions: None,
46 riff,
47 }
48 }
49
50 fn read(&mut self) -> ImageResult {
51 if self.riff.form_type() != b"WEBP" {
52 return Err(ImageError::InvalidSignature);
53 }
54
55 while let Some(mut chunk) = self.riff.read_chunk()? {
56 match chunk.identifier() {
57 b"ANMF" => self.animation_frames += 1,
58 b"VP8 " => self.dimensions = Some(read_vp8_chunk(&mut chunk)?),
59 b"VP8L" => self.dimensions = Some(read_vp8l_chunk(&mut chunk)?),
60 b"VP8X" => self.dimensions = Some(read_vp8x_chunk(&mut chunk)?),
61 _ => (),
62 }
63 }
64 Ok(())
65 }
66}
67
68fn read_vp8_chunk(chunk: &mut Chunk) -> ImageResult<Dimensions> {
69 let mut bits = [0u8; 3];
72 chunk.read_exact(&mut bits)?;
73 let key_frame = 0 == bits[0] & 1;
74
75 if key_frame {
76 let mut signature = [0u8; 3];
77 chunk.read_exact(&mut signature)?;
78 if signature != [0x9d, 0x01, 0x2a] {
79 return Err(ImageError::CorruptImage(
80 format!("Invalid key frame code: {:?}", signature).into(),
81 ));
82 }
83
84 let mut bits = [0u8; 2];
85 chunk.read_exact(&mut bits)?;
86 let (width, _) = extract_dimension(bits);
87 chunk.read_exact(&mut bits)?;
88 let (height, _) = extract_dimension(bits);
89
90 return Ok(Dimensions {
91 width: u32::from(width),
92 height: u32::from(height),
93 });
94 }
95
96 Err(ImageError::CorruptImage("Not key frame".into()))
97}
98
99fn read_vp8l_chunk(chunk: &mut Chunk) -> ImageResult<Dimensions> {
100 let signature = chunk.read_u8()?;
103 if signature != 0x2f {
104 return Err(ImageError::CorruptImage(
105 format!("Invalid signature: 0x{:x}", signature).into(),
106 ));
107 }
108
109 let mut bits = [0u8; 4];
110 chunk.read_exact(&mut bits)?;
111 let width = (u16::from(bits[1] & 0b0011_1111) << 8) | u16::from(bits[0]);
112 let height = (u16::from(bits[3] & 0b0000_1111) << 10)
113 | (u16::from(bits[2]) << 2)
114 | (u16::from(bits[1] & 0b1100_0000) >> 6);
115
116 Ok(Dimensions {
117 width: u32::from(width) + 1,
118 height: u32::from(height) + 1,
119 })
120}
121
122fn read_vp8x_chunk(chunk: &mut Chunk) -> ImageResult<Dimensions> {
123 let _bits = chunk.read_u32::<LittleEndian>()?;
126
127 let mut bits = [0u8; 6];
128 chunk.read_exact(&mut bits)?;
129 let width = (u32::from(bits[2]) << 16) | (u32::from(bits[1]) << 8) | u32::from(bits[0]);
130 let height = (u32::from(bits[5]) << 16) | (u32::from(bits[4]) << 8) | u32::from(bits[3]);
131
132 Ok(Dimensions {
133 width: width + 1,
134 height: height + 1,
135 })
136}
137
138fn extract_dimension(bits: [u8; 2]) -> (u16, u8) {
139 let size = (u16::from(bits[1] & 0b0011_1111) << 8) | u16::from(bits[0]);
140 let scale = (bits[1] & 0b1100_0000) >> 6;
141 (size, scale)
142}