// qcms // Copyright (C) 2009 Mozilla Corporation // Copyright (C) 1998-2007 Marti Maria // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. usecrate::{
iccread::LAB_SIGNATURE,
iccread::RGB_SIGNATURE,
iccread::XYZ_SIGNATURE,
iccread::{lutType, lutmABType, Profile, CMYK_SIGNATURE},
matrix::Matrix,
s15Fixed16Number_to_float,
transform_util::clamp_float,
transform_util::{
build_colorant_matrix, build_input_gamma_table, build_output_lut, lut_interp_linear,
lut_interp_linear_float,
},
};
result
} //Based on lcms cmsLab2XYZ fn f(t: f32) -> f32 { if t <= 24. / 116. * (24. / 116.) * (24. / 116.) {
(841. / 108. * t) + 16. / 116.
} else {
t.powf(1. / 3.)
}
} fn f_1(t: f32) -> f32 { if t <= 24.0 / 116.0 {
(108.0 / 841.0) * (t - 16.0 / 116.0)
} else {
t * t * t
}
}
#[allow(clippy::upper_case_acronyms)] struct LABtoXYZ; impl ModularTransform for LABtoXYZ { fn transform(&self, src: &[f32], dest: &mut[f32]) { // lcms: D50 XYZ values let WhitePointX: f32 = 0.9642; let WhitePointY: f32 = 1.0; let WhitePointZ: f32 = 0.8249;
for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let device_L: f32 = src[0] * 100.0; let device_a: f32 = src[1] * 255.0 - 128.0; let device_b: f32 = src[2] * 255.0 - 128.0;
let y: f32 = (device_L + 16.0) / 116.0;
let X = f_1(y + 0.002 * device_a) * WhitePointX; let Y = f_1(y) * WhitePointY; let Z = f_1(y - 0.005 * device_b) * WhitePointZ;
dest[0] = (X as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
dest[1] = (Y as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
dest[2] = (Z as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
}
}
}
#[allow(clippy::upper_case_acronyms)] struct XYZtoLAB; impl ModularTransform for XYZtoLAB { //Based on lcms cmsXYZ2Lab fn transform(&self, src: &[f32], dest: &mut[f32]) { // lcms: D50 XYZ values let WhitePointX: f32 = 0.9642; let WhitePointY: f32 = 1.0; let WhitePointZ: f32 = 0.8249; for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let device_x: f32 =
(src[0] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointX as f64) as f32; let device_y: f32 =
(src[1] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointY as f64) as f32; let device_z: f32 =
(src[2] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointZ as f64) as f32;
let fx = f(device_x); let fy = f(device_y); let fz = f(device_z);
let L: f32 = 116.0 * fy - 16.0; let a: f32 = 500.0 * (fx - fy); let b: f32 = 200.0 * (fy - fz);
dest[0] = L / 100.0;
dest[1] = (a + 128.0) / 255.0;
dest[2] = (b + 128.0) / 255.0;
}
}
} #[derive(Default)] struct ClutOnly {
clut: Option<Vec<f32>>,
grid_size: u16,
} impl ModularTransform for ClutOnly { fn transform(&self, src: &[f32], dest: &mut[f32]) { let xy_len: i32 = 1; let x_len: i32 = self.grid_size as i32; let len: i32 = x_len * x_len;
let r_table = &self.clut.as_ref().unwrap()[0..]; let g_table = &self.clut.as_ref().unwrap()[1..]; let b_table = &self.clut.as_ref().unwrap()[2..];
let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
debug_assert!(self.grid_size as i32 >= 1); let linear_r: f32 = src[0]; let linear_g: f32 = src[1]; let linear_b: f32 = src[2]; let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32; let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32; let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32; let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32; let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32; let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32; let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32; let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32; let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d); let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d); let r_y1: f32 = lerp(r_x1, r_x2, y_d); let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d); let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d); let r_y2: f32 = lerp(r_x3, r_x4, y_d); let clut_r: f32 = lerp(r_y1, r_y2, z_d);
let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d); let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d); let g_y1: f32 = lerp(g_x1, g_x2, y_d); let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d); let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d); let g_y2: f32 = lerp(g_x3, g_x4, y_d); let clut_g: f32 = lerp(g_y1, g_y2, z_d);
let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d); let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d); let b_y1: f32 = lerp(b_x1, b_x2, y_d); let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d); let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d); let b_y2: f32 = lerp(b_x3, b_x4, y_d); let clut_b: f32 = lerp(b_y1, b_y2, z_d);
let r_table = &self.clut.as_ref().unwrap()[0..]; let g_table = &self.clut.as_ref().unwrap()[1..]; let b_table = &self.clut.as_ref().unwrap()[2..]; let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
let input_clut_table_r = self.input_clut_table[0].as_ref().unwrap(); let input_clut_table_g = self.input_clut_table[1].as_ref().unwrap(); let input_clut_table_b = self.input_clut_table[2].as_ref().unwrap(); for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
debug_assert!(self.grid_size as i32 >= 1); let device_r: f32 = src[0]; let device_g: f32 = src[1]; let device_b: f32 = src[2]; let linear_r: f32 = lut_interp_linear_float(device_r, &input_clut_table_r); let linear_g: f32 = lut_interp_linear_float(device_g, &input_clut_table_g); let linear_b: f32 = lut_interp_linear_float(device_b, &input_clut_table_b); let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32; let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32; let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32; let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32; let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32; let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32; let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32; let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32; let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d); let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d); let r_y1: f32 = lerp(r_x1, r_x2, y_d); let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d); let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d); let r_y2: f32 = lerp(r_x3, r_x4, y_d); let clut_r: f32 = lerp(r_y1, r_y2, z_d);
let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d); let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d); let g_y1: f32 = lerp(g_x1, g_x2, y_d); let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d); let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d); let g_y2: f32 = lerp(g_x3, g_x4, y_d); let clut_g: f32 = lerp(g_y1, g_y2, z_d);
let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d); let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d); let b_y1: f32 = lerp(b_x1, b_x2, y_d); let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d); let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d); let b_y2: f32 = lerp(b_x3, b_x4, y_d); let clut_b: f32 = lerp(b_y1, b_y2, z_d); let pcs_r: f32 =
lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap()); let pcs_g: f32 =
lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap()); let pcs_b: f32 =
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);
}
}
} #[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);
for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let in_r: f32 = src[0]; let in_g: f32 = src[1]; let in_b: f32 = src[2];
out_r = lut_interp_linear_float(in_r, input_clut_table_r);
out_g = lut_interp_linear_float(in_g, input_clut_table_g);
out_b = lut_interp_linear_float(in_b, input_clut_table_b);
dest[0] = clamp_float(out_r);
dest[1] = clamp_float(out_g);
dest[2] = clamp_float(out_b);
}
}
} #[derive(Default)] struct GammaLut {
output_gamma_lut_r: Option<Vec<u16>>,
output_gamma_lut_g: Option<Vec<u16>>,
output_gamma_lut_b: Option<Vec<u16>>,
} impl ModularTransform for GammaLut { fn transform(&self, src: &[f32], dest: &mut[f32]) { letmut out_r: f32; letmut out_g: f32; letmut out_b: f32; for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let in_r: f32 = src[0]; let in_g: f32 = src[1]; let in_b: f32 = src[2];
out_r = lut_interp_linear(in_r as f64, &self.output_gamma_lut_r.as_ref().unwrap());
out_g = lut_interp_linear(in_g as f64, &self.output_gamma_lut_g.as_ref().unwrap());
out_b = lut_interp_linear(in_b as f64, &self.output_gamma_lut_b.as_ref().unwrap());
dest[0] = clamp_float(out_r);
dest[1] = clamp_float(out_g);
dest[2] = clamp_float(out_b);
}
}
} #[derive(Default)] struct MatrixTranslate {
matrix: Matrix,
tx: f32,
ty: f32,
tz: f32,
} impl ModularTransform for MatrixTranslate { fn transform(&self, src: &[f32], dest: &mut[f32]) { letmut mat: Matrix = Matrix { m: [[0.; 3]; 3] }; /* store the results in column major mode
* this makes doing the multiplication with sse easier */
mat.m[0][0] = self.matrix.m[0][0];
mat.m[1][0] = self.matrix.m[0][1];
mat.m[2][0] = self.matrix.m[0][2];
mat.m[0][1] = self.matrix.m[1][0];
mat.m[1][1] = self.matrix.m[1][1];
mat.m[2][1] = self.matrix.m[1][2];
mat.m[0][2] = self.matrix.m[2][0];
mat.m[1][2] = self.matrix.m[2][1];
mat.m[2][2] = self.matrix.m[2][2]; for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let in_r: f32 = src[0]; let in_g: f32 = src[1]; let in_b: f32 = src[2]; let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b + self.tx; let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b + self.ty; let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b + self.tz;
dest[0] = clamp_float(out_r);
dest[1] = clamp_float(out_g);
dest[2] = clamp_float(out_b);
}
}
} #[derive(Default)] struct MatrixTransform {
matrix: Matrix,
} impl ModularTransform for MatrixTransform { fn transform(&self, src: &[f32], dest: &mut[f32]) { letmut mat: Matrix = Matrix { m: [[0.; 3]; 3] }; /* store the results in column major mode
* this makes doing the multiplication with sse easier */
mat.m[0][0] = self.matrix.m[0][0];
mat.m[1][0] = self.matrix.m[0][1];
mat.m[2][0] = self.matrix.m[0][2];
mat.m[0][1] = self.matrix.m[1][0];
mat.m[1][1] = self.matrix.m[1][1];
mat.m[2][1] = self.matrix.m[1][2];
mat.m[0][2] = self.matrix.m[2][0];
mat.m[1][2] = self.matrix.m[2][1];
mat.m[2][2] = self.matrix.m[2][2]; for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) { let in_r: f32 = src[0]; let in_g: f32 = src[1]; let in_b: f32 = src[2]; let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b; let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b; let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b;
dest[0] = clamp_float(out_r);
dest[1] = clamp_float(out_g);
dest[2] = clamp_float(out_b);
}
}
}
fn modular_transform_create_mAB(lut: &lutmABType) -> Option<Vec<Box<dyn ModularTransform>>> { letmut transforms: Vec<Box<dyn ModularTransform>> = Vec::new(); if lut.a_curves[0].is_some() { let clut_length: usize; // If the A curve is present this also implies the // presence of a CLUT.
lut.clut_table.as_ref()?;
if lut.num_grid_points[0] as i32 != lut.num_grid_points[1] as i32
|| lut.num_grid_points[1] as i32 != lut.num_grid_points[2] as i32
{ //XXX: We don't currently support clut that are not squared! return None;
}
transforms.push(transform);
if output.color_space == RGB_SIGNATURE { let pcs_to_rgb = modular_transform_create_output(output); iflet Some(pcs_to_rgb) = pcs_to_rgb {
transforms.extend(pcs_to_rgb);
} else { return None;
}
} elseif 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");
}
// Not Completed //return qcms_modular_transform_reduce(first_transform);
Some(transforms)
} fn modular_transform_data(
transforms: Vec<Box<dyn ModularTransform>>, mut src: Vec<f32>, mut dest: Vec<f32>,
_len: usize,
) -> Vec<f32> { for transform in transforms { // Keep swaping src/dest when performing a transform to use less memory.
transform.transform(&src, &mut dest);
std::mem::swap(&mut src, &mut dest);
} // The results end up in the src buffer because of the switching
src
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.