use std::time::Instant;
use zune_core::log::Level::Trace;
use zune_core::log::{log_enabled, trace, Level};
use crate::codecs::ImageFormat;
use crate::errors::ImageErrors;
use crate::image::Image;
use crate::traits::{EncoderTrait, IntoImage, OperationsTrait};
#[derive(Copy, Clone, Debug)]
enum PipelineState {
Initialized,
Decode,
Operations,
Encode,
Finished
}
impl PipelineState {
pub fn next(self) -> Option<Self> {
match self {
PipelineState::Initialized => Some(PipelineState::Decode),
PipelineState::Decode => Some(PipelineState::Operations),
PipelineState::Operations => Some(PipelineState::Encode),
PipelineState::Encode => Some(PipelineState::Finished),
PipelineState::Finished => None
}
}
}
pub struct EncodeResult {
pub(crate) format: ImageFormat,
pub(crate) data: Vec<u8>
}
impl EncodeResult {
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn format(&self) -> ImageFormat {
self.format
}
}
pub struct Pipeline<T: IntoImage> {
state: Option<PipelineState>,
decode: Option<T>,
image: Vec<Image>,
operations: Vec<Box<dyn OperationsTrait>>,
encode: Vec<Box<dyn EncoderTrait>>,
encode_result: Vec<EncodeResult>
}
impl<T> Pipeline<T>
where
T: IntoImage
{
#[allow(clippy::new_without_default)]
pub fn new() -> Pipeline<T> {
Pipeline {
image: vec![],
state: Some(PipelineState::Initialized),
decode: None,
operations: vec![],
encode: vec![],
encode_result: vec![]
}
}
pub fn add_encoder(&mut self, encoder: Box<dyn EncoderTrait>) {
self.encode.push(encoder);
}
pub fn add_decoder(&mut self, decoder: T) {
self.decode = Some(decoder);
}
pub fn add_operation(&mut self, operations: Box<dyn OperationsTrait>) {
self.operations.push(operations);
}
pub fn chain_image(&mut self, image: Image) {
self.image.push(image);
}
pub fn chain_encoder(&mut self, encoder: Box<dyn EncoderTrait>) -> &mut Pipeline<T> {
self.encode.push(encoder);
self
}
pub fn chain_decoder(&mut self, decoder: T) -> &mut Pipeline<T> {
self.decode = Some(decoder);
self
}
pub fn chain_operations(&mut self, operations: Box<dyn OperationsTrait>) -> &mut Pipeline<T> {
self.operations.push(operations);
self
}
pub fn images(&self) -> &[Image] {
self.image.as_ref()
}
pub fn images_mut(&mut self) -> &mut [Image] {
self.image.as_mut()
}
pub fn advance(&mut self) -> Result<(), ImageErrors> {
if let Some(state) = self.state {
match state {
PipelineState::Decode => {
let start = Instant::now();
if self.decode.is_none() {
if self.image.is_empty() {
trace!("Image already present, no need to decode");
self.state = state.next();
return Ok(());
}
return Err(ImageErrors::NoImageForOperations);
}
if log_enabled!(Trace) {
println!();
trace!("Current state: {:?}\n", state);
}
let decode_op = self.decode.take().unwrap();
let img = decode_op.into_image()?;
self.image.push(img);
let stop = Instant::now();
self.state = state.next();
trace!("Finished decoding in {} ms", (stop - start).as_millis());
}
PipelineState::Operations => {
if self.image.is_empty() {
return Err(ImageErrors::NoImageForOperations);
}
if log_enabled!(Trace) && !self.operations.is_empty() {
println!();
trace!("Current state: {:?}\n", state);
}
for image in self.image.iter_mut() {
for operation in &self.operations {
let operation_name = operation.name();
trace!("Running {}", operation_name);
let start = Instant::now();
operation.execute(image)?;
let stop = Instant::now();
trace!(
"Finished running `{operation_name}` in {} ms",
(stop - start).as_millis()
);
}
self.state = state.next();
}
}
PipelineState::Encode => {
if self.image.is_empty() {
return Err(ImageErrors::NoImageForOperations);
}
if log_enabled!(Trace) && !self.encode.is_empty() {
println!();
trace!("Current state: {:?}\n", state);
}
for image in self.image.iter() {
for encoder in self.encode.iter_mut() {
let encoder_name = encoder.name();
trace!("Running {}", encoder_name);
let start = Instant::now();
let result = encoder.encode_to_result(image)?;
self.encode_result.push(result);
let stop = Instant::now();
trace!(
"Finished running `{encoder_name}` in {} ms",
(stop - start).as_millis()
);
if log_enabled!(Level::Info) {
eprintln!();
}
}
}
self.state = state.next();
}
PipelineState::Finished => {
trace!("Finished operations for this workflow");
self.state = state.next();
return Ok(());
}
_ => {
self.state = state.next();
}
}
}
Ok(())
}
pub fn advance_to_end(&mut self) -> Result<(), ImageErrors> {
if self.state.is_some() {
while self.state.is_some() {
self.advance()?;
}
}
Ok(())
}
pub fn get_results(&self) -> &[EncodeResult] {
&self.encode_result
}
}