Vendor rawler 0.7.2 unmodified
The crate as crates.io publishes it, minus .cargo-ok, its Cargo.lock and data/testdata (13 MB of sample files only its own tests read). Not yet routed through [patch.crates-io]; the next commit is the patch.
This commit is contained in:
@@ -0,0 +1,757 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::{
|
||||
BandParam, CodecParams, CrxError, Result,
|
||||
mdat::{Plane, Tile},
|
||||
};
|
||||
use crate::decompressors::crx::{idwt::WaveletTransform, mdat::parse_header, rice::RiceDecoder};
|
||||
use bitstream_io::BitReader;
|
||||
use itertools::izip;
|
||||
use log::debug;
|
||||
use rayon::prelude::*;
|
||||
use std::{convert::TryInto, io::Cursor, time::Instant};
|
||||
|
||||
/// Maximum value for K during Adaptive Golomb-Rice for K prediction
|
||||
pub(super) const PREDICT_K_MAX: u32 = 15;
|
||||
pub(super) const PREDICT_K_ESCAPE: u32 = 41;
|
||||
pub(super) const PREDICT_K_ESCBITS: u32 = 21;
|
||||
|
||||
struct PlaneLineIter<'a> {
|
||||
tile: &'a Tile,
|
||||
plane: &'a Plane,
|
||||
codec: CodecParams,
|
||||
params: Vec<BandParam<'a>>,
|
||||
iwt_transforms: Vec<WaveletTransform>,
|
||||
//plane_buf: Vec<i32>,
|
||||
next_row: usize,
|
||||
}
|
||||
|
||||
impl<'a> PlaneLineIter<'a> {
|
||||
/// Create a new PlaneLine iterator for decoding
|
||||
fn new(codec: CodecParams, tile: &'a Tile, plane: &'a Plane, mdat: &'a [u8]) -> Result<Self> {
|
||||
// Some checks for correct input
|
||||
assert!(tile.plane_height > 0);
|
||||
assert!(tile.plane_width > 0);
|
||||
|
||||
// Reference to data section in MDAT
|
||||
// All calculated offsets are relative to the data section.
|
||||
let data = codec.get_data(mdat);
|
||||
|
||||
let plane_mdat_offset =
|
||||
tile.data_offset + tile.qp_data.as_ref().map(|qp| qp.mdat_qp_data_size + qp.mdat_extra_size as u32).unwrap_or(0) as usize + plane.data_offset;
|
||||
|
||||
let mut params = Vec::with_capacity(plane.subbands.len());
|
||||
for (band_id, band) in plane.subbands.iter().enumerate() {
|
||||
let band_mdat_offset = plane_mdat_offset + band.data_offset;
|
||||
debug!("Band {} has MDAT offset: {}", band_id, band_mdat_offset);
|
||||
let band_buf = &data[band_mdat_offset..band_mdat_offset + band.data_size];
|
||||
// Line length is subband + one additional pixel at start and end
|
||||
let line_len = 1 + band.width + 1;
|
||||
let bitpump = BitReader::endian(Cursor::new(band_buf), bitstream_io::BigEndian);
|
||||
|
||||
let param = BandParam {
|
||||
subband_width: band.width,
|
||||
subband_height: band.height,
|
||||
rounded_bits_mask: if plane.support_partial && band_id == 0 { plane.rounded_bits_mask } else { 0 },
|
||||
rounded_bits: 0,
|
||||
cur_line: 0,
|
||||
line_buf: [vec![0; line_len], vec![0; line_len]],
|
||||
line_k: vec![0; line_len],
|
||||
line_pos: 0,
|
||||
line_len,
|
||||
s_param: 0,
|
||||
q_param: band.q_param,
|
||||
supports_partial: plane.support_partial && band_id == 0,
|
||||
rice: RiceDecoder::new(bitpump),
|
||||
};
|
||||
params.push(param);
|
||||
}
|
||||
|
||||
let mut iwt_transforms = Vec::with_capacity(codec.levels);
|
||||
|
||||
if codec.levels > 0 {
|
||||
// create Wavelet transforms
|
||||
for level in 0..codec.levels {
|
||||
let band = 3 * level + 1;
|
||||
let (height, width) = if level >= codec.levels - 1 {
|
||||
(tile.plane_height, tile.plane_width)
|
||||
} else {
|
||||
(plane.subbands[band + 3].height, plane.subbands[band + 4].width)
|
||||
};
|
||||
iwt_transforms.push(WaveletTransform::new(height, width));
|
||||
}
|
||||
codec.idwt_53_filter_init(tile, plane, &mut params, &mut iwt_transforms, codec.levels)?;
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
params,
|
||||
tile,
|
||||
plane,
|
||||
codec,
|
||||
iwt_transforms,
|
||||
next_row: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Decode a single line from plane
|
||||
fn decode_plane_line(&mut self) -> Result<&[i32]> {
|
||||
if self.next_row < self.tile.plane_height {
|
||||
self.next_row += 1;
|
||||
if self.codec.levels > 0 {
|
||||
self
|
||||
.codec
|
||||
.idwt_53_filter_decode(self.tile, self.plane, &mut self.params, &mut self.iwt_transforms, self.codec.levels - 1)?;
|
||||
self
|
||||
.codec
|
||||
.idwt_53_filter_transform(self.tile, self.plane, &mut self.params, &mut self.iwt_transforms, self.codec.levels - 1)?;
|
||||
let line_data = self.iwt_transforms[self.codec.levels - 1].getline();
|
||||
debug_assert_eq!(line_data.len(), self.tile.plane_width);
|
||||
Ok(line_data)
|
||||
} else {
|
||||
debug_assert_eq!(self.plane.subbands.len(), 1);
|
||||
let param = &mut self.params[0];
|
||||
self.codec.decode_line(param)?;
|
||||
let line_data = param.decoded_buf();
|
||||
debug_assert_eq!(line_data.len(), param.subband_width as usize);
|
||||
debug_assert_eq!(line_data.len(), self.tile.plane_width);
|
||||
Ok(line_data)
|
||||
}
|
||||
} else {
|
||||
Err(CrxError::General("All rows processed, can't decode more".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over a plane, returning on each call a new decoded line
|
||||
impl<'a> Iterator for PlaneLineIter<'a> {
|
||||
type Item = Result<PlaneLine>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.next_row < self.tile.plane_height {
|
||||
match self.decode_plane_line() {
|
||||
Ok(line) => Some(Ok(line.into())),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, Some(self.tile.plane_height))
|
||||
}
|
||||
}
|
||||
|
||||
/// A plane line is a vector if i32 values
|
||||
type PlaneLine = Vec<i32>;
|
||||
|
||||
/// Wrapper for PlaneLineIter
|
||||
/// Decodes a complete plane and returns it as vector of lines
|
||||
fn decode_full_plane(codec: &CodecParams, tile: &Tile, plane: &Plane, mdat: &[u8]) -> Result<Vec<PlaneLine>> {
|
||||
//eprintln!("Process tile {}, plane: {}", tile.id, plane.id);
|
||||
let line_decoder = PlaneLineIter::new(*codec, tile, plane, mdat)?;
|
||||
line_decoder.collect()
|
||||
}
|
||||
|
||||
impl CodecParams {
|
||||
/// Decode MDAT section into a single CFA image
|
||||
///
|
||||
/// Decoding processes all planes in all tiles and assembles the
|
||||
/// decoded planes into proper tile output position and CFA pattern.
|
||||
pub fn decode(mut self, mdat: &[u8]) -> Result<Vec<u16>> {
|
||||
let instant = Instant::now();
|
||||
debug!("Tile configuration: rows: {}, columns: {}", self.tile_rows, self.tile_cols);
|
||||
// Build nested Tiles/Planes/Bands
|
||||
let mut tiles = parse_header(self.get_header(mdat))?;
|
||||
self.process_tiles(&mut tiles);
|
||||
for tile in tiles.iter_mut() {
|
||||
tile.generate_qstep_table(&self, self.get_data(mdat))?;
|
||||
}
|
||||
|
||||
// cfa output is of final resolution
|
||||
let mut cfa: Vec<u16> = vec![0; self.resolution()];
|
||||
|
||||
// Combine all tiles and planes into parallel iterators
|
||||
// and decode the full planes.
|
||||
let plane_bufs: Result<Vec<Vec<Vec<PlaneLine>>>> = tiles
|
||||
.par_iter()
|
||||
.map(|tile| tile.planes.par_iter().map(move |plane| decode_full_plane(&self, tile, plane, mdat)).collect())
|
||||
.collect();
|
||||
|
||||
// Now we have a list of tiles->planes->plane-lines
|
||||
// and can combine them to the final CFA
|
||||
match plane_bufs {
|
||||
Ok(bufs) => {
|
||||
for (tile_id, tile) in bufs.into_iter().enumerate() {
|
||||
let plane_count = tile.len();
|
||||
debug_assert_eq!(plane_count, 4);
|
||||
// Convert vector of planes to excact count of 4 planes - or fail
|
||||
let planes: [Vec<PlaneLine>; 4] = tile
|
||||
.try_into()
|
||||
.map_err(|_| CrxError::General(format!("Invalid plane count {} (expected 4) for tile {}", plane_count, tile_id)))?;
|
||||
// References to all 4 plane buffers
|
||||
let (p0, p1, p2, p3) = (&planes[0], &planes[1], &planes[2], &planes[3]);
|
||||
// Process each PlaneLine in all 4 buffers
|
||||
for (plane_row, (l0, l1, l2, l3)) in izip!(p0, p1, p2, p3).enumerate() {
|
||||
let (c0, c1, c2, c3) = convert_plane_line(&self, l0, l1, l2, l3)?;
|
||||
integrate_cfa(&self, &tiles, &mut cfa, tile_id, 0, plane_row, &c0)?;
|
||||
integrate_cfa(&self, &tiles, &mut cfa, tile_id, 1, plane_row, &c1)?;
|
||||
integrate_cfa(&self, &tiles, &mut cfa, tile_id, 2, plane_row, &c2)?;
|
||||
integrate_cfa(&self, &tiles, &mut cfa, tile_id, 3, plane_row, &c3)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
debug!("MDAT decoding and CFA build: {} s", instant.elapsed().as_secs_f32());
|
||||
Ok(cfa)
|
||||
}
|
||||
|
||||
/// Decode top line without a previous K buffer
|
||||
fn decode_top_line_no_ref_prev_line(&self, p: &mut BandParam) -> Result<()> {
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
// Init coef a and c (real image pixel starts at 1)
|
||||
p.line_buf[0][p.line_pos - 1] = 0; // is [0] because at start line_pos is 1
|
||||
p.line_buf[1][p.line_pos - 1] = 0; // is [0] because at start line_pos is 1
|
||||
while remaining > 1 {
|
||||
//println!("remaining: {}", remaining);
|
||||
// Loop over full width of line (backwards)
|
||||
if p.coeff_a() != 0 {
|
||||
//println!("coeff {} is != 0", p.coeff_a());
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code);
|
||||
} else {
|
||||
//println!("coeff {} = 0", p.coeff_a());
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
//println!("found {} syms", n_syms);
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
// For the first line, run-length coding uses only the symbol
|
||||
// value 0, so we can fill the line buffer and K buffer with 0.
|
||||
p.line_buf[1][p.line_pos] = 0;
|
||||
p.line_k[p.line_pos - 1] = 0;
|
||||
p.line_pos += 1;
|
||||
}
|
||||
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code + 1); // Caution: + 1
|
||||
//println!("code: {}", p.line_buf[1][p.line_pos]);
|
||||
}
|
||||
p.line_k[p.line_pos - 1] = p.rice.k();
|
||||
p.line_pos += 1;
|
||||
remaining = remaining.saturating_sub(1);
|
||||
}
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code);
|
||||
p.line_k[p.line_pos - 1] = p.rice.k();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
p.line_buf[1][p.line_pos] = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode nontop line with a previous K buffer
|
||||
fn decode_nontop_line_no_ref_prev_line(&self, p: &mut BandParam) -> Result<()> {
|
||||
//println!("Decode nontop {}", p.cur_line);
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
while remaining > 1 {
|
||||
// Loop over full width of line (backwards)
|
||||
if (p.coeff_d() | p.coeff_b() | p.coeff_a()) != 0 {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, 0)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code);
|
||||
if p.line_k[p.line_pos].saturating_sub(p.rice.k()) <= 1 {
|
||||
if p.rice.k() >= 15 {
|
||||
p.rice.set_k(15);
|
||||
}
|
||||
} else {
|
||||
p.rice.set_k(p.rice.k() + 1);
|
||||
}
|
||||
} else {
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
debug_assert!(remaining != 1);
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
// For the first line, run-length coding uses only the symbol
|
||||
// value 0, so we can fill the line buffer and K buffer with 0.
|
||||
p.line_buf[1][p.line_pos] = 0;
|
||||
p.line_k[p.line_pos - 1] = 0;
|
||||
p.line_pos += 1;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
|
||||
if remaining <= 1 {
|
||||
if remaining == 1 {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code + 1);
|
||||
p.line_k[p.line_pos - 1] = p.rice.k();
|
||||
p.line_pos += 1;
|
||||
remaining = remaining.saturating_sub(1); // skip remaining check at end of function
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, 0)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code + 1); // Caution: + 1
|
||||
if p.line_k[p.line_pos].saturating_sub(p.rice.k()) <= 1 {
|
||||
if p.rice.k() >= 15 {
|
||||
p.rice.set_k(15);
|
||||
}
|
||||
} else {
|
||||
p.rice.set_k(p.rice.k() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
p.line_k[p.line_pos - 1] = p.rice.k();
|
||||
p.line_pos += 1;
|
||||
remaining = remaining.saturating_sub(1);
|
||||
}
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = error_code_signed(bit_code);
|
||||
p.line_k[p.line_pos - 1] = p.rice.k();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode top line
|
||||
/// For the first line (top) in a plane, no MED is used because
|
||||
/// there is no previous line for coeffs b, c and d.
|
||||
/// So this decoding is a simplified version from decode_nontop_line().
|
||||
fn decode_top_line(&self, p: &mut BandParam) -> Result<()> {
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
// Init coeff a (real image pixel starts at 1)
|
||||
p.line_buf[1][p.line_pos - 1] = 0; // is is [0] because at start line_pos is 1
|
||||
while remaining > 1 {
|
||||
// Loop over full width of line (backwards)
|
||||
if p.coeff_a() != 0 {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
} else {
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
p.line_buf[1][p.line_pos] = 0;
|
||||
}
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] += error_code_signed(bit_code);
|
||||
p.line_pos += 1;
|
||||
remaining = remaining.saturating_sub(1);
|
||||
}
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
let x = p.coeff_a(); // no MED, just use coeff a
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
p.line_buf[1][p.line_pos] = x + error_code_signed(bit_code);
|
||||
p.line_pos += 1;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a() + 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode a line which is not a top line
|
||||
/// This used run length coding, Median Edge Detection (MED) and
|
||||
/// adaptive Golomb-Rice entropy encoding.
|
||||
/// Golomb-Rice becomes more efficient when using an adaptive K value
|
||||
/// instead of a fixed one.
|
||||
/// The K parameter is used as q = n >> k where n is the sample to encode.
|
||||
fn decode_nontop_line(&self, p: &mut BandParam) -> Result<()> {
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
// Init coeff a: a = b
|
||||
p.line_buf[1][p.line_pos - 1] = p.coeff_b();
|
||||
// Loop over full width of line (backwards)
|
||||
while remaining > 1 {
|
||||
let mut x = 0;
|
||||
// c b d
|
||||
// a x n
|
||||
// Median Edge Detection to predict pixel x. Described in patent US2016/0323602 and T.87
|
||||
if p.coeff_a() == p.coeff_b() && p.coeff_a() == p.coeff_d() {
|
||||
// different than step [0104], where Condition: "a=c and c=b and b=d", c not used
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
if remaining > 0 {
|
||||
x = p.coeff_b(); // use new coeff b because we moved line_pos!
|
||||
}
|
||||
} else {
|
||||
// no run length coding, use MED instead
|
||||
x = med(p.coeff_a(), p.coeff_b(), p.coeff_c());
|
||||
}
|
||||
if remaining > 0 {
|
||||
let mut bit_code = p.rice.adaptive_rice_decode(false, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
// add converted (+/-) error code to predicted value
|
||||
p.line_buf[1][p.line_pos] = x + error_code_signed(bit_code);
|
||||
// for not end of the line - use one symbol ahead to estimate next K
|
||||
if remaining > 1 {
|
||||
let delta: i32 = (p.coeff_d() - p.coeff_b()) << 1;
|
||||
bit_code = (bit_code + delta.unsigned_abs()) >> 1;
|
||||
}
|
||||
p.rice.update_k_param(bit_code, PREDICT_K_MAX);
|
||||
p.line_pos += 1;
|
||||
}
|
||||
remaining = remaining.saturating_sub(1);
|
||||
} // end while length > 1
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
let x = med(p.coeff_a(), p.coeff_b(), p.coeff_c());
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
// add converted (+/-) error code to predicted value
|
||||
p.line_buf[1][p.line_pos] = x + error_code_signed(bit_code);
|
||||
p.line_pos += 1;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a() + 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode a symbol x in rounded mode.
|
||||
/// Used only when levels==0 (lossless mode)
|
||||
fn decode_symbol_rounded(&self, p: &mut BandParam, use_med: bool, not_eol: bool) -> Result<()> {
|
||||
let sym = if use_med { med(p.coeff_a(), p.coeff_b(), p.coeff_c()) } else { p.coeff_b() };
|
||||
let bit_code = p.rice.adaptive_rice_decode(false, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
let mut code = error_code_signed(bit_code);
|
||||
let x = p.rounded_bits_mask * 2 * code + (code >> 31);
|
||||
p.line_buf[1][p.line_pos] = x + sym;
|
||||
|
||||
if not_eol {
|
||||
if p.coeff_d() > p.coeff_b() {
|
||||
code = (p.coeff_d() - p.coeff_b() + p.rounded_bits_mask - 1) >> p.rounded_bits;
|
||||
} else {
|
||||
code = -((p.coeff_b() - p.coeff_d() + p.rounded_bits_mask) >> p.rounded_bits);
|
||||
}
|
||||
p.rice.update_k_param((bit_code + 2 * code.unsigned_abs()) >> 1, PREDICT_K_MAX);
|
||||
} else {
|
||||
p.rice.update_k_param(bit_code, PREDICT_K_MAX);
|
||||
}
|
||||
|
||||
p.line_pos += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode a rounded line which is not a top line
|
||||
fn decode_top_line_rounded(&self, p: &mut BandParam) -> Result<()> {
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
// Init coeff a (real image pixel starts at 1)
|
||||
p.line_buf[1][p.line_pos - 1] = 0; // is is [0] because at start line_pos is 1
|
||||
while remaining > 1 {
|
||||
// Loop over full width of line (backwards)
|
||||
if p.coeff_a().abs() > p.rounded_bits_mask {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
} else {
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
p.line_buf[1][p.line_pos] = 0;
|
||||
}
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
let code = error_code_signed(bit_code);
|
||||
p.line_buf[1][p.line_pos] += p.rounded_bits_mask * 2 * code + (code >> 31);
|
||||
p.line_pos += 1;
|
||||
remaining = remaining.saturating_sub(1);
|
||||
}
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
let bit_code = p.rice.adaptive_rice_decode(true, PREDICT_K_ESCAPE, PREDICT_K_ESCBITS, PREDICT_K_MAX)?;
|
||||
let code = error_code_signed(bit_code);
|
||||
p.line_buf[1][p.line_pos] += p.rounded_bits_mask * 2 * code + (code >> 31);
|
||||
p.line_pos += 1;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a() + 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode a line which is not a top line
|
||||
/// This used run length coding, Median Edge Detection (MED) and
|
||||
/// adaptive Golomb-Rice entropy encoding.
|
||||
/// Golomb-Rice becomes more efficient when using an adaptive K value
|
||||
/// instead of a fixed one.
|
||||
/// The K parameter is used as q = n >> k where n is the sample to encode.
|
||||
#[allow(clippy::comparison_chain)]
|
||||
fn decode_nontop_line_rounded(&self, p: &mut BandParam) -> Result<()> {
|
||||
debug_assert_eq!(p.line_pos, 1);
|
||||
let mut remaining = p.subband_width as u32;
|
||||
let mut value_reached = false;
|
||||
p.line_buf[0][p.line_pos - 1] = p.coeff_b();
|
||||
p.line_buf[1][p.line_pos - 1] = p.coeff_b();
|
||||
// Loop over full width of line (backwards)
|
||||
while remaining > 1 {
|
||||
if (p.coeff_d() - p.coeff_b()).abs() > p.rounded_bits_mask {
|
||||
self.decode_symbol_rounded(p, true, true)?;
|
||||
value_reached = true;
|
||||
} else if value_reached || (p.coeff_c() - p.coeff_a()).abs() > p.rounded_bits_mask {
|
||||
self.decode_symbol_rounded(p, true, true)?;
|
||||
value_reached = false;
|
||||
} else {
|
||||
if p.rice.bitstream_get_bits(1)? == 1 {
|
||||
let n_syms = self.symbol_run_count(p, remaining)?;
|
||||
remaining = remaining.saturating_sub(n_syms);
|
||||
// copy symbol n_syms times
|
||||
for _ in 0..n_syms {
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a();
|
||||
p.line_pos += 1;
|
||||
}
|
||||
} // if bitstream == 1
|
||||
if remaining > 1 {
|
||||
self.decode_symbol_rounded(p, false, true)?;
|
||||
value_reached = (p.coeff_b() - p.coeff_c()).abs() > p.rounded_bits_mask;
|
||||
} else if remaining == 1 {
|
||||
self.decode_symbol_rounded(p, false, false)?;
|
||||
}
|
||||
}
|
||||
remaining = remaining.saturating_sub(1);
|
||||
} // end while length > 1
|
||||
// Remaining pixel?
|
||||
if remaining == 1 {
|
||||
self.decode_symbol_rounded(p, true, false)?;
|
||||
}
|
||||
debug_assert!(p.line_pos < p.line_buf[1].len());
|
||||
p.line_buf[1][p.line_pos] = p.coeff_a() + 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decode a single line from input band
|
||||
/// For decoding, two line buffers are required (except for the first line).
|
||||
/// After each decoding line, the two buffers are swapped, so the previous one
|
||||
/// is always in line_buf[0] (containing coefficents c, b, d) and the current
|
||||
/// line is in line_buf[1] (containing coefficents a, x, n).
|
||||
///
|
||||
/// The line buffers has an extra sample on both ends. So the buffer layout is:
|
||||
///
|
||||
/// |E|Samples........................|E|
|
||||
/// |c|bd cb|d|
|
||||
/// |a|xn ax|n|
|
||||
/// ^ ^ ^
|
||||
/// | | |-- Extra sample to provide fake d coefficent
|
||||
/// | |---- First sample value
|
||||
/// |------ Extra sample to provide a fake a/c coefficent
|
||||
///
|
||||
/// After line is decoded, the E samples are ignored when
|
||||
/// copied into the final plane buffer.
|
||||
///
|
||||
/// For non-LL bands, decoding process differs a little bit
|
||||
/// because some value rounding is added.
|
||||
pub(super) fn decode_line(&self, param: &mut BandParam) -> Result<()> {
|
||||
debug_assert!(param.cur_line < param.subband_height);
|
||||
// We start at first real pixel value
|
||||
param.line_pos = 1;
|
||||
if param.cur_line == 0 {
|
||||
param.s_param = 0;
|
||||
param.rice.set_k(0); // TODO: required?
|
||||
if param.supports_partial {
|
||||
if param.rounded_bits_mask <= 0 {
|
||||
self.decode_top_line(param)?;
|
||||
} else {
|
||||
param.rounded_bits = 1;
|
||||
if (param.rounded_bits_mask & !1) != 0 {
|
||||
while param.rounded_bits_mask >> param.rounded_bits != 0 {
|
||||
param.rounded_bits += 1;
|
||||
}
|
||||
}
|
||||
self.decode_top_line_rounded(param)?;
|
||||
}
|
||||
} else {
|
||||
self.decode_top_line_no_ref_prev_line(param)?;
|
||||
}
|
||||
} else if !param.supports_partial {
|
||||
// Swap line buffers so previous decoded (1) is now above (0)
|
||||
param.line_buf.swap(0, 1);
|
||||
self.decode_nontop_line_no_ref_prev_line(param)?;
|
||||
} else if param.rounded_bits_mask <= 0 {
|
||||
// Swap line buffers so previous decoded (1) is now above (0)
|
||||
param.line_buf.swap(0, 1);
|
||||
self.decode_nontop_line(param)?;
|
||||
} else {
|
||||
// Swap line buffers so previous decoded (1) is now above (0)
|
||||
param.line_buf.swap(0, 1);
|
||||
self.decode_nontop_line_rounded(param)?;
|
||||
}
|
||||
param.cur_line += 1;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Constrain a given value into min/max
|
||||
pub(super) fn constrain(value: i32, min: i32, max: i32) -> i32 {
|
||||
std::cmp::min(std::cmp::max(value, min), max)
|
||||
/*
|
||||
let res = if value < min {
|
||||
min
|
||||
} else if value > max {
|
||||
max
|
||||
} else {
|
||||
value
|
||||
};
|
||||
debug_assert!(res <= u16::MAX as i32);
|
||||
res
|
||||
*/
|
||||
}
|
||||
|
||||
/// The error code contains a sign bit at bit 0.
|
||||
/// Example: 10010 1 -> negative value, 10010 0 -> positive value
|
||||
/// This routine converts an unsigned bit_code to the correct
|
||||
/// signed integer value.
|
||||
/// For this, the sign bit is inverted and XOR with
|
||||
/// the shifted integer value.
|
||||
pub(super) fn error_code_signed(bit_code: u32) -> i32 {
|
||||
-((bit_code & 1) as i32) ^ (bit_code >> 1) as i32
|
||||
}
|
||||
|
||||
/// Median Edge Detection
|
||||
/// [0053] Obtains a predictive value p of the coefficient by using
|
||||
/// MED prediction, thereby performing predictive coding.
|
||||
pub(super) fn med(a: i32, b: i32, c: i32) -> i32 {
|
||||
if c >= std::cmp::max(a, b) {
|
||||
std::cmp::min(a, b)
|
||||
} else if c <= std::cmp::min(a, b) {
|
||||
std::cmp::max(a, b)
|
||||
} else {
|
||||
a + b - c // no edge detected
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a decoded line to plane output
|
||||
/// Results from decode_line() are signed 32 bit integers.
|
||||
/// By using a median and max value, these are converted
|
||||
/// to unsigned 16 bit integers.
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn convert_plane_line(codec: &CodecParams, l0: &[i32], l1: &[i32], l2: &[i32], l3: &[i32]) -> Result<(Vec<u16>, Vec<u16>, Vec<u16>, Vec<u16>)> {
|
||||
let mut p0 = vec![0; l0.len()];
|
||||
let mut p1 = vec![0; l1.len()];
|
||||
let mut p2 = vec![0; l2.len()];
|
||||
let mut p3 = vec![0; l3.len()];
|
||||
|
||||
match codec.enc_type {
|
||||
0 => {
|
||||
let median: i32 = 1 << (codec.median_bits - 1);
|
||||
let max_val: i32 = (1 << codec.median_bits) - 1;
|
||||
|
||||
izip!(l0, l1, l2, l3).enumerate().for_each(|(i, (v0, v1, v2, v3))| {
|
||||
p0[i] = constrain(median + v0, 0, max_val) as u16;
|
||||
p1[i] = constrain(median + v1, 0, max_val) as u16;
|
||||
p2[i] = constrain(median + v2, 0, max_val) as u16;
|
||||
p3[i] = constrain(median + v3, 0, max_val) as u16;
|
||||
});
|
||||
}
|
||||
3 => {
|
||||
let median: i32 = 1 << (codec.median_bits - 1) << 10;
|
||||
let max_val: i32 = (1 << codec.median_bits) - 1;
|
||||
|
||||
izip!(l0, l1, l2, l3).enumerate().for_each(|(i, (v0, v1, v2, v3))| {
|
||||
let mut gr: i32 = median + (v0 << 10) - 168 * v1 - 585 * v3;
|
||||
if gr < 0 {
|
||||
gr = -(((gr.abs() + 512) >> 9) & !1);
|
||||
} else {
|
||||
gr = ((gr.abs() + 512) >> 9) & !1;
|
||||
}
|
||||
p0[i] = constrain((median + (v0 << 10) + 1510 * v3 + 512) >> 10, 0, max_val) as u16;
|
||||
p1[i] = constrain((v2 + gr + 1) >> 1, 0, max_val) as u16;
|
||||
p2[i] = constrain((gr - v2 + 1) >> 1, 0, max_val) as u16;
|
||||
p3[i] = constrain((median + (v0 << 10) + 1927 * v1 + 512) >> 10, 0, max_val) as u16;
|
||||
});
|
||||
}
|
||||
enc_type => {
|
||||
return Err(CrxError::General(format!("Unsupported encoding type {}", enc_type)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((p0, p1, p2, p3))
|
||||
}
|
||||
|
||||
/// Integrate a plane buffer into CFA output image
|
||||
///
|
||||
/// A plane is a single monochrome image for one of the four CFA colors.
|
||||
/// `plane_id` is 0, 1, 2 or 3 for R, G1, G2, B
|
||||
fn integrate_cfa(codec: &CodecParams, tiles: &[Tile], cfa_buf: &mut [u16], tile_id: usize, plane_id: usize, plane_row: usize, plane_buf: &[u16]) -> Result<()> {
|
||||
// 2x2 pixel for RGGB
|
||||
const CFA_DIM: usize = 2;
|
||||
|
||||
debug_assert_ne!(plane_buf.len(), 0);
|
||||
debug_assert_ne!(cfa_buf.len(), 0);
|
||||
debug_assert!(codec.tile_cols > 0);
|
||||
debug_assert!(codec.tile_rows > 0);
|
||||
|
||||
if plane_id > 3 {
|
||||
return Err(CrxError::Overflow(format!(
|
||||
"More then 4 planes detected, unable to process plane_id {}",
|
||||
plane_id
|
||||
)));
|
||||
}
|
||||
|
||||
let tile_row_idx = tile_id / codec.tile_cols; // round down
|
||||
let tile_col_idx = tile_id % codec.tile_cols; // round down
|
||||
|
||||
// Offset from top
|
||||
let row_offset = tile_row_idx * codec.tile_width;
|
||||
// Offset from left
|
||||
let col_offset = tile_col_idx * codec.tile_width;
|
||||
let (row_shift, col_shift) = match plane_id {
|
||||
0 => (0, 0),
|
||||
1 => (0, 1),
|
||||
2 => (1, 0),
|
||||
3 => (1, 1),
|
||||
_ => {
|
||||
return Err(CrxError::General("Invalid plane id".to_string()));
|
||||
}
|
||||
};
|
||||
//println!("plane_width: {}, buf_size: {}", tiles[tile_id].plane_width, plane_buf.len());
|
||||
let row_idx = row_offset + (plane_row * CFA_DIM) + row_shift;
|
||||
for plane_col in 0..tiles[tile_id].plane_width {
|
||||
// Row index into CFA for untiled full area
|
||||
let col_idx = col_offset + (plane_col * CFA_DIM) + col_shift;
|
||||
|
||||
// Copy from plane to CFA
|
||||
cfa_buf[(row_idx * codec.image_width) + col_idx] = plane_buf[plane_col];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,570 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::{
|
||||
BandParam, CodecParams, Result,
|
||||
mdat::{Plane, Tile},
|
||||
};
|
||||
|
||||
/// This structure holds the inverse transformation state
|
||||
/// Each level has it's own state, so for 3 levels of DWT
|
||||
/// 3 instances are required.
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct WaveletTransform {
|
||||
/// Contains the decoded data from LL band
|
||||
/// or from a previous level decode.
|
||||
band0_buf: Vec<i32>,
|
||||
/// Contains the decoded data for HL band of current level
|
||||
band1_buf: Vec<i32>,
|
||||
/// Contains the decoded data for LH band of current level
|
||||
band2_buf: Vec<i32>,
|
||||
/// Contains the decoded data for HH band of current level
|
||||
band3_buf: Vec<i32>,
|
||||
/// 8 temporary buffers for inverse transformation (5/3?)
|
||||
band0_pos: usize,
|
||||
band1_pos: usize,
|
||||
band2_pos: usize,
|
||||
band3_pos: usize,
|
||||
|
||||
line_buf: [Vec<i32>; 8],
|
||||
/// Current line position
|
||||
cur_line: usize,
|
||||
/// TODO ???
|
||||
cur_h: usize,
|
||||
/// TODO ???
|
||||
flt_tap_h: usize,
|
||||
/// Height of the final image for the current level
|
||||
height: usize,
|
||||
/// Width of the final image for the current level
|
||||
width: usize,
|
||||
}
|
||||
|
||||
impl WaveletTransform {
|
||||
pub(crate) fn new(height: usize, width: usize) -> Self {
|
||||
// Line buffers for inverse transformation
|
||||
let line_buf = [
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
vec![0; width],
|
||||
];
|
||||
Self {
|
||||
// We use empty vectors, they will be replaced
|
||||
// with the result of a line decode.
|
||||
band0_buf: Vec::new(),
|
||||
band1_buf: Vec::new(),
|
||||
band2_buf: Vec::new(),
|
||||
band3_buf: Vec::new(),
|
||||
band0_pos: 0,
|
||||
band1_pos: 0,
|
||||
band2_pos: 0,
|
||||
band3_pos: 0,
|
||||
line_buf,
|
||||
cur_line: 0,
|
||||
cur_h: 0,
|
||||
flt_tap_h: 0,
|
||||
height,
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn getline(&mut self) -> &Vec<i32> {
|
||||
let result = &self.line_buf[(self.flt_tap_h as i32 - self.cur_h as i32 + 5) as usize % 5 + 3];
|
||||
debug_assert!(self.cur_h > 0);
|
||||
self.cur_h -= 1;
|
||||
result
|
||||
}
|
||||
|
||||
pub(super) fn band0(&mut self, offset: usize) -> i32 {
|
||||
self.band0_buf[self.band0_pos + offset]
|
||||
}
|
||||
|
||||
pub(super) fn band1(&mut self, offset: usize) -> i32 {
|
||||
self.band1_buf[self.band1_pos + offset]
|
||||
}
|
||||
|
||||
pub(super) fn band2(&mut self, offset: usize) -> i32 {
|
||||
self.band2_buf[self.band2_pos + offset]
|
||||
}
|
||||
|
||||
pub(super) fn band3(&mut self, offset: usize) -> i32 {
|
||||
self.band3_buf[self.band3_pos + offset]
|
||||
}
|
||||
|
||||
pub(super) fn advance_bufs(&mut self, count: usize) {
|
||||
self.band0_pos += count;
|
||||
self.band1_pos += count;
|
||||
self.band2_pos += count;
|
||||
self.band3_pos += count;
|
||||
}
|
||||
|
||||
pub(super) fn reset_bufs(&mut self) {
|
||||
self.band0_pos = 0;
|
||||
self.band1_pos = 0;
|
||||
self.band2_pos = 0;
|
||||
self.band3_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl CodecParams {
|
||||
pub(super) fn idwt_53_filter_decode(
|
||||
&self,
|
||||
tile: &Tile,
|
||||
plane: &Plane,
|
||||
params: &mut Vec<BandParam>,
|
||||
iwt_transforms: &mut Vec<WaveletTransform>,
|
||||
level: usize,
|
||||
) -> Result<()> {
|
||||
if iwt_transforms[level].cur_h > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let cur_band = 3 * level;
|
||||
let q_step_level = tile.q_step.as_ref().map(|f| &f[level]);
|
||||
|
||||
if iwt_transforms[level].height - 3 <= iwt_transforms[level].cur_line && !tile.tiles_bottom {
|
||||
if iwt_transforms[level].height & 1 == 1 {
|
||||
if level > 0 {
|
||||
self.idwt_53_filter_decode(tile, plane, params, iwt_transforms, level - 1)?;
|
||||
} else {
|
||||
let sband = &plane.subbands[cur_band];
|
||||
iwt_transforms[level].band0_buf = self.decode_line_with_iquantization(sband, &mut params[cur_band], q_step_level)?;
|
||||
}
|
||||
let sband = &plane.subbands[cur_band + 1];
|
||||
iwt_transforms[level].band1_buf = self.decode_line_with_iquantization(sband, &mut params[cur_band + 1], q_step_level)?;
|
||||
}
|
||||
} else {
|
||||
if level > 0 {
|
||||
self.idwt_53_filter_decode(tile, plane, params, iwt_transforms, level - 1)?;
|
||||
} else {
|
||||
// LL band
|
||||
let sband = &plane.subbands[cur_band];
|
||||
iwt_transforms[level].band0_buf = self.decode_line_with_iquantization(sband, &mut params[cur_band], q_step_level)?;
|
||||
}
|
||||
|
||||
// HL, LH and HH band
|
||||
iwt_transforms[level].band1_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 1], &mut params[cur_band + 1], q_step_level)?;
|
||||
iwt_transforms[level].band2_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 2], &mut params[cur_band + 2], q_step_level)?;
|
||||
iwt_transforms[level].band3_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 3], &mut params[cur_band + 3], q_step_level)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn idwt_53_horizontal(&self, tile: &Tile, la: usize, lb: usize, wvlt: &mut WaveletTransform) {
|
||||
//let mut b0pos = 0;
|
||||
//let mut b1pos = 0;
|
||||
//let mut b2pos = 0;
|
||||
//let mut b3pos = 0;
|
||||
let mut lapos = 0;
|
||||
let mut lbpos = 0;
|
||||
wvlt.reset_bufs();
|
||||
|
||||
if wvlt.width <= 1 {
|
||||
wvlt.line_buf[la][0] = wvlt.band0(0);
|
||||
wvlt.line_buf[lb][0] = wvlt.band2(0);
|
||||
} else {
|
||||
if tile.tiles_left {
|
||||
// Untested
|
||||
wvlt.line_buf[la][0] = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[lb][0] = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.band1_pos += 1;
|
||||
wvlt.band3_pos += 1;
|
||||
} else {
|
||||
wvlt.line_buf[la][0] = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
wvlt.line_buf[lb][0] = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
}
|
||||
wvlt.band0_pos += 1;
|
||||
wvlt.band2_pos += 1;
|
||||
|
||||
//println!("config: tile: {}, {}, band1 width: {}", tile.id, wvlt.width - 3, wvlt.band1_buf.len());
|
||||
for _i in (0..(wvlt.width - 3)).step_by(2) {
|
||||
//println!("val: {}, band1_pos: {}, band1_size: {}", _i, wvlt.band1_pos, wvlt.band1_buf.len());
|
||||
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[la][lapos + 1] = wvlt.band1(0) + ((delta + wvlt.line_buf[la][lapos]) >> 1);
|
||||
wvlt.line_buf[la][lapos + 2] = delta;
|
||||
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[lb][lbpos + 1] = wvlt.band3(0) + ((delta + wvlt.line_buf[lb][lbpos]) >> 1);
|
||||
wvlt.line_buf[lb][lbpos + 2] = delta;
|
||||
|
||||
wvlt.advance_bufs(1);
|
||||
|
||||
lapos += 2;
|
||||
lbpos += 2;
|
||||
}
|
||||
if tile.tiles_right {
|
||||
// Untested
|
||||
let delta_a = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[la][lapos + 1] = wvlt.band1(0) + ((delta_a + wvlt.line_buf[la][lapos + 0]) >> 1);
|
||||
|
||||
let delta_b = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[lb][lbpos + 1] = wvlt.band3(0) + ((delta_b + wvlt.line_buf[lb][lbpos + 0]) >> 1);
|
||||
|
||||
if wvlt.width & 1 == 1 {
|
||||
wvlt.line_buf[la][lapos + 2] = delta_a;
|
||||
wvlt.line_buf[lb][lbpos + 2] = delta_b;
|
||||
}
|
||||
} else if wvlt.width & 1 == 1 {
|
||||
wvlt.line_buf[la][lapos + 1] = wvlt.band1(0) + ((wvlt.line_buf[la][lapos] + wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1)) >> 1);
|
||||
wvlt.line_buf[la][lapos + 2] = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
|
||||
wvlt.line_buf[lb][lbpos + 1] = wvlt.band3(0) + ((wvlt.line_buf[lb][lbpos] + wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1)) >> 1);
|
||||
wvlt.line_buf[lb][lbpos + 2] = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
} else {
|
||||
wvlt.line_buf[la][lapos + 1] = wvlt.line_buf[la][lapos + 0] + wvlt.band1(0);
|
||||
wvlt.line_buf[lb][lbpos + 1] = wvlt.line_buf[lb][lbpos + 0] + wvlt.band3(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn idwt_53_filter_init(
|
||||
&self,
|
||||
tile: &Tile,
|
||||
plane: &Plane,
|
||||
params: &mut Vec<BandParam>,
|
||||
iwt_transforms: &mut Vec<WaveletTransform>,
|
||||
level: usize,
|
||||
) -> Result<()> {
|
||||
assert!(level > 0);
|
||||
if level == 0 {
|
||||
// This code is not called from pathes where level is 0. But we keep this check.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut cur_band = 0;
|
||||
for cur_level in 0..level {
|
||||
let q_step_level = tile.q_step.as_ref().map(|f| &f[cur_level]);
|
||||
|
||||
if cur_level > 0 {
|
||||
iwt_transforms[cur_level].band0_buf = iwt_transforms[cur_level - 1].getline().clone();
|
||||
} else {
|
||||
iwt_transforms[cur_level].band0_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band], &mut params[cur_band], q_step_level)?;
|
||||
}
|
||||
|
||||
let wvlt = &mut iwt_transforms[cur_level];
|
||||
|
||||
let h0 = wvlt.flt_tap_h + 3;
|
||||
if wvlt.height > 1 {
|
||||
wvlt.band1_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 1], &mut params[cur_band + 1], q_step_level)?;
|
||||
wvlt.band2_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 2], &mut params[cur_band + 2], q_step_level)?;
|
||||
wvlt.band3_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 3], &mut params[cur_band + 3], q_step_level)?;
|
||||
|
||||
let l0 = 0;
|
||||
let l1 = 1;
|
||||
let l2 = 2;
|
||||
let mut l2_pos = 0;
|
||||
|
||||
if tile.tiles_top {
|
||||
self.idwt_53_horizontal(tile, l0, 1, wvlt);
|
||||
wvlt.band3_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 3], &mut params[cur_band + 3], q_step_level)?;
|
||||
wvlt.band2_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 2], &mut params[cur_band + 2], q_step_level)?;
|
||||
|
||||
// process L band
|
||||
if wvlt.width <= 1 {
|
||||
wvlt.line_buf[l2][0] = wvlt.band2(0);
|
||||
} else {
|
||||
if tile.tiles_left {
|
||||
wvlt.line_buf[l2][0] = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.band3_pos += 1;
|
||||
} else {
|
||||
wvlt.line_buf[l2][0] = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
}
|
||||
wvlt.band2_pos += 1;
|
||||
|
||||
for _i in (0..wvlt.width - 3).step_by(2) {
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[l2][1] = wvlt.band3(0) + ((wvlt.line_buf[l2][l2_pos] + delta) >> 1);
|
||||
wvlt.line_buf[l2][2] = delta;
|
||||
wvlt.band2_pos += 1;
|
||||
wvlt.band3_pos += 1;
|
||||
l2_pos += 2;
|
||||
}
|
||||
|
||||
if tile.tiles_right {
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[l2][l2_pos + 1] = wvlt.band3(0) + ((wvlt.line_buf[l2][l2_pos + 0] + delta) >> 1);
|
||||
if wvlt.width & 1 == 1 {
|
||||
wvlt.line_buf[l2][l2_pos + 1] = delta;
|
||||
}
|
||||
} else if wvlt.width & 1 == 1 {
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
wvlt.line_buf[l2][l2_pos + 1] = wvlt.band3(0) + ((wvlt.line_buf[l2][l2_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[l2][l2_pos + 2] = delta;
|
||||
} else {
|
||||
wvlt.line_buf[l2][l2_pos + 1] = wvlt.band3(0) + wvlt.line_buf[l2][l2_pos + 0];
|
||||
}
|
||||
}
|
||||
|
||||
// process H band
|
||||
for i in 0..wvlt.width {
|
||||
wvlt.line_buf[h0][i] = wvlt.line_buf[l0][i] - ((wvlt.line_buf[l1][i] + wvlt.line_buf[l2][i] + 2) >> 2);
|
||||
}
|
||||
} else {
|
||||
self.idwt_53_horizontal(tile, l0, 2, wvlt);
|
||||
for i in 0..wvlt.width {
|
||||
wvlt.line_buf[h0][i] = wvlt.line_buf[l0][i] - ((wvlt.line_buf[l2][i] + 1) >> 1);
|
||||
}
|
||||
self.idwt_53_filter_decode(tile, plane, params, iwt_transforms, cur_level)?;
|
||||
self.idwt_53_filter_transform(tile, plane, params, iwt_transforms, cur_level)?;
|
||||
}
|
||||
} else {
|
||||
// This is unused in real world
|
||||
|
||||
wvlt.band1_buf = self.decode_line_with_iquantization(&plane.subbands[cur_band + 1], &mut params[cur_band + 1], q_step_level)?;
|
||||
let mut h0_pos = 0;
|
||||
|
||||
// process H band
|
||||
if wvlt.width <= 1 {
|
||||
wvlt.line_buf[h0][0] = wvlt.band0(0);
|
||||
} else {
|
||||
if tile.tiles_left {
|
||||
wvlt.line_buf[h0][h0_pos] = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.band1_pos += 1;
|
||||
} else {
|
||||
wvlt.line_buf[h0][h0_pos] = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
}
|
||||
wvlt.band0_pos += 1;
|
||||
|
||||
for _i in (0..(wvlt.width - 3)).step_by(2) {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[h0][h0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[h0][h0_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[h0][h0_pos + 2] = delta;
|
||||
wvlt.band0_pos += 1;
|
||||
wvlt.band1_pos += 1;
|
||||
h0_pos += 2;
|
||||
}
|
||||
|
||||
if tile.tiles_right {
|
||||
// untested
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[h0][h0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[h0][h0_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[h0][h0_pos + 2] = delta;
|
||||
} else if wvlt.width & 1 == 1 {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
wvlt.line_buf[h0][h0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[h0][h0_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[h0][h0_pos + 2] = delta;
|
||||
} else {
|
||||
wvlt.line_buf[h0][h0_pos + 1] = wvlt.band1(0) + wvlt.line_buf[h0][h0_pos + 0];
|
||||
}
|
||||
}
|
||||
wvlt.cur_line += 1;
|
||||
wvlt.cur_h += 1;
|
||||
wvlt.flt_tap_h = (wvlt.flt_tap_h + 1) % 5;
|
||||
}
|
||||
cur_band += 3;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn idwt_53_filter_transform(
|
||||
&self,
|
||||
tile: &Tile,
|
||||
plane: &Plane,
|
||||
params: &mut Vec<BandParam>,
|
||||
iwt_transforms: &mut Vec<WaveletTransform>,
|
||||
level: usize,
|
||||
) -> Result<()> {
|
||||
if iwt_transforms[level].cur_h > 0 {
|
||||
return Ok(());
|
||||
}
|
||||
if iwt_transforms[level].cur_line >= iwt_transforms[level].height - 3 {
|
||||
if !tile.tiles_bottom {
|
||||
if iwt_transforms[level].height & 1 == 1 {
|
||||
if level > 0 {
|
||||
if iwt_transforms[level - 1].cur_h == 0 {
|
||||
self.idwt_53_filter_transform(tile, plane, params, iwt_transforms, level - 1)?;
|
||||
}
|
||||
iwt_transforms[level].band0_buf = iwt_transforms[level - 1].getline().clone();
|
||||
}
|
||||
let wvlt = &mut iwt_transforms[level];
|
||||
wvlt.reset_bufs();
|
||||
let h0 = wvlt.flt_tap_h + 3;
|
||||
let h1 = (wvlt.flt_tap_h + 1) % 5 + 3;
|
||||
let h2 = (wvlt.flt_tap_h + 2) % 5 + 3;
|
||||
let l0 = 0;
|
||||
let l1 = 1;
|
||||
let mut l0_pos = 0;
|
||||
//let mut l1_pos = 0;
|
||||
|
||||
// process L bands
|
||||
if wvlt.width <= 1 {
|
||||
wvlt.line_buf[l0][0] = wvlt.band0(0);
|
||||
} else {
|
||||
if tile.tiles_left {
|
||||
// untested
|
||||
wvlt.line_buf[l0][l0_pos] = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.band1_pos += 1;
|
||||
} else {
|
||||
wvlt.line_buf[l0][l0_pos] = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
}
|
||||
wvlt.band0_pos += 1;
|
||||
for _i in (0..(wvlt.width - 3)).step_by(2) {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[l0][l0_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta;
|
||||
wvlt.band0_pos += 1;
|
||||
wvlt.band1_pos += 1;
|
||||
l0_pos += 2;
|
||||
}
|
||||
if tile.tiles_right {
|
||||
// untested
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[l0][l0_pos + 0] + delta) >> 1);
|
||||
if wvlt.width & 1 == 1 {
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta;
|
||||
}
|
||||
} else if wvlt.width & 1 == 1 {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((wvlt.line_buf[l0][l0_pos + 0] + delta) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta;
|
||||
} else {
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + wvlt.line_buf[l0][l0_pos + 0];
|
||||
}
|
||||
}
|
||||
|
||||
// process H bands
|
||||
//wvlt.reset_bufs();
|
||||
|
||||
wvlt.line_buf.swap(1, 2);
|
||||
|
||||
for i in 0..wvlt.width {
|
||||
let delta = wvlt.line_buf[l0][i] - ((wvlt.line_buf[l1][i] + 1) >> 1);
|
||||
wvlt.line_buf[h1][i] = wvlt.line_buf[l1][i] + ((delta + wvlt.line_buf[h0][i]) >> 1);
|
||||
wvlt.line_buf[h2][i] = delta;
|
||||
}
|
||||
wvlt.cur_h += 3;
|
||||
wvlt.cur_line += 3;
|
||||
wvlt.flt_tap_h = (wvlt.flt_tap_h + 3) % 5;
|
||||
} else {
|
||||
let wvlt = &mut iwt_transforms[level];
|
||||
let l2 = 2;
|
||||
let h0 = wvlt.flt_tap_h + 3;
|
||||
let h1 = (wvlt.flt_tap_h + 1) % 5 + 3;
|
||||
|
||||
for i in 0..wvlt.width {
|
||||
wvlt.line_buf[h1][i] = wvlt.line_buf[h0][i] + wvlt.line_buf[l2][i];
|
||||
}
|
||||
|
||||
// The original libraw CRX decoder copies the pointer from line_buf[2] to [1].
|
||||
// But it doesn't makes sense, so we swap the buffers as we do on other locations.
|
||||
wvlt.line_buf.swap(1, 2);
|
||||
//wvlt.line_buf[1] = wvlt.line_buf[2].clone();
|
||||
|
||||
wvlt.cur_h += 2;
|
||||
wvlt.cur_line += 2;
|
||||
wvlt.flt_tap_h = (wvlt.flt_tap_h + 2) % 5;
|
||||
}
|
||||
} // end if !tile.tiles_bottom
|
||||
} else {
|
||||
if level > 0 {
|
||||
if iwt_transforms[level - 1].cur_h == 0 {
|
||||
self.idwt_53_filter_transform(tile, plane, params, iwt_transforms, level - 1)?;
|
||||
}
|
||||
iwt_transforms[level].band0_buf = iwt_transforms[level - 1].getline().clone();
|
||||
}
|
||||
let wvlt = &mut iwt_transforms[level];
|
||||
|
||||
wvlt.reset_bufs();
|
||||
|
||||
let l0 = 0;
|
||||
let l1 = 1;
|
||||
//let l2 = 2;
|
||||
let mut l0_pos = 0;
|
||||
let mut l1_pos = 0;
|
||||
//let mut l2_pos = 0;
|
||||
let h0 = wvlt.flt_tap_h + 3;
|
||||
let h1 = (wvlt.flt_tap_h + 1) % 5 + 3;
|
||||
let h2 = (wvlt.flt_tap_h + 2) % 5 + 3;
|
||||
|
||||
// process L bands
|
||||
if wvlt.width <= 1 {
|
||||
wvlt.line_buf[l0][0] = wvlt.band0(0);
|
||||
wvlt.line_buf[l1][0] = wvlt.band2(0);
|
||||
} else {
|
||||
// untested
|
||||
if tile.tiles_left {
|
||||
wvlt.line_buf[l0][0] = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[l1][0] = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.band1_pos += 1;
|
||||
wvlt.band3_pos += 1;
|
||||
} else {
|
||||
wvlt.line_buf[l0][0] = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
wvlt.line_buf[l1][0] = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
}
|
||||
wvlt.band0_pos += 1;
|
||||
wvlt.band2_pos += 1;
|
||||
for _i in (0..(wvlt.width - 3)).step_by(2) {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((delta + wvlt.line_buf[l0][l0_pos + 0]) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta;
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[l1][l1_pos + 1] = wvlt.band3(0) + ((delta + wvlt.line_buf[l1][l1_pos + 0]) >> 1);
|
||||
wvlt.line_buf[l1][l1_pos + 2] = delta;
|
||||
wvlt.advance_bufs(1);
|
||||
l0_pos += 2;
|
||||
l1_pos += 2;
|
||||
}
|
||||
if tile.tiles_right {
|
||||
// untested
|
||||
let delta_a = wvlt.band0(0) - ((wvlt.band1(0) + wvlt.band1(1) + 2) >> 2);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((delta_a + wvlt.line_buf[l0][l0_pos + 0]) >> 1);
|
||||
|
||||
let delta_b = wvlt.band2(0) - ((wvlt.band3(0) + wvlt.band3(1) + 2) >> 2);
|
||||
wvlt.line_buf[l1][l1_pos + 1] = wvlt.band3(0) + ((delta_b + wvlt.line_buf[l1][l1_pos + 0]) >> 1);
|
||||
|
||||
if wvlt.width & 1 == 1 {
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta_a;
|
||||
wvlt.line_buf[l1][l1_pos + 2] = delta_b;
|
||||
}
|
||||
} else if wvlt.width & 1 == 1 {
|
||||
let delta = wvlt.band0(0) - ((wvlt.band1(0) + 1) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.band1(0) + ((delta + wvlt.line_buf[l0][l0_pos + 0]) >> 1);
|
||||
wvlt.line_buf[l0][l0_pos + 2] = delta;
|
||||
|
||||
let delta = wvlt.band2(0) - ((wvlt.band3(0) + 1) >> 1);
|
||||
wvlt.line_buf[l1][l1_pos + 1] = wvlt.band3(0) + ((delta + wvlt.line_buf[l1][l1_pos + 0]) >> 1);
|
||||
wvlt.line_buf[l1][l1_pos + 2] = delta;
|
||||
} else {
|
||||
wvlt.line_buf[l0][l0_pos + 1] = wvlt.line_buf[l0][l0_pos] + wvlt.band1(0);
|
||||
wvlt.line_buf[l1][l1_pos + 1] = wvlt.line_buf[l1][l1_pos] + wvlt.band3(0);
|
||||
}
|
||||
}
|
||||
|
||||
// process H bands
|
||||
let wvlt = &mut iwt_transforms[level];
|
||||
|
||||
wvlt.line_buf.swap(1, 2);
|
||||
|
||||
let l0 = 0;
|
||||
let l1 = 1;
|
||||
let l2 = 2;
|
||||
for i in 0..wvlt.width {
|
||||
let delta = wvlt.line_buf[l0][i] - ((wvlt.line_buf[l2][i] + wvlt.line_buf[l1][i] + 2) >> 2);
|
||||
wvlt.line_buf[h1][i] = wvlt.line_buf[l1][i] + ((delta + wvlt.line_buf[h0][i]) >> 1);
|
||||
wvlt.line_buf[h2][i] = delta;
|
||||
}
|
||||
|
||||
if iwt_transforms[level].cur_line >= iwt_transforms[level].height - 3 && iwt_transforms[level].height & 1 == 1 {
|
||||
iwt_transforms[level].cur_h += 3;
|
||||
iwt_transforms[level].cur_line += 3;
|
||||
iwt_transforms[level].flt_tap_h = (iwt_transforms[level].flt_tap_h + 3) % 5;
|
||||
} else {
|
||||
iwt_transforms[level].cur_h += 2;
|
||||
iwt_transforms[level].cur_line += 2;
|
||||
iwt_transforms[level].flt_tap_h = (iwt_transforms[level].flt_tap_h + 2) % 5;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::{
|
||||
BandParam, CodecParams, CrxError, Result,
|
||||
decoder::constrain,
|
||||
mdat::{Subband, Tile},
|
||||
};
|
||||
use crate::decompressors::crx::{decoder::error_code_signed, rice::RiceDecoder};
|
||||
use bitstream_io::BitReader;
|
||||
use log::warn;
|
||||
use std::io::Cursor;
|
||||
|
||||
/// QStep table for QP [0,1,2,3,4,5]
|
||||
#[rustfmt::skip]
|
||||
pub(super) const Q_STEP_TBL: [u32; 6] = [0x28, 0x2D, 0x33, 0x39, 0x40, 0x48];
|
||||
|
||||
/// Holds the QStep information for a tile
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(unused)]
|
||||
pub struct QStep {
|
||||
/// QStep tables for each compression level
|
||||
pub q_step_tbl: Vec<u32>,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
}
|
||||
|
||||
impl QStep {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
q_step_tbl: Vec::with_capacity(width * height),
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodecParams {
|
||||
/// Update Q parameter.
|
||||
/// Seems not to be used in real world (untested).
|
||||
pub(super) fn update_q_param(_band: &Subband, param: &mut BandParam) -> Result<()> {
|
||||
warn!("Untested routine, please send in a sample file");
|
||||
let bit_code = param.rice.adaptive_rice_decode(true, 23, 8, 0)?;
|
||||
param.q_param = ((param.q_param as i32) + error_code_signed(bit_code)) as u32;
|
||||
if param.rice.k() > 7 {
|
||||
Err(CrxError::General(format!(
|
||||
"Overflow while updating Q parameter: K is out of range: {}",
|
||||
param.rice.k()
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode line with inverse quantization
|
||||
pub(super) fn decode_line_with_iquantization(&self, band: &Subband, param: &mut BandParam, q_step: Option<&QStep>) -> super::Result<Vec<i32>> {
|
||||
if band.data_size == 0 {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// only LL bands has support_partial, but quantization is not applied to
|
||||
// LL bands. So this never happen in real world.
|
||||
if band.support_partial && q_step.is_none() {
|
||||
debug_assert_eq!(1, 2); // make sure we detect such files, then this statement can be removed
|
||||
Self::update_q_param(band, param)?;
|
||||
}
|
||||
|
||||
// Entropy decode the current line, then apply inverse quantization
|
||||
self.decode_line(param)?;
|
||||
|
||||
match q_step {
|
||||
Some(q_step) => {
|
||||
// new version
|
||||
let q_step_tbl_ptr = &q_step.q_step_tbl[(q_step.width * band.get_subband_row(param.cur_line - 1))..];
|
||||
|
||||
for i in 0..band.col_start_addon {
|
||||
let quant_val = band.q_step_base + ((q_step_tbl_ptr[0] * band.q_step_multi as u32) >> 3) as i32;
|
||||
param.decoded_buf_mut()[i] *= constrain(quant_val, 1, 0x168000);
|
||||
}
|
||||
|
||||
for i in band.col_start_addon..(band.width - band.col_end_addon) {
|
||||
let idx = (i - band.col_start_addon) >> band.level_shift;
|
||||
let quant_val = band.q_step_base + ((q_step_tbl_ptr[idx] * band.q_step_multi as u32) >> 3) as i32;
|
||||
//eprintln!("{}", quant_val);
|
||||
param.decoded_buf_mut()[i] *= constrain(quant_val, 1, 0x168000);
|
||||
}
|
||||
|
||||
let last_idx = (band.width - band.col_end_addon - band.col_start_addon - 1) >> band.level_shift;
|
||||
|
||||
for i in (band.width - band.col_end_addon)..band.width {
|
||||
let quant_val = band.q_step_base + ((q_step_tbl_ptr[last_idx] * band.q_step_multi as u32) >> 3) as i32;
|
||||
param.decoded_buf_mut()[i] *= constrain(quant_val, 1, 0x168000);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
//eprintln!("q-param: {}", param.q_param);
|
||||
// prev. version
|
||||
let q_scale = if param.q_param / 6 >= 6 {
|
||||
Q_STEP_TBL[param.q_param as usize % 6] * (1 << (param.q_param / 6 + 26))
|
||||
} else {
|
||||
Q_STEP_TBL[param.q_param as usize % 6] >> (6 - param.q_param / 6)
|
||||
};
|
||||
// Optimization: if scale is 1, no multiplication is required
|
||||
if q_scale != 1 {
|
||||
//println!("scale width: {}", band.width);
|
||||
for i in 0..band.width {
|
||||
param.decoded_buf_mut()[i] *= q_scale as i32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Vec::from(param.decoded_buf()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
/// Predict symbol for QP table
|
||||
/// This uses MED but depending on the column position,
|
||||
/// b-c or d-b is used as delta_h
|
||||
fn predict_qp_symbol(left: i32, top: i32, delta_h: i32, delta_v: i32) -> i32 {
|
||||
match ((delta_v < 0) ^ (delta_h < 0), (left < top) ^ (delta_h < 0)) {
|
||||
(false, false) | (false, true) => left + delta_h,
|
||||
(true, false) => left,
|
||||
(true, true) => top,
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the QStep table out of the QP table
|
||||
fn make_qstep(&self, params: &CodecParams, qp_table: Vec<i32>) -> Result<Vec<QStep>> {
|
||||
debug_assert!(params.levels <= 3 && params.levels > 0);
|
||||
let qp_width = (self.plane_width >> 3) + if self.plane_width & 7 != 0 { 1 } else { 0 };
|
||||
let qp_height = (self.plane_height >> 1) + (self.plane_height & 1);
|
||||
let qp_height4 = (self.plane_height >> 2) + if self.plane_height & 3 > 0 { 1 } else { 0 };
|
||||
let qp_height8 = (self.plane_height >> 3) + if self.plane_height & 7 > 0 { 1 } else { 0 };
|
||||
|
||||
let mut q_steps = Vec::with_capacity(params.levels as usize);
|
||||
|
||||
// Lookup function into Q_STEP_TBL
|
||||
let q_lookup = |quant_val: i32| -> u32 {
|
||||
//eprintln!("quant_val: {quant_val}");
|
||||
if quant_val / 6 >= 6 {
|
||||
// Original code uses obscure calculation:
|
||||
//
|
||||
// Q_STEP_TBL[quant_val as usize % 6] * (1 << (quant_val as u32 / 6 + 26))
|
||||
//
|
||||
// But this branch is only selected when (quant_val / 6) is >= 6, so the bit shift count
|
||||
// is always 6 + 26 = 32 or even higher!
|
||||
// The shl operand is a 32 bit value, so maximum count for shift is 31. x86 processors do mask
|
||||
// the shift count to 0x1F, so this calculation would lead to 0 - which produces
|
||||
// artifacts in decompressed image.
|
||||
//
|
||||
// To fix these artifacts and shl overflow, we skip the multiplication
|
||||
// and use wrapping_shl() which auto-apply bit masking.
|
||||
Q_STEP_TBL[quant_val as usize % 6].wrapping_shl(quant_val as u32 / 6 + 26)
|
||||
} else {
|
||||
Q_STEP_TBL[quant_val as usize % 6] >> (6 - quant_val / 6)
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate 3, 2, 1
|
||||
for level in (1..=params.levels).rev() {
|
||||
match level {
|
||||
3 => {
|
||||
let mut q_step = QStep::new(qp_width, qp_height8);
|
||||
for qp_row in 0..qp_height8 {
|
||||
let mut row0_idx = qp_width * std::cmp::min(4 * qp_row + 0, qp_height - 1);
|
||||
let mut row1_idx = qp_width * std::cmp::min(4 * qp_row + 1, qp_height - 1);
|
||||
let mut row2_idx = qp_width * std::cmp::min(4 * qp_row + 2, qp_height - 1);
|
||||
let mut row3_idx = qp_width * std::cmp::min(4 * qp_row + 3, qp_height - 1);
|
||||
for _qp_col in 0..qp_width {
|
||||
let qp_sum = qp_table[row0_idx] + qp_table[row1_idx] + qp_table[row2_idx] + qp_table[row3_idx];
|
||||
let quant_val = if qp_sum.is_negative() {
|
||||
(qp_sum + 3) / 4 // Round?
|
||||
} else {
|
||||
qp_sum / 4
|
||||
};
|
||||
|
||||
let x = q_lookup(quant_val);
|
||||
//eprintln!("QSTEP 8: {:?}", x);
|
||||
q_step.q_step_tbl.push(x);
|
||||
row0_idx += 1;
|
||||
row1_idx += 1;
|
||||
row2_idx += 1;
|
||||
row3_idx += 1;
|
||||
}
|
||||
}
|
||||
debug_assert_eq!(q_step.q_step_tbl.len(), qp_width * qp_height8);
|
||||
|
||||
q_steps.push(q_step);
|
||||
}
|
||||
2 => {
|
||||
let mut q_step = QStep::new(qp_width, qp_height4);
|
||||
for qp_row in 0..qp_height4 {
|
||||
let mut row0_idx = qp_width * std::cmp::min(2 * qp_row + 0, qp_height - 1);
|
||||
let mut row1_idx = qp_width * std::cmp::min(2 * qp_row + 1, qp_height - 1);
|
||||
for _qp_col in 0..qp_width {
|
||||
let quant_val = (qp_table[row0_idx] + qp_table[row1_idx]) / 2;
|
||||
let x = q_lookup(quant_val);
|
||||
//eprintln!("QSTEP 4: {:?}", x);
|
||||
q_step.q_step_tbl.push(x);
|
||||
row0_idx += 1;
|
||||
row1_idx += 1;
|
||||
}
|
||||
}
|
||||
debug_assert_eq!(q_step.q_step_tbl.len(), qp_width * qp_height4);
|
||||
//eprintln!("4: {:?}, {:?}", q_step.q_step_tbl[405], q_step.q_step_tbl[8433]);
|
||||
q_steps.push(q_step);
|
||||
}
|
||||
1 => {
|
||||
//println!("1 qp_height: {}, qp_width: {}", qp_height, qp_width);
|
||||
let mut q_step = QStep::new(qp_width, qp_height);
|
||||
for qp_row in 0..qp_height {
|
||||
for qp_col in 0..qp_width {
|
||||
let quant_val = qp_table[(qp_row * qp_width) + qp_col];
|
||||
let x = q_lookup(quant_val);
|
||||
//eprintln!("QSTEP 0: {:?}", x);
|
||||
q_step.q_step_tbl.push(x);
|
||||
}
|
||||
}
|
||||
debug_assert_eq!(q_step.q_step_tbl.len(), qp_width * qp_height);
|
||||
q_steps.push(q_step);
|
||||
}
|
||||
_ => {
|
||||
return Err(CrxError::General(format!("Unsupported level while generating qstep data: {}", level)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(q_steps)
|
||||
}
|
||||
|
||||
pub(super) fn generate_qstep_table(&mut self, params: &CodecParams, data: &[u8]) -> Result<()> {
|
||||
match self.qp_data.as_ref() {
|
||||
Some(qp_data) => {
|
||||
//println!("generate: size: {}", qp_data.mdat_qp_data_size);
|
||||
let mdat_qp = &data[self.data_offset..self.data_offset + qp_data.mdat_qp_data_size as usize];
|
||||
let bitpump = BitReader::endian(Cursor::new(mdat_qp), bitstream_io::BigEndian);
|
||||
let mut rice = RiceDecoder::new(bitpump);
|
||||
|
||||
let qp_width = (self.plane_width >> 3) + if self.plane_width & 7 != 0 { 1 } else { 0 };
|
||||
let qp_height = (self.plane_height >> 1) + (self.plane_height & 1);
|
||||
let total_qp = qp_width * qp_height;
|
||||
|
||||
//eprintln!("tile: {} {}", self.width, self.height);
|
||||
//eprintln!("qp_width: {}, qp_height: {}, total_qp: {}", qp_width, qp_height, total_qp);
|
||||
|
||||
// Line length is width + one additional pixel at start end end (same as for pixel decoding)
|
||||
let line_len = 1 + qp_width + 1;
|
||||
let mut line_buf = [vec![0; line_len], vec![0; line_len]];
|
||||
let mut qp_table = vec![0; total_qp];
|
||||
|
||||
for qp_row in 0..qp_height {
|
||||
let mut line_pos = 1; // start at first real coeff x (skip a)
|
||||
if qp_row == 0 {
|
||||
// For first top row
|
||||
line_buf[1][line_pos - 1] = 0; // init coeff a
|
||||
for _ in (0..qp_width).rev() {
|
||||
let x = line_buf[1][line_pos - 1]; // x = a
|
||||
let qp = rice.adaptive_rice_decode(true, 23, 8, 7)?;
|
||||
line_buf[1][line_pos] = x + error_code_signed(qp);
|
||||
line_pos += 1;
|
||||
}
|
||||
line_buf[1][line_pos] = line_buf[1][line_pos - 1] + 1;
|
||||
} else {
|
||||
// For all other rows
|
||||
line_buf[1][line_pos - 1] = line_buf[0][line_pos]; // init coeff a = b
|
||||
// delta_h = b-c
|
||||
let mut delta_h = line_buf[0][line_pos] - line_buf[0][line_pos - 1];
|
||||
for width in (0..qp_width).rev() {
|
||||
let a = line_buf[1][line_pos - 1];
|
||||
let b = line_buf[0][line_pos];
|
||||
let c = line_buf[0][line_pos - 1];
|
||||
let d = line_buf[0][line_pos + 1];
|
||||
let x = Self::predict_qp_symbol(a, b, delta_h, c - a);
|
||||
let qp = rice.adaptive_rice_decode(false, 23, 8, 0)?;
|
||||
line_buf[1][line_pos] = x + error_code_signed(qp);
|
||||
if width > 0 {
|
||||
delta_h = d - b;
|
||||
rice.update_k_param((qp + 2 * delta_h.unsigned_abs()) >> 1, 7);
|
||||
} else {
|
||||
rice.update_k_param(qp, 7);
|
||||
}
|
||||
line_pos += 1;
|
||||
}
|
||||
line_buf[1][line_pos] = line_buf[1][line_pos - 1] + 1;
|
||||
}
|
||||
|
||||
for qp_col in 0..qp_width {
|
||||
qp_table[(qp_row * qp_width) + qp_col] = line_buf[1][qp_col + 1] + 4;
|
||||
}
|
||||
line_buf.swap(0, 1);
|
||||
}
|
||||
|
||||
//for qp in &qp_table {
|
||||
//eprintln!("QP: {}", qp);
|
||||
//}
|
||||
//eprintln!("QP: {:?}", &qp_table[..]);
|
||||
|
||||
self.q_step = Some(self.make_qstep(params, qp_table)?);
|
||||
Ok(())
|
||||
}
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::{Result, iquant::QStep};
|
||||
use crate::decompressors::crx::CrxError;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Tile {
|
||||
// Header fields
|
||||
pub ind: u16,
|
||||
pub size: u16,
|
||||
pub tile_size: usize,
|
||||
pub flags: u32,
|
||||
pub qp_data: Option<TileQPData>,
|
||||
// Calculated fields
|
||||
pub id: usize,
|
||||
pub counter: u32,
|
||||
pub tail_sign: u32,
|
||||
/// Offset of tile data relative to mdat header end
|
||||
pub data_offset: usize,
|
||||
pub tile_width: usize,
|
||||
pub tile_height: usize,
|
||||
pub plane_width: usize,
|
||||
pub plane_height: usize,
|
||||
pub tiles_top: bool,
|
||||
pub tiles_bottom: bool,
|
||||
pub tiles_left: bool,
|
||||
pub tiles_right: bool,
|
||||
/// Planes for tile
|
||||
pub planes: Vec<Plane>,
|
||||
/// QStep table for this tile and for each level (1, 2, 3)
|
||||
pub q_step: Option<Vec<QStep>>,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
pub fn new<R: Read>(id: usize, hdr: &mut R, ind: u16, tile_offset: usize) -> Result<Self> {
|
||||
let size = hdr.read_u16::<BigEndian>()?;
|
||||
let tile_size = hdr.read_u32::<BigEndian>()? as usize;
|
||||
let flags = hdr.read_u32::<BigEndian>()?;
|
||||
//let counter = flags >> 28;
|
||||
let counter = (flags >> 16) & 0xF;
|
||||
let tail_sign = flags & 0xFFFF;
|
||||
let qp_data = if size == 16 {
|
||||
let mdat_qp_data_size = hdr.read_u32::<BigEndian>()?;
|
||||
let mdat_extra_size = hdr.read_u16::<BigEndian>()?;
|
||||
let terminator = hdr.read_u16::<BigEndian>()?;
|
||||
assert!(terminator == 0);
|
||||
Some(TileQPData {
|
||||
mdat_qp_data_size,
|
||||
mdat_extra_size,
|
||||
terminator,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// TODO check on release
|
||||
assert!((size == 8 && tail_sign == 0) || (size == 16 && tail_sign == 0x4000));
|
||||
|
||||
Ok(Tile {
|
||||
id,
|
||||
ind,
|
||||
size,
|
||||
tile_size,
|
||||
flags,
|
||||
counter,
|
||||
tail_sign,
|
||||
data_offset: tile_offset,
|
||||
planes: vec![],
|
||||
tile_height: 0,
|
||||
tile_width: 0,
|
||||
plane_height: 0,
|
||||
plane_width: 0,
|
||||
qp_data,
|
||||
tiles_top: false,
|
||||
tiles_bottom: false,
|
||||
tiles_left: false,
|
||||
tiles_right: false,
|
||||
q_step: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn descriptor_line(&self) -> String {
|
||||
let extra_data = match self.qp_data.as_ref() {
|
||||
Some(qp_data) => {
|
||||
format!(
|
||||
" qp_data_size: {:#x} extra_size: {:#x}, terminator: {:#x}",
|
||||
qp_data.mdat_qp_data_size, qp_data.mdat_extra_size, qp_data.terminator
|
||||
)
|
||||
}
|
||||
None => String::from("NONE"),
|
||||
};
|
||||
format!(
|
||||
"Tile {:#x} size: {:#x} tile_size: {:#x} flags: {:#x} counter: {:#x} tail_sign: {:#x} extra: {}\n top: {}, left: {}, bottom: {}, right: {}",
|
||||
self.ind,
|
||||
self.size,
|
||||
self.tile_size,
|
||||
self.flags,
|
||||
self.counter,
|
||||
self.tail_sign,
|
||||
extra_data,
|
||||
self.tiles_top,
|
||||
self.tiles_left,
|
||||
self.tiles_bottom,
|
||||
self.tiles_right,
|
||||
//mdatQPDataSize.unwrap_or_default()
|
||||
)
|
||||
}
|
||||
|
||||
/// Tile may contain some extra data for quantization
|
||||
pub fn extra_size(&self) -> usize {
|
||||
match self.qp_data.as_ref() {
|
||||
Some(qp_data) => qp_data.mdat_qp_data_size as usize + qp_data.mdat_extra_size as usize,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TileQPData {
|
||||
/// Size in bytes of QP data for version 0x200
|
||||
pub mdat_qp_data_size: u32,
|
||||
/// Unused bytes to extend tile size to 0x8 boundary
|
||||
pub mdat_extra_size: u16,
|
||||
/// 0 - Terminator
|
||||
pub terminator: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub struct Plane {
|
||||
// Header fields
|
||||
pub ind: u16,
|
||||
pub size: u16,
|
||||
pub plane_size: usize,
|
||||
pub flags: u32,
|
||||
// Calculated fields
|
||||
pub id: usize,
|
||||
pub counter: u32,
|
||||
pub support_partial: bool,
|
||||
/// Rounded bits mask - only used for level=0 images
|
||||
/// with suuport_partial=true
|
||||
pub rounded_bits_mask: i32,
|
||||
pub data_offset: usize,
|
||||
pub parent_offset: usize,
|
||||
/// List of subbands
|
||||
pub subbands: Vec<Subband>,
|
||||
}
|
||||
|
||||
impl Plane {
|
||||
pub fn new<R: Read>(id: usize, hdr: &mut R, ind: u16, parent_offset: usize, plane_offset: usize) -> Result<Self> {
|
||||
let size = hdr.read_u16::<BigEndian>()?;
|
||||
let plane_size = hdr.read_u32::<BigEndian>()? as usize;
|
||||
let flags = hdr.read_u32::<BigEndian>()?;
|
||||
let counter = (flags >> 28) & 0xf; // 4 bits
|
||||
|
||||
//let support_partial = (flags >> 27) & 0x1; // 1 bit
|
||||
let support_partial: bool = (flags & 0x8000000) != 0;
|
||||
let mut rounded_bits_mask = ((flags >> 25) & 0x3) as i32; // 2 bit
|
||||
if rounded_bits_mask != 0 {
|
||||
rounded_bits_mask = 1 << (rounded_bits_mask - 1);
|
||||
}
|
||||
|
||||
assert!(flags & 0x00FFFFFF == 0);
|
||||
Ok(Plane {
|
||||
id,
|
||||
ind,
|
||||
size,
|
||||
plane_size,
|
||||
flags,
|
||||
counter,
|
||||
support_partial,
|
||||
rounded_bits_mask,
|
||||
data_offset: plane_offset,
|
||||
parent_offset,
|
||||
subbands: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn descriptor_line(&self) -> String {
|
||||
format!(
|
||||
" Plane {:#x} size: {:#x} plane_size: {:#x} flags: {:#x} counter: {:#x} support_partial: {} rounded_bits: {:#x}",
|
||||
self.ind, self.size, self.plane_size, self.flags, self.counter, self.support_partial, self.rounded_bits_mask
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Header information for a single subband
|
||||
///
|
||||
/// Two indicators are known: 0xFF03 and 0xFF13
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[allow(unused)]
|
||||
pub struct Subband {
|
||||
/// Indicator, 0xFF03 for version 1, 0xFF13 for version 2
|
||||
pub ind: u16,
|
||||
/// Header size
|
||||
pub header_size: u16,
|
||||
/// Subband size, uncorrected, size boundary = 0x8
|
||||
pub subband_size: usize,
|
||||
/// Flags like partial support or subband size correction value
|
||||
pub flags: u32,
|
||||
/// Q step base, used for inverse quantization (band != LL)
|
||||
pub q_step_base: i32,
|
||||
// Q step multiplicator, used for inverse quantization (band != LL)
|
||||
pub q_step_multi: u16,
|
||||
// --- Calculated fields
|
||||
/// Band ID (0-9)
|
||||
pub id: usize,
|
||||
/// Band counter (0-9)
|
||||
pub counter: u32,
|
||||
/// Partial decoding (only band LL)
|
||||
pub support_partial: bool,
|
||||
/// QP - quantization parameter for QStep
|
||||
/// Version 0x100 has no embedded QStep table, instead
|
||||
/// a predefined QStep table is used.
|
||||
pub q_param: u32,
|
||||
/// Unused bytes in band data at end
|
||||
pub unused_bytes: u32,
|
||||
/// Band data offset relative to plane offset
|
||||
pub data_offset: usize,
|
||||
/// Parent offset, TODO: Remove, it's not exact beacuse of tile extra data
|
||||
pub parent_offset: usize,
|
||||
/// Band data size, this is subband_size corrected by unused_bytes
|
||||
pub data_size: usize,
|
||||
/// Width of band in pixels
|
||||
pub width: usize,
|
||||
/// Height of band in pixels
|
||||
pub height: usize,
|
||||
|
||||
// For Wavelets
|
||||
pub row_start_addon: usize,
|
||||
pub row_end_addon: usize,
|
||||
pub col_start_addon: usize,
|
||||
pub col_end_addon: usize,
|
||||
pub level_shift: i16,
|
||||
}
|
||||
|
||||
impl Subband {
|
||||
pub fn new<R: Read>(id: usize, hdr: &mut R, ind: u16, parent_offset: usize, band_offset: usize) -> Result<Self> {
|
||||
let size = hdr.read_u16::<BigEndian>()?;
|
||||
assert!((size == 8 && ind == 0xFF03) || (size == 16 && ind == 0xFF13));
|
||||
let subband_size = hdr.read_u32::<BigEndian>()? as usize;
|
||||
match ind {
|
||||
0xFF03 => {
|
||||
let flags = hdr.read_u32::<BigEndian>()?;
|
||||
let counter = (flags >> 28) & 0xf; // 4 bits
|
||||
let support_partial: bool = (flags & 0x8000000) != 0;
|
||||
let q_param = (flags >> 19) & 0xFF; // 8 bit q_aram
|
||||
let unused_bytes = flags & 0x7FFFF; // 19 bit, related to subband_size
|
||||
let data_size: usize = (subband_size as u32 - unused_bytes) as usize;
|
||||
let q_step_base = 0;
|
||||
let q_step_multi = 0;
|
||||
|
||||
Ok(Subband {
|
||||
id,
|
||||
ind,
|
||||
header_size: size,
|
||||
subband_size,
|
||||
flags,
|
||||
counter,
|
||||
support_partial,
|
||||
q_param,
|
||||
q_step_base,
|
||||
q_step_multi,
|
||||
unused_bytes,
|
||||
data_offset: band_offset,
|
||||
parent_offset,
|
||||
data_size,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
0xFF13 => {
|
||||
// support_partial and q_Param are not supported in this version
|
||||
let q_param = 0;
|
||||
let support_partial = false;
|
||||
|
||||
let flags = hdr.read_u16::<BigEndian>()? as u32;
|
||||
let q_step_multi = hdr.read_u16::<BigEndian>()?;
|
||||
let q_step_base = hdr.read_i32::<BigEndian>()?;
|
||||
let unused_bytes = hdr.read_u16::<BigEndian>()? as u32;
|
||||
let end_marker = hdr.read_u16::<BigEndian>()?;
|
||||
assert!(end_marker == 0);
|
||||
let counter = (flags >> 12) & 0xf; // 4 bits
|
||||
let data_size: usize = (subband_size as u32 - unused_bytes) as usize;
|
||||
|
||||
Ok(Subband {
|
||||
id,
|
||||
ind,
|
||||
header_size: size,
|
||||
subband_size,
|
||||
flags,
|
||||
counter,
|
||||
support_partial,
|
||||
q_param,
|
||||
q_step_base,
|
||||
q_step_multi,
|
||||
unused_bytes,
|
||||
data_offset: band_offset,
|
||||
parent_offset,
|
||||
data_size,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
_ => Err(CrxError::General(format!("Unknown subband header indicator: {:?}", ind))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn descriptor_line(&self) -> String {
|
||||
format!(
|
||||
" Subband {:#x} size: {:#x} subband_size: {:#x} flags: {:#x} counter: {:#x} support_partial: {} q_param: {:#x} unused_bytes: {:#x} qStepBase: {:#x} qStepMult: {:#x} ",
|
||||
self.ind,
|
||||
self.header_size,
|
||||
self.subband_size,
|
||||
self.flags,
|
||||
self.counter,
|
||||
self.support_partial,
|
||||
self.q_param,
|
||||
self.unused_bytes,
|
||||
self.q_step_base,
|
||||
self.q_step_multi
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn get_subband_row(&self, row: usize) -> usize {
|
||||
if row < self.row_start_addon {
|
||||
0
|
||||
} else if row < self.height - self.row_end_addon {
|
||||
row - self.row_end_addon
|
||||
} else {
|
||||
self.height - self.row_end_addon - self.row_start_addon - 1
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn setup_idx(
|
||||
&mut self,
|
||||
version: u16,
|
||||
level: usize,
|
||||
col_start_idx: usize,
|
||||
band_width_ex_coef: usize,
|
||||
row_start_idx: usize,
|
||||
band_height_ex_coef: usize,
|
||||
) {
|
||||
//println!("Version: 0x{:x?}", version);
|
||||
if version == 0x200 {
|
||||
self.row_start_addon = row_start_idx;
|
||||
self.row_end_addon = band_height_ex_coef;
|
||||
self.col_start_addon = col_start_idx;
|
||||
self.col_end_addon = band_width_ex_coef;
|
||||
self.level_shift = 3 - level as i16;
|
||||
} else {
|
||||
self.row_start_addon = 0;
|
||||
self.row_end_addon = 0;
|
||||
self.col_start_addon = 0;
|
||||
self.col_end_addon = 0;
|
||||
self.level_shift = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_indicator<T: Read>(hdr: &mut T) -> Result<u16> {
|
||||
hdr
|
||||
.read_u16::<BigEndian>()
|
||||
.map_err(|_| CrxError::General("Header indicator read failed".into()))
|
||||
}
|
||||
|
||||
/// Parse MDAT header for structure of embedded data
|
||||
#[allow(clippy::while_let_loop)]
|
||||
pub(super) fn parse_header(mdat_hdr: &[u8]) -> Result<Vec<Tile>> {
|
||||
let mut hdr = Cursor::new(mdat_hdr);
|
||||
let mut tiles = Vec::new();
|
||||
let mut tile_offset: usize = 0;
|
||||
let mut plane_offset: usize = 0;
|
||||
let mut band_offset: usize = 0;
|
||||
|
||||
let mut ind = next_indicator(&mut hdr)?;
|
||||
loop {
|
||||
match ind {
|
||||
0xff01 | 0xff11 => {
|
||||
let mut tile = Tile::new(tiles.len(), &mut hdr, ind, tile_offset)?;
|
||||
ind = next_indicator(&mut hdr)?;
|
||||
loop {
|
||||
match ind {
|
||||
0xff02 | 0xff12 => {
|
||||
let mut plane = Plane::new(tile.planes.len(), &mut hdr, ind, tile.data_offset, plane_offset)?;
|
||||
ind = next_indicator(&mut hdr)?;
|
||||
loop {
|
||||
match ind {
|
||||
0xff03 | 0xff13 => {
|
||||
let subband = Subband::new(plane.subbands.len(), &mut hdr, ind, tile.data_offset + plane.data_offset, band_offset)?;
|
||||
band_offset += subband.subband_size;
|
||||
plane.subbands.push(subband);
|
||||
// Multi-tile files has no 0x0000 end marker, so we simulate it
|
||||
// on an read error.
|
||||
ind = next_indicator(&mut hdr).unwrap_or(0x0000);
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
plane_offset += plane.plane_size as usize;
|
||||
band_offset = 0; // reset band offset
|
||||
tile.planes.push(plane);
|
||||
}
|
||||
_ => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
tile_offset += tile.tile_size;
|
||||
plane_offset = 0; // reset plane offset
|
||||
tiles.push(tile);
|
||||
}
|
||||
0x0000 => {
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
return Err(CrxError::General(format!("Unexpected header record marker: {:x?}", ind)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(tiles)
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
// Crx is based on JPEG-LS from ITU T.78 and described in US patent US 2016/0323602 A1.
|
||||
// It has two modes:
|
||||
// - Lossless compression
|
||||
// - Lossy compression
|
||||
// For lossless (only LL band exists) and LL band from lossy compression,
|
||||
// prediction is used combined with adaptive Golomb-Rice entropy encoding for compression.
|
||||
// For lossy bands other than LL a special value rounding is introduced
|
||||
// into Golomb-Rice encoded values.
|
||||
//
|
||||
// LL band compression uses for the first image line run-length encoding
|
||||
// from JPEG-LS and adaptive Golomb-Rice encoding. For other lines,
|
||||
// MED (Median Edge Detection) is added to the encoding routines.
|
||||
//
|
||||
// For Lossy compression, image input is wavelet-transformed into subbands.
|
||||
// The LL band for low frequency part
|
||||
// The LH band for horizontal-direction frequency characteristic
|
||||
// The HL band for vertical-direction frequency characteristic
|
||||
// The HH band for oblique-direction frequency characteristic
|
||||
//
|
||||
// Transformation is directed by i, number of wavelet transformations.
|
||||
// Crx uses i=3, so the output is LL(3), LH(3), HL(3), HH(3), LH(2), HL(2), HH(2), LH(1), HL(1), HH(1)
|
||||
//
|
||||
|
||||
use self::{mdat::Tile, rice::RiceDecoder};
|
||||
use crate::formats::bmff::ext_cr3::cmp1::Cmp1Box;
|
||||
use bitstream_io::BitReader;
|
||||
use log::debug;
|
||||
use std::io::Cursor;
|
||||
use thiserror::Error;
|
||||
|
||||
mod decoder;
|
||||
mod idwt;
|
||||
mod iquant;
|
||||
mod mdat;
|
||||
mod rice;
|
||||
mod runlength;
|
||||
|
||||
/// Each level has 6*8 = 0x30 = 48 ex coef values
|
||||
/// Not every level is used. For example, an image with level=3
|
||||
/// only used the last level 3 values.
|
||||
#[rustfmt::skip]
|
||||
const EX_COEF_NUM_TBL:[usize; 0x30*3] = [
|
||||
// Level 1
|
||||
1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
||||
1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
|
||||
// Level 2
|
||||
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 2, 2, 1, 0, 0, 1, 1, 1, 1, 0, 0,
|
||||
// Level 3
|
||||
1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 1,
|
||||
1, 1, 1, 2, 2, 1, 1, 0, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1];
|
||||
|
||||
/// BitPump for Big Endian bit streams
|
||||
type BitPump<'a> = BitReader<Cursor<&'a [u8]>, bitstream_io::BigEndian>;
|
||||
|
||||
/// Error variants for compressor
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CrxError {
|
||||
/// Overflow of input, size constraints...
|
||||
#[error("Overflow error: {}", _0)]
|
||||
Overflow(String),
|
||||
|
||||
/// General error
|
||||
#[error("General error: {}", _0)]
|
||||
General(String),
|
||||
|
||||
/// General error
|
||||
#[error("Unsupported format: {}", _0)]
|
||||
Unsupp(String),
|
||||
|
||||
/// Error on internal cursor type
|
||||
#[error("I/O error")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// Result type for Compressor results
|
||||
type Result<T> = std::result::Result<T, CrxError>;
|
||||
|
||||
/// Codec parameters for decoding
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub struct CodecParams {
|
||||
#[allow(dead_code)]
|
||||
sample_precision: u8,
|
||||
image_width: usize,
|
||||
image_height: usize,
|
||||
plane_count: u8,
|
||||
//plane_width: usize,
|
||||
//plane_height: usize,
|
||||
#[allow(dead_code)]
|
||||
subband_count: u8,
|
||||
levels: usize,
|
||||
/// Bit depth of image
|
||||
#[allow(dead_code)]
|
||||
n_bits: u8,
|
||||
/// Bit depth for median (enc_type == 3, otherwise it is same as n_bits)
|
||||
median_bits: u8,
|
||||
enc_type: u8,
|
||||
tile_cols: usize,
|
||||
tile_rows: usize,
|
||||
tile_width: usize,
|
||||
tile_height: usize,
|
||||
mdat_hdr_size: u32,
|
||||
version: u16,
|
||||
}
|
||||
|
||||
impl CodecParams {
|
||||
#[inline(always)]
|
||||
fn get_header<'a>(&self, mdat: &'a [u8]) -> &'a [u8] {
|
||||
&mdat[..self.mdat_hdr_size as usize]
|
||||
}
|
||||
|
||||
/// The MDAT section contains the raw pixel data.
|
||||
/// Multiple images and data can be embedded into MDAT. The offsets
|
||||
/// and size is located in co64 and stsz BMF boxes. The raw data
|
||||
/// starts with an header block describing the data and subband offsets.
|
||||
///
|
||||
/// MDAT Layout:
|
||||
/// |-----|-----------|-----|-----------|------------|--------------|-----|
|
||||
/// | HDR | RAW-BANDS | HDR | RAW-BANDS | JPEG Thumb | JPEG Preview | ... |
|
||||
/// |-----|-----------|-----|-----------|------------|--------------|-----|
|
||||
#[inline(always)]
|
||||
fn get_data<'a>(&self, mdat: &'a [u8]) -> &'a [u8] {
|
||||
&mdat[self.mdat_hdr_size as usize..]
|
||||
}
|
||||
|
||||
fn resolution(&self) -> usize {
|
||||
self.image_width * self.image_height
|
||||
}
|
||||
|
||||
/// Create new codec parameters
|
||||
pub fn new(cmp1: &Cmp1Box) -> Result<Self> {
|
||||
const INCR_BIT_TABLE: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0];
|
||||
|
||||
if cmp1.n_planes != 4 {
|
||||
return Err(CrxError::General(format!("Plane configration {} is not supported", cmp1.n_planes)));
|
||||
}
|
||||
|
||||
//let tile_cols: usize = (cmp1.f_width / cmp1.tile_width) as usize;
|
||||
//let tile_rows: usize = (cmp1.f_height / cmp1.tile_height) as usize;
|
||||
|
||||
// Rounding for unbalanced sizes
|
||||
let tile_cols: usize = cmp1.f_width.div_ceil(cmp1.tile_width) as usize;
|
||||
let tile_rows: usize = cmp1.f_height.div_ceil(cmp1.tile_height) as usize;
|
||||
|
||||
assert!(tile_cols > 0);
|
||||
assert!(tile_rows > 0);
|
||||
|
||||
let params = Self {
|
||||
sample_precision: cmp1.n_bits as u8 + INCR_BIT_TABLE[4 * cmp1.enc_type as usize + 2] + 1,
|
||||
image_width: cmp1.f_width as usize,
|
||||
image_height: cmp1.f_height as usize,
|
||||
plane_count: cmp1.n_planes as u8,
|
||||
// 3 bands per level + one last LL
|
||||
// only 1 band for zero levels (uncompressed)
|
||||
subband_count: 3 * cmp1.image_levels as u8 + 1,
|
||||
levels: cmp1.image_levels as usize,
|
||||
n_bits: cmp1.n_bits,
|
||||
median_bits: cmp1.median_bits,
|
||||
enc_type: cmp1.enc_type as u8,
|
||||
tile_cols,
|
||||
tile_rows,
|
||||
tile_width: cmp1.tile_width as usize,
|
||||
tile_height: cmp1.tile_height as usize,
|
||||
mdat_hdr_size: cmp1.mdat_hdr_size,
|
||||
version: cmp1.version,
|
||||
};
|
||||
|
||||
if params.tile_cols > 0xff {
|
||||
return Err(CrxError::General(format!("Tile column count {} is not supported", tile_cols)));
|
||||
}
|
||||
if params.tile_rows > 0xff {
|
||||
return Err(CrxError::General(format!("Tile row count {} is not supported", tile_rows)));
|
||||
}
|
||||
//if params.tile_width < 0x16 || params.tile_height < 0x16 || params.plane_width > 0x7FFF || params.plane_height > 0x7FFF {
|
||||
// return Err(CrxError::General(format!("Invalid params for band decoding")));
|
||||
//}
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
/// Process tiles and update values
|
||||
pub(super) fn process_tiles(&mut self, tiles: &mut Vec<Tile>) {
|
||||
let tile_count = tiles.len();
|
||||
// Update each tile
|
||||
for cur_tile in tiles.iter_mut() {
|
||||
if (cur_tile.id + 1) % self.tile_cols != 0 {
|
||||
// not the last tile in a tile row
|
||||
cur_tile.tile_width = self.tile_width;
|
||||
cur_tile.plane_width = cur_tile.tile_width >> if self.plane_count == 4 { 1 } else { 0 };
|
||||
if self.tile_cols > 1 {
|
||||
cur_tile.tiles_right = true;
|
||||
if cur_tile.id % self.tile_cols != 0 {
|
||||
// not the first tile in tile row
|
||||
cur_tile.tiles_left = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// last tile in a tile row
|
||||
cur_tile.tile_width = self.image_width - self.tile_width * (self.tile_cols - 1);
|
||||
cur_tile.plane_width = cur_tile.tile_width >> if self.plane_count == 4 { 1 } else { 0 };
|
||||
if self.tile_cols > 1 {
|
||||
cur_tile.tiles_left = true;
|
||||
}
|
||||
}
|
||||
if (cur_tile.id) < (tile_count - self.tile_cols) {
|
||||
// in first tile row
|
||||
cur_tile.tile_height = self.tile_height;
|
||||
cur_tile.plane_height = cur_tile.tile_height >> if self.plane_count == 4 { 1 } else { 0 };
|
||||
if self.tile_rows > 1 {
|
||||
cur_tile.tiles_bottom = true;
|
||||
if cur_tile.id >= self.tile_cols {
|
||||
cur_tile.tiles_top = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// non first tile row
|
||||
cur_tile.tile_height = self.image_height - self.tile_height * (self.tile_rows - 1);
|
||||
cur_tile.plane_height = cur_tile.tile_height >> if self.plane_count == 4 { 1 } else { 0 };
|
||||
if self.tile_rows > 1 {
|
||||
cur_tile.tiles_top = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// process subbands
|
||||
for tile in tiles {
|
||||
debug!("{}", tile.descriptor_line());
|
||||
debug!("tile width: {}, tile height: {}", tile.tile_width, tile.tile_height);
|
||||
let mut plane_sizes = 0;
|
||||
self.process_subbands(tile);
|
||||
for plane in &mut tile.planes {
|
||||
debug!("{}", plane.descriptor_line());
|
||||
let mut band_sizes = 0;
|
||||
|
||||
for band in &mut plane.subbands {
|
||||
debug!("{}", band.descriptor_line());
|
||||
assert!(band.subband_size != 0);
|
||||
assert_eq!(band.subband_size % 8, 0);
|
||||
band_sizes += band.subband_size;
|
||||
}
|
||||
assert_eq!(plane.plane_size, band_sizes);
|
||||
plane_sizes += plane.plane_size;
|
||||
}
|
||||
// Tile may contain some extra bytes for quantization
|
||||
// This extra size must be subtracted before comaring to the
|
||||
// sum of plane sizes.
|
||||
assert_eq!(tile.tile_size - tile.extra_size(), plane_sizes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process tiles and update values
|
||||
pub(super) fn process_subbands(&self, tile: &mut Tile) {
|
||||
for plane in &mut tile.planes {
|
||||
let mut band_w = tile.plane_width;
|
||||
let mut band_h = tile.plane_height;
|
||||
let mut band_width_ex_coef = 0;
|
||||
let mut band_height_ex_coef = 0;
|
||||
if self.levels > 0 {
|
||||
let row_ex_coef = &EX_COEF_NUM_TBL[0x30 * (self.levels - 1) + 6 * (tile.plane_width & 7)..];
|
||||
let col_ex_coef = &EX_COEF_NUM_TBL[0x30 * (self.levels - 1) + 6 * (tile.plane_height & 7)..];
|
||||
|
||||
for lev in 0..self.levels {
|
||||
let w_odd_pixel = band_w & 1;
|
||||
let h_odd_pixel = band_h & 1;
|
||||
// With each level, width and hight are divided by 2
|
||||
band_w = (w_odd_pixel + band_w) >> 1;
|
||||
band_h = (h_odd_pixel + band_h) >> 1;
|
||||
|
||||
let mut w_ex_coef0 = 0;
|
||||
let mut w_ex_coef1 = 0;
|
||||
let mut h_ex_coef0 = 0;
|
||||
let mut h_ex_coef1 = 0;
|
||||
let mut col_start = 0;
|
||||
let mut row_start = 0;
|
||||
if tile.tiles_right {
|
||||
w_ex_coef0 = row_ex_coef[2 * lev];
|
||||
w_ex_coef1 = row_ex_coef[2 * lev + 1];
|
||||
}
|
||||
if tile.tiles_left {
|
||||
w_ex_coef0 += 1;
|
||||
col_start = 1;
|
||||
}
|
||||
if tile.tiles_bottom {
|
||||
h_ex_coef0 = col_ex_coef[2 * lev];
|
||||
h_ex_coef1 = col_ex_coef[2 * lev + 1];
|
||||
}
|
||||
if tile.tiles_top {
|
||||
h_ex_coef0 += 1;
|
||||
row_start = 1;
|
||||
}
|
||||
|
||||
// This sets the band width/height values.
|
||||
// Theoretically, it's just always plane_width/2.
|
||||
// But for multi-tile images, the band may contain
|
||||
// extra coefficents on any sides. Assumption is that
|
||||
// these extra coefficents are copied from the other tiles
|
||||
// over to improve compression or/and supress artefacts
|
||||
// on tile boundaries after tiles are assembled to full image.
|
||||
let i = (self.levels - lev) * 3;
|
||||
plane.subbands[i - 0].width = band_w + w_ex_coef0 - w_odd_pixel;
|
||||
plane.subbands[i - 0].height = band_h + h_ex_coef0 - h_odd_pixel;
|
||||
plane.subbands[i - 0].setup_idx(self.version, lev + 1, col_start, w_ex_coef0 - col_start, row_start, h_ex_coef0 - row_start);
|
||||
|
||||
plane.subbands[i - 1].width = band_w + w_ex_coef1;
|
||||
plane.subbands[i - 1].height = band_h + h_ex_coef0 - h_odd_pixel;
|
||||
plane.subbands[i - 1].setup_idx(self.version, lev + 1, 0, w_ex_coef1, row_start, h_ex_coef0 - row_start);
|
||||
|
||||
plane.subbands[i - 2].width = band_w + w_ex_coef0 - w_odd_pixel;
|
||||
plane.subbands[i - 2].height = band_h + h_ex_coef1;
|
||||
plane.subbands[i - 2].setup_idx(self.version, lev + 1, col_start, w_ex_coef0 - col_start, 0, h_ex_coef1);
|
||||
}
|
||||
band_width_ex_coef = 0;
|
||||
band_height_ex_coef = 0;
|
||||
if tile.tiles_right {
|
||||
band_width_ex_coef = row_ex_coef[2 * self.levels - 1];
|
||||
}
|
||||
if tile.tiles_bottom {
|
||||
band_height_ex_coef = col_ex_coef[2 * self.levels - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// LL3 band
|
||||
plane.subbands[0].width = band_width_ex_coef + band_w;
|
||||
plane.subbands[0].height = band_height_ex_coef + band_h;
|
||||
if self.levels > 0 {
|
||||
plane.subbands[0].setup_idx(self.version, self.levels, 0, band_width_ex_coef, 0, band_height_ex_coef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameter for a single Subband
|
||||
struct BandParam<'mdat> {
|
||||
/// Width of the band in pixels
|
||||
subband_width: usize,
|
||||
/// Height of the band in pixels
|
||||
subband_height: usize,
|
||||
/// Mask for bit rounding (unused)
|
||||
rounded_bits_mask: i32,
|
||||
/// Bits for rounding (unused)
|
||||
#[allow(dead_code)]
|
||||
rounded_bits: i32,
|
||||
/// Current line, starting with 0
|
||||
cur_line: usize,
|
||||
/// Two lines to decode current line [1] and lookup into prev line [0]
|
||||
/// After each line iteration, the two items are just swapped.
|
||||
line_buf: [Vec<i32>; 2],
|
||||
/// Previous K values for Golomb-Rice adaptive decoding
|
||||
/// This buffer is only used for non-LL bands
|
||||
line_k: Vec<u32>,
|
||||
/// Current position
|
||||
line_pos: usize,
|
||||
/// Length of the current line (unused, but good to keep)
|
||||
#[allow(dead_code)]
|
||||
line_len: usize,
|
||||
/// Runlength control parameter
|
||||
s_param: u32,
|
||||
/// Q parameter for QP (see Subband for more details)
|
||||
/// The MDAT header contains a Q parameter which should be constant.
|
||||
/// But for some (unused) decoding routines, the Q param needs to be updated,
|
||||
/// so we need a mutable copy in the BandParam.
|
||||
pub q_param: u32,
|
||||
/// Unsure what partial means...
|
||||
supports_partial: bool,
|
||||
/// Rice decoder, provides bit access to the MDAT stream
|
||||
rice: RiceDecoder<'mdat>,
|
||||
}
|
||||
|
||||
impl<'mdat> BandParam<'mdat> {
|
||||
/// Get coefficent `a` from line buffer
|
||||
/// c b d (buf 0)
|
||||
/// a x n (buf 1)
|
||||
fn coeff_a(&self) -> i32 {
|
||||
self.line_buf[1][self.line_pos - 1]
|
||||
}
|
||||
|
||||
/// Get coefficent `b` from line buffer
|
||||
/// c b d (buf 0)
|
||||
/// a x n (buf 1)
|
||||
fn coeff_b(&self) -> i32 {
|
||||
self.line_buf[0][self.line_pos]
|
||||
}
|
||||
|
||||
/// Get coefficent `c` from line buffer
|
||||
/// c b d (buf 0)
|
||||
/// a x n (buf 1)
|
||||
fn coeff_c(&self) -> i32 {
|
||||
self.line_buf[0][self.line_pos - 1]
|
||||
}
|
||||
|
||||
/// Get coefficent `d` from line buffer
|
||||
/// c b d (buf 0)
|
||||
/// a x n (buf 1)
|
||||
fn coeff_d(&self) -> i32 {
|
||||
self.line_buf[0][self.line_pos + 1]
|
||||
}
|
||||
|
||||
/// Get decoded buffer
|
||||
fn decoded_buf(&self) -> &[i32] {
|
||||
// Skip first and last extra pixel
|
||||
&self.line_buf[1][1..1 + self.subband_width]
|
||||
}
|
||||
|
||||
/// Get decoded buffer
|
||||
fn decoded_buf_mut(&mut self) -> &mut [i32] {
|
||||
// Skip first and last extra pixel
|
||||
&mut self.line_buf[1][1..1 + self.subband_width]
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompress a MDAT image buffer by given CMP1 box parameters
|
||||
pub fn decompress_crx_image(buf: &[u8], cmp1: &Cmp1Box) -> Result<Vec<u16>> {
|
||||
let image = CodecParams::new(cmp1)?;
|
||||
debug!("CRX codec parameter: {:?}", image);
|
||||
image.decode(buf)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::BitPump;
|
||||
use super::Result;
|
||||
use bitstream_io::BitRead;
|
||||
|
||||
/// Adaptive Golomb-Rice decoder
|
||||
pub(super) struct RiceDecoder<'mdat> {
|
||||
/// Bitstream from MDAT
|
||||
bitpump: BitPump<'mdat>,
|
||||
k_param: u32,
|
||||
}
|
||||
|
||||
impl<'mdat> RiceDecoder<'mdat> {
|
||||
/// Create new decoder for given bit pump
|
||||
pub(super) fn new(bitpump: BitPump<'mdat>) -> Self {
|
||||
Self { bitpump, k_param: 0 }
|
||||
}
|
||||
|
||||
/// Get current K parameter
|
||||
#[inline(always)]
|
||||
pub(super) fn k(&self) -> u32 {
|
||||
self.k_param
|
||||
}
|
||||
|
||||
/// Set K parameter
|
||||
#[inline(always)]
|
||||
pub(super) fn set_k(&mut self, k: u32) {
|
||||
self.k_param = k;
|
||||
}
|
||||
|
||||
/// Return the positive number of 0-bits in bitstream.
|
||||
/// All 0-bits are consumed.
|
||||
#[inline(always)]
|
||||
pub(super) fn bitstream_zeros(&mut self) -> Result<u32> {
|
||||
Ok(self.bitpump.read_unary::<1>()?)
|
||||
}
|
||||
|
||||
/// Return the requested bits
|
||||
// All bits are consumed.
|
||||
// The maximum number of bits are 32
|
||||
#[inline(always)]
|
||||
pub(super) fn bitstream_get_bits(&mut self, bits: u32) -> Result<u32> {
|
||||
debug_assert!(bits <= 32);
|
||||
Ok(self.bitpump.read_var(bits)?)
|
||||
}
|
||||
|
||||
/// Golomb-Rice decoding
|
||||
/// https://w3.ual.es/~vruiz/Docencia/Apuntes/Coding/Text/03-symbol_encoding/09-Golomb_coding/index.html
|
||||
/// escape and esc_bits are used to interrupt decoding when
|
||||
/// a value is not encoded using Golomb-Rice but directly encoded
|
||||
/// by esc_bits bits.
|
||||
fn rice_decode(&mut self, escape: u32, esc_bits: u32) -> Result<u32> {
|
||||
// q, quotient = n//m, with m = 2^k (Rice coding)
|
||||
let prefix = self.bitstream_zeros()?;
|
||||
if prefix >= escape {
|
||||
// n
|
||||
Ok(self.bitstream_get_bits(esc_bits)?)
|
||||
} else if self.k_param > 0 {
|
||||
// Golomb-Rice coding : n = q * 2^k + r, with r is next k bits. r is n - (q*2^k)
|
||||
Ok((prefix << self.k_param) | self.bitstream_get_bits(self.k_param)?)
|
||||
} else {
|
||||
// q
|
||||
Ok(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adaptive Golomb-Rice decoding, by adapting k value
|
||||
/// Sometimes adapting is based on the next coefficent (n) instead
|
||||
/// of current (x) coefficent. So you can disable it with `adapt_k`
|
||||
/// and update k later.
|
||||
pub(super) fn adaptive_rice_decode(&mut self, adapt_k: bool, escape: u32, esc_bits: u32, k_max: u32) -> Result<u32> {
|
||||
let val = self.rice_decode(escape, esc_bits)?;
|
||||
if adapt_k {
|
||||
self.k_param = Self::predict_k_param_max(self.k_param, val, k_max);
|
||||
}
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Update current K parameter
|
||||
pub(super) fn update_k_param(&mut self, bit_code: u32, k_max: u32) {
|
||||
self.k_param = Self::predict_k_param_max(self.k_param, bit_code, k_max);
|
||||
}
|
||||
|
||||
/// Predict K parameter with maximum constraint
|
||||
/// Golomb-Rice becomes more efficient when used with an adaptive
|
||||
/// K parameter. This is done by predicting the next K value for the
|
||||
/// next sample value.
|
||||
fn predict_k_param_max(prev_k: u32, value: u32, k_max: u32) -> u32 {
|
||||
let mut new_k = prev_k;
|
||||
if value >> prev_k > 2 {
|
||||
new_k += 1;
|
||||
}
|
||||
if value >> prev_k > 5 {
|
||||
new_k += 1;
|
||||
}
|
||||
if value < ((1 << prev_k) >> 1) {
|
||||
new_k -= 1;
|
||||
}
|
||||
|
||||
if k_max > 0 { std::cmp::min(new_k, k_max) } else { new_k }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2021 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
// Original Crx decoder crx.cpp was written by Alexey Danilchenko for libraw.
|
||||
// Rewritten in Rust by Daniel Vogelbacher, based on logic found in
|
||||
// crx.cpp and documentation done by Laurent Clévy (https://github.com/lclevy/canon_cr3).
|
||||
|
||||
use super::{BandParam, CodecParams, CrxError, Result};
|
||||
|
||||
/// See ITU T.78 Section A.2.1 Step 3
|
||||
/// Initialise the variables for the run mode: RUNindex=0 and J[0..31]
|
||||
#[rustfmt::skip]
|
||||
const J: [u32; 32] = [0, 0, 0, 0, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 3, 3, 3, 3,
|
||||
4, 4, 5, 5, 6, 6, 7, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15];
|
||||
|
||||
/// Precalculated values for (1 << J[0..31])
|
||||
#[rustfmt::skip]
|
||||
const JSHIFT: [u32; 32] = [1 << J[0], 1 << J[1], 1 << J[2], 1 << J[3],
|
||||
1 << J[4], 1 << J[5], 1 << J[6], 1 << J[7],
|
||||
1 << J[8], 1 << J[9], 1 << J[10], 1 << J[11],
|
||||
1 << J[12], 1 << J[13], 1 << J[14], 1 << J[15],
|
||||
1 << J[16], 1 << J[17], 1 << J[18], 1 << J[19],
|
||||
1 << J[20], 1 << J[21], 1 << J[22], 1 << J[23],
|
||||
1 << J[24], 1 << J[25], 1 << J[26], 1 << J[27],
|
||||
1 << J[28], 1 << J[29], 1 << J[30], 1 << J[31]];
|
||||
|
||||
impl CodecParams {
|
||||
/// Get symbol run count for run-length decoding
|
||||
/// See T.87 Section A.7.1.2 Run-length coding
|
||||
pub(super) fn symbol_run_count(&self, param: &mut BandParam, remaining: u32) -> Result<u32> {
|
||||
debug_assert!(remaining > 1);
|
||||
let mut run_cnt: u32 = 1;
|
||||
// See T.87 A.7.1.2 Code segment A.15
|
||||
// Bitstream 111110... means 5 lookups into J to decode final RUNcnt
|
||||
while run_cnt != remaining && param.rice.bitstream_get_bits(1)? == 1 {
|
||||
// JS is precalculated (1 << J[RUNindex])
|
||||
run_cnt += JSHIFT[param.s_param as usize];
|
||||
if run_cnt > remaining {
|
||||
run_cnt = remaining;
|
||||
break;
|
||||
}
|
||||
param.s_param = std::cmp::min(param.s_param + 1, 31);
|
||||
}
|
||||
// See T.87 A.7.1.2 Code segment A.16
|
||||
if run_cnt < remaining {
|
||||
if J[param.s_param as usize] > 0 {
|
||||
run_cnt += param.rice.bitstream_get_bits(J[param.s_param as usize])?;
|
||||
}
|
||||
param.s_param = param.s_param.saturating_sub(1); // prevent underflow
|
||||
if run_cnt > remaining {
|
||||
//println!("run_cnt: {}, remaining: {}", run_cnt, remaining);
|
||||
return Err(CrxError::General("Crx decoder error while decoding line".to_string()));
|
||||
}
|
||||
}
|
||||
Ok(run_cnt)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
use std::io::Read;
|
||||
|
||||
use libflate::zlib::Decoder;
|
||||
|
||||
use crate::{
|
||||
bits::{Binary16, Binary24, Binary32, Endian, FloatingPointParameters, extend_binary_floating_point},
|
||||
decompressors::{Decompressor, LineIteratorMut},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeflateDecompressor {
|
||||
pred_factor: usize,
|
||||
bps: u32,
|
||||
}
|
||||
|
||||
impl DeflateDecompressor {
|
||||
pub fn new(cpp: usize, predictor: u16, bps: u32, _endian: Endian) -> Self {
|
||||
let pred_factor = cpp
|
||||
* match predictor {
|
||||
3 => 1,
|
||||
34894 => 2,
|
||||
34895 => 4,
|
||||
_ => todo!(),
|
||||
};
|
||||
Self { pred_factor, bps }
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_delta_bytes(src: &mut [u8], factor: usize) {
|
||||
for col in factor..src.len() {
|
||||
src[col] = src[col].wrapping_add(src[col - factor]);
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_fp_delta_row<NARROW: FloatingPointParameters>(line: &mut [f32], row: &[u8], line_width: usize) {
|
||||
for (col, pix) in line.iter_mut().enumerate() {
|
||||
let mut tmp = [0; 4];
|
||||
assert!(NARROW::STORAGE_BYTES <= tmp.len());
|
||||
|
||||
for c in 0..NARROW::STORAGE_BYTES {
|
||||
tmp[c] = row[col + c * line_width];
|
||||
}
|
||||
let value = u32::from_be_bytes(tmp) >> (u32::BITS as usize - NARROW::STORAGE_WIDTH);
|
||||
*pix = f32::from_bits(extend_binary_floating_point::<NARROW, Binary32>(value));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, f32> for DeflateDecompressor {
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, f32>, line_width: usize) -> std::result::Result<(), String> {
|
||||
//eprintln!("Deflate: {:?}", self);
|
||||
let mut decoder = Decoder::new(src).unwrap();
|
||||
let mut decoded_data = Vec::new();
|
||||
decoder.read_to_end(&mut decoded_data).unwrap();
|
||||
|
||||
let bytesps = self.bps as usize / 8;
|
||||
assert!(bytesps >= 2 && bytesps <= 4);
|
||||
|
||||
assert_eq!(decoded_data.len(), bytesps * line_width * lines.len());
|
||||
|
||||
for (line, row) in lines.zip(decoded_data.chunks_exact_mut(bytesps as usize * line_width)).skip(skip_rows) {
|
||||
assert_eq!(line.len(), line_width);
|
||||
decode_delta_bytes(row, self.pred_factor);
|
||||
|
||||
match self.bps {
|
||||
16 => decode_fp_delta_row::<Binary16>(line, row, line_width),
|
||||
24 => decode_fp_delta_row::<Binary24>(line, row, line_width),
|
||||
32 => decode_fp_delta_row::<Binary32>(line, row, line_width),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
//let packed = PackedDecompressor::new(self.bps, self.endian);
|
||||
|
||||
//packed.decompress(&decoded_data, skip_rows, lines, line_width)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use crate::decompressors::ljpeg::LjpegDecompressor;
|
||||
use crate::decompressors::{Decompressor, LineIteratorMut};
|
||||
use crate::pixarray::PixU16;
|
||||
pub struct LJpegDecompressor {}
|
||||
|
||||
impl LJpegDecompressor {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, u16> for LJpegDecompressor {
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, u16>, line_width: usize) -> std::result::Result<(), String> {
|
||||
let decompressor = LjpegDecompressor::new(src)?;
|
||||
let mut pixbuf = PixU16::new(decompressor.width(), decompressor.height());
|
||||
|
||||
decompressor.decode(pixbuf.pixels_mut(), 0, decompressor.width(), decompressor.width(), decompressor.height(), false)?;
|
||||
|
||||
for (dst, src) in lines.zip(pixbuf.pixels().chunks_exact(line_width).skip(skip_rows)) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tile_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JpegDecompressor {}
|
||||
|
||||
impl JpegDecompressor {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, u16> for JpegDecompressor {
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, u16>, line_width: usize) -> std::result::Result<(), String> {
|
||||
let img = image::load_from_memory_with_format(src, image::ImageFormat::Jpeg).map_err(|err| format!("Lossy JPEG decompression failed: {:?}", err))?;
|
||||
match img {
|
||||
image::DynamicImage::ImageRgb8(image_buffer) => {
|
||||
for (dst, src) in lines.zip(image_buffer.chunks_exact(line_width).skip(skip_rows)) {
|
||||
for (dst, src) in dst.iter_mut().zip(src.iter()) {
|
||||
*dst = *src as u16; // Only change storage format, you MUST NOT scale up!
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tile_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2025 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
use jxl_oxide::JxlImage;
|
||||
|
||||
use crate::decompressors::{Decompressor, LineIteratorMut};
|
||||
|
||||
pub struct JpegXLDecompressor {
|
||||
bps: u32,
|
||||
}
|
||||
|
||||
impl JpegXLDecompressor {
|
||||
pub fn new(bps: u32) -> Self {
|
||||
Self { bps }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, u16> for JpegXLDecompressor {
|
||||
fn decompress(&self, src: &[u8], mut skip_rows: usize, lines: impl LineIteratorMut<'a, u16>, line_width: usize) -> std::result::Result<(), String> {
|
||||
let image = JxlImage::builder()
|
||||
.read(src)
|
||||
.map_err(|err| format!("Failed to read JPEG-XL image: {:?}", err))?;
|
||||
if let Some(header) = image.frame_header(0) {
|
||||
if header.bit_depth.bits_per_sample() != 8 && header.bit_depth.bits_per_sample() != 16 {
|
||||
// jxl_oxide scales the pixels into full range of storage type.
|
||||
// If we get e.g. 12 bit compressed data, output is scaled to 16 bit.
|
||||
// This breaks blacklevel scaling. We need to scale back to given bps (from TIFF).
|
||||
unimplemented!("JPEG-XL bit-depth {} not supported yet", header.bit_depth.bits_per_sample());
|
||||
}
|
||||
//eprintln!("JPEG-XL Bit-Depth: {:?}", header.bit_depth);
|
||||
}
|
||||
let frame = image.render_frame(0).map_err(|err| format!("Failed to render JPEG-XL image: {:?}", err))?;
|
||||
|
||||
let mut stream = frame.stream_no_alpha();
|
||||
|
||||
match self.bps {
|
||||
8 => {
|
||||
let mut tmp = vec![0_u8; line_width];
|
||||
|
||||
for line in lines.skip(skip_rows) {
|
||||
while skip_rows > 0 {
|
||||
let written = stream.write_to_buffer(&mut tmp);
|
||||
assert_eq!(line.len(), written);
|
||||
skip_rows -= 1;
|
||||
}
|
||||
let written = stream.write_to_buffer(&mut tmp);
|
||||
assert_eq!(line.len(), written);
|
||||
for (p, x) in line.iter_mut().zip(tmp.iter()) {
|
||||
*p = *x as u16; // Only change storage format, you MUST NOT scale up!
|
||||
}
|
||||
}
|
||||
}
|
||||
9..=16 => {
|
||||
for line in lines.skip(skip_rows) {
|
||||
while skip_rows > 0 {
|
||||
let written = stream.write_to_buffer(line);
|
||||
assert_eq!(line.len(), written);
|
||||
skip_rows -= 1;
|
||||
}
|
||||
let written = stream.write_to_buffer(line);
|
||||
assert_eq!(line.len(), written);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
/*
|
||||
let all_ch = frame.image_all_channels();
|
||||
|
||||
let pixbuf = all_ch.buf();
|
||||
for (line, buf) in lines.zip(pixbuf.chunks_exact(line_width).skip(skip_rows)) {
|
||||
for (p, f) in line.iter_mut().zip(buf.iter()) {
|
||||
//debug_assert!(*f <= (1.0 + f32::EPSILON));
|
||||
// *p = (f * u16::MAX as f32) as u16;
|
||||
// *p = *f as u16;
|
||||
}
|
||||
}
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
use super::LjpegDecompressor;
|
||||
use super::huffman::*;
|
||||
use crate::pumps::BitPumpJPEG;
|
||||
use crate::pumps::BitPumpMSB32;
|
||||
|
||||
/// Decode the ljpeg stream to `out`.
|
||||
/// `x` is the start of output in `out`, usually 0.
|
||||
/// `stripwidth` is the count of pixels of a single output strip (may
|
||||
/// be larger than width if each row has padding bytes).
|
||||
/// `width` is the output image width in pixels.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn decode_ljpeg(ljpeg: &LjpegDecompressor, out: &mut [u16], x: usize, stripwidth: usize, width: usize, height: usize) -> Result<(), String> {
|
||||
let ncomp: usize = ljpeg.components();
|
||||
if ljpeg.sof.width * ncomp < width || ljpeg.sof.height < height {
|
||||
return Err(format!(
|
||||
"ljpeg: trying to decode {}x{} into {}x{}",
|
||||
ljpeg.sof.width, ljpeg.sof.height, width, height
|
||||
));
|
||||
}
|
||||
|
||||
let htable = |index: usize| -> &HuffTable { &ljpeg.dhts[ljpeg.sof.components[index].dc_tbl_num] };
|
||||
let mut pump = BitPumpJPEG::new(ljpeg.buffer);
|
||||
let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1);
|
||||
|
||||
// initialize first pixel components
|
||||
for c in 0..ncomp {
|
||||
out[x + c] = (base_prediction + htable(c).huff_decode(&mut pump)?) as u16;
|
||||
}
|
||||
|
||||
let skip_x = ljpeg.sof.width - width / ncomp;
|
||||
|
||||
for row in 0..height {
|
||||
let startcol = if row == 0 { x + ncomp } else { x }; // skip first pixel in first row
|
||||
for col in (startcol..(width + x)).step_by(ncomp) {
|
||||
for c in 0..ncomp {
|
||||
let p: i32 = if col == x {
|
||||
// At start of line predictor starts with start of previous line
|
||||
out[(row - 1) * stripwidth + x + c] as i32
|
||||
} else {
|
||||
// All other cases use the two previous pixels in the same line
|
||||
match (row, ljpeg.predictor) {
|
||||
(row @ 0, _) | (row, 1) => {
|
||||
let a = out[row * stripwidth + (col - ncomp) + c] as i32;
|
||||
a
|
||||
}
|
||||
(row, 2) => {
|
||||
let b = out[(row - 1) * stripwidth + col + c] as i32;
|
||||
b
|
||||
}
|
||||
(row, 3) => {
|
||||
let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32;
|
||||
c
|
||||
}
|
||||
(row, 4) => {
|
||||
let a = out[row * stripwidth + (col - ncomp) + c] as i32;
|
||||
let b = out[(row - 1) * stripwidth + col + c] as i32;
|
||||
let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32;
|
||||
a + b - c
|
||||
}
|
||||
(row, 5) => {
|
||||
let a = out[row * stripwidth + (col - ncomp) + c] as i32;
|
||||
let b = out[(row - 1) * stripwidth + col + c] as i32;
|
||||
let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32;
|
||||
a + ((b - c) >> 1)
|
||||
}
|
||||
(row, 6) => {
|
||||
let a = out[row * stripwidth + (col - ncomp) + c] as i32;
|
||||
let b = out[(row - 1) * stripwidth + col + c] as i32;
|
||||
let c = out[(row - 1) * stripwidth + (col - ncomp) + c] as i32;
|
||||
b + ((a - c) >> 1)
|
||||
}
|
||||
(row, 7) => {
|
||||
let a = out[row * stripwidth + (col - ncomp) + c] as i32;
|
||||
let b = out[(row - 1) * stripwidth + col + c] as i32;
|
||||
(a + b) >> 1 // Adobe DNG SDK uses int32 and shifts, so we will do, too.
|
||||
}
|
||||
_ => {
|
||||
panic!("Unsupported prediction in LJPEG")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let diff = htable(c).huff_decode(&mut pump)?;
|
||||
out[row * stripwidth + col + c] = (p + diff) as u16;
|
||||
}
|
||||
}
|
||||
for _ in 0..skip_x {
|
||||
for c in 0..ncomp {
|
||||
// Skip extra encoded differences if the ljpeg frame is wider than the output
|
||||
htable(c).huff_decode(&mut pump)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_yuv_420(out: &mut [u16], row: usize, col: usize, width: usize, y1: i32, y2: i32, y3: i32, y4: i32, cb: i32, cr: i32) {
|
||||
let pix1 = row * width + col;
|
||||
let pix2 = pix1 + 3;
|
||||
let pix3 = (row + 1) * width + col;
|
||||
let pix4 = pix3 + 3;
|
||||
|
||||
debug_assert!(!y1.is_negative());
|
||||
debug_assert!(!y2.is_negative());
|
||||
debug_assert!(!y3.is_negative());
|
||||
debug_assert!(!y4.is_negative());
|
||||
debug_assert!(!cb.is_negative());
|
||||
debug_assert!(!cr.is_negative());
|
||||
|
||||
out[pix1 + 0] = y1 as u16;
|
||||
out[pix1 + 1] = cb as u16;
|
||||
out[pix1 + 2] = cr as u16;
|
||||
out[pix2 + 0] = y2 as u16;
|
||||
out[pix2 + 1] = cb as u16;
|
||||
out[pix2 + 2] = cr as u16;
|
||||
out[pix3 + 0] = y3 as u16;
|
||||
out[pix3 + 1] = cb as u16;
|
||||
out[pix3 + 2] = cr as u16;
|
||||
out[pix4 + 0] = y4 as u16;
|
||||
out[pix4 + 1] = cb as u16;
|
||||
out[pix4 + 2] = cr as u16;
|
||||
}
|
||||
|
||||
pub fn decode_sony_ljpeg_420(ljpeg: &LjpegDecompressor, out: &mut [u16], width: usize, height: usize) -> Result<(), String> {
|
||||
if ljpeg.sof.width * 3 != width || ljpeg.sof.height != height {
|
||||
return Err(format!(
|
||||
"ljpeg: trying to decode {}x{} into {}x{}",
|
||||
ljpeg.sof.width * 3,
|
||||
ljpeg.sof.height,
|
||||
width,
|
||||
height
|
||||
));
|
||||
}
|
||||
|
||||
debug_assert_eq!(width % 2, 0);
|
||||
debug_assert_eq!(width % 6, 0); // Ensure we have enough samples for .step_by(6)
|
||||
debug_assert_eq!(height % 2, 0);
|
||||
|
||||
let htable1 = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num];
|
||||
let htable2 = &ljpeg.dhts[ljpeg.sof.components[1].dc_tbl_num];
|
||||
let htable3 = &ljpeg.dhts[ljpeg.sof.components[2].dc_tbl_num];
|
||||
let mut pump = BitPumpJPEG::new(ljpeg.buffer);
|
||||
|
||||
let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1);
|
||||
|
||||
let y1 = base_prediction + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = y1 + htable1.huff_decode(&mut pump)?;
|
||||
let y3 = y1 + htable1.huff_decode(&mut pump)?; // y1 is sample above current row, column 0
|
||||
let y4 = y3 + htable1.huff_decode(&mut pump)?;
|
||||
|
||||
let cb = base_prediction + htable2.huff_decode(&mut pump)?;
|
||||
let cr = base_prediction + htable3.huff_decode(&mut pump)?;
|
||||
|
||||
set_yuv_420(out, 0, 0, width, y1, y2, y3, y4, cb, cr);
|
||||
|
||||
// first column|second column
|
||||
// | Y, Cb, Cr | py1, pcb, pcr | y1, cb, cr | y2, cb, cr | <- first row
|
||||
// | Y, Cb, Cr | py3, pcb, pcr | y3, cb, cr | y4, cb, cr | <- second row
|
||||
for row in (0..height).step_by(2) {
|
||||
let startcol = if row == 0 { 6 } else { 0 };
|
||||
for col in (startcol..width).step_by(6) {
|
||||
// Get previous values (for adding huff differnce)
|
||||
let (py1, py3, pcb, pcr) = if col == 0 {
|
||||
// This is possible broken 4:2:0 encoding by Sony, as the new row
|
||||
// has to use the sample from the second-previous row instead of the
|
||||
// first-previous row.
|
||||
let pos = (row - 2) * width; // reference is previous block, first row, first column
|
||||
(out[pos], 0, out[pos + 1], out[pos + 2]) // py3 is not required, instead py3 references to y1 on col == 0
|
||||
} else {
|
||||
let pos1 = row * width + col - 3; // reference is current block, first row, second column
|
||||
let pos3 = (row + 1) * width + col - 3; // reference current block, second row, second column
|
||||
(out[pos1], out[pos3], out[pos1 + 1], out[pos1 + 2])
|
||||
};
|
||||
// Calculate 4 Y samples, 1 Cb sample, 1 Cr sample
|
||||
let y1 = (py1 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = (y1 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y3 = if col == 0 {
|
||||
// y1 is sample above current row, column 0
|
||||
(y1 as i32) + htable1.huff_decode(&mut pump)?
|
||||
} else {
|
||||
// py3 is previous sample in same line
|
||||
(py3 as i32) + htable1.huff_decode(&mut pump)?
|
||||
};
|
||||
let y4 = (y3 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
|
||||
// Cb and Cr components
|
||||
let cb = (pcb as i32) + htable2.huff_decode(&mut pump)?;
|
||||
let cr = (pcr as i32) + htable3.huff_decode(&mut pump)?;
|
||||
set_yuv_420(out, row, col, width, y1, y2, y3, y4, cb, cr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_ljpeg_420(ljpeg: &LjpegDecompressor, out: &mut [u16], width: usize, height: usize) -> Result<(), String> {
|
||||
if ljpeg.sof.width * 3 != width || ljpeg.sof.height != height {
|
||||
return Err(format!(
|
||||
"ljpeg: trying to decode {}x{} into {}x{}",
|
||||
ljpeg.sof.width * 3,
|
||||
ljpeg.sof.height,
|
||||
width,
|
||||
height
|
||||
));
|
||||
}
|
||||
|
||||
debug_assert_eq!(width % 2, 0);
|
||||
debug_assert_eq!(width % 6, 0); // Ensure we have enough samples for .step_by(6)
|
||||
debug_assert_eq!(height % 2, 0);
|
||||
|
||||
let htable1 = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num];
|
||||
let htable2 = &ljpeg.dhts[ljpeg.sof.components[1].dc_tbl_num];
|
||||
let htable3 = &ljpeg.dhts[ljpeg.sof.components[2].dc_tbl_num];
|
||||
let mut pump = BitPumpJPEG::new(ljpeg.buffer);
|
||||
|
||||
let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1);
|
||||
let y1 = base_prediction + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = y1 + htable1.huff_decode(&mut pump)?;
|
||||
let y3 = y2 + htable1.huff_decode(&mut pump)?;
|
||||
let y4 = y3 + htable1.huff_decode(&mut pump)?;
|
||||
let cb = base_prediction + htable2.huff_decode(&mut pump)?;
|
||||
let cr = base_prediction + htable3.huff_decode(&mut pump)?;
|
||||
set_yuv_420(out, 0, 0, width, y1, y2, y3, y4, cb, cr);
|
||||
|
||||
for row in (0..height).step_by(2) {
|
||||
let startcol = if row == 0 { 6 } else { 0 };
|
||||
for col in (startcol..width).step_by(6) {
|
||||
let pos = if col == 0 {
|
||||
// At start of line predictor starts with first pixel of start of previous line
|
||||
(row - 2) * width
|
||||
} else {
|
||||
// All other cases use the last pixel in the same two lines
|
||||
(row + 1) * width + col - 3
|
||||
};
|
||||
let (py, pcb, pcr) = (out[pos], out[pos + 1], out[pos + 2]);
|
||||
|
||||
let y1 = (py as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = (y1 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y3 = (y2 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y4 = (y3 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let cb = (pcb as i32) + htable2.huff_decode(&mut pump)?;
|
||||
let cr = (pcr as i32) + htable3.huff_decode(&mut pump)?;
|
||||
set_yuv_420(out, row, col, width, y1, y2, y3, y4, cb, cr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_yuv_422(out: &mut [u16], row: usize, col: usize, width: usize, y1: i32, y2: i32, cb: i32, cr: i32) {
|
||||
let pix1 = row * width + col;
|
||||
let pix2 = pix1 + 3;
|
||||
|
||||
debug_assert!(!y1.is_negative());
|
||||
debug_assert!(!y2.is_negative());
|
||||
debug_assert!(!cb.is_negative());
|
||||
debug_assert!(!cr.is_negative());
|
||||
|
||||
out[pix1 + 0] = y1 as u16;
|
||||
out[pix1 + 1] = cb as u16;
|
||||
out[pix1 + 2] = cr as u16;
|
||||
out[pix2 + 0] = y2 as u16;
|
||||
out[pix2 + 1] = cb as u16;
|
||||
out[pix2 + 2] = cr as u16;
|
||||
}
|
||||
|
||||
pub fn decode_ljpeg_422(ljpeg: &LjpegDecompressor, out: &mut [u16], width: usize, height: usize) -> Result<(), String> {
|
||||
if ljpeg.sof.width * 3 != width || ljpeg.sof.height != height {
|
||||
return Err(format!(
|
||||
"ljpeg: trying to decode {}x{} into {}x{}",
|
||||
ljpeg.sof.width * 3,
|
||||
ljpeg.sof.height,
|
||||
width,
|
||||
height
|
||||
));
|
||||
}
|
||||
let htable1 = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num];
|
||||
let htable2 = &ljpeg.dhts[ljpeg.sof.components[1].dc_tbl_num];
|
||||
let htable3 = &ljpeg.dhts[ljpeg.sof.components[2].dc_tbl_num];
|
||||
let mut pump = BitPumpJPEG::new(ljpeg.buffer);
|
||||
|
||||
let base_prediction = 1 << (ljpeg.sof.precision - ljpeg.point_transform - 1);
|
||||
let y1 = base_prediction + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = y1 + htable1.huff_decode(&mut pump)?;
|
||||
let cb = base_prediction + htable2.huff_decode(&mut pump)?;
|
||||
let cr = base_prediction + htable3.huff_decode(&mut pump)?;
|
||||
set_yuv_422(out, 0, 0, width, y1, y2, cb, cr);
|
||||
|
||||
for row in 0..height {
|
||||
let startcol = if row == 0 { 6 } else { 0 };
|
||||
for col in (startcol..width).step_by(6) {
|
||||
let pos = if col == 0 {
|
||||
// At start of line predictor starts with first pixel of start of previous line
|
||||
(row - 1) * width
|
||||
} else {
|
||||
// All other cases use the last pixel in the same two lines
|
||||
row * width + col - 3
|
||||
};
|
||||
let (py, pcb, pcr) = (out[pos], out[pos + 1], out[pos + 2]);
|
||||
|
||||
let y1 = (py as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let y2 = (y1 as i32) + htable1.huff_decode(&mut pump)?;
|
||||
let cb = (pcb as i32) + htable2.huff_decode(&mut pump)?;
|
||||
let cr = (pcr as i32) + htable3.huff_decode(&mut pump)?;
|
||||
|
||||
set_yuv_422(out, row, col, width, y1, y2, cb, cr);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_hasselblad(ljpeg: &LjpegDecompressor, out: &mut [u16], width: usize) -> Result<(), String> {
|
||||
// Pixels are packed two at a time, not like LJPEG:
|
||||
// [p1_length_as_huffman][p2_length_as_huffman][p0_diff_with_length][p1_diff_with_length]|NEXT PIXELS
|
||||
let mut pump = BitPumpMSB32::new(ljpeg.buffer);
|
||||
let htable = &ljpeg.dhts[ljpeg.sof.components[0].dc_tbl_num];
|
||||
|
||||
for line in out.chunks_exact_mut(width) {
|
||||
let mut p1: i32 = 0x8000;
|
||||
let mut p2: i32 = 0x8000;
|
||||
for o in line.chunks_exact_mut(2) {
|
||||
let len1 = htable.huff_len(&mut pump);
|
||||
let len2 = htable.huff_len(&mut pump);
|
||||
p1 += htable.huff_diff(&mut pump, len1);
|
||||
p2 += htable.huff_diff(&mut pump, len2);
|
||||
o[0] = p1 as u16;
|
||||
o[1] = p2 as u16;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_leaf_strip(src: &[u8], out: &mut [u16], width: usize, height: usize, htable1: &HuffTable, htable2: &HuffTable, bpred: i32) -> Result<(), String> {
|
||||
let mut pump = BitPumpJPEG::new(src);
|
||||
out[0] = (bpred + htable1.huff_decode(&mut pump)?) as u16;
|
||||
out[1] = (bpred + htable2.huff_decode(&mut pump)?) as u16;
|
||||
for row in 0..height {
|
||||
let startcol = if row == 0 { 2 } else { 0 };
|
||||
for col in (startcol..width).step_by(2) {
|
||||
let pos = if col == 0 {
|
||||
// At start of line predictor starts with start of previous line
|
||||
(row - 1) * width
|
||||
} else {
|
||||
// All other cases use the two previous pixels in the same line
|
||||
row * width + col - 2
|
||||
};
|
||||
let (p1, p2) = (out[pos], out[pos + 1]);
|
||||
|
||||
let diff1 = htable1.huff_decode(&mut pump)?;
|
||||
let diff2 = htable2.huff_decode(&mut pump)?;
|
||||
out[row * width + col] = ((p1 as i32) + diff1) as u16;
|
||||
out[row * width + col + 1] = ((p2 as i32) + diff2) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
use crate::pumps::BitPump;
|
||||
use std::fmt;
|
||||
|
||||
const DECODE_CACHE_BITS: u32 = 13;
|
||||
|
||||
pub struct HuffTable {
|
||||
// These two fields directly represent the contents of a JPEG DHT marker
|
||||
pub bits: [u32; 17],
|
||||
pub huffval: [u32; 256],
|
||||
|
||||
// Represent the weird shifts that are needed for some NEF files
|
||||
pub shiftval: [u32; 256],
|
||||
|
||||
// Enable the workaround for 16 bit decodes in DNG that need to consume those
|
||||
// bits instead of the value being implied
|
||||
pub dng_bug: bool,
|
||||
|
||||
// In CRW we only use the len code so the cache is not needed
|
||||
pub disable_cache: bool,
|
||||
|
||||
// The remaining fields are computed from the above to allow more
|
||||
// efficient coding and decoding and thus private
|
||||
|
||||
// The max number of bits in a huffman code and the table that converts those
|
||||
// bits into how many bits to consume and the decoded length and shift
|
||||
pub nbits: u32,
|
||||
|
||||
// Fast lookup for self.peek_bits(nbits). This contains the huffval
|
||||
// for all combinations of <code>+<extrabits>
|
||||
// This is: (bits, len, shift) where:
|
||||
// bits: the actual count of bits to represent the code
|
||||
// len: extra bits for difference encoding
|
||||
// shift: special shift value for some Nikon models
|
||||
//
|
||||
// The huffman code (e.g. 0b1111111110) is the vector index inself, extended
|
||||
// with all possible extra bit values. For example:
|
||||
// nbits = 4
|
||||
// code = 0b110
|
||||
// bits: 3
|
||||
// len: 1
|
||||
// Then the array contains the values:
|
||||
// [0b110 0] = (3, 1, 0)
|
||||
// [0b110 1] = (3, 1, 0)
|
||||
pub hufftable: Vec<(u8, u8, u8)>,
|
||||
|
||||
// A pregenerated table that goes straight to decoding a diff without first
|
||||
// finding a length, fetching bits, and sign extending them. The table is
|
||||
// sized by DECODE_CACHE_BITS and can have 99%+ hit rate with 13 bits
|
||||
decodecache: [Option<(u8, i16)>; 1 << DECODE_CACHE_BITS],
|
||||
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
struct MockPump {
|
||||
bits: u64,
|
||||
nbits: u32,
|
||||
}
|
||||
|
||||
impl MockPump {
|
||||
pub fn empty() -> Self {
|
||||
MockPump { bits: 0, nbits: 0 }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, bits: u32, nbits: u32) {
|
||||
self.bits = (bits as u64) << 32;
|
||||
self.nbits = nbits + 32;
|
||||
}
|
||||
|
||||
pub fn validbits(&self) -> i32 {
|
||||
self.nbits as i32 - 32
|
||||
}
|
||||
}
|
||||
|
||||
impl BitPump for MockPump {
|
||||
fn peek_bits(&mut self, num: u32) -> u32 {
|
||||
(self.bits >> (self.nbits - num)) as u32
|
||||
}
|
||||
|
||||
fn consume_bits(&mut self, num: u32) {
|
||||
self.nbits -= num;
|
||||
self.bits &= (1 << self.nbits) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl HuffTable {
|
||||
pub fn empty() -> HuffTable {
|
||||
HuffTable {
|
||||
bits: [0; 17],
|
||||
huffval: [0; 256],
|
||||
shiftval: [0; 256],
|
||||
dng_bug: false,
|
||||
disable_cache: false,
|
||||
|
||||
nbits: 0,
|
||||
hufftable: Vec::new(),
|
||||
decodecache: [None; 1 << DECODE_CACHE_BITS],
|
||||
initialized: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(bits: [u32; 17], huffval: [u32; 256], dng_bug: bool) -> Result<HuffTable, String> {
|
||||
let mut tbl = HuffTable {
|
||||
bits,
|
||||
huffval,
|
||||
shiftval: [0; 256],
|
||||
dng_bug,
|
||||
disable_cache: false,
|
||||
|
||||
nbits: 0,
|
||||
hufftable: Vec::new(),
|
||||
decodecache: [None; 1 << DECODE_CACHE_BITS],
|
||||
initialized: false,
|
||||
};
|
||||
tbl.initialize()?;
|
||||
Ok(tbl)
|
||||
}
|
||||
|
||||
pub fn initialize(&mut self) -> Result<(), String> {
|
||||
// Find out the max code length and allocate a table with that size
|
||||
self.nbits = 16;
|
||||
for i in 0..16 {
|
||||
if self.bits[16 - i] != 0 {
|
||||
break;
|
||||
}
|
||||
self.nbits -= 1;
|
||||
}
|
||||
self.hufftable = vec![(0, 0, 0); 1 << self.nbits];
|
||||
|
||||
// Fill in the table itself
|
||||
let mut h = 0;
|
||||
let mut pos = 0;
|
||||
for len in 0..self.nbits {
|
||||
// Fill for each number of huffman codes of length i (=len+1)
|
||||
for _ in 0..self.bits[len as usize + 1] {
|
||||
// Fill for all possible extra bits, payload is always the same for fast lookup bases on peek_bits(self.nbits)
|
||||
for _ in 0..(1 << (self.nbits - len - 1)) {
|
||||
self.hufftable[h] = (len as u8 + 1, self.huffval[pos] as u8, self.shiftval[pos] as u8);
|
||||
h += 1;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the decode cache by running the slow code over all the possible
|
||||
// values DECODE_CACHE_BITS wide
|
||||
if !self.disable_cache {
|
||||
let mut pump = MockPump::empty();
|
||||
let mut i = 0;
|
||||
loop {
|
||||
pump.set(i, DECODE_CACHE_BITS);
|
||||
let (bits, decode) = self.huff_decode_slow(&mut pump);
|
||||
if pump.validbits() >= 0 {
|
||||
self.decodecache[i as usize] = Some((bits, decode as i16));
|
||||
}
|
||||
i += 1;
|
||||
if i >= 1 << DECODE_CACHE_BITS {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.initialized = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32, String> {
|
||||
let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
|
||||
if let Some((bits, decode)) = self.decodecache[code] {
|
||||
match (decode, self.dng_bug) {
|
||||
// Special case: for -32768 no SSSS bits are stored
|
||||
(-32768, false) => {
|
||||
debug_assert!(bits > 16);
|
||||
pump.consume_bits(bits as u32 - 16);
|
||||
}
|
||||
_ => {
|
||||
pump.consume_bits(bits as u32);
|
||||
}
|
||||
}
|
||||
Ok(decode as i32)
|
||||
} else {
|
||||
let decode = self.huff_decode_slow(pump);
|
||||
Ok(decode.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u8, i32) {
|
||||
let len = self.huff_len(pump);
|
||||
(len.0 + len.1, self.huff_diff(pump, len))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u8, u8, u8) {
|
||||
let code = pump.peek_bits(self.nbits) as usize;
|
||||
let (bits, len, shift) = self.hufftable[code];
|
||||
pump.consume_bits(bits as u32);
|
||||
(bits, len, shift)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn huff_get_bits(&self, pump: &mut dyn BitPump) -> u32 {
|
||||
let code = pump.peek_bits(self.nbits) as usize;
|
||||
let (bits, len, _) = self.hufftable[code];
|
||||
pump.consume_bits(bits as u32);
|
||||
len as u32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u8, u8, u8)) -> i32 {
|
||||
let (_, len, shift) = input;
|
||||
|
||||
match len {
|
||||
0 => 0,
|
||||
16 => {
|
||||
if self.dng_bug {
|
||||
pump.get_bits(16); // consume can fail because we haven't peeked yet
|
||||
}
|
||||
-32768
|
||||
}
|
||||
len => {
|
||||
// decode the difference and extend sign bit
|
||||
let fulllen: i32 = len as i32 + shift as i32;
|
||||
let shift: i32 = shift as i32;
|
||||
let bits = pump.get_bits(len as u32) as i32;
|
||||
let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
|
||||
if (diff & (1 << (fulllen - 1))) == 0 {
|
||||
diff -= (1 << fulllen) - ((shift == 0) as i32);
|
||||
}
|
||||
diff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for HuffTable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.initialized {
|
||||
write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
|
||||
} else {
|
||||
write!(f, "HuffTable {{ uninitialized }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
use crate::bits::Endian;
|
||||
use crate::decoders::decode_threaded_multiline;
|
||||
use crate::decompressors::ljpeg::decompressors::*;
|
||||
use crate::decompressors::ljpeg::huffman::*;
|
||||
use crate::pixarray::PixU16;
|
||||
use crate::pumps::ByteStream;
|
||||
|
||||
mod decompressors;
|
||||
pub mod huffman;
|
||||
|
||||
enum Marker {
|
||||
Stuff = 0x00,
|
||||
SOF3 = 0xc3, // lossless
|
||||
DHT = 0xc4, // huffman tables
|
||||
SOI = 0xd8, // start of image
|
||||
EOI = 0xd9, // end of image
|
||||
SOS = 0xda, // start of scan
|
||||
DQT = 0xdb, // quantization tables
|
||||
Fill = 0xff,
|
||||
}
|
||||
|
||||
fn m(marker: Marker) -> u8 {
|
||||
marker as u8
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct JpegComponentInfo {
|
||||
// These values are fixed over the whole image, read from the SOF marker.
|
||||
id: usize, // identifier for this component (0..255)
|
||||
#[allow(dead_code)]
|
||||
index: usize, // its index in SOF or cPtr->compInfo[]
|
||||
|
||||
// Huffman table selector (0..3). The value may vary between scans.
|
||||
// It is read from the SOS marker.
|
||||
dc_tbl_num: usize,
|
||||
super_h: usize, // Horizontal Supersampling
|
||||
super_v: usize, // Vertical Supersampling
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct SOFInfo {
|
||||
width: usize,
|
||||
height: usize,
|
||||
cps: usize,
|
||||
precision: usize,
|
||||
components: Vec<JpegComponentInfo>,
|
||||
csfix: bool,
|
||||
}
|
||||
|
||||
impl SOFInfo {
|
||||
fn empty(csfix: bool) -> SOFInfo {
|
||||
SOFInfo {
|
||||
width: 0,
|
||||
height: 0,
|
||||
cps: 0,
|
||||
precision: 0,
|
||||
components: Vec::new(),
|
||||
csfix,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_sof(&mut self, input: &mut ByteStream) -> Result<(), String> {
|
||||
let header_length = input.get_u16() as usize;
|
||||
self.precision = input.get_u8() as usize;
|
||||
self.height = input.get_u16() as usize;
|
||||
self.width = input.get_u16() as usize;
|
||||
self.cps = input.get_u8() as usize;
|
||||
|
||||
if self.precision > 16 {
|
||||
return Err("ljpeg: More than 16 bits per channel is not supported.".to_string());
|
||||
}
|
||||
if self.cps > 4 || self.cps < 1 {
|
||||
return Err("ljpeg: Only from 1 to 4 components are supported.".to_string());
|
||||
}
|
||||
if header_length != 8 + self.cps * 3 {
|
||||
return Err("ljpeg: Header size mismatch.".to_string());
|
||||
}
|
||||
|
||||
for i in 0..self.cps {
|
||||
let id = input.get_u8() as usize;
|
||||
let subs = input.get_u8() as usize;
|
||||
input.get_u8(); // Skip info about quantized
|
||||
|
||||
self.components.push(JpegComponentInfo {
|
||||
id,
|
||||
index: i,
|
||||
dc_tbl_num: 0,
|
||||
super_v: subs & 0xf,
|
||||
super_h: subs >> 4,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_sos(&mut self, input: &mut ByteStream) -> Result<(usize, usize), String> {
|
||||
if self.width == 0 {
|
||||
return Err("ljpeg: Trying to parse SOS before SOF".to_string());
|
||||
}
|
||||
input.get_u16(); //skip header length
|
||||
let soscps = input.get_u8() as usize;
|
||||
if self.cps != soscps {
|
||||
return Err("ljpeg: component number mismatch in SOS".to_string());
|
||||
}
|
||||
for cs in 0..self.cps {
|
||||
// At least some MOS cameras have this broken
|
||||
let readcs = input.get_u8() as usize;
|
||||
let cs = if self.csfix { cs } else { readcs };
|
||||
let component = match self.components.iter_mut().find(|&&mut c| c.id == cs) {
|
||||
Some(val) => val,
|
||||
None => return Err(format!("ljpeg: invalid component selector {}", cs)),
|
||||
};
|
||||
let td = (input.get_u8() as usize) >> 4;
|
||||
if td > 3 {
|
||||
return Err("ljpeg: Invalid Huffman table selection".to_string());
|
||||
}
|
||||
component.dc_tbl_num = td;
|
||||
}
|
||||
let pred = input.get_u8() as usize;
|
||||
input.get_u8(); // Se + Ah Not used in LJPEG
|
||||
let pt = (input.get_u8() as usize) & 0xf; // Point Transform
|
||||
Ok((pred, pt))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LjpegDecompressor<'a> {
|
||||
buffer: &'a [u8],
|
||||
sof: SOFInfo,
|
||||
predictor: usize,
|
||||
point_transform: usize,
|
||||
dhts: Vec<HuffTable>,
|
||||
}
|
||||
|
||||
impl<'a> LjpegDecompressor<'a> {
|
||||
pub fn new(src: &'a [u8]) -> Result<LjpegDecompressor<'a>, String> {
|
||||
LjpegDecompressor::new_full(src, false, false)
|
||||
}
|
||||
|
||||
pub fn new_full(src: &'a [u8], dng_bug: bool, csfix: bool) -> Result<LjpegDecompressor<'a>, String> {
|
||||
let mut input = ByteStream::new(src, Endian::Big);
|
||||
if LjpegDecompressor::get_next_marker(&mut input, false)? != m(Marker::SOI) {
|
||||
return Err("ljpeg: Image did not start with SOI. Probably not LJPEG".to_string());
|
||||
}
|
||||
|
||||
let mut sof = SOFInfo::empty(csfix);
|
||||
let mut dht_init = [false; 4];
|
||||
let mut dht_bits = [[0_u32; 17]; 4];
|
||||
let mut dht_huffval = [[0_u32; 256]; 4];
|
||||
let pred;
|
||||
let pt;
|
||||
loop {
|
||||
let marker = LjpegDecompressor::get_next_marker(&mut input, true)?;
|
||||
if marker == m(Marker::SOF3) {
|
||||
// Start of the frame, giving us the basic info
|
||||
sof.parse_sof(&mut input)?;
|
||||
if sof.precision > 16 || sof.precision < 10 {
|
||||
return Err(format!("ljpeg: sof.precision {}", sof.precision));
|
||||
}
|
||||
} else if marker == m(Marker::DHT) {
|
||||
// Huffman table settings
|
||||
LjpegDecompressor::parse_dht(&mut input, &mut dht_init, &mut dht_bits, &mut dht_huffval)?;
|
||||
} else if marker == m(Marker::SOS) {
|
||||
// Start of the actual stream, we can decode after this
|
||||
let (a, b) = sof.parse_sos(&mut input)?;
|
||||
pred = a;
|
||||
pt = b;
|
||||
break;
|
||||
} else if marker == m(Marker::EOI) {
|
||||
// Should never be reached as we stop at SOS
|
||||
return Err("ljpeg: reached EOI before SOS".to_string());
|
||||
} else if marker == m(Marker::DQT) {
|
||||
return Err("ljpeg: not a valid raw file, found DQT".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let mut dhts = Vec::new();
|
||||
for i in 0..4 {
|
||||
dhts.push(if dht_init[i] {
|
||||
HuffTable::new(dht_bits[i], dht_huffval[i], dng_bug)?
|
||||
} else {
|
||||
HuffTable::empty()
|
||||
});
|
||||
}
|
||||
|
||||
log::debug!(
|
||||
"LJPEGDecompressor: super_h: {}, super_v: {}, pred: {}, pt: {}, prec: {}, cps: {}",
|
||||
sof.components[0].super_h,
|
||||
sof.components[0].super_v,
|
||||
pred,
|
||||
pt,
|
||||
sof.precision,
|
||||
sof.cps,
|
||||
);
|
||||
|
||||
if sof.components[0].super_h == 2 && sof.components[0].super_v == 2 {
|
||||
log::debug!("LJPEG with YUV 4:2:0 encoding");
|
||||
} else if sof.components[0].super_h == 2 && sof.components[0].super_v == 1 {
|
||||
log::debug!("LJPEG with YUV 4:2:2 encoding");
|
||||
}
|
||||
|
||||
let offset = input.get_pos();
|
||||
Ok(LjpegDecompressor {
|
||||
buffer: &src[offset..],
|
||||
sof,
|
||||
predictor: pred,
|
||||
point_transform: pt,
|
||||
dhts,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_next_marker(input: &mut ByteStream, allowskip: bool) -> Result<u8, String> {
|
||||
if !allowskip {
|
||||
let fill = input.get_u8();
|
||||
if fill != m(Marker::Fill) {
|
||||
return Err(format!("ljpeg get_next_marker() (noskip) expected fill marker 0XFF but got 0x{:X}", fill));
|
||||
}
|
||||
let mark = input.get_u8();
|
||||
if mark == m(Marker::Stuff) || mark == m(Marker::Fill) {
|
||||
return Err(format!(
|
||||
"ljpeg get_next_marker() (noskip) expected marker but found STUFF or FILL (0x{:X})",
|
||||
mark
|
||||
));
|
||||
}
|
||||
return Ok(mark);
|
||||
}
|
||||
input.skip_to_marker()?;
|
||||
|
||||
Ok(input.get_u8())
|
||||
}
|
||||
|
||||
fn parse_dht(input: &mut ByteStream, init: &mut [bool; 4], bits: &mut [[u32; 17]; 4], huffval: &mut [[u32; 256]; 4]) -> Result<(), String> {
|
||||
let mut length = (input.get_u16() as usize) - 2;
|
||||
|
||||
while length > 0 {
|
||||
let b = input.get_u8() as usize;
|
||||
let tc = b >> 4;
|
||||
let th = b & 0xf;
|
||||
|
||||
if tc != 0 {
|
||||
return Err("ljpeg: unsuported table class in DHT".to_string());
|
||||
}
|
||||
if th > 3 {
|
||||
return Err(format!("ljpeg: unsuported table id {}", th));
|
||||
}
|
||||
|
||||
let mut acc: usize = 0;
|
||||
for i in 0..16 {
|
||||
bits[th][i + 1] = input.get_u8() as u32;
|
||||
acc += bits[th][i + 1] as usize;
|
||||
}
|
||||
bits[th][0] = 0;
|
||||
|
||||
if acc > 256 {
|
||||
return Err("ljpeg: invalid DHT table".to_string());
|
||||
}
|
||||
|
||||
if length < 1 + 16 + acc {
|
||||
return Err("ljpeg: invalid DHT table length".to_string());
|
||||
}
|
||||
|
||||
for i in 0..acc {
|
||||
huffval[th][i] = input.get_u8() as u32;
|
||||
}
|
||||
|
||||
init[th] = true;
|
||||
length -= 1 + 16 + acc;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle special SONY YUV 4:2:0 encoding in ILCE-7RM5
|
||||
pub fn decode_sony(&self, out: &mut [u16], x: usize, stripwidth: usize, width: usize, height: usize, dummy: bool) -> Result<(), String> {
|
||||
if dummy {
|
||||
return Ok(());
|
||||
}
|
||||
log::debug!("LJPEG decode with special Sony mode");
|
||||
if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 2 {
|
||||
decode_sony_ljpeg_420(self, out, width, height)
|
||||
} else if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 1 {
|
||||
decode_ljpeg_422(self, out, width, height)
|
||||
} else if self.sof.components[0].super_h == 1 && self.sof.components[0].super_v == 1 {
|
||||
match self.predictor {
|
||||
1 | 2 | 3 | 4 | 5 | 6 | 7 => decode_ljpeg(self, out, x, stripwidth, width, height),
|
||||
8 => decode_hasselblad(self, out, width),
|
||||
p => Err(format!("ljpeg: predictor {} not supported", p)),
|
||||
}
|
||||
} else {
|
||||
Err(format!(
|
||||
"ljpeg: unsupported interleave configuration, super_h: {}, super_v: {}",
|
||||
self.sof.components[0].super_h, self.sof.components[0].super_v
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode(&self, out: &mut [u16], x: usize, stripwidth: usize, width: usize, height: usize, dummy: bool) -> Result<(), String> {
|
||||
if dummy {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 2 {
|
||||
decode_ljpeg_420(self, out, width, height)
|
||||
} else if self.sof.components[0].super_h == 2 && self.sof.components[0].super_v == 1 {
|
||||
return decode_ljpeg_422(self, out, width, height);
|
||||
} else if self.sof.components[0].super_h == 1 && self.sof.components[0].super_v == 1 {
|
||||
match self.predictor {
|
||||
1 | 2 | 3 | 4 | 5 | 6 | 7 => decode_ljpeg(self, out, x, stripwidth, width, height),
|
||||
8 => decode_hasselblad(self, out, width),
|
||||
p => Err(format!("ljpeg: predictor {} not supported", p)),
|
||||
}
|
||||
} else {
|
||||
Err(format!(
|
||||
"ljpeg: unsupported interleave configuration, super_h: {}, super_v: {}",
|
||||
self.sof.components[0].super_h, self.sof.components[0].super_v
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_leaf(&self, width: usize, height: usize) -> Result<PixU16, String> {
|
||||
let mut offsets = vec![0_usize; 1];
|
||||
let mut input = ByteStream::new(self.buffer, Endian::Big);
|
||||
|
||||
while let Ok(marker) = LjpegDecompressor::get_next_marker(&mut input, true) {
|
||||
if marker == m(Marker::EOI) {
|
||||
break;
|
||||
}
|
||||
offsets.push(input.get_pos());
|
||||
}
|
||||
let nstrips = (height - 1) / 8 + 1;
|
||||
if offsets.len() != nstrips {
|
||||
return Err(format!("MOS: expecting {} strips found {}", nstrips, offsets.len()));
|
||||
}
|
||||
|
||||
let htable1 = &self.dhts[self.sof.components[0].dc_tbl_num];
|
||||
let htable2 = &self.dhts[self.sof.components[1].dc_tbl_num];
|
||||
let bpred = 1 << (self.sof.precision - self.point_transform - 1);
|
||||
decode_threaded_multiline(
|
||||
width,
|
||||
height,
|
||||
8,
|
||||
false,
|
||||
&(|strip: &mut [u16], block| {
|
||||
let block = block / 8;
|
||||
let offset = offsets[block];
|
||||
let nlines = strip.len() / width;
|
||||
decode_leaf_strip(&self.buffer[offset..], strip, width, nlines, htable1, htable2, bpred)?;
|
||||
Ok(())
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.sof.width * self.sof.cps
|
||||
}
|
||||
pub fn height(&self) -> usize {
|
||||
self.sof.height
|
||||
}
|
||||
pub fn super_v(&self) -> usize {
|
||||
self.sof.components[0].super_v
|
||||
}
|
||||
pub fn super_h(&self) -> usize {
|
||||
self.sof.components[0].super_h
|
||||
}
|
||||
pub fn components(&self) -> usize {
|
||||
self.sof.components.len()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2025 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
|
||||
use crate::pixarray::SubPixel;
|
||||
|
||||
pub mod crx;
|
||||
pub mod deflate;
|
||||
pub mod jpeg;
|
||||
pub mod jpegxl;
|
||||
pub mod ljpeg;
|
||||
pub mod packed;
|
||||
pub mod radc;
|
||||
|
||||
/// Trait for mutable line iterators over image data.
|
||||
///
|
||||
/// This trait is implemented for iterators that yield mutable slices of subpixel data.
|
||||
/// It is used to provide mutable access to each line of an image during decompression.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `'a`: Lifetime of the data.
|
||||
/// - `T`: The subpixel type, which must implement [`SubPixel`].
|
||||
pub trait LineIteratorMut<'a, T>: Iterator<Item = &'a mut [T]> + ExactSizeIterator
|
||||
where
|
||||
T: SubPixel + 'a,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, T, I> LineIteratorMut<'a, T> for I
|
||||
where
|
||||
I: ExactSizeIterator<Item = &'a mut [T]>,
|
||||
T: SubPixel + 'a,
|
||||
{
|
||||
}
|
||||
|
||||
/// Trait for immutable line iterators over image data.
|
||||
///
|
||||
/// This trait is implemented for iterators that yield immutable slices of subpixel data.
|
||||
/// It is used to provide read-only access to each line of an image.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `'a`: Lifetime of the data.
|
||||
/// - `T`: The subpixel type, which must implement [`SubPixel`].
|
||||
pub trait LineIterator<'a, T>: Iterator<Item = &'a [T]> + ExactSizeIterator
|
||||
where
|
||||
T: SubPixel + 'a,
|
||||
{
|
||||
}
|
||||
|
||||
/// Trait for decompressors handling raw image data.
|
||||
///
|
||||
/// Implementors of this trait provide functionality to decompress raw image data into pixel lines (slices).
|
||||
/// The decompressor operates on a source byte slice and writes decompressed data into provided line buffers.
|
||||
///
|
||||
/// # Type Parameters
|
||||
/// - `'a`: Lifetime of the data.
|
||||
/// - `T`: The subpixel type, which must implement [`SubPixel`].
|
||||
pub trait Decompressor<'a, T>: Send + Sync
|
||||
where
|
||||
T: SubPixel + 'a,
|
||||
{
|
||||
/// Decompresses the source data into the provided line buffers.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `src` - Source byte slice containing compressed image data.
|
||||
/// * `skip_rows` - Number of rows to skip before starting decompression.
|
||||
/// * `lines` - Mutable iterator over destination lines to write decompressed data.
|
||||
/// * `line_width` - The width of each line in pixels.
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(())` on success.
|
||||
/// * `Err(String)` with an error message on failure.
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, T>, line_width: usize) -> std::result::Result<(), String>;
|
||||
|
||||
/// Returns `true` if the decompressor is optimized for strip-based processing.
|
||||
fn strips_optimized(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns `true` if the decompressor is optimized for tile-based processing.
|
||||
fn tile_optimized(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
use multiversion::multiversion;
|
||||
|
||||
use crate::bits::{BEf16, BEf24, BEf32, BEu16, LEf16, LEf24, LEf32, LEu16};
|
||||
use crate::decompressors::LineIteratorMut;
|
||||
use crate::pumps::{BitPump, BitPumpMSB};
|
||||
use crate::{bits::Endian, decompressors::Decompressor};
|
||||
|
||||
/// Decompressor for packed data
|
||||
pub struct PackedDecompressor {
|
||||
bps: u32,
|
||||
endian: Endian,
|
||||
}
|
||||
|
||||
impl PackedDecompressor {
|
||||
pub fn new(bps: u32, endian: Endian) -> Self {
|
||||
Self { bps, endian }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, u16> for PackedDecompressor {
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, u16>, line_width: usize) -> std::result::Result<(), String> {
|
||||
match (self.endian, self.bps) {
|
||||
// 16 bits, encoding depends on TIFF endianess
|
||||
(Endian::Big, 16) => unpack_16be(lines, src, skip_rows, line_width),
|
||||
(Endian::Little, 16) => unpack_16le(lines, src, skip_rows, line_width),
|
||||
// 12 Bits, DNG spec says it must be always encoded as big-endian
|
||||
(_, 12) => unpack_12be(lines, src, skip_rows, line_width),
|
||||
// 10 Bits, DNG spec says it must be always encoded as big-endian
|
||||
(_, 10) => unpack_10be(lines, src, skip_rows, line_width),
|
||||
// 8 bits
|
||||
(_, 8) => unpack_8bit(lines, src, skip_rows, line_width),
|
||||
// Generic MSB decoder for exotic packed bit sizes
|
||||
(_, bps) if bps > 0 && bps < 16 => unpack_generic_msb(lines, src, skip_rows, line_width, self.bps),
|
||||
// Unhandled bits
|
||||
(_, bps) => return Err(format_args!("DNG: Don't know how to handle DNG with {} bps", bps).to_string()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn strips_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn tile_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_16be<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * 2)..];
|
||||
for (out, bytes) in line.iter_mut().zip(inb.chunks_exact(2)) {
|
||||
*out = BEu16(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_16le<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * 2)..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(2)) {
|
||||
line[i] = LEu16(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_12be<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * 12 / 8)..];
|
||||
for (o, i) in line.chunks_exact_mut(2).zip(inb.chunks_exact(3)) {
|
||||
let g1: u16 = i[0] as u16;
|
||||
let g2: u16 = i[1] as u16;
|
||||
let g3: u16 = i[2] as u16;
|
||||
|
||||
o[0] = (g1 << 4) | (g2 >> 4);
|
||||
o[1] = ((g2 & 0x0f) << 8) | g3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_10be<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * 10 / 8)..];
|
||||
|
||||
for (o, i) in line.chunks_exact_mut(4).zip(inb.chunks_exact(5)) {
|
||||
let g1: u16 = i[0] as u16;
|
||||
let g2: u16 = i[1] as u16;
|
||||
let g3: u16 = i[2] as u16;
|
||||
let g4: u16 = i[3] as u16;
|
||||
let g5: u16 = i[4] as u16;
|
||||
|
||||
o[0] = (g1 << 2) | (g2 >> 6);
|
||||
o[1] = ((g2 & 0x3f) << 4) | (g3 >> 4);
|
||||
o[2] = ((g3 & 0x0f) << 6) | (g4 >> 2);
|
||||
o[3] = ((g4 & 0x03) << 8) | g5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_8bit<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width)..];
|
||||
for (o, i) in line.iter_mut().zip(inb.iter()) {
|
||||
*o = *i as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_generic_msb<'a>(lines: impl LineIteratorMut<'a, u16>, src: &[u8], skip_rows: usize, width: usize, bits: u32) {
|
||||
assert!(bits <= 16);
|
||||
let skip_bits = skip_rows * width * bits as usize;
|
||||
let offset = skip_bits / 8;
|
||||
let bias = skip_bits % 8;
|
||||
let mut pump = BitPumpMSB::new(&src[offset..]);
|
||||
pump.consume_bits(bias as u32);
|
||||
for line in lines {
|
||||
for p in line {
|
||||
*p = pump.get_bits(bits) as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Decompressor<'a, f32> for PackedDecompressor {
|
||||
fn decompress(&self, src: &[u8], skip_rows: usize, lines: impl LineIteratorMut<'a, f32>, line_width: usize) -> std::result::Result<(), String> {
|
||||
match (self.endian, self.bps) {
|
||||
// 16 bits, encoding depends on TIFF endianess
|
||||
(Endian::Big, 32) => unpack_f32be(lines, src, skip_rows, line_width),
|
||||
(Endian::Little, 32) => unpack_f32le(lines, src, skip_rows, line_width),
|
||||
|
||||
(Endian::Big, 24) => unpack_f24be(lines, src, skip_rows, line_width),
|
||||
(Endian::Little, 24) => unpack_f24le(lines, src, skip_rows, line_width),
|
||||
|
||||
(Endian::Big, 16) => unpack_f16be(lines, src, skip_rows, line_width),
|
||||
(Endian::Little, 16) => unpack_f16le(lines, src, skip_rows, line_width),
|
||||
|
||||
// Unhandled bits
|
||||
(_, bps) => return Err(format_args!("DNG: Don't know how to handle FP DNG with {} bps", bps).to_string()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn strips_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn tile_optimized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f32be<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * size_of::<f32>())..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(4)) {
|
||||
line[i] = BEf32(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f32le<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * size_of::<f32>())..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(4)) {
|
||||
line[i] = LEf32(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f24le<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
const SIZEOF_FP24: usize = 3;
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * SIZEOF_FP24)..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(SIZEOF_FP24)) {
|
||||
line[i] = LEf24(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f24be<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
const SIZEOF_FP24: usize = 3;
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * SIZEOF_FP24)..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(SIZEOF_FP24)) {
|
||||
line[i] = BEf24(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f16le<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
const SIZEOF_FP16: usize = 2;
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * SIZEOF_FP16)..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(SIZEOF_FP16)) {
|
||||
line[i] = LEf16(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
|
||||
fn unpack_f16be<'a>(lines: impl LineIteratorMut<'a, f32>, src: &[u8], skip_rows: usize, width: usize) {
|
||||
const SIZEOF_FP16: usize = 2;
|
||||
for (row, line) in lines.enumerate() {
|
||||
let inb = &src[((skip_rows + row) * width * SIZEOF_FP16)..];
|
||||
for (i, bytes) in (0..width).zip(inb.chunks_exact(SIZEOF_FP16)) {
|
||||
line[i] = BEf16(bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1
|
||||
// Copyright 2024 Daniel Vogelbacher <daniel@chaospixel.com>
|
||||
// Originally written in C in dcraw.c by Dave Coffin
|
||||
//
|
||||
// Kodak Run Adaptive Differential Coding (RADC)
|
||||
|
||||
use rayon::iter::IndexedParallelIterator;
|
||||
use rayon::iter::ParallelIterator;
|
||||
|
||||
use crate::Result;
|
||||
use crate::alloc_image_ok;
|
||||
use crate::bits::LookupTable;
|
||||
use crate::buffer::PaddedBuf;
|
||||
use crate::pixarray::PixU16;
|
||||
use crate::pumps::BitPump;
|
||||
use crate::pumps::BitPumpMSB;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const HUFF_INIT: [(u8, i8); 130] = [
|
||||
(1,1), (2,3), (3,4), (4,2), (5,7), (6,5), (7,6), (7,8),
|
||||
(1,0), (2,1), (3,3), (4,4), (5,2), (6,7), (7,6), (8,5), (8,8),
|
||||
(2,1), (2,3), (3,0), (3,2), (3,4), (4,6), (5,5), (6,7), (6,8),
|
||||
(2,0), (2,1), (2,3), (3,2), (4,4), (5,6), (6,7), (7,5), (7,8),
|
||||
(2,1), (2,4), (3,0), (3,2), (3,3), (4,7), (5,5), (6,6), (6,8),
|
||||
(2,3), (3,1), (3,2), (3,4), (3,5), (3,6), (4,7), (5,0), (5,8),
|
||||
(2,3), (2,6), (3,0), (3,1), (4,4), (4,5), (4,7), (5,2), (5,8),
|
||||
(2,4), (2,7), (3,3), (3,6), (4,1), (4,2), (4,5), (5,0), (5,8),
|
||||
(2,6), (3,1), (3,3), (3,5), (3,7), (3,8), (4,0), (5,2), (5,4),
|
||||
(2,0), (2,1), (3,2), (3,3), (4,4), (4,5), (5,6), (5,7), (4,8),
|
||||
(1,0), (2,2), (2,-2),
|
||||
(1,-3), (1,3),
|
||||
(2,-17), (2,-5), (2,5), (2,17),
|
||||
(2,-7), (2,2), (2,9), (2,18),
|
||||
(2,-18), (2,-9), (2,-2), (2,7),
|
||||
(2,-28), (2,28), (3,-49), (3,-9), (3,9), (4,49), (5,-79), (5,79),
|
||||
(2,-1), (2,13), (2,26), (3,39), (4,-16), (5,55), (6,-37), (6,76),
|
||||
(2,-26), (2,-13), (2,1), (3,-39), (4,16), (5,-55), (6,-76), (6,37)
|
||||
];
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq, PartialOrd)]
|
||||
struct HuffSymbol {
|
||||
bitcnt: u8,
|
||||
value: u8,
|
||||
}
|
||||
|
||||
struct HuffDecoder {
|
||||
cache: [[HuffSymbol; 256]; 19],
|
||||
}
|
||||
|
||||
impl HuffDecoder {
|
||||
/// Create new HuffmanDecoder
|
||||
///
|
||||
/// cbpp is Compressed Bits Per Pixel
|
||||
fn new(cbpp: u8) -> Self {
|
||||
let mut cache = [[HuffSymbol::default(); 256]; 19];
|
||||
let mut a = 0;
|
||||
for x in HUFF_INIT {
|
||||
for _ in 0..(256 >> x.0) {
|
||||
// max bit value in cache
|
||||
cache.as_flattened_mut()[a].bitcnt = x.0;
|
||||
cache.as_flattened_mut()[a].value = x.1 as u8;
|
||||
a += 1;
|
||||
}
|
||||
}
|
||||
for c in 0..256 {
|
||||
cache[18][c].bitcnt = 8 - cbpp;
|
||||
cache[18][c].value = ((c as u8) >> cbpp << cbpp) | (1 << (cbpp - 1));
|
||||
}
|
||||
Self { cache }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn huff_decode(&self, pump: &mut dyn BitPump, tree: usize) -> i8 {
|
||||
let code = pump.peek_bits(8) as usize;
|
||||
let sym = self.cache[tree][code];
|
||||
pump.consume_bits(sym.bitcnt as u32);
|
||||
sym.value as i8
|
||||
}
|
||||
}
|
||||
|
||||
/// Decompress a RADC buffer
|
||||
///
|
||||
/// cbpp is Compressed Bits Per Pixel
|
||||
pub fn decompress(src: &PaddedBuf, width: usize, height: usize, cbpp: u8, dummy: bool) -> Result<PixU16> {
|
||||
log::debug!("RADC decompress with cbpp: {}, width: {}, height: {}", cbpp, width, height);
|
||||
let mut out = alloc_image_ok!(width, height, dummy);
|
||||
|
||||
let mut last: [i16; 3] = [16, 16, 16];
|
||||
let mut mul: [i16; 3];
|
||||
let mut buf: [[[i16; 386]; 3]; 3] = [[[2048; 386]; 3]; 3];
|
||||
|
||||
let tbl = {
|
||||
const PT: [(usize, f32); 6] = [(0, 0.0), (1280, 1344.0), (2320, 3616.0), (3328, 8000.0), (4095, 16383.0), (65535, 16383.0)];
|
||||
let mut curve = vec![0; 65536];
|
||||
for i in 1..PT.len() {
|
||||
for c in PT[i - 1].0..=PT[i].0 {
|
||||
curve[c] = ((c - PT[i - 1].0) as f32 / (PT[i].0 - PT[i - 1].0) as f32 * (PT[i].1 - PT[i - 1].1) + PT[i - 1].1 + 0.5) as u16;
|
||||
}
|
||||
}
|
||||
LookupTable::new_with_bits(&curve, 16)
|
||||
};
|
||||
|
||||
let dec = HuffDecoder::new(cbpp);
|
||||
let mut pump = BitPumpMSB::new(src);
|
||||
|
||||
for row in (0..height).step_by(4) {
|
||||
mul = [pump.get_bits(6) as i16, pump.get_bits(6) as i16, pump.get_bits(6) as i16];
|
||||
|
||||
for c in 0..3 {
|
||||
let predictor = |buf: &[[[i16; 386]; 3]; 3], x: usize, y: usize| -> i16 {
|
||||
(if c > 0 {
|
||||
(buf[c][y - 1][x] as i32 + buf[c][y][x + 1] as i32) / 2
|
||||
} else {
|
||||
(buf[c][y - 1][x + 1] as i32 + 2 * buf[c][y - 1][x] as i32 + buf[c][y][x + 1] as i32) / 4
|
||||
}) as i16
|
||||
};
|
||||
|
||||
let mut val: i32 = ((0x1000000 / (last[c] as i32) + 0x7ff) >> 12) * mul[c] as i32;
|
||||
let s = if val > 65564 { 10 } else { 12 };
|
||||
let x: i32 = (1 << (s - 1)) - 1;
|
||||
val <<= 12 - s;
|
||||
buf[c].as_flattened_mut().iter_mut().for_each(|i| *i = ((*i as i32 * val + x) >> s) as i16);
|
||||
last[c] = mul[c];
|
||||
|
||||
let max = if c == 0 { 1 } else { 0 };
|
||||
for r in 0..=max {
|
||||
buf[c][1][width / 2] = mul[c] << 7;
|
||||
buf[c][2][width / 2] = mul[c] << 7;
|
||||
|
||||
let mut tree = 1;
|
||||
let mut col = width / 2;
|
||||
while col > 0 {
|
||||
tree = dec.huff_decode(&mut pump, tree) as usize;
|
||||
if tree != 0 {
|
||||
col -= 2;
|
||||
if tree == 8 {
|
||||
for y in 1..3 {
|
||||
for x in (col..=(col + 1)).rev() {
|
||||
buf[c][y][x] = (dec.huff_decode(&mut pump, 18) as u8) as i16 * mul[c];
|
||||
assert!(buf[c][y][x] >= 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for y in 1..3 {
|
||||
for x in (col..=(col + 1)).rev() {
|
||||
buf[c][y][x] = dec.huff_decode(&mut pump, tree + 10) as i16 * 16 + predictor(&buf, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
let nreps = if col > 2 { dec.huff_decode(&mut pump, 9) + 1 } else { 1 };
|
||||
|
||||
for rep in 0..8 {
|
||||
if rep < nreps && col > 0 {
|
||||
col -= 2;
|
||||
for y in 1..3 {
|
||||
for x in (col..=(col + 1)).rev() {
|
||||
buf[c][y][x] = predictor(&buf, x, y);
|
||||
}
|
||||
}
|
||||
if rep & 1 > 0 {
|
||||
let step = dec.huff_decode(&mut pump, 10) << 4;
|
||||
for y in 1..3 {
|
||||
for x in (col..=(col + 1)).rev() {
|
||||
buf[c][y][x] += step as i16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if nreps != 9 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for y in 0..2 {
|
||||
for x in 0..(width / 2) {
|
||||
let val = ((buf[c][y + 1][x] as i32) << 4) / mul[c] as i32;
|
||||
let val = if val < 0 { 0 } else { val };
|
||||
if c > 0 {
|
||||
*out.at_mut(row + y * 2 + c - 1, x * 2 + 2 - c) = val as u16;
|
||||
} else {
|
||||
*out.at_mut(row + r * 2 + y, x * 2 + y) = val as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy buffer from buf[c][2] to buf[c][0]
|
||||
// Borrow checker needs this hack...
|
||||
let (dst, src) = buf[c].split_at_mut(2);
|
||||
if c == 0 {
|
||||
dst[0][1..].copy_from_slice(&src[2 - 2][..386 - 1]);
|
||||
} else {
|
||||
dst[0][..].copy_from_slice(&src[2 - 2][..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for y in row..row + 4 {
|
||||
for x in 0..width {
|
||||
if ((x + y) & 1) > 0 {
|
||||
let r = if x > 0 { x - 1 } else { x + 1 };
|
||||
let s = if x + 1 < width { x + 1 } else { x - 1 };
|
||||
let val = (*out.at(y, x) as i32 - 2048) * 2 + ((*out.at(y, r) as i32 + *out.at(y, s) as i32) / 2);
|
||||
if val < 0 {
|
||||
*out.at_mut(y, x) = 0;
|
||||
} else {
|
||||
*out.at_mut(y, x) = val as u16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.par_pixel_rows_mut().enumerate().for_each(|(_row, line)| {
|
||||
let mut random = ((line[0] as u32) << 16) | line[1] as u32;
|
||||
for x in line {
|
||||
*x = tbl.dither(*x, &mut random);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
Reference in New Issue
Block a user