Skip to content

Commit

Permalink
Add spot colour channel rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
EugeneVert committed Jun 13, 2023
1 parent b26f211 commit fe614fa
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 2 deletions.
35 changes: 34 additions & 1 deletion crates/jxl-oxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ impl<R: Read> JxlImage<R> {
image_header: &self.image_header,
embedded_icc: self.embedded_icc.as_deref(),
ctx,
render_spot_colour: !self.image_header.metadata.grayscale(),
crop_region: None,
end_of_image: false,
}
Expand All @@ -157,6 +158,7 @@ pub struct JxlRenderer<'img, R> {
image_header: &'img ImageHeader,
embedded_icc: Option<&'img [u8]>,
ctx: RenderContext<'img>,
render_spot_colour: bool,
crop_region: Option<CropInfo>,
end_of_image: bool,
}
Expand Down Expand Up @@ -186,6 +188,23 @@ impl<'img, R: Read> JxlRenderer<'img, R> {
self.crop_region.map(|info| (info.left, info.top, info.width, info.height))
}

/// Sets whether the spot colour channels will be rendered.
#[inline]
pub fn set_render_spot_colour(&mut self, render_spot_colour: bool) -> &mut Self {
if render_spot_colour && self.image_header.metadata.grayscale() {
tracing::warn!("Spot colour channels are not rendered on grayscale images");
return self;
}
self.render_spot_colour = render_spot_colour;
self
}

/// Returns whether the spot color channels will be rendered.
#[inline]
pub fn render_spot_colour(&self) -> bool {
self.render_spot_colour
}

/// Returns the embedded ICC profile.
#[inline]
pub fn embedded_icc(&self) -> Option<&'img [u8]> {
Expand Down Expand Up @@ -272,7 +291,7 @@ impl<'img, R: Read> JxlRenderer<'img, R> {
let mut grids = self.ctx.render_keyframe_cropped(keyframe_index, region)?;

let color_channels = if self.image_header.metadata.grayscale() { 1 } else { 3 };
let color_channels: Vec<_> = grids.drain(..color_channels).collect();
let mut color_channels: Vec<_> = grids.drain(..color_channels).collect();
let extra_channels: Vec<_> = grids
.into_iter()
.zip(&self.image_header.metadata.ec_info)
Expand All @@ -283,6 +302,14 @@ impl<'img, R: Read> JxlRenderer<'img, R> {
})
.collect();

if self.render_spot_colour {
for ec in &extra_channels {
if ec.is_spot_colour() {
jxl_render::render_spot_colour(&mut color_channels, &ec.grid, &ec.ty)?;
}
}
}

let frame = self.ctx.keyframe(keyframe_index).unwrap();
let frame_header = frame.header();
let result = Render {
Expand Down Expand Up @@ -497,6 +524,12 @@ impl ExtraChannel {
pub fn is_alpha(&self) -> bool {
matches!(self.ty, ExtraChannelType::Alpha { .. })
}

/// Returns `true` if the channel is a spot colour channel.
#[inline]
pub fn is_spot_colour(&self) -> bool {
matches!(self.ty, ExtraChannelType::SpotColour { .. })
}
}

/// Cropping region information.
Expand Down
1 change: 1 addition & 0 deletions crates/jxl-oxide/tests/conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ fn run_test<R: std::io::Read>(
let debug = std::env::var("JXL_OXIDE_DEBUG").is_ok();

let mut renderer = image.renderer();
renderer.set_render_spot_colour(false);

let transform = target_icc.map(|target_icc| {
let source_profile = Profile::new_icc(&renderer.rendered_icc()).expect("failed to parse ICC profile");
Expand Down
34 changes: 33 additions & 1 deletion crates/jxl-render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::io::Read;
use jxl_bitstream::Bitstream;
use jxl_frame::{Frame, ProgressiveResult};
use jxl_grid::SimpleGrid;
use jxl_image::ImageHeader;
use jxl_image::{ImageHeader, ExtraChannelType};

mod blend;
mod cut_grid;
Expand Down Expand Up @@ -385,3 +385,35 @@ impl<'a> std::ops::DerefMut for IndexedFrame<'a> {
&mut self.f
}
}

/// Renders a spot color channel onto color_channels
///
/// If the color space is [`Grey`](jxl_color::header::ColourSpace::Grey),
/// no rendering is done.
pub fn render_spot_colour(
color_channels: &mut [SimpleGrid<f32>],
ec_grid: &SimpleGrid<f32>,
ec_ty: &ExtraChannelType,
) -> Result<()> {
let ExtraChannelType::SpotColour { red, green, blue, solidity } = ec_ty else {
return Err(Error::NotSupported("not a spot colour ec"));
};
if color_channels.len() != 3 {
return Ok(())
}

let spot_colors = [red, green, blue];
let s = ec_grid.buf();

(0..3).for_each(|c| {
let channel = color_channels[c].buf_mut();
let color = spot_colors[c];
assert_eq!(channel.len(), s.len());

(0..channel.len()).for_each(|i| {
let mix = s[i] * solidity;
channel[i] = mix * color + (1.0 - mix) * channel[i];
});
});
Ok(())
}

0 comments on commit fe614fa

Please sign in to comment.