mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
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:
parent
5409c57826
commit
33c441281c
@ -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 }
|
||||
|
BIN
gfx/qcms/profiles/ps_cmyk_min.icc
Normal file
BIN
gfx/qcms/profiles/ps_cmyk_min.icc
Normal file
Binary file not shown.
@ -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 */
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user