Bug 1706144 - Add support for CMYK. r=aosmond

This is not enabled in Gecko yet.

Differential Revision: https://phabricator.services.mozilla.com/D112643
This commit is contained in:
Jeff Muizelaar 2021-04-23 01:36:00 +00:00
parent 5409c57826
commit 33c441281c
7 changed files with 392 additions and 12 deletions

View File

@ -12,10 +12,11 @@ keywords = ["color"]
categories = ["graphics"]
[features]
default = ["iccv4-enabled"]
default = ["iccv4-enabled", "cmyk"]
c_bindings = ["libc"]
neon = []
iccv4-enabled = []
cmyk = []
[dependencies]
libc = {version = "0.2", optional = true }

Binary file not shown.

View File

@ -111,7 +111,8 @@ typedef enum {
QCMS_DATA_RGBA_8,
QCMS_DATA_BGRA_8,
QCMS_DATA_GRAY_8,
QCMS_DATA_GRAYA_8
QCMS_DATA_GRAYA_8,
QCMS_DATA_CMYK
} qcms_data_type;
/* the names for the following two types are sort of ugly */

View File

@ -24,7 +24,7 @@ use crate::{
iccread::LAB_SIGNATURE,
iccread::RGB_SIGNATURE,
iccread::XYZ_SIGNATURE,
iccread::{lutType, lutmABType, Profile},
iccread::{lutType, lutmABType, Profile, CMYK_SIGNATURE},
matrix::Matrix,
s15Fixed16Number_to_float,
transform_util::clamp_float,
@ -214,13 +214,13 @@ impl ModularTransform for ClutOnly {
}
}
#[derive(Default)]
struct Clut {
struct Clut3x3 {
input_clut_table: [Option<Vec<f32>>; 3],
clut: Option<Vec<f32>>,
grid_size: u16,
output_clut_table: [Option<Vec<f32>>; 3],
}
impl ModularTransform for Clut {
impl ModularTransform for Clut3x3 {
fn transform(&self, src: &[f32], dest: &mut [f32]) {
let xy_len: i32 = 1;
let x_len: i32 = self.grid_size as i32;
@ -287,6 +287,88 @@ impl ModularTransform for Clut {
}
}
}
#[derive(Default)]
struct Clut4x3 {
input_clut_table: [Option<Vec<f32>>; 4],
clut: Option<Vec<f32>>,
grid_size: u16,
output_clut_table: [Option<Vec<f32>>; 3],
}
impl ModularTransform for Clut4x3 {
fn transform(&self, src: &[f32], dest: &mut [f32]) {
let z_stride: i32 = self.grid_size as i32;
let y_stride: i32 = z_stride * z_stride;
let x_stride: i32 = z_stride * z_stride * z_stride;
let r_tbl = &self.clut.as_ref().unwrap()[0..];
let g_tbl = &self.clut.as_ref().unwrap()[1..];
let b_tbl = &self.clut.as_ref().unwrap()[2..];
let CLU =
|table: &[f32], x, y, z, w| table[((x * x_stride + y * y_stride + z * z_stride + w) * 3) as usize];
let input_clut_table_0 = self.input_clut_table[0].as_ref().unwrap();
let input_clut_table_1 = self.input_clut_table[1].as_ref().unwrap();
let input_clut_table_2 = self.input_clut_table[2].as_ref().unwrap();
let input_clut_table_3 = self.input_clut_table[3].as_ref().unwrap();
for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(4)) {
debug_assert!(self.grid_size as i32 >= 1);
let linear_x: f32 = lut_interp_linear_float(src[0], &input_clut_table_0);
let linear_y: f32 = lut_interp_linear_float(src[1], &input_clut_table_1);
let linear_z: f32 = lut_interp_linear_float(src[2], &input_clut_table_2);
let linear_w: f32 = lut_interp_linear_float(src[3], &input_clut_table_3);
let x: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).floor() as i32;
let y: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).floor() as i32;
let z: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).floor() as i32;
let w: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).floor() as i32;
let x_n: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).ceil() as i32;
let y_n: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).ceil() as i32;
let z_n: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).ceil() as i32;
let w_n: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).ceil() as i32;
let x_d: f32 = linear_x * (self.grid_size as i32 - 1) as f32 - x as f32;
let y_d: f32 = linear_y * (self.grid_size as i32 - 1) as f32 - y as f32;
let z_d: f32 = linear_z * (self.grid_size as i32 - 1) as f32 - z as f32;
let w_d: f32 = linear_w * (self.grid_size as i32 - 1) as f32 - w as f32;
let quadlinear = |tbl| {
let CLU = |x, y, z, w| CLU(tbl, x, y, z, w);
let r_x1 = lerp(CLU(x, y, z, w), CLU(x_n, y, z, w), x_d);
let r_x2 = lerp(CLU(x, y_n, z, w), CLU(x_n, y_n, z, w), x_d);
let r_y1 = lerp(r_x1, r_x2, y_d);
let r_x3 = lerp(CLU(x, y, z_n, w), CLU(x_n, y, z_n, w), x_d);
let r_x4 = lerp(CLU(x, y_n, z_n, w), CLU(x_n, y_n, z_n, w), x_d);
let r_y2 = lerp(r_x3, r_x4, y_d);
let r_z1 = lerp(r_y1, r_y2, z_d);
let r_x1 = lerp(CLU(x, y, z, w_n), CLU(x_n, y, z, w_n), x_d);
let r_x2 = lerp(CLU(x, y_n, z, w_n), CLU(x_n, y_n, z, w_n), x_d);
let r_y1 = lerp(r_x1, r_x2, y_d);
let r_x3 = lerp(CLU(x, y, z_n, w_n), CLU(x_n, y, z_n, w_n), x_d);
let r_x4 = lerp(CLU(x, y_n, z_n, w_n), CLU(x_n, y_n, z_n, w_n), x_d);
let r_y2 = lerp(r_x3, r_x4, y_d);
let r_z2 = lerp(r_y1, r_y2, z_d);
lerp(r_z1, r_z2, w_d)
};
// TODO: instead of reading each component separately we should read all three components at once.
let clut_r = quadlinear(r_tbl);
let clut_g = quadlinear(g_tbl);
let clut_b = quadlinear(b_tbl);
let pcs_r =
lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap());
let pcs_g =
lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap());
let pcs_b =
lut_interp_linear_float(clut_b, &self.output_clut_table[2].as_ref().unwrap());
dest[0] = clamp_float(pcs_r);
dest[1] = clamp_float(pcs_g);
dest[2] = clamp_float(pcs_b);
}
}
}
/* NOT USED
static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
{
@ -628,7 +710,7 @@ fn modular_transform_create_lut(lut: &lutType) -> Option<Vec<Box<dyn ModularTran
transforms.push(transform);
// Prepare input curves
let mut transform = Box::new(Clut::default());
let mut transform = Box::new(Clut3x3::default());
transform.input_clut_table[0] =
Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
transform.input_clut_table[1] = Some(
@ -666,10 +748,64 @@ fn modular_transform_create_lut(lut: &lutType) -> Option<Vec<Box<dyn ModularTran
None
}
fn modular_transform_create_lut4x3(lut: &lutType) -> Option<Vec<Box<dyn ModularTransform>>> {
let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
let clut_length: usize;
// the matrix of lutType is only used when the input color space is XYZ.
// Prepare input curves
let mut transform = Box::new(Clut4x3::default());
transform.input_clut_table[0] =
Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
transform.input_clut_table[1] = Some(
lut.input_table
[lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
.to_vec(),
);
transform.input_clut_table[2] = Some(
lut.input_table
[lut.num_input_table_entries as usize * 2..lut.num_input_table_entries as usize * 3]
.to_vec(),
);
transform.input_clut_table[3] = Some(
lut.input_table
[lut.num_input_table_entries as usize * 3..lut.num_input_table_entries as usize * 4]
.to_vec(),
);
// Prepare table
clut_length = (lut.num_clut_grid_points as usize).pow(lut.num_input_channels as u32)
* lut.num_output_channels as usize;
assert_eq!(clut_length, lut.clut_table.len());
transform.clut = Some(lut.clut_table.clone());
transform.grid_size = lut.num_clut_grid_points as u16;
// Prepare output curves
transform.output_clut_table[0] =
Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
transform.output_clut_table[1] = Some(
lut.output_table
[lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
.to_vec(),
);
transform.output_clut_table[2] = Some(
lut.output_table
[lut.num_output_table_entries as usize * 2..lut.num_output_table_entries as usize * 3]
.to_vec(),
);
transforms.push(transform);
Some(transforms)
}
fn modular_transform_create_input(input: &Profile) -> Option<Vec<Box<dyn ModularTransform>>> {
let mut transforms = Vec::new();
if input.A2B0.is_some() {
let lut_transform = modular_transform_create_lut(input.A2B0.as_deref().unwrap());
if let Some(A2B0) = &input.A2B0 {
let lut_transform;
if A2B0.num_input_channels == 4 {
lut_transform = modular_transform_create_lut4x3(&A2B0);
} else {
lut_transform = modular_transform_create_lut(&A2B0);
}
if let Some(lut_transform) = lut_transform {
transforms.extend(lut_transform);
} else {
@ -827,7 +963,7 @@ fn modular_transform_create(
output: &Profile,
) -> Option<Vec<Box<dyn ModularTransform>>> {
let mut transforms = Vec::new();
if input.color_space == RGB_SIGNATURE {
if input.color_space == RGB_SIGNATURE || input.color_space == CMYK_SIGNATURE {
let rgb_to_pcs = modular_transform_create_input(input);
if let Some(rgb_to_pcs) = rgb_to_pcs {
transforms.extend(rgb_to_pcs);
@ -865,6 +1001,9 @@ fn modular_transform_create(
} else {
return None;
}
} else if output.color_space == CMYK_SIGNATURE {
let pcs_to_cmyk = modular_transform_create_output(output)?;
transforms.extend(pcs_to_cmyk);
} else {
debug_assert!(false, "output color space not supported");
}

View File

@ -925,4 +925,23 @@ mod test {
xfm.apply(&mut data);
assert_eq!(data, [188, 188, 189]);
}
#[test]
fn cmyk() {
let input = profile_from_path("ps_cmyk_min.icc");
let output = Profile::new_sRGB();
let xfm = crate::Transform::new_to(
&input,
&output,
crate::DataType::CMYK,
crate::DataType::RGB8,
crate::Intent::default(),
)
.unwrap();
let src = [4, 30, 80, 10];
let mut dst = [0, 0, 0];
xfm.convert(&src, &mut dst);
assert_eq!(dst, [252, 237, 211]);
}
}

View File

@ -39,6 +39,7 @@ pub const RGB_SIGNATURE: u32 = 0x52474220;
pub const GRAY_SIGNATURE: u32 = 0x47524159;
pub const XYZ_SIGNATURE: u32 = 0x58595A20;
pub const LAB_SIGNATURE: u32 = 0x4C616220;
pub const CMYK_SIGNATURE: u32 = 0x434D594B; // 'CMYK'
/// A color profile
#[derive(Default)]
@ -285,6 +286,8 @@ fn read_color_space(mut profile: &mut Profile, mem: &mut MemSource) {
profile.color_space = read_u32(mem, 16);
match profile.color_space {
RGB_SIGNATURE | GRAY_SIGNATURE => {}
#[cfg(feature = "cmyk")]
CMYK_SIGNATURE => {}
_ => {
invalid_source(mem, "Unsupported colorspace");
}
@ -734,8 +737,8 @@ fn read_tag_lutType(src: &mut MemSource, tag: &Tag) -> Option<Box<lutType>> {
}
let in_chan = read_u8(src, (offset + 8) as usize);
let out_chan = read_u8(src, (offset + 9) as usize);
if in_chan != 3 || out_chan != 3 {
invalid_source(src, "CLUT only supports RGB");
if !(in_chan == 3 || in_chan == 4) || out_chan != 3 {
invalid_source(src, "CLUT only supports RGB and CMYK");
return None;
}
@ -1170,6 +1173,15 @@ impl Profile {
} else if profile.color_space == GRAY_SIGNATURE {
profile.grayTRC = read_tag_curveType(src, &index, TAG_kTRC);
profile.grayTRC.as_ref()?;
} else if profile.color_space == CMYK_SIGNATURE {
if let Some(A2B0) = find_tag(&index, TAG_A2B0) {
let lut_type = read_u32(src, A2B0.offset as usize);
if lut_type == LUT8_TYPE || lut_type == LUT16_TYPE {
profile.A2B0 = read_tag_lutType(src, A2B0)
} else if lut_type == LUT_MBA_TYPE {
profile.mAB = read_tag_lutmABType(src, A2B0)
}
}
} else {
debug_assert!(false, "read_color_space protects against entering here");
return None;

View File

@ -125,6 +125,7 @@ pub enum DataType {
BGRA8 = 2,
Gray8 = 3,
GrayA8 = 4,
CMYK = 5,
}
impl DataType {
@ -135,6 +136,7 @@ impl DataType {
BGRA8 => 4,
Gray8 => 1,
GrayA8 => 2,
CMYK => 4,
}
}
}
@ -841,6 +843,170 @@ unsafe extern "C" fn qcms_transform_data_tetra_clut_template<F: Format>(
i += 1
}
}
unsafe fn tetra(
transform: &qcms_transform,
table: *const f32,
in_r: u8,
in_g: u8,
in_b: u8,
) -> (f32, f32, f32) {
let r_table: *const f32 = table;
let g_table: *const f32 = table.offset(1);
let b_table: *const f32 = table.offset(2);
let linear_r: f32 = in_r as i32 as f32 / 255.0;
let linear_g: f32 = in_g as i32 as f32 / 255.0;
let linear_b: f32 = in_b as i32 as f32 / 255.0;
let xy_len: i32 = 1;
let x_len: i32 = (*transform).grid_size as i32;
let len: i32 = x_len * x_len;
let x: i32 = in_r as i32 * ((*transform).grid_size as i32 - 1) / 255;
let y: i32 = in_g as i32 * ((*transform).grid_size as i32 - 1) / 255;
let z: i32 = in_b as i32 * ((*transform).grid_size as i32 - 1) / 255;
let x_n: i32 = int_div_ceil(in_r as i32 * ((*transform).grid_size as i32 - 1), 255);
let y_n: i32 = int_div_ceil(in_g as i32 * ((*transform).grid_size as i32 - 1), 255);
let z_n: i32 = int_div_ceil(in_b as i32 * ((*transform).grid_size as i32 - 1), 255);
let rx: f32 = linear_r * ((*transform).grid_size as i32 - 1) as f32 - x as f32;
let ry: f32 = linear_g * ((*transform).grid_size as i32 - 1) as f32 - y as f32;
let rz: f32 = linear_b * ((*transform).grid_size as i32 - 1) as f32 - z as f32;
let CLU = |table: *const f32, x, y, z| unsafe {
*table.offset(((x * len + y * x_len + z * xy_len) * 3) as isize)
};
let c0_r: f32;
let c1_r: f32;
let c2_r: f32;
let c3_r: f32;
let c0_g: f32;
let c1_g: f32;
let c2_g: f32;
let c3_g: f32;
let c0_b: f32;
let c1_b: f32;
let c2_b: f32;
let c3_b: f32;
c0_r = CLU(r_table, x, y, z);
c0_g = CLU(g_table, x, y, z);
c0_b = CLU(b_table, x, y, z);
if rx >= ry {
if ry >= rz {
//rx >= ry && ry >= rz
c1_r = CLU(r_table, x_n, y, z) - c0_r;
c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
c1_g = CLU(g_table, x_n, y, z) - c0_g;
c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
c1_b = CLU(b_table, x_n, y, z) - c0_b;
c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
} else if rx >= rz {
//rx >= rz && rz >= ry
c1_r = CLU(r_table, x_n, y, z) - c0_r;
c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
c1_g = CLU(g_table, x_n, y, z) - c0_g;
c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
c1_b = CLU(b_table, x_n, y, z) - c0_b;
c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
} else {
//rz > rx && rx >= ry
c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
c3_r = CLU(r_table, x, y, z_n) - c0_r;
c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
c3_g = CLU(g_table, x, y, z_n) - c0_g;
c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
c3_b = CLU(b_table, x, y, z_n) - c0_b;
}
} else if rx >= rz {
//ry > rx && rx >= rz
c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
c2_r = CLU(r_table, x, y_n, z) - c0_r;
c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
c2_g = CLU(g_table, x, y_n, z) - c0_g;
c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
c2_b = CLU(b_table, x, y_n, z) - c0_b;
c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
} else if ry >= rz {
//ry >= rz && rz > rx
c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
c2_r = CLU(r_table, x, y_n, z) - c0_r;
c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
c2_g = CLU(g_table, x, y_n, z) - c0_g;
c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
c2_b = CLU(b_table, x, y_n, z) - c0_b;
c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
} else {
//rz > ry && ry > rx
c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
c3_r = CLU(r_table, x, y, z_n) - c0_r;
c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
c3_g = CLU(g_table, x, y, z_n) - c0_g;
c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
c3_b = CLU(b_table, x, y, z_n) - c0_b;
}
let clut_r = c0_r + c1_r * rx + c2_r * ry + c3_r * rz;
let clut_g = c0_g + c1_g * rx + c2_g * ry + c3_g * rz;
let clut_b = c0_b + c1_b * rx + c2_b * ry + c3_b * rz;
(clut_r, clut_g, clut_b)
}
#[inline]
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a * (1.0 - t) + b * t
}
// lerp between two tetrahedral interpolations
// See lcms:Eval4InputsFloat
unsafe fn qcms_transform_data_tetra_clut_cmyk(
transform: &qcms_transform,
mut src: *const u8,
mut dest: *mut u8,
length: usize,
) {
let table = (*transform).clut.as_ref().unwrap().as_ptr();
assert!(
(*transform).clut.as_ref().unwrap().len()
>= ((transform.grid_size as i32).pow(4) * 3) as usize
);
for _ in 0..length {
let c: u8 = *src.add(0);
let m: u8 = *src.add(1);
let y: u8 = *src.add(2);
let k: u8 = *src.add(3);
src = src.offset(4);
let linear_k: f32 = k as i32 as f32 / 255.0;
let grid_size = (*transform).grid_size as i32;
let w: i32 = k as i32 * ((*transform).grid_size as i32 - 1) / 255;
let w_n: i32 = int_div_ceil(k as i32 * ((*transform).grid_size as i32 - 1), 255);
let t: f32 = linear_k * ((*transform).grid_size as i32 - 1) as f32 - w as f32;
let table1 = table.offset((w * grid_size * grid_size * grid_size * 3) as isize);
let table2 = table.offset((w_n * grid_size * grid_size * grid_size * 3) as isize);
let (r1, g1, b1) = tetra(transform, table1, c, m, y);
let (r2, g2, b2) = tetra(transform, table2, c, m, y);
let r = lerp(r1, r2, t);
let g = lerp(g1, g2, t);
let b = lerp(b1, b2, t);
*dest.add(0) = clamp_u8(r * 255.0);
*dest.add(1) = clamp_u8(g * 255.0);
*dest.add(2) = clamp_u8(b * 255.0);
dest = dest.offset(3);
}
}
unsafe fn qcms_transform_data_tetra_clut_rgb(
transform: &qcms_transform,
src: *const u8,
@ -1089,6 +1255,44 @@ fn transform_precacheLUT_float(
Some(transform)
}
fn transform_precacheLUT_cmyk_float(
mut transform: Box<qcms_transform>,
input: &Profile,
output: &Profile,
samples: i32,
in_type: DataType,
) -> Option<Box<qcms_transform>> {
/* The range between which 2 consecutive sample points can be used to interpolate */
let lutSize: u32 = (4 * samples * samples * samples * samples) as u32;
let mut src = Vec::with_capacity(lutSize as usize);
let dest = vec![0.; lutSize as usize];
/* Prepare a list of points we want to sample */
for k in 0..samples {
for c in 0..samples {
for m in 0..samples {
for y in 0..samples {
src.push(c as f32 / (samples - 1) as f32);
src.push(m as f32 / (samples - 1) as f32);
src.push(y as f32 / (samples - 1) as f32);
src.push(k as f32 / (samples - 1) as f32);
}
}
}
}
let lut = chain_transform(input, output, src, dest, lutSize as usize);
if let Some(lut) = lut {
transform.clut = Some(lut);
transform.grid_size = samples as u16;
assert!(in_type == DataType::CMYK);
transform.transform_fn = Some(qcms_transform_data_tetra_clut_cmyk)
} else {
return None;
}
Some(transform)
}
pub fn transform_create(
input: &Profile,
in_type: DataType,
@ -1103,6 +1307,7 @@ pub fn transform_create(
(BGRA8, BGRA8) => true,
(Gray8, out_type) => matches!(out_type, RGB8 | RGBA8 | BGRA8),
(GrayA8, out_type) => matches!(out_type, RGBA8 | BGRA8),
(CMYK, RGB8) => true,
_ => false,
};
if !matching_format {
@ -1119,12 +1324,15 @@ pub fn transform_create(
}
// This precache assumes RGB_SIGNATURE (fails on GRAY_SIGNATURE, for instance)
if SUPPORTS_ICCV4.load(Ordering::Relaxed)
&& (in_type == RGB8 || in_type == RGBA8 || in_type == BGRA8)
&& (in_type == RGB8 || in_type == RGBA8 || in_type == BGRA8 || in_type == CMYK)
&& (input.A2B0.is_some()
|| output.B2A0.is_some()
|| input.mAB.is_some()
|| output.mAB.is_some())
{
if in_type == CMYK {
return transform_precacheLUT_cmyk_float(transform, input, output, 17, in_type);
}
// Precache the transformation to a CLUT 33x33x33 in size.
// 33 is used by many profiles and works well in pratice.
// This evenly divides 256 into blocks of 8x8x8.