229 lines
7.1 KiB
Rust
229 lines
7.1 KiB
Rust
// Get Jakob tables into a native rust format.
|
|
|
|
use std::{
|
|
env,
|
|
fs::File,
|
|
io::{self, Read, Write},
|
|
path::Path,
|
|
};
|
|
|
|
/// How many polynomial coefficients?
|
|
const RGB2SPEC_N_COEFFS: usize = 3;
|
|
|
|
/// Table resolution.
|
|
const TABLE_RES: usize = 64;
|
|
|
|
// For the small table, what is the middle value used?
|
|
const MID_VALUE: f32 = 0.5;
|
|
|
|
fn main() {
|
|
// Write tables to Rust file
|
|
let out_dir = env::var("OUT_DIR").unwrap();
|
|
let dest_path = Path::new(&out_dir).join("jakob_table_inc.rs");
|
|
let mut f = File::create(&dest_path).unwrap();
|
|
|
|
// Rec.709
|
|
let rec709_table = rgb2spec_load_small("jakob_tables/srgb.coeff");
|
|
f.write_all(format!("\nconst REC709_TABLE_RES: usize = {};", TABLE_RES).as_bytes())
|
|
.unwrap();
|
|
f.write_all(format!("\nconst REC709_TABLE_MID_VALUE: f32 = {};", MID_VALUE).as_bytes())
|
|
.unwrap();
|
|
f.write_all("\n#[allow(clippy::unreadable_literal, clippy::approx_constant)]".as_bytes())
|
|
.unwrap();
|
|
f.write_all("\npub static REC709_TABLE: &[[(f32, f32, f32); 2]; 64 * 64 * 3] = &[".as_bytes())
|
|
.unwrap();
|
|
for item in &rec709_table {
|
|
f.write_all(
|
|
format!(
|
|
"\n [({}, {}, {}), ({}, {}, {})],",
|
|
item[0].0, item[0].1, item[0].2, item[1].0, item[1].1, item[1].2
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
f.write_all("\n];".as_bytes()).unwrap();
|
|
|
|
// Rec.2020
|
|
let rec2020_table = rgb2spec_load_small("jakob_tables/rec2020.coeff");
|
|
f.write_all(format!("\nconst REC2020_TABLE_RES: usize = {};", TABLE_RES).as_bytes())
|
|
.unwrap();
|
|
f.write_all(format!("\nconst REC2020_TABLE_MID_VALUE: f32 = {};", MID_VALUE).as_bytes())
|
|
.unwrap();
|
|
f.write_all("\n#[allow(clippy::unreadable_literal, clippy::approx_constant)]".as_bytes())
|
|
.unwrap();
|
|
f.write_all("\npub static REC2020_TABLE: &[[(f32, f32, f32); 2]; 64 * 64 * 3] = &[".as_bytes())
|
|
.unwrap();
|
|
for item in &rec2020_table {
|
|
f.write_all(
|
|
format!(
|
|
"\n [({}, {}, {}), ({}, {}, {})],",
|
|
item[0].0, item[0].1, item[0].2, item[1].0, item[1].1, item[1].2
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
f.write_all("\n];".as_bytes()).unwrap();
|
|
|
|
// sRGB / ACES
|
|
let aces_table = rgb2spec_load_small("jakob_tables/aces2065_1.coeff");
|
|
f.write_all(format!("\nconst ACES_TABLE_RES: usize = {};", TABLE_RES).as_bytes())
|
|
.unwrap();
|
|
f.write_all(format!("\nconst ACES_TABLE_MID_VALUE: f32 = {};", MID_VALUE).as_bytes())
|
|
.unwrap();
|
|
f.write_all("\n#[allow(clippy::unreadable_literal, clippy::approx_constant)]".as_bytes())
|
|
.unwrap();
|
|
f.write_all("\npub static ACES_TABLE: &[[(f32, f32, f32); 2]; 64 * 64 * 3] = &[".as_bytes())
|
|
.unwrap();
|
|
for item in &aces_table {
|
|
f.write_all(
|
|
format!(
|
|
"\n [({}, {}, {}), ({}, {}, {})],",
|
|
item[0].0, item[0].1, item[0].2, item[1].0, item[1].1, item[1].2
|
|
)
|
|
.as_bytes(),
|
|
)
|
|
.unwrap();
|
|
}
|
|
f.write_all("\n];".as_bytes()).unwrap();
|
|
}
|
|
|
|
/// Underlying representation
|
|
pub struct RGB2Spec {
|
|
res: usize,
|
|
scale: Vec<f32>,
|
|
data: Vec<[f32; RGB2SPEC_N_COEFFS]>,
|
|
}
|
|
|
|
pub fn rgb2spec_load(filepath: &str) -> RGB2Spec {
|
|
let file_contents = {
|
|
let mut file_contents = Vec::new();
|
|
let mut f = io::BufReader::new(File::open(filepath).unwrap());
|
|
f.read_to_end(&mut file_contents).unwrap();
|
|
file_contents
|
|
};
|
|
|
|
// Check the header
|
|
let header = &file_contents[0..4];
|
|
if header != "SPEC".as_bytes() {
|
|
panic!("Not a spectral table.");
|
|
}
|
|
|
|
// Get resolution of the table
|
|
let res = u32::from_le_bytes([
|
|
file_contents[4],
|
|
file_contents[5],
|
|
file_contents[6],
|
|
file_contents[7],
|
|
]) as usize;
|
|
|
|
// Calculate sizes
|
|
let size_scale = res;
|
|
let size_data = res * res * res * RGB2SPEC_N_COEFFS;
|
|
|
|
// Load the table scale data
|
|
let mut scale = Vec::with_capacity(size_scale);
|
|
for i in 0..size_scale {
|
|
let ii = i * 4 + 8;
|
|
let n = f32::from_bits(u32::from_le_bytes([
|
|
file_contents[ii],
|
|
file_contents[ii + 1],
|
|
file_contents[ii + 2],
|
|
file_contents[ii + 3],
|
|
]));
|
|
scale.push(n);
|
|
}
|
|
|
|
// Load the table coefficient data
|
|
let mut data = Vec::with_capacity(size_data);
|
|
for i in 0..size_data {
|
|
let ii = i * 4 * RGB2SPEC_N_COEFFS + 8 + (size_scale * 4);
|
|
let n1 = f32::from_bits(u32::from_le_bytes([
|
|
file_contents[ii],
|
|
file_contents[ii + 1],
|
|
file_contents[ii + 2],
|
|
file_contents[ii + 3],
|
|
]));
|
|
let n2 = f32::from_bits(u32::from_le_bytes([
|
|
file_contents[ii + 4],
|
|
file_contents[ii + 5],
|
|
file_contents[ii + 6],
|
|
file_contents[ii + 7],
|
|
]));
|
|
let n3 = f32::from_bits(u32::from_le_bytes([
|
|
file_contents[ii + 8],
|
|
file_contents[ii + 9],
|
|
file_contents[ii + 10],
|
|
file_contents[ii + 11],
|
|
]));
|
|
data.push([n1, n2, n3]);
|
|
}
|
|
|
|
RGB2Spec {
|
|
res: res,
|
|
scale: scale,
|
|
data: data,
|
|
}
|
|
}
|
|
|
|
pub fn rgb2spec_load_small(filepath: &str) -> Vec<[(f32, f32, f32); 2]> {
|
|
let big_table = rgb2spec_load(filepath);
|
|
assert!(big_table.res == TABLE_RES);
|
|
|
|
// Calculate z offsets and such for the mid value.
|
|
let dz: usize = 1 * big_table.res * big_table.res;
|
|
let z05_i = rgb2spec_find_interval(&big_table.scale, MID_VALUE);
|
|
let z05_1: f32 = (MID_VALUE - big_table.scale[z05_i])
|
|
/ (big_table.scale[z05_i + 1] - big_table.scale[z05_i]);
|
|
let z05_0: f32 = 1.0 - z05_1;
|
|
|
|
// Fill in table.
|
|
let mut table = vec![[(0.0, 0.0, 0.0); 2]; TABLE_RES * TABLE_RES * 3];
|
|
for i in 0..3 {
|
|
let offset = i * big_table.res * big_table.res * big_table.res;
|
|
for j in 0..(big_table.res * big_table.res) {
|
|
let one_coef = big_table.data[offset + ((TABLE_RES - 1) * dz) + j];
|
|
|
|
let mid_coef_0 = big_table.data[offset + (z05_i * dz) + j];
|
|
let mid_coef_1 = big_table.data[offset + ((z05_i + 1) * dz) + j];
|
|
let mid_coef = [
|
|
(mid_coef_0[0] * z05_0) + (mid_coef_1[0] * z05_1),
|
|
(mid_coef_0[1] * z05_0) + (mid_coef_1[1] * z05_1),
|
|
(mid_coef_0[2] * z05_0) + (mid_coef_1[2] * z05_1),
|
|
];
|
|
|
|
table[(i * big_table.res * big_table.res) + j] = [
|
|
(mid_coef[0], mid_coef[1], mid_coef[2]),
|
|
(one_coef[0], one_coef[1], one_coef[2]),
|
|
];
|
|
}
|
|
}
|
|
|
|
table
|
|
}
|
|
|
|
fn rgb2spec_find_interval(values: &[f32], x: f32) -> usize {
|
|
let last_interval = values.len() - 2;
|
|
let mut left = 0;
|
|
let mut size = last_interval;
|
|
|
|
while size > 0 {
|
|
let half = size >> 1;
|
|
let middle = left + half + 1;
|
|
|
|
if values[middle] < x {
|
|
left = middle;
|
|
size -= half + 1;
|
|
} else {
|
|
size = half;
|
|
}
|
|
}
|
|
|
|
if left < last_interval {
|
|
left
|
|
} else {
|
|
last_interval
|
|
}
|
|
}
|