aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/gif.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/image/src/codecs/gif.rs')
-rw-r--r--vendor/image/src/codecs/gif.rs606
1 files changed, 0 insertions, 606 deletions
diff --git a/vendor/image/src/codecs/gif.rs b/vendor/image/src/codecs/gif.rs
deleted file mode 100644
index dcbd841..0000000
--- a/vendor/image/src/codecs/gif.rs
+++ /dev/null
@@ -1,606 +0,0 @@
-//! Decoding of GIF Images
-//!
-//! GIF (Graphics Interchange Format) is an image format that supports lossless compression.
-//!
-//! # Related Links
-//! * <http://www.w3.org/Graphics/GIF/spec-gif89a.txt> - The GIF Specification
-//!
-//! # Examples
-//! ```rust,no_run
-//! use image::codecs::gif::{GifDecoder, GifEncoder};
-//! use image::{ImageDecoder, AnimationDecoder};
-//! use std::fs::File;
-//! # fn main() -> std::io::Result<()> {
-//! // Decode a gif into frames
-//! let file_in = File::open("foo.gif")?;
-//! let mut decoder = GifDecoder::new(file_in).unwrap();
-//! let frames = decoder.into_frames();
-//! let frames = frames.collect_frames().expect("error decoding gif");
-//!
-//! // Encode frames into a gif and save to a file
-//! let mut file_out = File::open("out.gif")?;
-//! let mut encoder = GifEncoder::new(file_out);
-//! encoder.encode_frames(frames.into_iter());
-//! # Ok(())
-//! # }
-//! ```
-#![allow(clippy::while_let_loop)]
-
-use std::convert::TryFrom;
-use std::convert::TryInto;
-use std::io::{self, Cursor, Read, Write};
-use std::marker::PhantomData;
-use std::mem;
-
-use gif::ColorOutput;
-use gif::{DisposalMethod, Frame};
-use num_rational::Ratio;
-
-use crate::animation;
-use crate::color::{ColorType, Rgba};
-use crate::error::{
- DecodingError, EncodingError, ImageError, ImageResult, ParameterError, ParameterErrorKind,
- UnsupportedError, UnsupportedErrorKind,
-};
-use crate::image::{self, AnimationDecoder, ImageDecoder, ImageFormat};
-use crate::io::Limits;
-use crate::traits::Pixel;
-use crate::ImageBuffer;
-
-/// GIF decoder
-pub struct GifDecoder<R: Read> {
- reader: gif::Decoder<R>,
- limits: Limits,
-}
-
-impl<R: Read> GifDecoder<R> {
- /// Creates a new decoder that decodes the input steam `r`
- pub fn new(r: R) -> ImageResult<GifDecoder<R>> {
- let mut decoder = gif::DecodeOptions::new();
- decoder.set_color_output(ColorOutput::RGBA);
-
- Ok(GifDecoder {
- reader: decoder.read_info(r).map_err(ImageError::from_decoding)?,
- limits: Limits::default(),
- })
- }
-
- /// Creates a new decoder that decodes the input steam `r`, using limits `limits`
- pub fn with_limits(r: R, limits: Limits) -> ImageResult<GifDecoder<R>> {
- let mut decoder = gif::DecodeOptions::new();
- decoder.set_color_output(ColorOutput::RGBA);
-
- Ok(GifDecoder {
- reader: decoder.read_info(r).map_err(ImageError::from_decoding)?,
- limits,
- })
- }
-}
-
-/// Wrapper struct around a `Cursor<Vec<u8>>`
-pub struct GifReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
-impl<R> Read for GifReader<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 GifDecoder<R> {
- type Reader = GifReader<R>;
-
- fn dimensions(&self) -> (u32, u32) {
- (
- u32::from(self.reader.width()),
- u32::from(self.reader.height()),
- )
- }
-
- fn color_type(&self) -> ColorType {
- ColorType::Rgba8
- }
-
- fn into_reader(self) -> ImageResult<Self::Reader> {
- Ok(GifReader(
- Cursor::new(image::decoder_to_vec(self)?),
- PhantomData,
- ))
- }
-
- fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
- assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
-
- let frame = match self
- .reader
- .next_frame_info()
- .map_err(ImageError::from_decoding)?
- {
- Some(frame) => FrameInfo::new_from_frame(frame),
- None => {
- return Err(ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::NoMoreData,
- )))
- }
- };
-
- let (width, height) = self.dimensions();
-
- if frame.left == 0
- && frame.width == width
- && (frame.top as u64 + frame.height as u64 <= height as u64)
- {
- // If the frame matches the logical screen, or, as a more general case,
- // fits into it and touches its left and right borders, then
- // we can directly write it into the buffer without causing line wraparound.
- let line_length = usize::try_from(width)
- .unwrap()
- .checked_mul(self.color_type().bytes_per_pixel() as usize)
- .unwrap();
-
- // isolate the portion of the buffer to read the frame data into.
- // the chunks above and below it are going to be zeroed.
- let (blank_top, rest) =
- buf.split_at_mut(line_length.checked_mul(frame.top as usize).unwrap());
- let (buf, blank_bottom) =
- rest.split_at_mut(line_length.checked_mul(frame.height as usize).unwrap());
-
- debug_assert_eq!(buf.len(), self.reader.buffer_size());
-
- // this is only necessary in case the buffer is not zeroed
- for b in blank_top {
- *b = 0;
- }
- // fill the middle section with the frame data
- self.reader
- .read_into_buffer(buf)
- .map_err(ImageError::from_decoding)?;
- // this is only necessary in case the buffer is not zeroed
- for b in blank_bottom {
- *b = 0;
- }
- } else {
- // If the frame does not match the logical screen, read into an extra buffer
- // and 'insert' the frame from left/top to logical screen width/height.
- let buffer_size = self.reader.buffer_size();
-
- self.limits.reserve_usize(buffer_size)?;
-
- let mut frame_buffer = vec![0; buffer_size];
-
- self.limits.free_usize(buffer_size);
-
- self.reader
- .read_into_buffer(&mut frame_buffer[..])
- .map_err(ImageError::from_decoding)?;
-
- let frame_buffer = ImageBuffer::from_raw(frame.width, frame.height, frame_buffer);
- let image_buffer = ImageBuffer::from_raw(width, height, buf);
-
- // `buffer_size` uses wrapping arithmetic, thus might not report the
- // correct storage requirement if the result does not fit in `usize`.
- // `ImageBuffer::from_raw` detects overflow and reports by returning `None`.
- if frame_buffer.is_none() || image_buffer.is_none() {
- return Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Gif.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Image dimensions ({}, {}) are too large",
- frame.width, frame.height
- )),
- ),
- ));
- }
-
- let frame_buffer = frame_buffer.unwrap();
- let mut image_buffer = image_buffer.unwrap();
-
- for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
- let frame_x = x.wrapping_sub(frame.left);
- let frame_y = y.wrapping_sub(frame.top);
-
- if frame_x < frame.width && frame_y < frame.height {
- *pixel = *frame_buffer.get_pixel(frame_x, frame_y);
- } else {
- // this is only necessary in case the buffer is not zeroed
- *pixel = Rgba([0, 0, 0, 0]);
- }
- }
- }
-
- Ok(())
- }
-}
-
-struct GifFrameIterator<R: Read> {
- reader: gif::Decoder<R>,
-
- width: u32,
- height: u32,
-
- non_disposed_frame: ImageBuffer<Rgba<u8>, Vec<u8>>,
-}
-
-impl<R: Read> GifFrameIterator<R> {
- fn new(decoder: GifDecoder<R>) -> GifFrameIterator<R> {
- let (width, height) = decoder.dimensions();
-
- // intentionally ignore the background color for web compatibility
-
- // create the first non disposed frame
- let non_disposed_frame = ImageBuffer::from_pixel(width, height, Rgba([0, 0, 0, 0]));
-
- GifFrameIterator {
- reader: decoder.reader,
- width,
- height,
- non_disposed_frame,
- }
- }
-}
-
-impl<R: Read> Iterator for GifFrameIterator<R> {
- type Item = ImageResult<animation::Frame>;
-
- fn next(&mut self) -> Option<ImageResult<animation::Frame>> {
- // begin looping over each frame
-
- let frame = match self.reader.next_frame_info() {
- Ok(frame_info) => {
- if let Some(frame) = frame_info {
- FrameInfo::new_from_frame(frame)
- } else {
- // no more frames
- return None;
- }
- }
- Err(err) => return Some(Err(ImageError::from_decoding(err))),
- };
-
- let mut vec = vec![0; self.reader.buffer_size()];
- if let Err(err) = self.reader.read_into_buffer(&mut vec) {
- return Some(Err(ImageError::from_decoding(err)));
- }
-
- // create the image buffer from the raw frame.
- // `buffer_size` uses wrapping arithmetic, thus might not report the
- // correct storage requirement if the result does not fit in `usize`.
- // on the other hand, `ImageBuffer::from_raw` detects overflow and
- // reports by returning `None`.
- let mut frame_buffer = match ImageBuffer::from_raw(frame.width, frame.height, vec) {
- Some(frame_buffer) => frame_buffer,
- None => {
- return Some(Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Gif.into(),
- UnsupportedErrorKind::GenericFeature(format!(
- "Image dimensions ({}, {}) are too large",
- frame.width, frame.height
- )),
- ),
- )))
- }
- };
-
- // blend the current frame with the non-disposed frame, then update
- // the non-disposed frame according to the disposal method.
- fn blend_and_dispose_pixel(
- dispose: DisposalMethod,
- previous: &mut Rgba<u8>,
- current: &mut Rgba<u8>,
- ) {
- let pixel_alpha = current.channels()[3];
- if pixel_alpha == 0 {
- *current = *previous;
- }
-
- match dispose {
- DisposalMethod::Any | DisposalMethod::Keep => {
- // do not dispose
- // (keep pixels from this frame)
- // note: the `Any` disposal method is underspecified in the GIF
- // spec, but most viewers treat it identically to `Keep`
- *previous = *current;
- }
- DisposalMethod::Background => {
- // restore to background color
- // (background shows through transparent pixels in the next frame)
- *previous = Rgba([0, 0, 0, 0]);
- }
- DisposalMethod::Previous => {
- // restore to previous
- // (dispose frames leaving the last none disposal frame)
- }
- }
- }
-
- // if `frame_buffer`'s frame exactly matches the entire image, then
- // use it directly, else create a new buffer to hold the composited
- // image.
- let image_buffer = if (frame.left, frame.top) == (0, 0)
- && (self.width, self.height) == frame_buffer.dimensions()
- {
- for (x, y, pixel) in frame_buffer.enumerate_pixels_mut() {
- let previous_pixel = self.non_disposed_frame.get_pixel_mut(x, y);
- blend_and_dispose_pixel(frame.disposal_method, previous_pixel, pixel);
- }
- frame_buffer
- } else {
- ImageBuffer::from_fn(self.width, self.height, |x, y| {
- let frame_x = x.wrapping_sub(frame.left);
- let frame_y = y.wrapping_sub(frame.top);
- let previous_pixel = self.non_disposed_frame.get_pixel_mut(x, y);
-
- if frame_x < frame_buffer.width() && frame_y < frame_buffer.height() {
- let mut pixel = *frame_buffer.get_pixel(frame_x, frame_y);
- blend_and_dispose_pixel(frame.disposal_method, previous_pixel, &mut pixel);
- pixel
- } else {
- // out of bounds, return pixel from previous frame
- *previous_pixel
- }
- })
- };
-
- Some(Ok(animation::Frame::from_parts(
- image_buffer,
- 0,
- 0,
- frame.delay,
- )))
- }
-}
-
-impl<'a, R: Read + 'a> AnimationDecoder<'a> for GifDecoder<R> {
- fn into_frames(self) -> animation::Frames<'a> {
- animation::Frames::new(Box::new(GifFrameIterator::new(self)))
- }
-}
-
-struct FrameInfo {
- left: u32,
- top: u32,
- width: u32,
- height: u32,
- disposal_method: DisposalMethod,
- delay: animation::Delay,
-}
-
-impl FrameInfo {
- fn new_from_frame(frame: &Frame) -> FrameInfo {
- FrameInfo {
- left: u32::from(frame.left),
- top: u32::from(frame.top),
- width: u32::from(frame.width),
- height: u32::from(frame.height),
- disposal_method: frame.dispose,
- // frame.delay is in units of 10ms so frame.delay*10 is in ms
- delay: animation::Delay::from_ratio(Ratio::new(u32::from(frame.delay) * 10, 1)),
- }
- }
-}
-
-/// Number of repetitions for a GIF animation
-#[derive(Clone, Copy, Debug)]
-pub enum Repeat {
- /// Finite number of repetitions
- Finite(u16),
- /// Looping GIF
- Infinite,
-}
-
-impl Repeat {
- pub(crate) fn to_gif_enum(&self) -> gif::Repeat {
- match self {
- Repeat::Finite(n) => gif::Repeat::Finite(*n),
- Repeat::Infinite => gif::Repeat::Infinite,
- }
- }
-}
-
-/// GIF encoder.
-pub struct GifEncoder<W: Write> {
- w: Option<W>,
- gif_encoder: Option<gif::Encoder<W>>,
- speed: i32,
- repeat: Option<Repeat>,
-}
-
-impl<W: Write> GifEncoder<W> {
- /// Creates a new GIF encoder with a speed of 1. This prioritizes quality over performance at any cost.
- pub fn new(w: W) -> GifEncoder<W> {
- Self::new_with_speed(w, 1)
- }
-
- /// Create a new GIF encoder, and has the speed parameter `speed`. See
- /// [`Frame::from_rgba_speed`](https://docs.rs/gif/latest/gif/struct.Frame.html#method.from_rgba_speed)
- /// for more information.
- pub fn new_with_speed(w: W, speed: i32) -> GifEncoder<W> {
- assert!(
- (1..=30).contains(&speed),
- "speed needs to be in the range [1, 30]"
- );
- GifEncoder {
- w: Some(w),
- gif_encoder: None,
- speed,
- repeat: None,
- }
- }
-
- /// Set the repeat behaviour of the encoded GIF
- pub fn set_repeat(&mut self, repeat: Repeat) -> ImageResult<()> {
- if let Some(ref mut encoder) = self.gif_encoder {
- encoder
- .set_repeat(repeat.to_gif_enum())
- .map_err(ImageError::from_encoding)?;
- }
- self.repeat = Some(repeat);
- Ok(())
- }
-
- /// Encode a single image.
- pub fn encode(
- &mut self,
- data: &[u8],
- width: u32,
- height: u32,
- color: ColorType,
- ) -> ImageResult<()> {
- let (width, height) = self.gif_dimensions(width, height)?;
- match color {
- ColorType::Rgb8 => self.encode_gif(Frame::from_rgb(width, height, data)),
- ColorType::Rgba8 => {
- self.encode_gif(Frame::from_rgba(width, height, &mut data.to_owned()))
- }
- _ => Err(ImageError::Unsupported(
- UnsupportedError::from_format_and_kind(
- ImageFormat::Gif.into(),
- UnsupportedErrorKind::Color(color.into()),
- ),
- )),
- }
- }
-
- /// Encode one frame of animation.
- pub fn encode_frame(&mut self, img_frame: animation::Frame) -> ImageResult<()> {
- let frame = self.convert_frame(img_frame)?;
- self.encode_gif(frame)
- }
-
- /// Encodes Frames.
- /// Consider using `try_encode_frames` instead to encode an `animation::Frames` like iterator.
- pub fn encode_frames<F>(&mut self, frames: F) -> ImageResult<()>
- where
- F: IntoIterator<Item = animation::Frame>,
- {
- for img_frame in frames {
- self.encode_frame(img_frame)?;
- }
- Ok(())
- }
-
- /// Try to encode a collection of `ImageResult<animation::Frame>` objects.
- /// Use this function to encode an `animation::Frames` like iterator.
- /// Whenever an `Err` item is encountered, that value is returned without further actions.
- pub fn try_encode_frames<F>(&mut self, frames: F) -> ImageResult<()>
- where
- F: IntoIterator<Item = ImageResult<animation::Frame>>,
- {
- for img_frame in frames {
- self.encode_frame(img_frame?)?;
- }
- Ok(())
- }
-
- pub(crate) fn convert_frame(
- &mut self,
- img_frame: animation::Frame,
- ) -> ImageResult<Frame<'static>> {
- // get the delay before converting img_frame
- let frame_delay = img_frame.delay().into_ratio().to_integer();
- // convert img_frame into RgbaImage
- let mut rbga_frame = img_frame.into_buffer();
- let (width, height) = self.gif_dimensions(rbga_frame.width(), rbga_frame.height())?;
-
- // Create the gif::Frame from the animation::Frame
- let mut frame = Frame::from_rgba_speed(width, height, &mut rbga_frame, self.speed);
- // Saturate the conversion to u16::MAX instead of returning an error as that
- // would require a new special cased variant in ParameterErrorKind which most
- // likely couldn't be reused for other cases. This isn't a bad trade-off given
- // that the current algorithm is already lossy.
- frame.delay = (frame_delay / 10).try_into().unwrap_or(std::u16::MAX);
-
- Ok(frame)
- }
-
- fn gif_dimensions(&self, width: u32, height: u32) -> ImageResult<(u16, u16)> {
- fn inner_dimensions(width: u32, height: u32) -> Option<(u16, u16)> {
- let width = u16::try_from(width).ok()?;
- let height = u16::try_from(height).ok()?;
- Some((width, height))
- }
-
- // TODO: this is not very idiomatic yet. Should return an EncodingError.
- inner_dimensions(width, height).ok_or_else(|| {
- ImageError::Parameter(ParameterError::from_kind(
- ParameterErrorKind::DimensionMismatch,
- ))
- })
- }
-
- pub(crate) fn encode_gif(&mut self, mut frame: Frame) -> ImageResult<()> {
- let gif_encoder;
- if let Some(ref mut encoder) = self.gif_encoder {
- gif_encoder = encoder;
- } else {
- let writer = self.w.take().unwrap();
- let mut encoder = gif::Encoder::new(writer, frame.width, frame.height, &[])
- .map_err(ImageError::from_encoding)?;
- if let Some(ref repeat) = self.repeat {
- encoder
- .set_repeat(repeat.to_gif_enum())
- .map_err(ImageError::from_encoding)?;
- }
- self.gif_encoder = Some(encoder);
- gif_encoder = self.gif_encoder.as_mut().unwrap()
- }
-
- frame.dispose = gif::DisposalMethod::Background;
-
- gif_encoder
- .write_frame(&frame)
- .map_err(ImageError::from_encoding)
- }
-}
-
-impl ImageError {
- fn from_decoding(err: gif::DecodingError) -> ImageError {
- use gif::DecodingError::*;
- match err {
- err @ Format(_) => {
- ImageError::Decoding(DecodingError::new(ImageFormat::Gif.into(), err))
- }
- Io(io_err) => ImageError::IoError(io_err),
- }
- }
-
- fn from_encoding(err: gif::EncodingError) -> ImageError {
- use gif::EncodingError::*;
- match err {
- err @ Format(_) => {
- ImageError::Encoding(EncodingError::new(ImageFormat::Gif.into(), err))
- }
- Io(io_err) => ImageError::IoError(io_err),
- }
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn frames_exceeding_logical_screen_size() {
- // This is a gif with 10x10 logical screen, but a 16x16 frame + 6px offset inside.
- let data = vec![
- 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0A, 0x00, 0x0A, 0x00, 0xF0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x0E, 0xFF, 0x1F, 0x21, 0xF9, 0x04, 0x09, 0x64, 0x00, 0x00, 0x00, 0x2C,
- 0x06, 0x00, 0x06, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x23, 0x84, 0x8F, 0xA9,
- 0xBB, 0xE1, 0xE8, 0x42, 0x8A, 0x0F, 0x50, 0x79, 0xAE, 0xD1, 0xF9, 0x7A, 0xE8, 0x71,
- 0x5B, 0x48, 0x81, 0x64, 0xD5, 0x91, 0xCA, 0x89, 0x4D, 0x21, 0x63, 0x89, 0x4C, 0x09,
- 0x77, 0xF5, 0x6D, 0x14, 0x00, 0x3B,
- ];
-
- let decoder = GifDecoder::new(Cursor::new(data)).unwrap();
- let mut buf = vec![0u8; decoder.total_bytes() as usize];
-
- assert!(decoder.read_image(&mut buf).is_ok());
- }
-}