From 05f9621ac5bb4f1249cab8397d7a6f366b39a146 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sun, 20 Sep 2020 15:16:20 +0900 Subject: [PATCH] Added a FloatLuv decode function to decode to Yuv instead of XYZ. This is useful because it's super fast, and chromaticity lookups are typical for spectral upsampling anyway, so this will likely enable cutting out a bunch of unecessary intermediate calculations. --- sub_crates/trifloat/benches/bench.rs | 11 ++++++++++ sub_crates/trifloat/src/fluv32.rs | 33 +++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/sub_crates/trifloat/benches/bench.rs b/sub_crates/trifloat/benches/bench.rs index 80582ab..1e7002b 100644 --- a/sub_crates/trifloat/benches/bench.rs +++ b/sub_crates/trifloat/benches/bench.rs @@ -105,6 +105,16 @@ fn fluv32_decode_1000_values(bench: &mut Bencher) { }); } +fn fluv32_decode_yuv_1000_values(bench: &mut Bencher) { + let mut rng = SmallRng::from_entropy(); + bench.iter(|| { + let v = rng.gen::(); + for _ in 0..1000 { + black_box(fluv32::decode_yuv(black_box(v))); + } + }); +} + //---- benchmark_group!( @@ -117,5 +127,6 @@ benchmark_group!( signed48_decode_1000_values, fluv32_encode_1000_values, fluv32_decode_1000_values, + fluv32_decode_yuv_1000_values, ); benchmark_main!(benches); diff --git a/sub_crates/trifloat/src/fluv32.rs b/sub_crates/trifloat/src/fluv32.rs index 18d003f..40d4a95 100644 --- a/sub_crates/trifloat/src/fluv32.rs +++ b/sub_crates/trifloat/src/fluv32.rs @@ -46,7 +46,9 @@ #![allow(clippy::cast_lossless)] const EXP_BIAS: i32 = 27; -const UV_SCALE: f32 = 410.0; + +/// The scale factor of the quantized UV components. +pub const UV_SCALE: f32 = 410.0; /// Largest representable Y component. pub const Y_MAX: f32 = ((1u64 << (64 - EXP_BIAS)) - (1u64 << (64 - EXP_BIAS - 11))) as f32; @@ -143,6 +145,27 @@ pub fn decode(fluv32: u32) -> (f32, f32, f32) { (x, y, z) } +/// Decodes from 32-bit FloatLuv to Yuv. +/// +/// The Y component is the luminance, and is simply the Y from CIE XYZ. +/// +/// The u and v components are the CIE LUV u' and v' chromaticity coordinates, +/// but returned as `u8`s, and scaled by `UV_SCALE` to fit the range 0-255. +#[inline] +pub fn decode_yuv(fluv32: u32) -> (f32, u8, u8) { + // Check for zero. + if fluv32 & 0xffff0000 == 0 { + return (0.0, (fluv32 >> 8) as u8, fluv32 as u8); + } + + // Calculate y. + let l_exp = fluv32 >> 26; + let l_mant = (fluv32 >> 16) & 0x3ff; + let y = f32::from_bits(((l_exp + 127 - EXP_BIAS as u32) << 23) | (l_mant << 13)); + + (y, (fluv32 >> 8) as u8, fluv32 as u8) +} + #[cfg(test)] mod tests { use super::*; @@ -231,6 +254,14 @@ mod tests { assert_eq!(2048.0, round_trip(fs).1); } + #[test] + fn decode_yuv_01() { + let fs = (1.0, 1.0, 1.0); + let a = encode(fs); + + assert_eq!((1.0, 0x56, 0xc2), decode_yuv(a)); + } + #[test] fn saturate_y() { let fs = (1.0e+20, 1.0e+20, 1.0e+20);