546 lines
18 KiB
Rust
546 lines
18 KiB
Rust
use std::io::{Read, Take};
|
|
|
|
use crate::TiffError;
|
|
|
|
pub struct LogLuv24<R> {
|
|
reader: Take<R>,
|
|
/// The required buffer size. We only decompressed all samples at once.
|
|
chunk_sz: usize,
|
|
}
|
|
|
|
impl<R> LogLuv24<R> {
|
|
pub fn new(reader: R, n: u64, npixels: u32) -> Result<Self, TiffError>
|
|
where
|
|
R: Read,
|
|
{
|
|
let chunk_sz = usize::try_from(
|
|
u64::from(npixels)
|
|
.checked_mul(4 * 3)
|
|
.ok_or(TiffError::LimitsExceeded)?,
|
|
)?;
|
|
|
|
let reader = reader.take(n);
|
|
Ok(LogLuv24 { reader, chunk_sz })
|
|
}
|
|
|
|
fn expand_buf(buf: &mut [u8]) {
|
|
let data_len = buf.len() / 4;
|
|
|
|
for i in (0..data_len / 3).rev() {
|
|
let ch = buf.as_chunks_mut::<3>().0[i];
|
|
let [a, b, c] = ch.map(u32::from);
|
|
|
|
let raw = a << 16 | b << 8 | c;
|
|
let l = (raw >> 14) & 0x3ff;
|
|
let uv = decode_quantized_uv(raw & 0x3fff);
|
|
|
|
let by = dequantize_24_luma(l);
|
|
let (u, v) = dequantize_24(uv);
|
|
|
|
let s = 1. / (6. * u - 16. * v + 12.);
|
|
let x = 9. * u * s;
|
|
let y = 4. * v * s;
|
|
|
|
let bx = x / y * by;
|
|
let bz = (1. - x - y) / y * by;
|
|
|
|
let [r, g, b] = xyz_to_rgb([bx, by, bz]);
|
|
|
|
let into = &mut buf.as_chunks_mut::<12>().0[i];
|
|
into[0..4].copy_from_slice(&f32::to_ne_bytes(r));
|
|
into[4..8].copy_from_slice(&f32::to_ne_bytes(g));
|
|
into[8..12].copy_from_slice(&f32::to_ne_bytes(b));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<R: Read> Read for LogLuv24<R> {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
self.read_exact(buf)?;
|
|
Ok(buf.len())
|
|
}
|
|
|
|
// FIXME: this is the interface we have for compressed chunks but it is not optimal for what we
|
|
// want. Note `fill_buf` is not super helpful since it does not guarantee any size of the
|
|
// filled buffer but we need 24-bits at a time. Two code paths is even more awkward.
|
|
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
|
if !buf.len().is_multiple_of(self.chunk_sz) {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidInput,
|
|
format!(
|
|
"buffer size {0} must match the chunk size for compression method SGILog24 {1}",
|
|
buf.len(),
|
|
self.chunk_sz,
|
|
),
|
|
));
|
|
}
|
|
|
|
let data_len = buf.len() / 4;
|
|
self.reader.read_exact(&mut buf[..data_len])?;
|
|
Self::expand_buf(buf);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn packbits_decode<R: Read, const N: usize>(
|
|
reader: &mut R,
|
|
buf: &mut [[u8; N]],
|
|
) -> std::io::Result<()> {
|
|
let expected = buf.len();
|
|
for offset in (0..N).rev() {
|
|
let mut i = 0;
|
|
let mut code = [0u8; 1];
|
|
|
|
while let Some(pixels) = buf.get_mut(i..) {
|
|
if pixels.is_empty() {
|
|
break;
|
|
}
|
|
|
|
let pixels = pixels.iter_mut();
|
|
reader.read_exact(&mut code)?;
|
|
let [repeat] = code;
|
|
|
|
if repeat >= 128 {
|
|
reader.read_exact(&mut code)?;
|
|
let [value] = code;
|
|
let rc = (repeat - 128) + 2;
|
|
|
|
for (_, px) in (0..rc).zip(pixels) {
|
|
px[offset] = value;
|
|
}
|
|
|
|
i += usize::from(rc);
|
|
} else {
|
|
for (_, px) in (0..repeat).zip(pixels) {
|
|
reader.read_exact(&mut code)?;
|
|
let [value] = code;
|
|
px[offset] = value;
|
|
}
|
|
|
|
i += usize::from(repeat);
|
|
}
|
|
}
|
|
|
|
if i < expected {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::UnexpectedEof,
|
|
"PackBits RLE decoded fewer pixels than expected",
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub struct LogLuv32<R> {
|
|
reader: Take<R>,
|
|
/// The required buffer size. We only decompressed all samples at once.
|
|
chunk_sz: usize,
|
|
}
|
|
|
|
impl<R: Read> LogLuv32<R> {
|
|
pub fn new(reader: R, n: u64, npixels: u32) -> Result<Self, TiffError> {
|
|
let chunk_sz = usize::try_from(
|
|
u64::from(npixels)
|
|
.checked_mul(4 * 3)
|
|
.ok_or(TiffError::LimitsExceeded)?,
|
|
)?;
|
|
|
|
let reader = reader.take(n);
|
|
Ok(LogLuv32 { reader, chunk_sz })
|
|
}
|
|
|
|
fn decode_rle_row(&mut self, row: &mut [u8]) -> std::io::Result<()>
|
|
where
|
|
R: Read,
|
|
{
|
|
// 32-bit per pixel format.
|
|
let n_pixels = self.chunk_sz / 12;
|
|
// buffer for reading (using only 32-bit per pixel)
|
|
let raw = &mut row.as_chunks_mut::<4>().0[..n_pixels];
|
|
packbits_decode::<_, 4>(&mut self.reader, raw)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn expand_row(row: &mut [u8]) {
|
|
let uvscale = 1.0 / 410.0;
|
|
let n_pixels = row.len() / 12;
|
|
|
|
// Work backwards, the 32-bit compressed raw data is at the front of the row buffer and
|
|
// this avoids overwriting data before we read it.
|
|
for i in (0..n_pixels).rev() {
|
|
let raw = row.as_chunks_mut::<4>().0[i];
|
|
|
|
let l = u16::from(raw[0]) << 8 | u16::from(raw[1]);
|
|
let by = log16_to_y(l);
|
|
|
|
let u = uvscale * (f32::from(raw[2]) + 0.5);
|
|
let v = uvscale * (f32::from(raw[3]) + 0.5);
|
|
|
|
let s = 1. / (6. * u - 16. * v + 12.);
|
|
let x = 9. * u * s;
|
|
let y = 4. * v * s;
|
|
|
|
let bx = x / y * by;
|
|
let bz = (1. - x - y) / y * by;
|
|
|
|
let [r, g, b] = xyz_to_rgb([bx, by, bz]);
|
|
let into = &mut row.as_chunks_mut::<12>().0[i];
|
|
into[0..4].copy_from_slice(&f32::to_ne_bytes(r));
|
|
into[4..8].copy_from_slice(&f32::to_ne_bytes(g));
|
|
into[8..12].copy_from_slice(&f32::to_ne_bytes(b));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<R: Read> Read for LogLuv32<R> {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
self.read_exact(buf)?;
|
|
Ok(buf.len())
|
|
}
|
|
|
|
// FIXME: See `LogLuv24::read_exact`
|
|
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
|
for row in buf.chunks_exact_mut(self.chunk_sz) {
|
|
self.decode_rle_row(row)?;
|
|
Self::expand_row(row);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Pack-Bits like encoding for a single 16-bit log-L sample.
|
|
pub struct LogLuv16<R> {
|
|
reader: Take<R>,
|
|
/// The required buffer size. We only decompressed all samples at once.
|
|
chunk_sz: usize,
|
|
}
|
|
|
|
impl<R: Read> LogLuv16<R> {
|
|
pub fn new(reader: R, n: u64, npixels: u32) -> Result<Self, TiffError> {
|
|
let chunk_sz = usize::try_from(
|
|
u64::from(npixels)
|
|
.checked_mul(4)
|
|
.ok_or(TiffError::LimitsExceeded)?,
|
|
)?;
|
|
|
|
let reader = reader.take(n);
|
|
Ok(LogLuv16 { reader, chunk_sz })
|
|
}
|
|
|
|
fn decode_rle_row(&mut self, row: &mut [u8]) -> std::io::Result<()>
|
|
where
|
|
R: Read,
|
|
{
|
|
// 32-bit per pixel format.
|
|
let n_pixels = self.chunk_sz / 4;
|
|
// buffer for reading (16-bit per pixel)
|
|
let raw = &mut row.as_chunks_mut::<2>().0[..n_pixels];
|
|
packbits_decode::<_, 2>(&mut self.reader, raw)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn expand_row(row: &mut [u8]) {
|
|
let n_pixels = row.len() / 4;
|
|
|
|
// Work backwards, the 32-bit compressed raw data is at the front of the row buffer and
|
|
// this avoids overwriting data before we read it.
|
|
for i in (0..n_pixels).rev() {
|
|
let raw = row.as_chunks_mut::<2>().0[i];
|
|
|
|
let l = u16::from(raw[0]) << 8 | u16::from(raw[1]);
|
|
let by = log16_to_y(l);
|
|
|
|
let into = &mut row.as_chunks_mut::<4>().0[i];
|
|
into[0..4].copy_from_slice(&f32::to_ne_bytes(by));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<R: Read> Read for LogLuv16<R> {
|
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
self.read_exact(buf)?;
|
|
Ok(buf.len())
|
|
}
|
|
|
|
// FIXME: See `LogLuv24::read_exact`
|
|
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
|
for row in buf.chunks_exact_mut(self.chunk_sz) {
|
|
self.decode_rle_row(row)?;
|
|
Self::expand_row(row);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Table information from: `libtiff`, 1997 by Greg Ward Larson, SGI
|
|
#[doc(alias = "UV_SQSIZ")]
|
|
const UV_QUANTIZATION_WIDTH: f32 = 0.003500;
|
|
|
|
// The `v` value corresonding to the bottom most row (quantized v' = 0).
|
|
const UV_VSTART: f32 = 0.016940;
|
|
|
|
/// Number of distinct 'u' rows, each at a different v level. Each is quantized individually with
|
|
/// the first quantization level starting at its `ustart` and `UV_QUANTIZATION_WIDTH` wide.
|
|
const UV_NVS: usize = 163;
|
|
|
|
/// Contrary to the original C code we calculate the dependent values (cumulative sum).
|
|
struct UvRow {
|
|
ustart: f32,
|
|
num_us: u16,
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
const UV_ROWS: [UvRow; UV_NVS] = [
|
|
UvRow { ustart: 0.247663, num_us: 4, },
|
|
UvRow { ustart: 0.243779, num_us: 6, },
|
|
UvRow { ustart: 0.241684, num_us: 7, },
|
|
UvRow { ustart: 0.237874, num_us: 9, },
|
|
UvRow { ustart: 0.235906, num_us: 10, },
|
|
UvRow { ustart: 0.232153, num_us: 12, },
|
|
UvRow { ustart: 0.228352, num_us: 14, },
|
|
UvRow { ustart: 0.226259, num_us: 15, },
|
|
UvRow { ustart: 0.222371, num_us: 17, },
|
|
UvRow { ustart: 0.220410, num_us: 18, },
|
|
UvRow { ustart: 0.214710, num_us: 21, },
|
|
UvRow { ustart: 0.212714, num_us: 22, },
|
|
UvRow { ustart: 0.210721, num_us: 23, },
|
|
UvRow { ustart: 0.204976, num_us: 26, },
|
|
UvRow { ustart: 0.202986, num_us: 27, },
|
|
UvRow { ustart: 0.199245, num_us: 29, },
|
|
UvRow { ustart: 0.195525, num_us: 31, },
|
|
UvRow { ustart: 0.193560, num_us: 32, },
|
|
UvRow { ustart: 0.189878, num_us: 34, },
|
|
UvRow { ustart: 0.186216, num_us: 36, },
|
|
UvRow { ustart: 0.186216, num_us: 36, },
|
|
UvRow { ustart: 0.182592, num_us: 38, },
|
|
UvRow { ustart: 0.179003, num_us: 40, },
|
|
UvRow { ustart: 0.175466, num_us: 42, },
|
|
UvRow { ustart: 0.172001, num_us: 44, },
|
|
UvRow { ustart: 0.172001, num_us: 44, },
|
|
UvRow { ustart: 0.168612, num_us: 46, },
|
|
UvRow { ustart: 0.168612, num_us: 46, },
|
|
UvRow { ustart: 0.163575, num_us: 49, },
|
|
UvRow { ustart: 0.158642, num_us: 52, },
|
|
UvRow { ustart: 0.158642, num_us: 52, },
|
|
UvRow { ustart: 0.158642, num_us: 52, },
|
|
UvRow { ustart: 0.153815, num_us: 55, },
|
|
UvRow { ustart: 0.153815, num_us: 55, },
|
|
UvRow { ustart: 0.149097, num_us: 58, },
|
|
UvRow { ustart: 0.149097, num_us: 58, },
|
|
UvRow { ustart: 0.142746, num_us: 62, },
|
|
UvRow { ustart: 0.142746, num_us: 62, },
|
|
UvRow { ustart: 0.142746, num_us: 62, },
|
|
UvRow { ustart: 0.138270, num_us: 65, },
|
|
UvRow { ustart: 0.138270, num_us: 65, },
|
|
UvRow { ustart: 0.138270, num_us: 65, },
|
|
UvRow { ustart: 0.132166, num_us: 69, },
|
|
UvRow { ustart: 0.132166, num_us: 69, },
|
|
UvRow { ustart: 0.126204, num_us: 73, },
|
|
UvRow { ustart: 0.126204, num_us: 73, },
|
|
UvRow { ustart: 0.126204, num_us: 73, },
|
|
UvRow { ustart: 0.120381, num_us: 77, },
|
|
UvRow { ustart: 0.120381, num_us: 77, },
|
|
UvRow { ustart: 0.120381, num_us: 77, },
|
|
UvRow { ustart: 0.120381, num_us: 77, },
|
|
UvRow { ustart: 0.112962, num_us: 82, },
|
|
UvRow { ustart: 0.112962, num_us: 82, },
|
|
UvRow { ustart: 0.112962, num_us: 82, },
|
|
UvRow { ustart: 0.107450, num_us: 86, },
|
|
UvRow { ustart: 0.107450, num_us: 86, },
|
|
UvRow { ustart: 0.107450, num_us: 86, },
|
|
UvRow { ustart: 0.107450, num_us: 86, },
|
|
UvRow { ustart: 0.100343, num_us: 91, },
|
|
UvRow { ustart: 0.100343, num_us: 91, },
|
|
UvRow { ustart: 0.100343, num_us: 91, },
|
|
UvRow { ustart: 0.095126, num_us: 95, },
|
|
UvRow { ustart: 0.095126, num_us: 95, },
|
|
UvRow { ustart: 0.095126, num_us: 95, },
|
|
UvRow { ustart: 0.095126, num_us: 95, },
|
|
UvRow { ustart: 0.088276, num_us: 100, },
|
|
UvRow { ustart: 0.088276, num_us: 100, },
|
|
UvRow { ustart: 0.088276, num_us: 100, },
|
|
UvRow { ustart: 0.088276, num_us: 100, },
|
|
UvRow { ustart: 0.081523, num_us: 105, },
|
|
UvRow { ustart: 0.081523, num_us: 105, },
|
|
UvRow { ustart: 0.081523, num_us: 105, },
|
|
UvRow { ustart: 0.081523, num_us: 105, },
|
|
UvRow { ustart: 0.074861, num_us: 110, },
|
|
UvRow { ustart: 0.074861, num_us: 110, },
|
|
UvRow { ustart: 0.074861, num_us: 110, },
|
|
UvRow { ustart: 0.074861, num_us: 110, },
|
|
UvRow { ustart: 0.068290, num_us: 115, },
|
|
UvRow { ustart: 0.068290, num_us: 115, },
|
|
UvRow { ustart: 0.068290, num_us: 115, },
|
|
UvRow { ustart: 0.068290, num_us: 115, },
|
|
UvRow { ustart: 0.063573, num_us: 119, },
|
|
UvRow { ustart: 0.063573, num_us: 119, },
|
|
UvRow { ustart: 0.063573, num_us: 119, },
|
|
UvRow { ustart: 0.063573, num_us: 119, },
|
|
UvRow { ustart: 0.057219, num_us: 124, },
|
|
UvRow { ustart: 0.057219, num_us: 124, },
|
|
UvRow { ustart: 0.057219, num_us: 124, },
|
|
UvRow { ustart: 0.057219, num_us: 124, },
|
|
UvRow { ustart: 0.050985, num_us: 129, },
|
|
UvRow { ustart: 0.050985, num_us: 129, },
|
|
UvRow { ustart: 0.050985, num_us: 129, },
|
|
UvRow { ustart: 0.050985, num_us: 129, },
|
|
UvRow { ustart: 0.050985, num_us: 129, },
|
|
UvRow { ustart: 0.044859, num_us: 134, },
|
|
UvRow { ustart: 0.044859, num_us: 134, },
|
|
UvRow { ustart: 0.044859, num_us: 134, },
|
|
UvRow { ustart: 0.044859, num_us: 134, },
|
|
UvRow { ustart: 0.040571, num_us: 138, },
|
|
UvRow { ustart: 0.040571, num_us: 138, },
|
|
UvRow { ustart: 0.040571, num_us: 138, },
|
|
UvRow { ustart: 0.040571, num_us: 138, },
|
|
UvRow { ustart: 0.036339, num_us: 142, },
|
|
UvRow { ustart: 0.036339, num_us: 142, },
|
|
UvRow { ustart: 0.036339, num_us: 142, },
|
|
UvRow { ustart: 0.036339, num_us: 142, },
|
|
UvRow { ustart: 0.032139, num_us: 146, },
|
|
UvRow { ustart: 0.032139, num_us: 146, },
|
|
UvRow { ustart: 0.032139, num_us: 146, },
|
|
UvRow { ustart: 0.032139, num_us: 146, },
|
|
UvRow { ustart: 0.027947, num_us: 150, },
|
|
UvRow { ustart: 0.027947, num_us: 150, },
|
|
UvRow { ustart: 0.027947, num_us: 150, },
|
|
UvRow { ustart: 0.023739, num_us: 154, },
|
|
UvRow { ustart: 0.023739, num_us: 154, },
|
|
UvRow { ustart: 0.023739, num_us: 154, },
|
|
UvRow { ustart: 0.023739, num_us: 154, },
|
|
UvRow { ustart: 0.019504, num_us: 158, },
|
|
UvRow { ustart: 0.019504, num_us: 158, },
|
|
UvRow { ustart: 0.019504, num_us: 158, },
|
|
UvRow { ustart: 0.016976, num_us: 161, },
|
|
UvRow { ustart: 0.016976, num_us: 161, },
|
|
UvRow { ustart: 0.016976, num_us: 161, },
|
|
UvRow { ustart: 0.016976, num_us: 161, },
|
|
UvRow { ustart: 0.012639, num_us: 165, },
|
|
UvRow { ustart: 0.012639, num_us: 165, },
|
|
UvRow { ustart: 0.012639, num_us: 165, },
|
|
UvRow { ustart: 0.009991, num_us: 168, },
|
|
UvRow { ustart: 0.009991, num_us: 168, },
|
|
UvRow { ustart: 0.009991, num_us: 168, },
|
|
UvRow { ustart: 0.009016, num_us: 170, },
|
|
UvRow { ustart: 0.009016, num_us: 170, },
|
|
UvRow { ustart: 0.009016, num_us: 170, },
|
|
UvRow { ustart: 0.006217, num_us: 173, },
|
|
UvRow { ustart: 0.006217, num_us: 173, },
|
|
UvRow { ustart: 0.005097, num_us: 175, },
|
|
UvRow { ustart: 0.005097, num_us: 175, },
|
|
UvRow { ustart: 0.005097, num_us: 175, },
|
|
UvRow { ustart: 0.003909, num_us: 177, },
|
|
UvRow { ustart: 0.003909, num_us: 177, },
|
|
UvRow { ustart: 0.002340, num_us: 177, },
|
|
UvRow { ustart: 0.002389, num_us: 170, },
|
|
UvRow { ustart: 0.001068, num_us: 164, },
|
|
UvRow { ustart: 0.001653, num_us: 157, },
|
|
UvRow { ustart: 0.000717, num_us: 150, },
|
|
UvRow { ustart: 0.001614, num_us: 143, },
|
|
UvRow { ustart: 0.000270, num_us: 136, },
|
|
UvRow { ustart: 0.000484, num_us: 129, },
|
|
UvRow { ustart: 0.001103, num_us: 123, },
|
|
UvRow { ustart: 0.001242, num_us: 115, },
|
|
UvRow { ustart: 0.001188, num_us: 109, },
|
|
UvRow { ustart: 0.001011, num_us: 103, },
|
|
UvRow { ustart: 0.000709, num_us: 97, },
|
|
UvRow { ustart: 0.000301, num_us: 89, },
|
|
UvRow { ustart: 0.002416, num_us: 82, },
|
|
UvRow { ustart: 0.003251, num_us: 76, },
|
|
UvRow { ustart: 0.003246, num_us: 69, },
|
|
UvRow { ustart: 0.004141, num_us: 62, },
|
|
UvRow { ustart: 0.005963, num_us: 55, },
|
|
UvRow { ustart: 0.008839, num_us: 47, },
|
|
UvRow { ustart: 0.010490, num_us: 40, },
|
|
UvRow { ustart: 0.016994, num_us: 31, },
|
|
UvRow { ustart: 0.023659, num_us: 21, },
|
|
];
|
|
|
|
const UV_CUMULATIVE: [u16; UV_NVS] = {
|
|
let mut arr = [0u16; UV_NVS];
|
|
let mut sum = 0;
|
|
let mut i = 0;
|
|
while i < UV_NVS {
|
|
arr[i] = sum;
|
|
sum += UV_ROWS[i].num_us;
|
|
i += 1;
|
|
}
|
|
arr
|
|
};
|
|
|
|
const UV_NDIVS: u16 = UV_CUMULATIVE[UV_NVS - 1] + UV_ROWS[UV_NVS - 1].num_us;
|
|
|
|
fn decode_quantized_uv(uv: u32) -> (u8, u8) {
|
|
if uv >= UV_NDIVS as u32 {
|
|
return (0, 0);
|
|
}
|
|
|
|
// Find the start of the row, or the index after that.
|
|
let v_index = match UV_CUMULATIVE.binary_search(&(uv as u16)) {
|
|
Ok(n) => n,
|
|
Err(n) => n - 1,
|
|
};
|
|
|
|
let u_index = uv as u16 - UV_CUMULATIVE[v_index];
|
|
(u_index as u8, v_index as u8)
|
|
}
|
|
|
|
fn dequantize_24_luma(l: u32) -> f32 {
|
|
if l == 0 {
|
|
0.0
|
|
} else {
|
|
let le = (l & 0x3ff) as f32;
|
|
let exponent = ((le / 64.0).floor() as i32) - 24;
|
|
let mantissa = le - (exponent as f32 + 24.0) * 64.0;
|
|
(mantissa + 64.0) / 64.0 * 2f32.powi(exponent)
|
|
}
|
|
}
|
|
|
|
fn dequantize_24((u, v): (u8, u8)) -> (f32, f32) {
|
|
let v_f = UV_VSTART + (v as f32) * UV_QUANTIZATION_WIDTH;
|
|
let u_row = &UV_ROWS[v as usize];
|
|
let u_f = u_row.ustart + (u as f32) * UV_QUANTIZATION_WIDTH;
|
|
(u_f, v_f)
|
|
}
|
|
|
|
fn log16_to_y(l: u16) -> f32 {
|
|
if l == 0 {
|
|
return 0.0;
|
|
}
|
|
|
|
let le = f32::from(l & 0x7fff);
|
|
// Y = exp(M_LN2 / 256. * (Le + .5) - M_LN2 * 64.);
|
|
let y = (std::f32::consts::LN_2 / 256.0 * (le + 0.5) - std::f32::consts::LN_2 * 64.0).exp();
|
|
|
|
if l & 0x8000 != 0 {
|
|
-y
|
|
} else {
|
|
y
|
|
}
|
|
}
|
|
|
|
fn xyz_to_rgb([l, u, v]: [f32; 3]) -> [f32; 3] {
|
|
// XYZ to sRGB primaries at D65
|
|
let r = 3.2404542 * l + -1.5371385 * u + -0.4985314 * v;
|
|
let g = -0.969266 * l + 1.8760108 * u + 0.0415560 * v;
|
|
let b = 0.0556434 * l + -0.2040259 * u + 1.0572252 * v;
|
|
[r, g, b]
|
|
}
|
|
|
|
#[test]
|
|
fn test_decode_quantized_uv() {
|
|
// Test some known values from the original C code.
|
|
assert_eq!(decode_quantized_uv(0), (0, 0));
|
|
assert_eq!(decode_quantized_uv(3), (3, 0));
|
|
assert_eq!(decode_quantized_uv(4), (0, 1));
|
|
assert_eq!(decode_quantized_uv(9), (5, 1));
|
|
assert_eq!(decode_quantized_uv(16288), (20, 162));
|
|
assert_eq!(decode_quantized_uv(16289), (0, 0)); // Out of range
|
|
}
|