1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use byteorder::{LittleEndian, ReadBytesExt};
use std::default::Default;
use std::io;
use std::io::{Cursor, Read};

use image;
use image::ImageDecoder;
use image::ImageResult;

use color;

use super::vp8::Frame;
use super::vp8::VP8Decoder;

/// Webp Image format decoder. Currently only supportes the luma channel (meaning that decoded
/// images will be grayscale).
pub struct WebpDecoder<R> {
    r: R,
    frame: Frame,
    have_frame: bool,
}

impl<R: Read> WebpDecoder<R> {
    /// Create a new WebpDecoder from the Reader ```r```.
    /// This function takes ownership of the Reader.
    pub fn new(r: R) -> ImageResult<WebpDecoder<R>> {
        let f: Frame = Default::default();

        let mut decoder = WebpDecoder {
            r,
            have_frame: false,
            frame: f,
        };
        decoder.read_metadata()?;
        Ok(decoder)
    }

    fn read_riff_header(&mut self) -> ImageResult<u32> {
        let mut riff = Vec::with_capacity(4);
        try!(self.r.by_ref().take(4).read_to_end(&mut riff));
        let size = try!(self.r.read_u32::<LittleEndian>());
        let mut webp = Vec::with_capacity(4);
        try!(self.r.by_ref().take(4).read_to_end(&mut webp));

        if &*riff != b"RIFF" {
            return Err(image::ImageError::FormatError(
                "Invalid RIFF signature.".to_string(),
            ));
        }

        if &*webp != b"WEBP" {
            return Err(image::ImageError::FormatError(
                "Invalid WEBP signature.".to_string(),
            ));
        }

        Ok(size)
    }

    fn read_vp8_header(&mut self) -> ImageResult<()> {
        let mut vp8 = Vec::with_capacity(4);
        try!(self.r.by_ref().take(4).read_to_end(&mut vp8));

        if &*vp8 != b"VP8 " {
            return Err(image::ImageError::FormatError(
                "Invalid VP8 signature.".to_string(),
            ));
        }

        let _len = try!(self.r.read_u32::<LittleEndian>());

        Ok(())
    }

    fn read_frame(&mut self) -> ImageResult<()> {
        let mut framedata = Vec::new();
        try!(self.r.read_to_end(&mut framedata));
        let m = io::Cursor::new(framedata);

        let mut v = VP8Decoder::new(m);
        let frame = try!(v.decode_frame());

        self.frame = frame.clone();

        Ok(())
    }

    fn read_metadata(&mut self) -> ImageResult<()> {
        if !self.have_frame {
            try!(self.read_riff_header());
            try!(self.read_vp8_header());
            try!(self.read_frame());

            self.have_frame = true;
        }

        Ok(())
    }
}

impl<R: Read> ImageDecoder for WebpDecoder<R> {
    type Reader = Cursor<Vec<u8>>;

    fn dimensions(&self) -> (u64, u64) {
        (self.frame.width as u64, self.frame.height as u64)
    }

    fn colortype(&self) -> color::ColorType {
        color::ColorType::Gray(8)
    }

    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(Cursor::new(self.frame.ybuf))
    }

    fn read_image(self) -> ImageResult<Vec<u8>> {
        Ok(self.frame.ybuf)
    }
}