mod immediate;
mod multithreaded;
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
mod rayon;
use crate::decoder::{choose_color_convert_func, ColorTransform};
use crate::error::Result;
use crate::parser::{Component, Dimensions};
use crate::upsampler::Upsampler;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::RefCell;
pub struct RowData {
pub index: usize,
pub component: Component,
pub quantization_table: Arc<[u16; 64]>,
}
pub trait Worker {
fn start(&mut self, row_data: RowData) -> Result<()>;
fn append_row(&mut self, row: (usize, Vec<i16>)) -> Result<()>;
fn get_result(&mut self, index: usize) -> Result<Vec<u8>>;
fn append_rows(&mut self, row: &mut dyn Iterator<Item = (usize, Vec<i16>)>) -> Result<()> {
for item in row {
self.append_row(item)?;
}
Ok(())
}
}
#[allow(dead_code)]
pub enum PreferWorkerKind {
Immediate,
Multithreaded,
}
#[derive(Default)]
pub struct WorkerScope {
inner: core::cell::RefCell<Option<WorkerScopeInner>>,
}
enum WorkerScopeInner {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
Rayon(Box<rayon::Scoped>),
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
Multithreaded(multithreaded::MpscWorker),
Immediate(immediate::ImmediateWorker),
}
impl WorkerScope {
pub fn with<T>(with: impl FnOnce(&Self) -> T) -> T {
with(&WorkerScope {
inner: RefCell::default(),
})
}
pub fn get_or_init_worker<T>(
&self,
prefer: PreferWorkerKind,
f: impl FnOnce(&mut dyn Worker) -> T,
) -> T {
let mut inner = self.inner.borrow_mut();
let inner = inner.get_or_insert_with(move || match prefer {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Rayon(Default::default()),
#[allow(unreachable_patterns)]
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Multithreaded(Default::default()),
_ => WorkerScopeInner::Immediate(Default::default()),
});
f(match &mut *inner {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
WorkerScopeInner::Rayon(worker) => worker.as_mut(),
#[cfg(not(any(target_arch = "asmjs", target_arch = "wasm32")))]
WorkerScopeInner::Multithreaded(worker) => worker,
WorkerScopeInner::Immediate(worker) => worker,
})
}
}
pub fn compute_image_parallel(
components: &[Component],
data: Vec<Vec<u8>>,
output_size: Dimensions,
color_transform: ColorTransform,
) -> Result<Vec<u8>> {
#[cfg(all(
not(any(target_arch = "asmjs", target_arch = "wasm32")),
feature = "rayon"
))]
return rayon::compute_image_parallel(components, data, output_size, color_transform);
#[allow(unreachable_code)]
{
let color_convert_func = choose_color_convert_func(components.len(), color_transform)?;
let upsampler = Upsampler::new(components, output_size.width, output_size.height)?;
let line_size = output_size.width as usize * components.len();
let mut image = vec![0u8; line_size * output_size.height as usize];
for (row, line) in image.chunks_mut(line_size).enumerate() {
upsampler.upsample_and_interleave_row(
&data,
row,
output_size.width as usize,
line,
color_convert_func,
);
}
Ok(image)
}
}