use crate::{ColorType, DisposalMethod, Error, Frame, Image, ImageSequence, LoopCount, Pixel};
use std::ops::DerefMut;
use std::{
io::{Read, Write},
ops::Deref,
time::Duration,
};
mod sealed {
use super::{ColorType, DisposalMethod, Duration, Frame, Image, LoopCount, Pixel};
pub trait HasEncoderMetadata<C: Default, P: Pixel>: Sized {
fn width(&self) -> u32;
fn height(&self) -> u32;
fn sequence(&self) -> Option<(usize, LoopCount)> {
None
}
fn color_type(&self) -> ColorType;
fn bit_depth(&self) -> u8;
fn palette(&self) -> Option<&[P::Color]> {
None
}
fn config(self) -> C {
C::default()
}
}
pub trait FrameLike<P: Pixel> {
fn image(&self) -> &Image<P>;
fn delay(&self) -> Option<Duration>;
fn disposal(&self) -> Option<DisposalMethod>;
}
impl<P: Pixel> FrameLike<P> for Image<P> {
fn image(&self) -> &Self {
self
}
fn delay(&self) -> Option<Duration> {
None
}
fn disposal(&self) -> Option<DisposalMethod> {
None
}
}
impl<P: Pixel> FrameLike<P> for Frame<P> {
fn image(&self) -> &Image<P> {
self.image()
}
fn delay(&self) -> Option<Duration> {
Some(self.delay())
}
fn disposal(&self) -> Option<DisposalMethod> {
Some(self.disposal())
}
}
}
pub(crate) use sealed::*;
impl<'a, C: Default, P: Pixel> HasEncoderMetadata<C, P> for &'a Image<P> {
fn width(&self) -> u32 {
self.width.get()
}
fn height(&self) -> u32 {
self.height.get()
}
fn sequence(&self) -> Option<(usize, LoopCount)> {
None
}
fn color_type(&self) -> ColorType {
self.data.first().map_or(P::COLOR_TYPE, P::color_type)
}
fn bit_depth(&self) -> u8 {
P::BIT_DEPTH
}
fn palette(&self) -> Option<&[P::Color]> {
Image::palette(self)
}
}
impl<'a, C: Default, P: Pixel> HasEncoderMetadata<C, P> for &'a Frame<P> {
fn width(&self) -> u32 {
self.image().width.get()
}
fn height(&self) -> u32 {
self.image().height.get()
}
fn sequence(&self) -> Option<(usize, LoopCount)> {
Some((1, LoopCount::Infinite))
}
fn color_type(&self) -> ColorType {
self.data.first().map_or(P::COLOR_TYPE, P::color_type)
}
fn bit_depth(&self) -> u8 {
P::BIT_DEPTH
}
fn palette(&self) -> Option<&[P::Color]> {
Image::palette(self.image())
}
}
impl<'a, C: Default, P: Pixel> HasEncoderMetadata<C, P> for &'a ImageSequence<P> {
fn width(&self) -> u32 {
self.first_frame()
.ok_or(Error::EmptyImageError)
.unwrap()
.width
.get()
}
fn height(&self) -> u32 {
self.first_frame()
.ok_or(Error::EmptyImageError)
.unwrap()
.height
.get()
}
fn sequence(&self) -> Option<(usize, LoopCount)> {
Some((self.len(), self.loop_count()))
}
fn color_type(&self) -> ColorType {
self.first_frame().map_or(P::COLOR_TYPE, |image| {
image.data.first().map_or(P::COLOR_TYPE, P::color_type)
})
}
fn bit_depth(&self) -> u8 {
P::BIT_DEPTH
}
fn palette(&self) -> Option<&[P::Color]> {
self.first_frame()
.and_then(|frame| Image::palette(frame.image()))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EncoderMetadata<P: Pixel> {
pub width: u32,
pub height: u32,
pub sequence: Option<(usize, LoopCount)>,
pub color_type: ColorType,
pub bit_depth: u8,
pub palette: Option<Box<[P::Color]>>,
}
macro_rules! impl_from_metadata {
($($t:ident)+) => {
$(
impl<'a, P: Pixel> From<&'a $t<P>> for EncoderMetadata<P> {
fn from(metadata: &'a $t<P>) -> Self {
Self {
width: HasEncoderMetadata::<(), P>::width(&metadata),
height: HasEncoderMetadata::<(), P>::height(&metadata),
sequence: HasEncoderMetadata::<(), P>::sequence(&metadata),
color_type: HasEncoderMetadata::<(), P>::color_type(&metadata),
bit_depth: HasEncoderMetadata::<(), P>::bit_depth(&metadata),
palette: HasEncoderMetadata::<(), P>::palette(&metadata).map(|p| p.to_vec().into_boxed_slice()),
}
}
}
)+
}
}
impl_from_metadata!(Image Frame ImageSequence);
impl<C: Default, P: Pixel> HasEncoderMetadata<C, P> for EncoderMetadata<P> {
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
fn sequence(&self) -> Option<(usize, LoopCount)> {
self.sequence
}
fn color_type(&self) -> ColorType {
self.color_type
}
fn bit_depth(&self) -> u8 {
self.bit_depth
}
fn palette(&self) -> Option<&[P::Color]> {
self.palette.as_deref()
}
}
impl<P: Pixel> EncoderMetadata<P> {
#[must_use]
pub const fn new(width: u32, height: u32) -> Self {
Self {
width,
height,
sequence: None,
color_type: P::COLOR_TYPE,
bit_depth: P::BIT_DEPTH,
palette: None,
}
}
#[must_use]
pub const fn with_width(mut self, width: u32) -> Self {
self.width = width;
self
}
#[must_use]
pub const fn with_height(mut self, height: u32) -> Self {
self.height = height;
self
}
#[must_use]
pub const fn with_sequence(mut self, frame_count: usize, loop_count: LoopCount) -> Self {
self.sequence = Some((frame_count, loop_count));
self
}
#[must_use]
pub fn with_frame_count(mut self, frame_count: usize) -> Self {
self.sequence = Some((
frame_count,
self.sequence.map_or(LoopCount::Infinite, |(_, l)| l),
));
self
}
#[must_use]
pub fn with_loop_count(mut self, loop_count: LoopCount) -> Self {
self.sequence = Some((self.sequence.map_or(0, |(f, _)| f), loop_count));
self
}
#[must_use]
pub const fn with_color_type(mut self, color_type: ColorType) -> Self {
self.color_type = color_type;
self
}
#[must_use]
pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
self.bit_depth = bit_depth;
self
}
#[must_use]
pub const fn with_config<C: Default>(self, config: C) -> EncoderMetadataWithConfig<C, P> {
EncoderMetadataWithConfig {
metadata: self,
config,
}
}
#[must_use]
pub fn with_palette(mut self, palette: impl Deref<Target = [P::Color]>) -> Self {
self.palette = Some(palette.to_vec().into_boxed_slice());
self
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct EncoderMetadataWithConfig<C: Default, P: Pixel> {
pub metadata: EncoderMetadata<P>,
pub config: C,
}
impl<C: Default, P: Pixel> HasEncoderMetadata<C, P> for EncoderMetadataWithConfig<C, P> {
fn width(&self) -> u32 {
HasEncoderMetadata::<C, _>::width(&self.metadata)
}
fn height(&self) -> u32 {
HasEncoderMetadata::<C, _>::height(&self.metadata)
}
fn sequence(&self) -> Option<(usize, LoopCount)> {
HasEncoderMetadata::<C, _>::sequence(&self.metadata)
}
fn color_type(&self) -> ColorType {
HasEncoderMetadata::<C, _>::color_type(&self.metadata)
}
fn bit_depth(&self) -> u8 {
HasEncoderMetadata::<C, _>::bit_depth(&self.metadata)
}
fn palette(&self) -> Option<&[P::Color]> {
HasEncoderMetadata::<C, _>::palette(&self.metadata)
}
fn config(self) -> C {
self.config
}
}
impl<C: Default, P: Pixel> Deref for EncoderMetadataWithConfig<C, P> {
type Target = EncoderMetadata<P>;
fn deref(&self) -> &Self::Target {
&self.metadata
}
}
impl<C: Default, P: Pixel> DerefMut for EncoderMetadataWithConfig<C, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.metadata
}
}
pub trait Encoder<P: Pixel, W: Write>: Sized {
type Config: Default;
fn new(dest: W, metadata: impl HasEncoderMetadata<Self::Config, P>) -> crate::Result<Self>;
fn add_frame(&mut self, frame: &impl FrameLike<P>) -> crate::Result<()>;
fn finish(self) -> crate::Result<()>;
fn encode_static(image: &Image<P>, dest: W) -> crate::Result<()> {
let mut encoder = Self::new(dest, image)?;
encoder.add_frame(image)?;
encoder.finish()
}
fn encode_sequence(sequence: &ImageSequence<P>, dest: W) -> crate::Result<()> {
let mut encoder = Self::new(dest, sequence)?;
for frame in sequence.iter() {
encoder.add_frame(frame)?;
}
encoder.finish()
}
}
pub trait Decoder<P: Pixel, R: Read> {
type Sequence: FrameIterator<P>;
fn decode(&mut self, stream: R) -> crate::Result<Image<P>>;
fn decode_sequence(&mut self, stream: R) -> crate::Result<Self::Sequence>;
}
pub trait FrameIterator<P: Pixel>: Iterator<Item = crate::Result<Frame<P>>> {
fn len(&self) -> u32;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn loop_count(&self) -> LoopCount;
fn into_sequence(self: Box<Self>) -> crate::Result<ImageSequence<P>> {
let loop_count = self.loop_count();
let frames = self.collect::<crate::Result<Vec<_>>>()?;
Ok(ImageSequence::from_frames(frames).with_loop_count(loop_count))
}
}
#[allow(clippy::large_enum_variant)]
pub struct SingleFrameIterator<P: Pixel>(Option<Image<P>>);
impl<P: Pixel> SingleFrameIterator<P> {
#[must_use]
pub const fn new(image: Image<P>) -> Self {
Self(Some(image))
}
}
impl<P: Pixel> FrameIterator<P> for SingleFrameIterator<P> {
fn len(&self) -> u32 {
1
}
fn loop_count(&self) -> LoopCount {
LoopCount::Exactly(1)
}
fn into_sequence(mut self: Box<Self>) -> crate::Result<ImageSequence<P>> {
let image = self.0.take().unwrap();
let frame = Frame::from_image(image);
Ok(ImageSequence::new().with_frame(frame))
}
}
impl<P: Pixel> Iterator for SingleFrameIterator<P> {
type Item = crate::Result<Frame<P>>;
fn next(&mut self) -> Option<Self::Item> {
self.0.take().map(|image| Ok(Frame::from_image(image)))
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.0.is_some() {
(1, Some(1))
} else {
(0, Some(0))
}
}
}