mod immediate;
mod multithreaded;
#[cfg(all(
not(target_arch = "wasm32"),
feature = "rayon"
))]
mod rayon;
use crate::decode::decoder::{choose_color_convert_func, ColorTransform};
use crate::decode::error::Result;
use crate::decode::parser::{Component, Dimensions};
use crate::decode::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 recycled: Option<Vec<u8>>,
}
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 reclaim_buffer(&mut self) -> Option<Vec<i16>> {
None
}
fn append_rows(&mut self, row: &mut dyn Iterator<Item = (usize, Vec<i16>)>) -> Result<()> {
for item in row {
self.append_row(item)?;
}
Ok(())
}
fn as_immediate(&mut self) -> Option<&mut immediate::ImmediateWorker> {
None
}
fn supports_fused(&self) -> bool {
false
}
fn fused_block(&mut self, _index: usize, _block_y: usize, _block_x: usize, _coeffs: &[i16; 64]) {
}
}
#[allow(dead_code)]
#[cfg(feature = "std")]
static LAST_WORKER_WAS_IMMEDIATE: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
#[cfg(feature = "std")]
fn record_worker_choice(prefer: &PreferWorkerKind) {
LAST_WORKER_WAS_IMMEDIATE.store(
matches!(prefer, PreferWorkerKind::Immediate),
core::sync::atomic::Ordering::Relaxed,
);
}
#[cfg(not(feature = "std"))]
fn record_worker_choice(_prefer: &PreferWorkerKind) {}
#[cfg(feature = "std")]
pub fn last_worker_was_immediate() -> bool {
LAST_WORKER_WAS_IMMEDIATE.load(core::sync::atomic::Ordering::Relaxed)
}
#[derive(Debug)]
pub enum PreferWorkerKind {
Immediate,
Multithreaded,
}
#[derive(Default)]
pub struct WorkerScope {
inner: core::cell::RefCell<Option<WorkerScopeInner>>,
}
enum WorkerScopeInner {
#[cfg(all(
not(target_arch = "wasm32"),
feature = "rayon"
))]
Rayon(Box<rayon::Scoped>),
#[cfg(not(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();
if inner.is_none() {
record_worker_choice(&prefer);
}
let inner = inner.get_or_insert_with(move || match prefer {
#[cfg(all(
not(target_arch = "wasm32"),
feature = "rayon"
))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Rayon(Default::default()),
#[allow(unreachable_patterns)]
#[cfg(not(target_arch = "wasm32"))]
PreferWorkerKind::Multithreaded => WorkerScopeInner::Multithreaded(Default::default()),
_ => WorkerScopeInner::Immediate(Default::default()),
});
f(match &mut *inner {
#[cfg(all(
not(target_arch = "wasm32"),
feature = "rayon"
))]
WorkerScopeInner::Rayon(worker) => worker.as_mut(),
#[cfg(not(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(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)
}
}