зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1799258 - [qcms] Add query for profile data and lut tables. r=jrmuizel
Differential Revision: https://phabricator.services.mozilla.com/D163663
This commit is contained in:
Родитель
5cf668fe9b
Коммит
3bd71468e2
|
@ -11,35 +11,35 @@ extern "C" {
|
|||
#ifndef ICC_H
|
||||
/* icc34 defines */
|
||||
|
||||
/*****************************************************************
|
||||
/*****************************************************************
|
||||
Copyright (c) 1994-1996 SunSoft, Inc.
|
||||
|
||||
Rights Reserved
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
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 restrict-
|
||||
ion, 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
|
||||
files (the "Software"), to deal in the Software without restrict-
|
||||
ion, 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 NON-
|
||||
INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT
|
||||
COMPANY 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.
|
||||
|
||||
Except as contained in this notice, the name of SunSoft, Inc.
|
||||
shall not be used in advertising or otherwise to promote the
|
||||
sale, use or other dealings in this Software without written
|
||||
authorization from SunSoft Inc.
|
||||
INFRINGEMENT. IN NO EVENT SHALL SUNSOFT, INC. OR ITS PARENT
|
||||
COMPANY 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.
|
||||
|
||||
Except as contained in this notice, the name of SunSoft, Inc.
|
||||
shall not be used in advertising or otherwise to promote the
|
||||
sale, use or other dealings in this Software without written
|
||||
authorization from SunSoft Inc.
|
||||
******************************************************************/
|
||||
|
||||
/*
|
||||
|
@ -48,11 +48,11 @@ authorization from SunSoft Inc.
|
|||
* don't use the same objects on different threads at the same time.
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* Color Space Signatures
|
||||
* Note that only icSigXYZData and icSigLabData are valid
|
||||
* Profile Connection Spaces (PCSs)
|
||||
*/
|
||||
*/
|
||||
typedef enum {
|
||||
icSigXYZData = 0x58595A20L, /* 'XYZ ' */
|
||||
icSigLabData = 0x4C616220L, /* 'Lab ' */
|
||||
|
@ -79,12 +79,13 @@ typedef enum {
|
|||
icSig13colorData = 0x44434C52L, /* 'DCLR' */
|
||||
icSig14colorData = 0x45434C52L, /* 'ECLR' */
|
||||
icSig15colorData = 0x46434C52L, /* 'FCLR' */
|
||||
icMaxEnumData = 0xFFFFFFFFL
|
||||
icMaxEnumData = 0xFFFFFFFFL
|
||||
} icColorSpaceSignature;
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <cstdint>
|
||||
|
||||
struct _qcms_transform;
|
||||
typedef struct _qcms_transform qcms_transform;
|
||||
|
@ -196,6 +197,42 @@ void qcms_enable_iccv4();
|
|||
void qcms_enable_neon();
|
||||
void qcms_enable_avx();
|
||||
|
||||
|
||||
|
||||
// -
|
||||
/*
|
||||
struct qcms_mat3r3 {
|
||||
struct row {
|
||||
float cols[3];
|
||||
};
|
||||
row rows[3];
|
||||
};
|
||||
*/
|
||||
struct qcms_profile_data {
|
||||
uint32_t class_type;
|
||||
uint32_t color_space;
|
||||
uint32_t pcs;
|
||||
qcms_intent rendering_intent;
|
||||
float red_colorant_xyzd50[3];
|
||||
float blue_colorant_xyzd50[3];
|
||||
float green_colorant_xyzd50[3];
|
||||
int32_t linear_from_trc_red_samples;
|
||||
int32_t linear_from_trc_blue_samples;
|
||||
int32_t linear_from_trc_green_samples;
|
||||
};
|
||||
|
||||
void qcms_profile_get_data(const qcms_profile*, qcms_profile_data* out_data);
|
||||
|
||||
|
||||
enum class qcms_color_channel : uint8_t {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
};
|
||||
|
||||
void qcms_profile_get_lut(const qcms_profile*, qcms_color_channel,
|
||||
float* begin, float* end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,9 +7,11 @@ use libc::{fclose, fopen, fread, free, malloc, memset, FILE};
|
|||
use crate::{
|
||||
double_to_s15Fixed16Number,
|
||||
iccread::*,
|
||||
s15Fixed16Number_to_float,
|
||||
transform::get_rgb_colorants,
|
||||
transform::DataType,
|
||||
transform::{qcms_transform, transform_create},
|
||||
transform_util,
|
||||
Intent,
|
||||
};
|
||||
|
||||
|
@ -375,6 +377,124 @@ pub unsafe extern "C" fn qcms_transform_data(
|
|||
length,
|
||||
);
|
||||
}
|
||||
/*
|
||||
use crate::matrix;
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct qcms_mat3r3 {
|
||||
pub rows: [[f32; 3] ; 3],
|
||||
}
|
||||
impl qcms_mat3r3 {
|
||||
fn from(m: matrix::Matrix) -> qcms_mat3r3 {
|
||||
qcms_mat3r3{
|
||||
rows: [
|
||||
m.row(0),
|
||||
m.row(1),
|
||||
m.row(2),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct qcms_profile_data {
|
||||
pub class_type: u32,
|
||||
pub color_space: u32,
|
||||
pub pcs: u32,
|
||||
pub rendering_intent: Intent,
|
||||
pub red_colorant_xyzd50: [f32; 3],
|
||||
pub blue_colorant_xyzd50: [f32; 3],
|
||||
pub green_colorant_xyzd50: [f32; 3],
|
||||
// Number of samples in the e.g. gamma->linear LUT.
|
||||
pub linear_from_trc_red_samples: i32,
|
||||
pub linear_from_trc_blue_samples: i32,
|
||||
pub linear_from_trc_green_samples: i32,
|
||||
}
|
||||
|
||||
pub use crate::iccread::Profile as qcms_profile;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn qcms_profile_get_data(
|
||||
profile: &qcms_profile,
|
||||
out_data: &mut qcms_profile_data,
|
||||
) {
|
||||
out_data.class_type = profile.class_type;
|
||||
out_data.color_space = profile.color_space;
|
||||
out_data.pcs = profile.pcs;
|
||||
out_data.rendering_intent = profile.rendering_intent;
|
||||
|
||||
fn colorant(c: &XYZNumber) -> [f32;3] {
|
||||
[c.X, c.Y, c.Z].map(s15Fixed16Number_to_float)
|
||||
}
|
||||
out_data.red_colorant_xyzd50 = colorant(&profile.redColorant);
|
||||
out_data.blue_colorant_xyzd50 = colorant(&profile.blueColorant);
|
||||
out_data.green_colorant_xyzd50 = colorant(&profile.greenColorant);
|
||||
|
||||
fn trc_to_samples(trc: &Option<Box<curveType>>) -> i32 {
|
||||
if let Some(ref trc) = *trc {
|
||||
match &**trc {
|
||||
curveType::Curve(v) => {
|
||||
let len = v.len();
|
||||
if len <= 1 {
|
||||
-1
|
||||
} else {
|
||||
len as i32
|
||||
}
|
||||
},
|
||||
curveType::Parametric(_) => -1,
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
out_data.linear_from_trc_red_samples = trc_to_samples(&profile.redTRC);
|
||||
out_data.linear_from_trc_blue_samples = trc_to_samples(&profile.blueTRC);
|
||||
out_data.linear_from_trc_green_samples = trc_to_samples(&profile.greenTRC);
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum qcms_color_channel {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn qcms_profile_get_lut(
|
||||
profile: &qcms_profile,
|
||||
channel: qcms_color_channel, // FYI: UB if you give Rust something out of range!
|
||||
out_begin: *mut f32,
|
||||
out_end: *mut f32,
|
||||
) {
|
||||
let out_slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(out_begin, out_end.offset_from(out_begin) as usize)
|
||||
};
|
||||
|
||||
let trc = match channel {
|
||||
qcms_color_channel::Red => &profile.redTRC,
|
||||
qcms_color_channel::Green => &profile.greenTRC,
|
||||
qcms_color_channel::Blue => &profile.blueTRC,
|
||||
};
|
||||
|
||||
let samples_u16 = if let Some(trc) = trc {
|
||||
let trc = &*trc;
|
||||
// Yes, sub-optimal, but easier to implement, and these aren't big or hot:
|
||||
// 1. Ask for a new vec<u16> lut based on the trc.
|
||||
// * (eat the extra alloc)
|
||||
// 2. Convert the u16s back out to f32s in our slice.
|
||||
// * (eat the copy and quantization error from f32->u16->f32 roundtrip)
|
||||
transform_util::build_lut_for_linear_from_tf(trc, Some(out_slice.len()))
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
assert_eq!(samples_u16.len(), out_slice.len());
|
||||
for (d, s) in out_slice.iter_mut().zip(samples_u16.into_iter()) {
|
||||
*d = (s as f32) / (u16::MAX as f32);
|
||||
}
|
||||
}
|
||||
|
||||
pub type icColorSpaceSignature = u32;
|
||||
pub const icSigGrayData: icColorSpaceSignature = 0x47524159; // 'GRAY'
|
||||
|
@ -382,7 +502,6 @@ pub const icSigRgbData: icColorSpaceSignature = 0x52474220; // 'RGB '
|
|||
pub const icSigCmykData: icColorSpaceSignature = 0x434d594b; // 'CMYK'
|
||||
|
||||
pub use crate::iccread::qcms_profile_is_bogus;
|
||||
pub use crate::iccread::Profile as qcms_profile;
|
||||
pub use crate::transform::{
|
||||
qcms_enable_iccv4, qcms_profile_precache_output_transform, qcms_transform_release,
|
||||
};
|
||||
|
|
|
@ -50,6 +50,8 @@ pub struct Profile {
|
|||
pub(crate) redColorant: XYZNumber,
|
||||
pub(crate) blueColorant: XYZNumber,
|
||||
pub(crate) greenColorant: XYZNumber,
|
||||
// "TRC" is EOTF, e.g. gamma->linear transfer function.
|
||||
// Because ICC profiles are phrased as decodings to the xyzd50-linear PCS.
|
||||
pub(crate) redTRC: Option<Box<curveType>>,
|
||||
pub(crate) blueTRC: Option<Box<curveType>>,
|
||||
pub(crate) greenTRC: Option<Box<curveType>>,
|
||||
|
@ -93,7 +95,7 @@ pub(crate) struct lutmABType {
|
|||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum curveType {
|
||||
Curve(Vec<uInt16Number>),
|
||||
Curve(Vec<uInt16Number>), // len=0 => Linear, len=1 => Gamma(v[0]), _ => lut
|
||||
/// The ICC parametricCurveType is specified in terms of s15Fixed16Number,
|
||||
/// so it's possible to use this variant to specify greater precision than
|
||||
/// any raw ICC profile could
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
#![cfg_attr(
|
||||
feature = "neon",
|
||||
feature(arm_target_feature, raw_ref_op)
|
||||
|
||||
)]
|
||||
|
||||
/// These values match the Rendering Intent values from the ICC spec
|
||||
#[repr(u32)]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Intent {
|
||||
AbsoluteColorimetric = 3,
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Matrix {
|
||||
pub m: [[f32; 3]; 3],
|
||||
pub m: [[f32; 3]; 3], // Three rows of three elems.
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -39,6 +39,10 @@ impl Matrix {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn row(&self, r: usize) -> [f32; 3] {
|
||||
self.m[r]
|
||||
}
|
||||
|
||||
//probably reuse this computation in matrix_invert
|
||||
pub fn det(&self) -> f32 {
|
||||
let det: f32 = self.m[0][0] * self.m[1][1] * self.m[2][2]
|
||||
|
|
|
@ -440,10 +440,10 @@ invert_lut will produce an inverse of:
|
|||
which has an maximum error of about 9855 (pixel difference of ~38.346)
|
||||
|
||||
For now, we punt the decision of output size to the caller. */
|
||||
fn invert_lut(table: &[u16], out_length: i32) -> Vec<u16> {
|
||||
fn invert_lut(table: &[u16], out_length: usize) -> Vec<u16> {
|
||||
/* for now we invert the lut by creating a lut of size out_length
|
||||
* and attempting to lookup a value for each entry using lut_inverse_interp16 */
|
||||
let mut output = Vec::with_capacity(out_length as usize);
|
||||
let mut output = Vec::with_capacity(out_length);
|
||||
for i in 0..out_length {
|
||||
let x: f64 = i as f64 * 65535.0f64 / (out_length - 1) as f64;
|
||||
let input: uint16_fract_t = (x + 0.5f64).floor() as uint16_fract_t;
|
||||
|
@ -476,7 +476,7 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU
|
|||
curveType::Parametric(params) => {
|
||||
let mut gamma_table_uint: [u16; 256] = [0; 256];
|
||||
|
||||
let mut inverted_size: i32 = 256;
|
||||
let mut inverted_size: usize = 256;
|
||||
let gamma_table = compute_curve_gamma_table_type_parametric(params);
|
||||
let mut i: u16 = 0u16;
|
||||
while (i as i32) < 256 {
|
||||
|
@ -498,7 +498,7 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU
|
|||
0 => compute_precache_linear(output),
|
||||
1 => compute_precache_pow(output, 1. / u8Fixed8Number_to_float(data[0])),
|
||||
_ => {
|
||||
let mut inverted_size = data.len() as i32;
|
||||
let mut inverted_size = data.len();
|
||||
//XXX: the choice of a minimum of 256 here is not backed by any theory,
|
||||
// measurement or data, however it is what lcms uses.
|
||||
// the maximum number we would need is 65535 because that's the
|
||||
|
@ -514,8 +514,8 @@ pub(crate) fn compute_precache(trc: &curveType, output: &mut [u8; PRECACHE_OUTPU
|
|||
}
|
||||
true
|
||||
}
|
||||
fn build_linear_table(length: i32) -> Vec<u16> {
|
||||
let mut output = Vec::with_capacity(length as usize);
|
||||
fn build_linear_table(length: usize) -> Vec<u16> {
|
||||
let mut output = Vec::with_capacity(length);
|
||||
for i in 0..length {
|
||||
let x: f64 = i as f64 * 65535.0f64 / (length - 1) as f64;
|
||||
let input: uint16_fract_t = (x + 0.5f64).floor() as uint16_fract_t;
|
||||
|
@ -523,8 +523,8 @@ fn build_linear_table(length: i32) -> Vec<u16> {
|
|||
}
|
||||
output
|
||||
}
|
||||
fn build_pow_table(gamma: f32, length: i32) -> Vec<u16> {
|
||||
let mut output = Vec::with_capacity(length as usize);
|
||||
fn build_pow_table(gamma: f32, length: usize) -> Vec<u16> {
|
||||
let mut output = Vec::with_capacity(length);
|
||||
for i in 0..length {
|
||||
let mut x: f64 = i as f64 / (length - 1) as f64;
|
||||
x = x.powf(gamma as f64);
|
||||
|
@ -534,36 +534,75 @@ fn build_pow_table(gamma: f32, length: i32) -> Vec<u16> {
|
|||
output
|
||||
}
|
||||
|
||||
pub(crate) fn build_output_lut(trc: &curveType) -> Option<Vec<u16>> {
|
||||
fn to_lut(params: &Param, len: usize) -> Vec<u16> {
|
||||
let mut output = Vec::with_capacity(len);
|
||||
for i in 0..len {
|
||||
let X = i as f32 / (len-1) as f32;
|
||||
output.push((params.eval(X) * 65535.) as u16);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
pub(crate) fn build_lut_for_linear_from_tf(trc: &curveType,
|
||||
lut_len: Option<usize>) -> Vec<u16> {
|
||||
match trc {
|
||||
curveType::Parametric(params) => {
|
||||
let lut_len = lut_len.unwrap_or(256);
|
||||
let params = Param::new(params);
|
||||
let inv_params = params.invert()?;
|
||||
|
||||
let mut output = Vec::with_capacity(256);
|
||||
for i in 0..256 {
|
||||
let X = i as f32 / 255.;
|
||||
output.push((inv_params.eval(X) * 65535.) as u16);
|
||||
}
|
||||
Some(output)
|
||||
}
|
||||
to_lut(¶ms, lut_len)
|
||||
},
|
||||
curveType::Curve(data) => {
|
||||
let autogen_lut_len = lut_len.unwrap_or(4096);
|
||||
match data.len() {
|
||||
0 => Some(build_linear_table(4096)),
|
||||
0 => build_linear_table(autogen_lut_len),
|
||||
1 => {
|
||||
let gamma = 1. / u8Fixed8Number_to_float(data[0]);
|
||||
Some(build_pow_table(gamma, 4096))
|
||||
let gamma = u8Fixed8Number_to_float(data[0]);
|
||||
build_pow_table(gamma, autogen_lut_len)
|
||||
}
|
||||
_ => {
|
||||
//XXX: the choice of a minimum of 256 here is not backed by any theory,
|
||||
// measurement or data, however it is what lcms uses.
|
||||
let mut output_gamma_lut_length = data.len();
|
||||
if output_gamma_lut_length < 256 {
|
||||
output_gamma_lut_length = 256
|
||||
}
|
||||
Some(invert_lut(data, output_gamma_lut_length as i32))
|
||||
let lut_len = lut_len.unwrap_or(data.len());
|
||||
assert_eq!(lut_len, data.len());
|
||||
data.clone() // I feel bad about this.
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_lut_for_tf_from_linear(trc: &curveType) -> Option<Vec<u16>> {
|
||||
match trc {
|
||||
curveType::Parametric(params) => {
|
||||
let lut_len = 256;
|
||||
let params = Param::new(params);
|
||||
if let Some(inv_params) = params.invert() {
|
||||
return Some(to_lut(&inv_params, lut_len));
|
||||
}
|
||||
// else return None instead of fallthrough to generic lut inversion.
|
||||
return None;
|
||||
},
|
||||
curveType::Curve(data) => {
|
||||
let autogen_lut_len = 4096;
|
||||
match data.len() {
|
||||
0 => {
|
||||
return Some(build_linear_table(autogen_lut_len));
|
||||
},
|
||||
1 => {
|
||||
let gamma = 1. / u8Fixed8Number_to_float(data[0]);
|
||||
return Some(build_pow_table(gamma, autogen_lut_len));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let linear_from_tf = build_lut_for_linear_from_tf(trc, None);
|
||||
|
||||
//XXX: the choice of a minimum of 256 here is not backed by any theory,
|
||||
// measurement or data, however it is what lcms uses.
|
||||
let inverted_lut_len = std::cmp::max(linear_from_tf.len(), 256);
|
||||
Some(invert_lut(&linear_from_tf, inverted_lut_len))
|
||||
}
|
||||
|
||||
pub(crate) fn build_output_lut(trc: &curveType) -> Option<Vec<u16>> {
|
||||
build_lut_for_tf_from_linear(trc)
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче