use alloc::boxed::Box;
use core::any::Any;
use crate::StopToken;
use crate::format::ImageFormat;
use crate::{EncodeCapabilities, EncodeOutput, Metadata, ResourceLimits};
use enough::Stop;
use zenpixels::{PixelDescriptor, PixelSlice, PixelSliceMut};
use super::BoxedError;
use super::encoder::{AnimationFrameEncoder, Encoder};
use super::encoding::{EncodeJob, EncoderConfig};
pub trait DynEncoder: Send {
fn preferred_strip_height(&self) -> u32;
fn encode(self: Box<Self>, pixels: PixelSlice<'_>) -> Result<EncodeOutput, BoxedError>;
fn encode_srgba8(
self: Box<Self>,
data: &mut [u8],
make_opaque: bool,
width: u32,
height: u32,
stride_pixels: u32,
) -> Result<EncodeOutput, BoxedError>;
fn push_rows(&mut self, rows: PixelSlice<'_>) -> Result<(), BoxedError>;
fn finish(self: Box<Self>) -> Result<EncodeOutput, BoxedError>;
fn encode_from(
self: Box<Self>,
source: &mut dyn FnMut(u32, PixelSliceMut<'_>) -> usize,
) -> Result<EncodeOutput, BoxedError>;
}
impl core::fmt::Debug for dyn DynEncoder + '_ {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DynEncoder").finish_non_exhaustive()
}
}
pub(super) struct EncoderShim<E>(pub(super) E);
impl<E: Encoder + Send> DynEncoder for EncoderShim<E> {
fn preferred_strip_height(&self) -> u32 {
self.0.preferred_strip_height()
}
fn encode(self: Box<Self>, pixels: PixelSlice<'_>) -> Result<EncodeOutput, BoxedError> {
self.0.encode(pixels).map_err(|e| Box::new(e) as BoxedError)
}
fn encode_srgba8(
self: Box<Self>,
data: &mut [u8],
make_opaque: bool,
width: u32,
height: u32,
stride_pixels: u32,
) -> Result<EncodeOutput, BoxedError> {
self.0
.encode_srgba8(data, make_opaque, width, height, stride_pixels)
.map_err(|e| Box::new(e) as BoxedError)
}
fn push_rows(&mut self, rows: PixelSlice<'_>) -> Result<(), BoxedError> {
self.0
.push_rows(rows)
.map_err(|e| Box::new(e) as BoxedError)
}
fn finish(self: Box<Self>) -> Result<EncodeOutput, BoxedError> {
self.0.finish().map_err(|e| Box::new(e) as BoxedError)
}
fn encode_from(
self: Box<Self>,
source: &mut dyn FnMut(u32, PixelSliceMut<'_>) -> usize,
) -> Result<EncodeOutput, BoxedError> {
self.0
.encode_from(source)
.map_err(|e| Box::new(e) as BoxedError)
}
}
pub trait DynAnimationFrameEncoder: Send {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
fn push_frame(
&mut self,
pixels: PixelSlice<'_>,
duration_ms: u32,
stop: Option<&dyn Stop>,
) -> Result<(), BoxedError>;
fn finish(self: Box<Self>, stop: Option<&dyn Stop>) -> Result<EncodeOutput, BoxedError>;
}
impl core::fmt::Debug for dyn DynAnimationFrameEncoder + '_ {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DynAnimationFrameEncoder")
.finish_non_exhaustive()
}
}
pub(super) struct AnimationFrameEncoderShim<F>(pub(super) F);
impl<F: AnimationFrameEncoder + Send + 'static> DynAnimationFrameEncoder
for AnimationFrameEncoderShim<F>
{
fn as_any(&self) -> &dyn Any {
&self.0
}
fn as_any_mut(&mut self) -> &mut dyn Any {
&mut self.0
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
Box::new(self.0)
}
fn push_frame(
&mut self,
pixels: PixelSlice<'_>,
duration_ms: u32,
stop: Option<&dyn Stop>,
) -> Result<(), BoxedError> {
self.0
.push_frame(pixels, duration_ms, stop)
.map_err(|e| Box::new(e) as BoxedError)
}
fn finish(self: Box<Self>, stop: Option<&dyn Stop>) -> Result<EncodeOutput, BoxedError> {
self.0.finish(stop).map_err(|e| Box::new(e) as BoxedError)
}
}
pub trait DynEncodeJob {
fn set_stop(&mut self, stop: StopToken);
fn set_limits(&mut self, limits: ResourceLimits);
fn set_policy(&mut self, policy: crate::EncodePolicy);
fn set_metadata(&mut self, meta: Metadata);
fn set_canvas_size(&mut self, width: u32, height: u32);
fn set_loop_count(&mut self, count: Option<u32>);
fn extensions(&self) -> Option<&dyn Any>;
fn extensions_mut(&mut self) -> Option<&mut dyn Any>;
fn into_encoder(self: Box<Self>) -> Result<Box<dyn DynEncoder>, BoxedError>;
fn into_animation_frame_encoder(
self: Box<Self>,
) -> Result<Box<dyn DynAnimationFrameEncoder>, BoxedError>;
}
struct EncodeJobShim<J>(Option<J>);
impl<J> EncodeJobShim<J> {
fn take(&mut self) -> Result<J, BoxedError> {
self.0
.take()
.ok_or_else(|| "EncodeJobShim: job already consumed (double take)".into())
}
fn put(&mut self, job: J) {
self.0 = Some(job);
}
}
impl<J> DynEncodeJob for EncodeJobShim<J>
where
J: EncodeJob,
J::Enc: Encoder + Send,
J::AnimationFrameEnc: AnimationFrameEncoder,
{
fn set_stop(&mut self, stop: StopToken) {
if let Ok(job) = self.take() {
self.put(job.with_stop(stop));
}
}
fn set_limits(&mut self, limits: ResourceLimits) {
if let Ok(job) = self.take() {
self.put(job.with_limits(limits));
}
}
fn set_policy(&mut self, policy: crate::EncodePolicy) {
if let Ok(job) = self.take() {
self.put(job.with_policy(policy));
}
}
fn set_metadata(&mut self, meta: Metadata) {
if let Ok(job) = self.take() {
self.put(job.with_metadata(meta));
}
}
fn set_canvas_size(&mut self, width: u32, height: u32) {
if let Ok(job) = self.take() {
self.put(job.with_canvas_size(width, height));
}
}
fn set_loop_count(&mut self, count: Option<u32>) {
if let Ok(job) = self.take() {
self.put(job.with_loop_count(count));
}
}
fn extensions(&self) -> Option<&dyn Any> {
self.0.as_ref().and_then(|j| j.extensions())
}
fn extensions_mut(&mut self) -> Option<&mut dyn Any> {
self.0.as_mut().and_then(|j| j.extensions_mut())
}
fn into_encoder(mut self: Box<Self>) -> Result<Box<dyn DynEncoder>, BoxedError> {
let job = self.take()?;
let enc = job.encoder().map_err(|e| Box::new(e) as BoxedError)?;
Ok(Box::new(EncoderShim(enc)))
}
fn into_animation_frame_encoder(
mut self: Box<Self>,
) -> Result<Box<dyn DynAnimationFrameEncoder>, BoxedError> {
let job = self.take()?;
let enc = job
.animation_frame_encoder()
.map_err(|e| Box::new(e) as BoxedError)?;
Ok(Box::new(AnimationFrameEncoderShim(enc)))
}
}
pub trait DynEncoderConfig: Send + Sync {
fn as_any(&self) -> &dyn Any;
fn format(&self) -> ImageFormat;
fn supported_descriptors(&self) -> &'static [PixelDescriptor];
fn capabilities(&self) -> &'static EncodeCapabilities;
fn dyn_job(&self) -> Box<dyn DynEncodeJob + 'static>;
}
impl<C> DynEncoderConfig for C
where
C: EncoderConfig + 'static,
<C::Job as EncodeJob>::Enc: Encoder + Send,
<C::Job as EncodeJob>::AnimationFrameEnc: AnimationFrameEncoder,
{
fn as_any(&self) -> &dyn Any {
self
}
fn format(&self) -> ImageFormat {
C::format()
}
fn supported_descriptors(&self) -> &'static [PixelDescriptor] {
C::supported_descriptors()
}
fn capabilities(&self) -> &'static EncodeCapabilities {
C::capabilities()
}
fn dyn_job(&self) -> Box<dyn DynEncodeJob + 'static> {
Box::new(EncodeJobShim(Some(self.clone().job())))
}
}