image_meta/loader/
webp.rs

1use 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    // See https://tools.ietf.org/html/rfc6386#page-30
70
71    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    // See https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
101
102    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    // See https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
124
125    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}