Files
lightcraft/crates/raw/src/unpack.rs
T
radialmonster a12f1697a1 LR-IMP-FORMATS: decode 12-bit raws packed in 32-bit little-endian words (Fujifilm RAF flag 0xf006, Coolpix NRW data block)
Raw-IFD flag 0xf006 = 1 marks RAF samples packed MSB-first into 32-bit little-endian words at any depth; 12-bit files with it (FinePix S1, X-A10, SL1000) were read as an LSB-first stream. Nikon uncompressed 12-bit strips of exactly 1.5 bytes per pixel in a file whose maker note has an NRW data block (COOLPIX B700, P950, P1000, P1100) use the same words in file byte order; without the block (E5700, E8400, E8800) they stay an MSB-first byte stream. Shared unpack_words32_msb and Packing::Words32Msb. RENDER_CACHE_VERSION 15 to 16.
2026-10-08 21:26:26 -04:00

224 lines
8.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Sample unpacking: bit packing, byte orders, 16/24/32-bit floats, Deflate and TIFF / DNG predictors.
//!
//! Sources: TIFF 6.0 §14 (Differencing Predictor), Adobe Photoshop TIFF Technical Note 3 (floating-point
//! predictor), DNG 1.7 (predictors 34892–34895, bit packing, 16/24-bit floats), RFC 1950/1951 (zlib/deflate).
use crate::{RawError, Result};
use lightcraft_tiff::ByteOrder;
/// Unpack `out.len()` MSB-first packed samples of `bits` (1..=16) from `src` (TIFF bit order).
pub fn unpack_msb(src: &[u8], bits: u32, out: &mut [u16]) {
debug_assert!((1..=16).contains(&bits));
let mut acc: u64 = 0;
let mut nacc: u32 = 0;
let mut it = src.iter();
let mask = (1u64 << bits) - 1;
for o in out.iter_mut() {
while nacc < bits {
acc = (acc << 8) | *it.next().unwrap_or(&0) as u64;
nacc += 8;
}
*o = ((acc >> (nacc - bits)) & mask) as u16;
nacc -= bits;
}
}
/// Unpack LSB-first packed samples (bits taken from the low end of little-endian words).
pub fn unpack_lsb(src: &[u8], bits: u32, out: &mut [u16]) {
let mut acc: u64 = 0;
let mut nacc: u32 = 0;
let mut it = src.iter();
let mask = (1u64 << bits) - 1;
for o in out.iter_mut() {
while nacc < bits {
acc |= (*it.next().unwrap_or(&0) as u64) << nacc;
nacc += 8;
}
*o = (acc & mask) as u16;
acc >>= bits;
nacc -= bits;
}
}
/// Unpack MSB-first packed samples of `bits` from a stream of 32-bit words stored in `order` (a word is read as a
/// number first, then its bits are taken from the top): the layout of some cameras' uncompressed 12- and 14-bit
/// data, which is not the byte-wise MSB stream of [`unpack_msb`] unless `order` is big-endian. A short last word
/// is padded with zeros.
pub fn unpack_words32_msb(src: &[u8], order: ByteOrder, bits: u32, out: &mut [u16]) {
let words = src.chunks(4).flat_map(|c| {
let mut w = [0u8; 4];
w[..c.len()].copy_from_slice(c);
order.u32(w).to_be_bytes()
});
unpack_msb(&words.collect::<Vec<u8>>(), bits, out);
}
pub fn read_u16s(src: &[u8], order: ByteOrder, out: &mut [u16]) {
for (o, c) in out.iter_mut().zip(src.as_chunks::<2>().0) {
*o = order.u16([c[0], c[1]]);
}
}
/// IEEE 754 half → f32.
pub fn f16_to_f32(h: u16) -> f32 {
let sign = ((h >> 15) as u32) << 31;
let exp = ((h >> 10) & 0x1f) as u32;
let man = (h & 0x3ff) as u32;
let bits = if exp == 0 {
if man == 0 {
sign
} else {
// subnormal: normalise
let mut e = 127 - 15 + 1;
let mut m = man;
while m & 0x400 == 0 {
m <<= 1;
e -= 1;
}
sign | (e << 23) | ((m & 0x3ff) << 13)
}
} else if exp == 31 {
sign | 0x7f80_0000 | (man << 13)
} else {
sign | ((exp + 127 - 15) << 23) | (man << 13)
};
f32::from_bits(bits)
}
/// DNG 24-bit float (1 sign, 7 exponent bias 63, 16 mantissa) → f32.
pub fn f24_to_f32(v: u32) -> f32 {
let sign = (v >> 23) & 1;
let exp = (v >> 16) & 0x7f;
let man = v & 0xffff;
let bits = if exp == 0 && man == 0 {
sign << 31
} else if exp == 0x7f {
(sign << 31) | 0x7f80_0000 | (man << 7)
} else if exp == 0 {
// denormal: value = man * 2^(1-63-16)
let v = man as f32 * 2f32.powi(1 - 63 - 16);
return if sign == 1 { -v } else { v };
} else {
(sign << 31) | ((exp + 127 - 63) << 23) | (man << 7)
};
f32::from_bits(bits)
}
/// Inflate a zlib stream, refusing to produce more than `limit` bytes.
pub fn inflate(src: &[u8], limit: usize) -> Result<Vec<u8>> {
miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(src, limit).map_err(|e| RawError::Corrupt(format!("deflate: {e:?}")))
}
/// Undo horizontal differencing on one row of integer samples with a sample `stride` (cpp × factor).
pub fn undo_diff_u16(row: &mut [u16], stride: usize) {
for i in stride..row.len() {
row[i] = row[i].wrapping_add(row[i - stride]);
}
}
pub fn undo_diff_u8(row: &mut [u8], stride: usize) {
for i in stride..row.len() {
row[i] = row[i].wrapping_add(row[i - stride]);
}
}
/// Undo the floating-point predictor on one row: `row` holds `n` samples of `bytes_per` bytes, byte-differenced
/// with stride `stride` samples, then stored as byte planes (most significant first). Returns the samples as
/// big-endian byte groups in place.
pub fn undo_float_predictor(row: &mut [u8], n: usize, bytes_per: usize, stride: usize) {
undo_diff_u8(row, stride);
let tmp = row.to_vec();
for i in 0..n {
for b in 0..bytes_per {
if let (Some(dst), Some(&s)) = (row.get_mut(i * bytes_per + b), tmp.get(b * n + i)) {
*dst = s;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn words32_are_read_as_numbers_then_msb_first() {
// eight 12-bit samples occupy 96 bits = three 32-bit words, filled from the top
let samples: [u16; 8] = [0x123, 0x456, 0x789, 0xabc, 0xdef, 0x012, 0x345, 0x678];
let stream = samples.iter().fold(0u128, |acc, &v| (acc << 12) | v as u128);
let words = [(stream >> 64) as u32, (stream >> 32) as u32, stream as u32];
let le: Vec<u8> = words.iter().flat_map(|w| w.to_le_bytes()).collect();
let be: Vec<u8> = words.iter().flat_map(|w| w.to_be_bytes()).collect();
let mut o = [0u16; 8];
unpack_words32_msb(&le, ByteOrder::Little, 12, &mut o);
assert_eq!(o, samples);
o = [0; 8];
unpack_words32_msb(&be, ByteOrder::Big, 12, &mut o);
assert_eq!(o, samples);
// read as a plain byte stream the little-endian words give different samples
o = [0; 8];
unpack_msb(&le, 12, &mut o);
assert_ne!(o, samples);
// a short last word is zero padded
let mut o = [9u16; 2];
unpack_words32_msb(&[0x34, 0x12], ByteOrder::Little, 12, &mut o);
assert_eq!(o, [0x000, 0x012]); // the word 0x00001234, top bits first
}
#[test]
fn packing() {
// 12-bit MSB: 0xABC, 0xDEF -> AB CD EF
let mut o = [0u16; 2];
unpack_msb(&[0xab, 0xcd, 0xef], 12, &mut o);
assert_eq!(o, [0xabc, 0xdef]);
unpack_lsb(&[0xbc, 0xfa, 0xde], 12, &mut o);
assert_eq!(o, [0xabc, 0xdef]);
let mut o = [0u16; 3];
unpack_msb(&[0b1010_1100, 0b0100_0000], 3, &mut o);
assert_eq!(o, [0b101, 0b011, 0b000]);
let mut o = [0u16; 2];
read_u16s(&[1, 2, 3, 4], ByteOrder::Big, &mut o);
assert_eq!(o, [0x0102, 0x0304]);
// short source: pads with zeros
let mut o = [9u16; 4];
unpack_msb(&[0xff], 14, &mut o);
assert_eq!(o, [0x3fc0, 0, 0, 0]);
}
#[test]
fn floats() {
assert_eq!(f16_to_f32(0x3c00), 1.0);
assert_eq!(f16_to_f32(0xc000), -2.0);
assert_eq!(f16_to_f32(0x0001), 2f32.powi(-24));
assert!(f16_to_f32(0x7c00).is_infinite());
// 24-bit: 1.0 = exp 63, mantissa 0
assert_eq!(f24_to_f32(63 << 16), 1.0);
assert_eq!(f24_to_f32((1 << 23) | (64 << 16) | 0x8000), -3.0);
assert_eq!(f24_to_f32(0), 0.0);
}
#[test]
fn predictors() {
let mut r = [1u16, 1, 1, 1, 65535, 2];
undo_diff_u16(&mut r, 2);
assert_eq!(r, [1, 1, 2, 2, 1, 4]);
// float predictor: two 32-bit samples 1.0 (3f800000), 2.0 (40000000), stride 1
let planes: [u8; 8] = [0x3f, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut row = planes;
for i in (1..row.len()).rev() {
row[i] = row[i].wrapping_sub(row[i - 1]);
}
undo_float_predictor(&mut row, 2, 4, 1);
assert_eq!(f32::from_be_bytes([row[0], row[1], row[2], row[3]]), 1.0);
assert_eq!(f32::from_be_bytes([row[4], row[5], row[6], row[7]]), 2.0);
}
#[test]
fn inflate_limit() {
let z = miniz_oxide::deflate::compress_to_vec_zlib(&[7u8; 1000], 6);
assert_eq!(inflate(&z, 1000).unwrap(), vec![7u8; 1000]);
assert!(inflate(&z, 10).is_err());
assert!(inflate(&[1, 2, 3], 10).is_err());
}
}