Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ path = "benches/blur.rs"
name = "blur"
harness = false

[[example]]
name = "tiff_img_stack"
required-features = ["tiff"]

# because of https://github.com/image-rs/image/pull/2583
# TODO: remove when shipping the next major release after 0.25
[package.metadata.cargo-semver-checks.lints]
Expand Down
34 changes: 34 additions & 0 deletions examples/tiff_img_stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! An example of opening an image.
extern crate image;

use std::env;
use std::fs::File;
use std::path::Path;

use image::ImageStackDecoder;
use image::{codecs::tiff::TiffDecoder, GenericImageView};
use std::io::BufReader;

fn main() {
let file = if env::args().count() == 2 {
env::args().nth(1).unwrap()
} else {
panic!("Please enter a file")
};

// create a buf reader
let buf_reader = BufReader::new(File::open(Path::new(&file)).unwrap());

let decoder = TiffDecoder::new(buf_reader).unwrap();

let img_stack_it = decoder.into_frames();

for (i, frame) in img_stack_it.enumerate() {
dbg!(i);
let frame = frame.unwrap();
let buffer = frame.buffer();
let dimensions = buffer.dimensions();
let color = buffer.color();
println!("Frame {i}: dimensions: {dimensions:?}, color: {color:?}");
}
}
81 changes: 21 additions & 60 deletions src/animation.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,30 @@
use std::cmp::Ordering;
use std::time::Duration;

use crate::error::ImageResult;
use crate::image_stack::Frame;
use crate::image_stack::Stack;
use crate::RgbaImage;

/// An implementation dependent iterator, reading the frames as requested
pub struct Frames<'a> {
iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>,
}

impl<'a> Frames<'a> {
/// Creates a new `Frames` from an implementation specific iterator.
#[must_use]
pub fn new(iterator: Box<dyn Iterator<Item = ImageResult<Frame>> + 'a>) -> Self {
Frames { iterator }
}

/// Steps through the iterator from the current frame until the end and pushes each frame into
/// a `Vec`.
/// If en error is encountered that error is returned instead.
///
/// Note: This is equivalent to `Frames::collect::<ImageResult<Vec<Frame>>>()`
pub fn collect_frames(self) -> ImageResult<Vec<Frame>> {
self.collect()
}
}

impl Iterator for Frames<'_> {
type Item = ImageResult<Frame>;

fn next(&mut self) -> Option<ImageResult<Frame>> {
self.iterator.next()
}
}
/// An alias for animation frames iterator
pub type AnimationFrames<'a> = Stack<'a, AnimationFrame>;

/// A single animation frame
pub struct Frame {
/// Delay between the frames in milliseconds
pub struct AnimationFrame {
delay: Delay,
/// x offset
left: u32,
/// y offset
top: u32,
buffer: RgbaImage,
frame: Frame<RgbaImage>,
}

impl Clone for Frame {
impl Clone for AnimationFrame {
fn clone(&self) -> Self {
Self {
delay: self.delay,
left: self.left,
top: self.top,
buffer: self.buffer.clone(),
frame: self.frame.clone(),
}
}

fn clone_from(&mut self, source: &Self) {
self.delay = source.delay;
self.left = source.left;
self.top = source.top;
self.buffer.clone_from(&source.buffer);
self.frame.clone_from(&source.frame);
}
}

Expand All @@ -82,26 +47,22 @@ pub struct Delay {
ratio: Ratio,
}

impl Frame {
impl AnimationFrame {
/// Constructs a new frame without any delay.
#[must_use]
pub fn new(buffer: RgbaImage) -> Frame {
Frame {
pub fn new(buffer: RgbaImage) -> AnimationFrame {
AnimationFrame {
delay: Delay::from_ratio(Ratio { numer: 0, denom: 1 }),
left: 0,
top: 0,
buffer,
frame: Frame::new(buffer),
}
}

/// Constructs a new frame
#[must_use]
pub fn from_parts(buffer: RgbaImage, left: u32, top: u32, delay: Delay) -> Frame {
Frame {
pub fn from_parts(buffer: RgbaImage, left: u32, top: u32, delay: Delay) -> AnimationFrame {
AnimationFrame {
delay,
left,
top,
buffer,
frame: Frame::from_parts(buffer, left, top),
}
}

Expand All @@ -114,30 +75,30 @@ impl Frame {
/// Returns the image buffer
#[must_use]
pub fn buffer(&self) -> &RgbaImage {
&self.buffer
self.frame.buffer()
}

/// Returns a mutable image buffer
pub fn buffer_mut(&mut self) -> &mut RgbaImage {
&mut self.buffer
&mut self.frame.buffer
}

/// Returns the image buffer
#[must_use]
pub fn into_buffer(self) -> RgbaImage {
self.buffer
self.frame.buffer().clone()
}

/// Returns the x offset
#[must_use]
pub fn left(&self) -> u32 {
self.left
self.frame.left()
}

/// Returns the y offset
#[must_use]
pub fn top(&self) -> u32 {
self.top
self.frame.top()
}
}

Expand Down
34 changes: 17 additions & 17 deletions src/codecs/gif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! # Examples
//! ```rust,no_run
//! use image::codecs::gif::{GifDecoder, GifEncoder};
//! use image::{ImageDecoder, AnimationDecoder};
//! use image::{ImageDecoder, ImageStackDecoder};
//! use std::fs::File;
//! use std::io::BufReader;
//! # fn main() -> std::io::Result<()> {
Expand Down Expand Up @@ -42,8 +42,8 @@ use crate::error::{
};
use crate::traits::Pixel;
use crate::{
AnimationDecoder, ExtendedColorType, ImageBuffer, ImageDecoder, ImageEncoder, ImageFormat,
Limits,
AnimationFrame, ExtendedColorType, ImageBuffer, ImageDecoder, ImageEncoder, ImageFormat,
ImageStackDecoder, Limits,
};

/// GIF decoder
Expand Down Expand Up @@ -261,9 +261,9 @@ impl<R: BufRead + Seek> GifFrameIterator<R> {
}

impl<R: Read> Iterator for GifFrameIterator<R> {
type Item = ImageResult<animation::Frame>;
type Item = ImageResult<AnimationFrame>;

fn next(&mut self) -> Option<ImageResult<animation::Frame>> {
fn next(&mut self) -> Option<ImageResult<AnimationFrame>> {
if self.is_end {
return None;
}
Expand Down Expand Up @@ -412,7 +412,7 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
})
};

Some(Ok(animation::Frame::from_parts(
Some(Ok(AnimationFrame::from_parts(
image_buffer,
0,
0,
Expand All @@ -421,9 +421,9 @@ impl<R: Read> Iterator for GifFrameIterator<R> {
}
}

impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for GifDecoder<R> {
fn into_frames(self) -> animation::Frames<'a> {
animation::Frames::new(Box::new(GifFrameIterator::new(self)))
impl<'a, R: BufRead + Seek + 'a> ImageStackDecoder<'a, AnimationFrame> for GifDecoder<R> {
fn into_frames(self) -> animation::AnimationFrames<'a> {
animation::AnimationFrames::new(Box::new(GifFrameIterator::new(self)))
}
}

Expand Down Expand Up @@ -548,29 +548,29 @@ impl<W: Write> GifEncoder<W> {
}

/// Encode one frame of animation.
pub fn encode_frame(&mut self, img_frame: animation::Frame) -> ImageResult<()> {
pub fn encode_frame(&mut self, img_frame: AnimationFrame) -> 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.
/// Consider using `try_encode_frames` instead to encode an `animation::AnimationFrames` like iterator.
pub fn encode_frames<F>(&mut self, frames: F) -> ImageResult<()>
where
F: IntoIterator<Item = animation::Frame>,
F: IntoIterator<Item = AnimationFrame>,
{
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.
/// Try to encode a collection of `ImageResult<animation::AnimationFrame>` objects.
/// Use this function to encode an `animation::AnimationFrames` 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>>,
F: IntoIterator<Item = ImageResult<AnimationFrame>>,
{
for img_frame in frames {
self.encode_frame(img_frame?)?;
Expand All @@ -580,15 +580,15 @@ impl<W: Write> GifEncoder<W> {

pub(crate) fn convert_frame(
&mut self,
img_frame: animation::Frame,
img_frame: AnimationFrame,
) -> 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
// Create the gif::Frame from the animation::AnimationFrame
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
Expand Down
16 changes: 8 additions & 8 deletions src/codecs/png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::io::{BufRead, Seek, Write};

use png::{BlendOp, DeflateCompression, DisposeOp};

use crate::animation::{Delay, Frame, Frames, Ratio};
use crate::animation::{AnimationFrame, AnimationFrames, Delay, Ratio};
use crate::color::{Blend, ColorType, ExtendedColorType};
use crate::error::{
DecodingError, ImageError, ImageResult, LimitError, LimitErrorKind, ParameterError,
Expand All @@ -19,8 +19,8 @@ use crate::error::{
use crate::math::Rect;
use crate::utils::vec_try_with_capacity;
use crate::{
AnimationDecoder, DynamicImage, GenericImage, GenericImageView, ImageBuffer, ImageDecoder,
ImageEncoder, ImageFormat, Limits, Luma, LumaA, Rgb, Rgba, RgbaImage,
DynamicImage, GenericImage, GenericImageView, ImageBuffer, ImageDecoder, ImageEncoder,
ImageFormat, ImageStackDecoder, Limits, Luma, LumaA, Rgb, Rgba, RgbaImage,
};

// http://www.w3.org/TR/PNG-Structure.html
Expand Down Expand Up @@ -518,12 +518,12 @@ impl<R: BufRead + Seek> ApngDecoder<R> {
}
}

impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
fn into_frames(self) -> Frames<'a> {
impl<'a, R: BufRead + Seek + 'a> ImageStackDecoder<'a, AnimationFrame> for ApngDecoder<R> {
fn into_frames(self) -> AnimationFrames<'a> {
struct FrameIterator<R: BufRead + Seek>(ApngDecoder<R>);

impl<R: BufRead + Seek> Iterator for FrameIterator<R> {
type Item = ImageResult<Frame>;
type Item = ImageResult<AnimationFrame>;

fn next(&mut self) -> Option<Self::Item> {
let image = match self.0.mix_next_frame() {
Expand All @@ -542,11 +542,11 @@ impl<'a, R: BufRead + Seek + 'a> AnimationDecoder<'a> for ApngDecoder<R> {
d => u32::from(d),
};
let delay = Delay::from_ratio(Ratio::new(num, denom));
Some(Ok(Frame::from_parts(image, 0, 0, delay)))
Some(Ok(AnimationFrame::from_parts(image, 0, 0, delay)))
}
}

Frames::new(Box::new(FrameIterator(self)))
AnimationFrames::new(Box::new(FrameIterator(self)))
}
}

Expand Down
Loading
Loading