use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{self, Cursor, Error, Read};
use std::marker::PhantomData;
use std::{error, fmt, mem};
use crate::error::{DecodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind};
use crate::image::{ImageDecoder, ImageFormat};
use crate::{color, AnimationDecoder, Frames, Rgba};
use super::lossless::{LosslessDecoder, LosslessFrame};
use super::vp8::{Frame as VP8Frame, Vp8Decoder};
use super::extended::{read_extended_header, ExtendedImage};
#[derive(Debug, Clone, Copy)]
pub(crate) enum DecoderError {
RiffSignatureInvalid([u8; 4]),
WebpSignatureInvalid([u8; 4]),
ChunkHeaderInvalid([u8; 4]),
}
impl fmt::Display for DecoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct SignatureWriter([u8; 4]);
impl fmt::Display for SignatureWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{:#04X?}, {:#04X?}, {:#04X?}, {:#04X?}]",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
match self {
DecoderError::RiffSignatureInvalid(riff) => f.write_fmt(format_args!(
"Invalid RIFF signature: {}",
SignatureWriter(*riff)
)),
DecoderError::WebpSignatureInvalid(webp) => f.write_fmt(format_args!(
"Invalid WebP signature: {}",
SignatureWriter(*webp)
)),
DecoderError::ChunkHeaderInvalid(header) => f.write_fmt(format_args!(
"Invalid Chunk header: {}",
SignatureWriter(*header)
)),
}
}
}
impl From<DecoderError> for ImageError {
fn from(e: DecoderError) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormat::WebP.into(), e))
}
}
impl error::Error for DecoderError {}
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum WebPRiffChunk {
RIFF,
WEBP,
VP8,
VP8L,
VP8X,
ANIM,
ANMF,
ALPH,
ICCP,
EXIF,
XMP,
}
impl WebPRiffChunk {
pub(crate) fn from_fourcc(chunk_fourcc: [u8; 4]) -> ImageResult<Self> {
match &chunk_fourcc {
b"RIFF" => Ok(Self::RIFF),
b"WEBP" => Ok(Self::WEBP),
b"VP8 " => Ok(Self::VP8),
b"VP8L" => Ok(Self::VP8L),
b"VP8X" => Ok(Self::VP8X),
b"ANIM" => Ok(Self::ANIM),
b"ANMF" => Ok(Self::ANMF),
b"ALPH" => Ok(Self::ALPH),
b"ICCP" => Ok(Self::ICCP),
b"EXIF" => Ok(Self::EXIF),
b"XMP " => Ok(Self::XMP),
_ => Err(DecoderError::ChunkHeaderInvalid(chunk_fourcc).into()),
}
}
pub(crate) fn to_fourcc(&self) -> [u8; 4] {
match self {
Self::RIFF => *b"RIFF",
Self::WEBP => *b"WEBP",
Self::VP8 => *b"VP8 ",
Self::VP8L => *b"VP8L",
Self::VP8X => *b"VP8X",
Self::ANIM => *b"ANIM",
Self::ANMF => *b"ANMF",
Self::ALPH => *b"ALPH",
Self::ICCP => *b"ICCP",
Self::EXIF => *b"EXIF",
Self::XMP => *b"XMP ",
}
}
}
enum WebPImage {
Lossy(VP8Frame),
Lossless(LosslessFrame),
Extended(ExtendedImage),
}
pub struct WebPDecoder<R> {
r: R,
image: WebPImage,
}
impl<R: Read> WebPDecoder<R> {
pub fn new(r: R) -> ImageResult<WebPDecoder<R>> {
let image = WebPImage::Lossy(Default::default());
let mut decoder = WebPDecoder { r, image };
decoder.read_data()?;
Ok(decoder)
}
fn read_riff_header(&mut self) -> ImageResult<u32> {
let mut riff = [0; 4];
self.r.read_exact(&mut riff)?;
if &riff != b"RIFF" {
return Err(DecoderError::RiffSignatureInvalid(riff).into());
}
let size = self.r.read_u32::<LittleEndian>()?;
let mut webp = [0; 4];
self.r.read_exact(&mut webp)?;
if &webp != b"WEBP" {
return Err(DecoderError::WebpSignatureInvalid(webp).into());
}
Ok(size)
}
fn read_frame(&mut self) -> ImageResult<WebPImage> {
let chunk = read_chunk(&mut self.r)?;
match chunk {
Some((cursor, WebPRiffChunk::VP8)) => {
let mut vp8_decoder = Vp8Decoder::new(cursor);
let frame = vp8_decoder.decode_frame()?;
Ok(WebPImage::Lossy(frame.clone()))
}
Some((cursor, WebPRiffChunk::VP8L)) => {
let mut lossless_decoder = LosslessDecoder::new(cursor);
let frame = lossless_decoder.decode_frame()?;
Ok(WebPImage::Lossless(frame.clone()))
}
Some((mut cursor, WebPRiffChunk::VP8X)) => {
let info = read_extended_header(&mut cursor)?;
let image = ExtendedImage::read_extended_chunks(&mut self.r, info)?;
Ok(WebPImage::Extended(image))
}
None => Err(ImageError::IoError(Error::from(
io::ErrorKind::UnexpectedEof,
))),
Some((_, chunk)) => Err(DecoderError::ChunkHeaderInvalid(chunk.to_fourcc()).into()),
}
}
fn read_data(&mut self) -> ImageResult<()> {
let _size = self.read_riff_header()?;
let image = self.read_frame()?;
self.image = image;
Ok(())
}
pub fn has_animation(&self) -> bool {
match &self.image {
WebPImage::Lossy(_) => false,
WebPImage::Lossless(_) => false,
WebPImage::Extended(extended) => extended.has_animation(),
}
}
pub fn set_background_color(&mut self, color: Rgba<u8>) -> ImageResult<()> {
match &mut self.image {
WebPImage::Extended(image) => image.set_background_color(color),
_ => Err(ImageError::Parameter(ParameterError::from_kind(
ParameterErrorKind::Generic(
"Background color can only be set on animated webp".to_owned(),
),
))),
}
}
}
pub(crate) fn read_len_cursor<R>(r: &mut R) -> ImageResult<Cursor<Vec<u8>>>
where
R: Read,
{
let unpadded_len = u64::from(r.read_u32::<LittleEndian>()?);
let len = unpadded_len + (unpadded_len % 2);
let mut framedata = Vec::new();
r.by_ref().take(len).read_to_end(&mut framedata)?;
if unpadded_len % 2 == 1 {
framedata.pop();
}
Ok(io::Cursor::new(framedata))
}
pub(crate) fn read_fourcc<R: Read>(r: &mut R) -> ImageResult<Option<ImageResult<WebPRiffChunk>>> {
let mut chunk_fourcc = [0; 4];
let result = r.read_exact(&mut chunk_fourcc);
match result {
Ok(()) => {}
Err(err) => {
if err.kind() == io::ErrorKind::UnexpectedEof {
return Ok(None);
} else {
return Err(err.into());
}
}
}
let chunk = WebPRiffChunk::from_fourcc(chunk_fourcc);
Ok(Some(chunk))
}
pub(crate) fn read_chunk<R>(r: &mut R) -> ImageResult<Option<(Cursor<Vec<u8>>, WebPRiffChunk)>>
where
R: Read,
{
if let Some(chunk) = read_fourcc(r)? {
let chunk = chunk?;
let cursor = read_len_cursor(r)?;
Ok(Some((cursor, chunk)))
} else {
Ok(None)
}
}
pub struct WebpReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for WebpReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.0.position() == 0 && buf.is_empty() {
mem::swap(buf, self.0.get_mut());
Ok(buf.len())
} else {
self.0.read_to_end(buf)
}
}
}
impl<'a, R: 'a + Read> ImageDecoder<'a> for WebPDecoder<R> {
type Reader = WebpReader<R>;
fn dimensions(&self) -> (u32, u32) {
match &self.image {
WebPImage::Lossy(vp8_frame) => {
(u32::from(vp8_frame.width), u32::from(vp8_frame.height))
}
WebPImage::Lossless(lossless_frame) => (
u32::from(lossless_frame.width),
u32::from(lossless_frame.height),
),
WebPImage::Extended(extended) => extended.dimensions(),
}
}
fn color_type(&self) -> color::ColorType {
match &self.image {
WebPImage::Lossy(_) => color::ColorType::Rgb8,
WebPImage::Lossless(_) => color::ColorType::Rgba8,
WebPImage::Extended(extended) => extended.color_type(),
}
}
fn into_reader(self) -> ImageResult<Self::Reader> {
match &self.image {
WebPImage::Lossy(vp8_frame) => {
let mut data = vec![0; vp8_frame.get_buf_size()];
vp8_frame.fill_rgb(data.as_mut_slice());
Ok(WebpReader(Cursor::new(data), PhantomData))
}
WebPImage::Lossless(lossless_frame) => {
let mut data = vec![0; lossless_frame.get_buf_size()];
lossless_frame.fill_rgba(data.as_mut_slice());
Ok(WebpReader(Cursor::new(data), PhantomData))
}
WebPImage::Extended(extended) => {
let mut data = vec![0; extended.get_buf_size()];
extended.fill_buf(data.as_mut_slice());
Ok(WebpReader(Cursor::new(data), PhantomData))
}
}
}
fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
match &self.image {
WebPImage::Lossy(vp8_frame) => {
vp8_frame.fill_rgb(buf);
}
WebPImage::Lossless(lossless_frame) => {
lossless_frame.fill_rgba(buf);
}
WebPImage::Extended(extended) => {
extended.fill_buf(buf);
}
}
Ok(())
}
fn icc_profile(&mut self) -> Option<Vec<u8>> {
if let WebPImage::Extended(extended) = &self.image {
extended.icc_profile()
} else {
None
}
}
}
impl<'a, R: 'a + Read> AnimationDecoder<'a> for WebPDecoder<R> {
fn into_frames(self) -> Frames<'a> {
match self.image {
WebPImage::Lossy(_) | WebPImage::Lossless(_) => {
Frames::new(Box::new(std::iter::empty()))
}
WebPImage::Extended(extended_image) => extended_image.into_frames(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_with_overflow_size() {
let bytes = vec![
0x52, 0x49, 0x46, 0x46, 0xaf, 0x37, 0x80, 0x47, 0x57, 0x45, 0x42, 0x50, 0x6c, 0x64,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x7e, 0x73, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x40, 0xfb, 0xff, 0xff, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x65,
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49,
0x49, 0x54, 0x55, 0x50, 0x4c, 0x54, 0x59, 0x50, 0x45, 0x33, 0x37, 0x44, 0x4d, 0x46,
];
let data = std::io::Cursor::new(bytes);
let _ = WebPDecoder::new(data);
}
}