use crate::{DynamicFrameIterator, Error, Image, ImageFormat, Pixel, Result};
use std::{
fs::File,
io::{Read, Write},
path::Path,
time::Duration,
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DisposalMethod {
None,
Background,
Previous,
}
impl Default for DisposalMethod {
fn default() -> Self {
Self::None
}
}
#[derive(Clone)]
pub struct Frame<P: Pixel> {
inner: Image<P>,
delay: Duration,
disposal: DisposalMethod,
}
impl<P: Pixel> Frame<P> {
#[must_use]
pub fn from_image(image: Image<P>) -> Self {
Self {
inner: image,
delay: Duration::default(),
disposal: DisposalMethod::default(),
}
}
pub fn set_delay(&mut self, delay: Duration) {
self.delay = delay;
}
#[must_use]
pub const fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
pub fn set_disposal(&mut self, disposal: DisposalMethod) {
self.disposal = disposal;
}
#[must_use]
pub const fn with_disposal(mut self, disposal: DisposalMethod) -> Self {
self.disposal = disposal;
self
}
#[must_use]
pub const fn image(&self) -> &Image<P> {
&self.inner
}
#[must_use]
pub fn map_image<T: Pixel>(self, f: impl FnOnce(Image<P>) -> Image<T>) -> Frame<T> {
Frame {
inner: f(self.inner),
delay: self.delay,
disposal: self.disposal,
}
}
pub fn image_mut(&mut self) -> &mut Image<P> {
&mut self.inner
}
#[allow(clippy::missing_const_for_fn)] #[must_use]
pub fn into_image(self) -> Image<P> {
self.inner
}
#[must_use]
pub const fn delay(&self) -> Duration {
self.delay
}
#[must_use]
pub const fn disposal(&self) -> DisposalMethod {
self.disposal
}
}
impl<P: Pixel> From<Image<P>> for Frame<P> {
fn from(image: Image<P>) -> Self {
Self::from_image(image)
}
}
impl<P: Pixel> From<Frame<P>> for Image<P> {
fn from(frame: Frame<P>) -> Self {
frame.into_image()
}
}
impl<P: Pixel> std::ops::Deref for Frame<P> {
type Target = Image<P>;
fn deref(&self) -> &Self::Target {
self.image()
}
}
impl<P: Pixel> std::ops::DerefMut for Frame<P> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.image_mut()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum LoopCount {
Infinite,
Exactly(u32),
}
impl Default for LoopCount {
fn default() -> Self {
Self::Infinite
}
}
impl LoopCount {
#[must_use]
pub const fn count_or_zero(self) -> u32 {
match self {
Self::Infinite => 0,
Self::Exactly(count) => count,
}
}
}
#[derive(Clone, Default)]
pub struct ImageSequence<P: Pixel> {
frames: Vec<Frame<P>>,
loops: LoopCount,
}
impl<P: Pixel> IntoIterator for ImageSequence<P> {
type Item = Frame<P>;
type IntoIter = std::vec::IntoIter<Frame<P>>;
fn into_iter(self) -> Self::IntoIter {
self.frames.into_iter()
}
}
impl<P: Pixel> FromIterator<Frame<P>> for ImageSequence<P> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = Frame<P>>,
{
Self::from_frames(iter.into_iter().collect())
}
}
impl<P: Pixel> FromIterator<Result<Frame<P>>> for ImageSequence<P> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = Result<Frame<P>>>,
{
Self::from_frames(iter.into_iter().collect::<Result<Vec<_>>>().unwrap())
}
}
impl<P: Pixel> ImageSequence<P> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn from_reader<R: Read>(
format: ImageFormat,
bytes: R,
) -> Result<DynamicFrameIterator<P, R>> {
format.run_sequence_decoder(bytes)
}
pub fn from_reader_inferred<R: Read + Write>(
mut bytes: R,
) -> Result<DynamicFrameIterator<P, R>> {
let mut buffer = Vec::new();
bytes.read_to_end(&mut buffer)?;
match ImageFormat::infer_encoding(&buffer) {
ImageFormat::Unknown => Err(Error::UnknownEncodingFormat),
format => {
bytes.write_all(&buffer)?;
format.run_sequence_decoder(bytes)
}
}
}
pub fn from_bytes(format: ImageFormat, bytes: &[u8]) -> Result<DynamicFrameIterator<P, &[u8]>> {
format.run_sequence_decoder(bytes)
}
pub fn from_bytes_inferred(bytes: &[u8]) -> Result<DynamicFrameIterator<P, &[u8]>> {
match ImageFormat::infer_encoding(bytes) {
ImageFormat::Unknown => Err(Error::UnknownEncodingFormat),
format => format.run_sequence_decoder(bytes),
}
}
pub fn open(path: impl AsRef<Path>) -> Result<DynamicFrameIterator<P, File>> {
let file = File::open(path.as_ref())?;
let format = match ImageFormat::from_path(path)? {
ImageFormat::Unknown => return Err(Error::UnknownEncodingFormat),
format => format,
};
format.run_sequence_decoder(file)
}
pub fn encode(&self, encoding: ImageFormat, dest: &mut impl Write) -> Result<()> {
encoding.run_sequence_encoder(self, dest)
}
pub fn save(&self, encoding: ImageFormat, path: impl AsRef<Path>) -> Result<()> {
let mut file = File::create(path).map_err(Error::IOError)?;
self.encode(encoding, &mut file)
}
pub fn save_inferred(&self, path: impl AsRef<Path>) -> Result<()> {
let encoding = ImageFormat::from_path(path.as_ref())?;
match encoding {
ImageFormat::Unknown => Err(Error::UnknownEncodingFormat),
_ => self.save(encoding, path),
}
}
#[must_use]
pub fn from_frames(frames: Vec<Frame<P>>) -> Self {
Self {
frames,
..Self::default()
}
}
#[must_use]
pub fn with_frame(mut self, frame: Frame<P>) -> Self {
self.frames.push(frame);
self
}
pub fn push_frame(&mut self, frame: Frame<P>) {
self.frames.push(frame);
}
pub fn extend_frames<I>(&mut self, frames: I)
where
I: IntoIterator<Item = Frame<P>>,
{
self.frames.extend(frames);
}
#[must_use]
pub const fn loop_count(&self) -> LoopCount {
self.loops
}
#[must_use]
pub const fn with_loop_count(mut self, loops: LoopCount) -> Self {
self.loops = loops;
self
}
pub fn set_loop_count(&mut self, loops: LoopCount) {
self.loops = loops;
}
#[must_use]
pub const fn looped_exactly(self, loops: u32) -> Self {
self.with_loop_count(LoopCount::Exactly(loops))
}
#[must_use]
pub const fn looped_infinitely(self) -> Self {
self.with_loop_count(LoopCount::Infinite)
}
#[must_use]
#[allow(clippy::missing_const_for_fn)]
pub fn into_frames(self) -> Vec<Frame<P>> {
self.frames
}
pub fn iter(&self) -> impl Iterator<Item = &Frame<P>> {
self.frames.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Frame<P>> {
self.frames.iter_mut()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.frames.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.frames.len()
}
#[must_use]
pub fn into_first_image(self) -> Image<P> {
self.into_frames().swap_remove(0).into_image()
}
#[must_use]
pub fn first_frame(&self) -> Option<&Frame<P>> {
self.frames.get(0)
}
#[must_use]
pub unsafe fn first_frame_unchecked(&self) -> &Frame<P> {
self.frames.get_unchecked(0)
}
#[must_use]
pub fn first_frame_mut(&mut self) -> Option<&mut Frame<P>> {
self.frames.get_mut(0)
}
#[must_use]
pub unsafe fn first_frame_unchecked_mut(&mut self) -> &mut Frame<P> {
self.frames.get_unchecked_mut(0)
}
}