From 721ed95aa1c0622e4e6d9082bf25fd2024ee2aae Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Thu, 19 Sep 2024 03:07:44 +0200 Subject: [PATCH] Draft interface for a render pipeline. --- jxl/src/lib.rs | 1 + jxl/src/render/mod.rs | 106 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 jxl/src/render/mod.rs diff --git a/jxl/src/lib.rs b/jxl/src/lib.rs index bc36143..97f0a76 100644 --- a/jxl/src/lib.rs +++ b/jxl/src/lib.rs @@ -10,4 +10,5 @@ pub mod error; pub mod headers; pub mod icc; pub mod image; +pub mod render; mod util; diff --git a/jxl/src/render/mod.rs b/jxl/src/render/mod.rs new file mode 100644 index 0000000..87d0273 --- /dev/null +++ b/jxl/src/render/mod.rs @@ -0,0 +1,106 @@ +use crate::{ + error::Result, + image::{ImageDataType, ImageRectMut}, +}; + +// All *_row_chunk functions here take one or two slices of slices. +// The slices have as many entries as the number of used channels for the stage, i.e. the channels +// for which `uses_channel` returns true. + +// These are the only stages that are assumed to have observable effects, i.e. calls to process_row +// for other stages may be omitted if it can be shown they can't affect any Input stage process_row +// call that happens inside image boundaries. +pub trait RenderPipelineInputStage { + fn uses_channel(&self, c: usize) -> bool; + fn process_row_chunk(&mut self, position: (usize, usize), xsize: usize, row: &[&[InputT]]); +} + +// Modifies channels in-place. +pub trait RenderPipelineInPlaceStage { + fn uses_channel(&self, c: usize) -> bool; + fn process_row_chunk(&mut self, position: (usize, usize), xsize: usize, row: &mut [&mut [T]]); +} + +// Modifies data and writes it to a new buffer, of possibly different type. +pub trait RenderPipelineInOutStage { + // Amount of padding required on the input side. + const BORDER: (usize, usize); + // log2 of the number of rows/columns produced for each row/column of input. + const SHIFT: (usize, usize); + + fn uses_channel(&self, c: usize) -> bool; + + // For each channel: + // - the input slice contains 1 + Self::BORDER.1 * 2 slices, each of length + // xsize + Self::BORDER.0 * 2, i.e. covering one input row and up to BORDER pixels of + // padding on either side. + // - the output slice contains 1 << SHIFT.1 slices, each of length xsize << SHIFT.0, the + // corresponding output pixels. + fn process_row_chunk( + &mut self, + position: (usize, usize), + xsize: usize, + input: &[&[&[InputT]]], + output: &mut [&mut [&mut [OutputT]]], + ); +} + +// Does not directly modify the current image pixels, but extends the current image with +// additional data. +pub trait RenderPipelineExtendStage { + // uses_channel is assumed to be always true + fn new_size(&self) -> (usize, usize); + fn original_data_origin(&self) -> (usize, usize); + // passed buffer must always be entirely outside of the original image. + fn fill_padding_row_chunk( + &mut self, + new_position: (usize, usize), + xsize: usize, + row: &mut [&mut [T]], + ); +} + +pub trait RenderPipelineBuilder: Sized { + type RenderPipeline: RenderPipeline; + + fn new(num_channels: usize, size: (usize, usize), group_size: usize) -> Self; + + fn add_input_stage>( + self, + stage: Stage, + ) -> Result; + + fn add_inplace_stage>( + self, + stage: Stage, + ) -> Result; + + fn add_inout_stage< + InT: ImageDataType, + OutT: ImageDataType, + Stage: RenderPipelineInOutStage, + >( + self, + stage: Stage, + ) -> Result; + + fn add_extend_stage>( + self, + stage: Stage, + ) -> Result; + + fn build(self) -> Self::RenderPipeline; +} + +pub trait RenderPipeline { + type Builder: RenderPipelineBuilder; + + // TODO(veluca): figure out a good multithreaded interface. + fn fill_group_input, usize) -> bool>( + &mut self, + _group_id: usize, + _do_fill_group: F, + ) { + todo!() + } +}