use crate::render::RenderPipelineInPlaceStage;
pub struct BlackChannelStage {
black_c: usize,
}
impl std::fmt::Display for BlackChannelStage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "black channel stage for channel {}", self.black_c)
}
}
impl BlackChannelStage {
pub fn new(black_c_offset: usize) -> Self {
Self {
black_c: 3 + black_c_offset,
}
}
}
impl RenderPipelineInPlaceStage for BlackChannelStage {
type Type = f32;
fn uses_channel(&self, c: usize) -> bool {
c < 3 || c == self.black_c
}
fn process_row_chunk(
&self,
_position: (usize, usize),
xsize: usize,
row: &mut [&mut [f32]],
_state: Option<&mut (dyn std::any::Any + Send)>,
) {
let [row_c, row_m, row_y, row_k] = row else {
panic!(
"incorrect number of channels; expected 4, found {}",
row.len()
);
};
assert!(
xsize <= row_c.len()
&& xsize <= row_m.len()
&& xsize <= row_y.len()
&& xsize <= row_k.len()
);
for idx in 0..xsize {
let k = row_k[idx];
row_c[idx] *= k;
row_m[idx] *= k;
row_y[idx] *= k;
}
}
}
#[cfg(test)]
mod test {
use test_log::test;
use super::*;
use crate::error::Result;
use crate::image::Image;
use crate::render::test::make_and_run_simple_pipeline;
use crate::util::test::assert_all_almost_abs_eq;
#[test]
fn consistency() -> Result<()> {
crate::render::test::test_stage_consistency(|| BlackChannelStage::new(0), (500, 500), 4)
}
#[test]
fn cmyk_to_rgb_white() -> Result<()> {
let mut input_c = Image::new((3, 1))?;
let mut input_m = Image::new((3, 1))?;
let mut input_y = Image::new((3, 1))?;
let mut input_k = Image::new((3, 1))?;
input_c.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_m.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_y.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_k.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
let stage = BlackChannelStage::new(0);
let output = make_and_run_simple_pipeline(
stage,
&[input_c, input_m, input_y, input_k],
(3, 1),
0,
256,
)?;
assert_all_almost_abs_eq(output[0].row(0), &[1.0, 1.0, 1.0], 1e-6);
assert_all_almost_abs_eq(output[1].row(0), &[1.0, 1.0, 1.0], 1e-6);
assert_all_almost_abs_eq(output[2].row(0), &[1.0, 1.0, 1.0], 1e-6);
Ok(())
}
#[test]
fn cmyk_to_rgb_black() -> Result<()> {
let mut input_c = Image::new((3, 1))?;
let mut input_m = Image::new((3, 1))?;
let mut input_y = Image::new((3, 1))?;
let mut input_k = Image::new((3, 1))?;
input_c.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_m.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_y.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_k.row_mut(0).copy_from_slice(&[0.0, 0.0, 0.0]);
let stage = BlackChannelStage::new(0);
let output = make_and_run_simple_pipeline(
stage,
&[input_c, input_m, input_y, input_k],
(3, 1),
0,
256,
)?;
assert_all_almost_abs_eq(output[0].row(0), &[0.0, 0.0, 0.0], 1e-6);
assert_all_almost_abs_eq(output[1].row(0), &[0.0, 0.0, 0.0], 1e-6);
assert_all_almost_abs_eq(output[2].row(0), &[0.0, 0.0, 0.0], 1e-6);
Ok(())
}
#[test]
fn cmyk_to_rgb_gray() -> Result<()> {
let mut input_c = Image::new((3, 1))?;
let mut input_m = Image::new((3, 1))?;
let mut input_y = Image::new((3, 1))?;
let mut input_k = Image::new((3, 1))?;
input_c.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_m.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_y.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_k.row_mut(0).copy_from_slice(&[0.5, 0.5, 0.5]);
let stage = BlackChannelStage::new(0);
let output = make_and_run_simple_pipeline(
stage,
&[input_c, input_m, input_y, input_k],
(3, 1),
0,
256,
)?;
assert_all_almost_abs_eq(output[0].row(0), &[0.5, 0.5, 0.5], 1e-6);
assert_all_almost_abs_eq(output[1].row(0), &[0.5, 0.5, 0.5], 1e-6);
assert_all_almost_abs_eq(output[2].row(0), &[0.5, 0.5, 0.5], 1e-6);
Ok(())
}
#[test]
fn cmyk_to_rgb_cyan() -> Result<()> {
let mut input_c = Image::new((3, 1))?;
let mut input_m = Image::new((3, 1))?;
let mut input_y = Image::new((3, 1))?;
let mut input_k = Image::new((3, 1))?;
input_c.row_mut(0).copy_from_slice(&[0.0, 0.0, 0.0]); input_m.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_y.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
input_k.row_mut(0).copy_from_slice(&[1.0, 1.0, 1.0]);
let stage = BlackChannelStage::new(0);
let output = make_and_run_simple_pipeline(
stage,
&[input_c, input_m, input_y, input_k],
(3, 1),
0,
256,
)?;
assert_all_almost_abs_eq(output[0].row(0), &[0.0, 0.0, 0.0], 1e-6);
assert_all_almost_abs_eq(output[1].row(0), &[1.0, 1.0, 1.0], 1e-6);
assert_all_almost_abs_eq(output[2].row(0), &[1.0, 1.0, 1.0], 1e-6);
Ok(())
}
}