Skip to content

Commit

Permalink
Rewrite frame decoding (#68)
Browse files Browse the repository at this point in the history
* Rewrite frame decoding

* Simplify types

* Simplify DCT API

* Improve accuracy

* Update fixtures

* Extract load_lf_groups
  • Loading branch information
tirr-c authored Aug 7, 2023
1 parent 41007f3 commit dcf1841
Show file tree
Hide file tree
Showing 22 changed files with 1,059 additions and 1,720 deletions.
8 changes: 1 addition & 7 deletions crates/jxl-frame/src/data/lf_global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,6 @@ impl Bundle<(&ImageHeader, &FrameHeader)> for LfGlobal {
}
}

impl LfGlobal {
pub(crate) fn apply_modular_inverse_transform(&mut self) {
self.gmodular.modular.inverse_transform();
}
}

define_bundle! {
#[derive(Debug)]
pub struct LfGlobalVarDct error(crate::Error) {
Expand All @@ -100,7 +94,7 @@ define_bundle! {
}
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct GlobalModular {
pub ma_config: Option<MaConfig>,
pub modular: Modular,
Expand Down
183 changes: 75 additions & 108 deletions crates/jxl-frame/src/data/pass_group.rs
Original file line number Diff line number Diff line change
@@ -1,135 +1,102 @@
use jxl_bitstream::{read_bits, Bitstream, Bundle};
use jxl_bitstream::{Bitstream, Bundle};
use jxl_grid::CutGrid;
use jxl_modular::{ChannelShift, Modular};
use jxl_vardct::{HfCoeff, HfCoeffParams};
use jxl_vardct::{HfCoeffParams, write_hf_coeff};

use crate::{FrameHeader, Result};
use super::{
GlobalModular,
LfGlobal,
LfGlobalVarDct,
LfGroup,
HfGlobal,
};

#[derive(Debug, Clone, Copy)]
pub struct PassGroupParams<'a> {
frame_header: &'a FrameHeader,
gmodular: &'a GlobalModular,
lf_vardct: Option<&'a LfGlobalVarDct>,
lf_group: &'a LfGroup,
hf_global: Option<&'a HfGlobal>,
pass_idx: u32,
group_idx: u32,
shift: Option<(i32, i32)>,
}

impl<'a> PassGroupParams<'a> {
pub fn new(
frame_header: &'a FrameHeader,
lf_global: &'a LfGlobal,
lf_group: &'a LfGroup,
hf_global: Option<&'a HfGlobal>,
pass_idx: u32,
group_idx: u32,
shift: Option<(i32, i32)>,
) -> Self {
Self {
frame_header,
gmodular: &lf_global.gmodular,
lf_vardct: lf_global.vardct.as_ref(),
lf_group,
hf_global,
pass_idx,
group_idx,
shift,
}
}
#[derive(Debug)]
pub struct PassGroupParams<'frame, 'buf, 'g> {
pub frame_header: &'frame FrameHeader,
pub lf_group: &'frame LfGroup,
pub pass_idx: u32,
pub group_idx: u32,
pub shift: Option<(i32, i32)>,
pub gmodular: &'g mut GlobalModular,
pub vardct: Option<PassGroupParamsVardct<'frame, 'buf, 'g>>,
}

#[derive(Debug)]
pub struct PassGroup {
pub hf_coeff: Option<HfCoeff>,
pub modular: Modular,
pub struct PassGroupParamsVardct<'frame, 'buf, 'g> {
pub lf_vardct: &'frame LfGlobalVarDct,
pub hf_global: &'frame HfGlobal,
pub hf_coeff_output: &'buf mut [CutGrid<'g, f32>; 3],
}

impl Bundle<PassGroupParams<'_>> for PassGroup {
type Error = crate::Error;

fn parse<R: std::io::Read>(bitstream: &mut Bitstream<R>, params: PassGroupParams<'_>) -> Result<Self> {
let PassGroupParams {
frame_header,
gmodular,
lf_vardct,
lf_group,
hf_global,
pass_idx,
group_idx,
shift,
} = params;
pub fn decode_pass_group<R: std::io::Read>(
bitstream: &mut Bitstream<R>,
params: PassGroupParams,
) -> Result<()> {
let PassGroupParams {
frame_header,
lf_group,
pass_idx,
group_idx,
shift,
gmodular,
vardct,
} = params;

let hf_coeff = lf_vardct
.zip(lf_group.hf_meta.as_ref())
.zip(hf_global)
.map(|((lf_vardct, hf_meta), hf_global)| {
let hf_pass = &hf_global.hf_passes[pass_idx as usize];
let coeff_shift = frame_header.passes.shift.get(pass_idx as usize)
.copied()
.unwrap_or(0);
if let (Some(PassGroupParamsVardct { lf_vardct, hf_global, hf_coeff_output }), Some(hf_meta)) = (vardct, &lf_group.hf_meta) {
let hf_pass = &hf_global.hf_passes[pass_idx as usize];
let coeff_shift = frame_header.passes.shift.get(pass_idx as usize)
.copied()
.unwrap_or(0);

let group_col = group_idx % frame_header.groups_per_row();
let group_row = group_idx / frame_header.groups_per_row();
let lf_col = (group_col % 8) as usize;
let lf_row = (group_row % 8) as usize;
let group_dim_blocks = (frame_header.group_dim() / 8) as usize;
let group_col = group_idx % frame_header.groups_per_row();
let group_row = group_idx / frame_header.groups_per_row();
let lf_col = (group_col % 8) as usize;
let lf_row = (group_row % 8) as usize;
let group_dim_blocks = (frame_header.group_dim() / 8) as usize;

let block_info = &hf_meta.block_info;
let block_info = &hf_meta.block_info;

let block_left = lf_col * group_dim_blocks;
let block_top = lf_row * group_dim_blocks;
let block_width = (block_info.width() - block_left).min(group_dim_blocks);
let block_height = (block_info.height() - block_top).min(group_dim_blocks);
let block_left = lf_col * group_dim_blocks;
let block_top = lf_row * group_dim_blocks;
let block_width = (block_info.width() - block_left).min(group_dim_blocks);
let block_height = (block_info.height() - block_top).min(group_dim_blocks);

let jpeg_upsampling = frame_header.jpeg_upsampling;
let block_info = block_info.subgrid(block_left, block_top, block_width, block_height);
let lf_quant: Option<[_; 3]> = lf_group.lf_coeff.as_ref().map(|lf_coeff| {
let lf_quant_channels = lf_coeff.lf_quant.image().channel_data();
std::array::from_fn(|idx| {
let lf_quant = &lf_quant_channels[[1, 0, 2][idx]];
let shift = ChannelShift::from_jpeg_upsampling(jpeg_upsampling, idx);
let jpeg_upsampling = frame_header.jpeg_upsampling;
let block_info = block_info.subgrid(block_left, block_top, block_width, block_height);
let lf_quant: Option<[_; 3]> = lf_group.lf_coeff.as_ref().map(|lf_coeff| {
let lf_quant_channels = lf_coeff.lf_quant.image().channel_data();
std::array::from_fn(|idx| {
let lf_quant = &lf_quant_channels[[1, 0, 2][idx]];
let shift = ChannelShift::from_jpeg_upsampling(jpeg_upsampling, idx);

let block_left = block_left >> shift.hshift();
let block_top = block_top >> shift.vshift();
let (block_width, block_height) = shift.shift_size((block_width as u32, block_height as u32));
lf_quant.subgrid(block_left, block_top, block_width as usize, block_height as usize)
})
});

let params = HfCoeffParams {
num_hf_presets: hf_global.num_hf_presets,
hf_block_ctx: &lf_vardct.hf_block_ctx,
block_info,
jpeg_upsampling,
lf_quant,
hf_pass,
coeff_shift,
};
HfCoeff::parse(bitstream, params)
let block_left = block_left >> shift.hshift();
let block_top = block_top >> shift.vshift();
let (block_width, block_height) = shift.shift_size((block_width as u32, block_height as u32));
lf_quant.subgrid(block_left, block_top, block_width as usize, block_height as usize)
})
.transpose()?;
});

let modular = if let Some((minshift, maxshift)) = shift {
let modular_params = gmodular.modular.make_subimage_params_pass_group(gmodular.ma_config.as_ref(), group_idx, minshift, maxshift);
let mut modular = read_bits!(bitstream, Bundle(Modular), modular_params)?;
modular.decode_image(bitstream, 1 + 3 * frame_header.num_lf_groups() + 17 + pass_idx * frame_header.num_groups() + group_idx)?;
modular.inverse_transform();
modular
} else {
Modular::empty()
let params = HfCoeffParams {
num_hf_presets: hf_global.num_hf_presets,
hf_block_ctx: &lf_vardct.hf_block_ctx,
block_info,
jpeg_upsampling,
lf_quant,
hf_pass,
coeff_shift,
};

Ok(Self {
hf_coeff,
modular,
})
write_hf_coeff(bitstream, params, hf_coeff_output)?;
}

if let Some((minshift, maxshift)) = shift {
let modular_params = gmodular.modular.make_subimage_params_pass_group(gmodular.ma_config.as_ref(), group_idx, minshift, maxshift);
let mut modular = Modular::parse(bitstream, modular_params)?;
modular.decode_image(bitstream, 1 + 3 * frame_header.num_lf_groups() + 17 + pass_idx * frame_header.num_groups() + group_idx)?;
modular.inverse_transform();
gmodular.modular.copy_from_modular(modular);
}

Ok(())
}
63 changes: 28 additions & 35 deletions crates/jxl-frame/src/data/toc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pub struct Toc {
num_lf_groups: usize,
num_groups: usize,
groups: Vec<TocGroup>,
bitstream_order: Vec<usize>,
bitstream_to_original: Vec<usize>,
original_to_bitstream: Vec<usize>,
total_size: u64,
}

Expand All @@ -38,7 +39,7 @@ impl std::fmt::Debug for Toc {
"bitstream_order",
&format_args!(
"({})",
if self.bitstream_order.is_empty() { "empty" } else { "non-empty" },
if self.bitstream_to_original.is_empty() { "empty" } else { "non-empty" },
),
)
.finish_non_exhaustive()
Expand Down Expand Up @@ -97,7 +98,7 @@ impl PartialOrd for TocGroupKind {
impl Toc {
/// Returns the offset to the beginning of the data.
pub fn bookmark(&self) -> Bookmark {
let idx = self.bitstream_order.first().copied().unwrap_or(0);
let idx = self.bitstream_to_original.first().copied().unwrap_or(0);
self.groups[idx].offset
}

Expand All @@ -106,31 +107,22 @@ impl Toc {
self.groups.len() <= 1
}

pub fn lf_global(&self) -> TocGroup {
self.groups[0]
}

pub fn lf_group(&self, idx: u32) -> TocGroup {
if self.is_single_entry() {
panic!("cannot obtain LfGroup offset of single entry frame");
} else if (idx as usize) >= self.num_lf_groups {
panic!("index out of range: {} >= {} (num_lf_groups)", idx, self.num_lf_groups);
} else {
self.groups[idx as usize + 1]
}
}

pub fn hf_global(&self) -> TocGroup {
self.groups[self.num_lf_groups + 1]
}
pub fn group_index_bitstream_order(&self, kind: TocGroupKind) -> usize {
let original_order = match kind {
TocGroupKind::All if self.is_single_entry() => 0,
_ if self.is_single_entry() => panic!("Cannot request group type of {:?} for single-group frame", kind),
TocGroupKind::All => panic!("Cannot request group type of All for multi-group frame"),
TocGroupKind::LfGlobal => 0,
TocGroupKind::LfGroup(lf_group_idx) => 1 + lf_group_idx as usize,
TocGroupKind::HfGlobal => 1 + self.num_lf_groups,
TocGroupKind::GroupPass { pass_idx, group_idx } =>
1 + self.num_lf_groups + 1 + pass_idx as usize * self.num_groups + group_idx as usize,
};

pub fn pass_group(&self, pass_idx: u32, group_idx: u32) -> TocGroup {
if self.is_single_entry() {
panic!("cannot obtain PassGroup offset of single entry frame");
if self.original_to_bitstream.is_empty() {
original_order
} else {
let mut idx = 1 + self.num_lf_groups + 1;
idx += (pass_idx as usize * self.num_groups) + group_idx as usize;
self.groups[idx]
self.original_to_bitstream[original_order]
}
}

Expand All @@ -140,10 +132,10 @@ impl Toc {
}

pub fn iter_bitstream_order(&self) -> impl Iterator<Item = TocGroup> + Send {
let groups = if self.bitstream_order.is_empty() {
let groups = if self.bitstream_to_original.is_empty() {
self.groups.clone()
} else {
self.bitstream_order.iter().map(|&idx| self.groups[idx]).collect()
self.bitstream_to_original.iter().map(|&idx| self.groups[idx]).collect()
};
groups.into_iter()
}
Expand Down Expand Up @@ -211,18 +203,18 @@ impl Bundle<&crate::FrameHeader> for Toc {
out
};

let (offsets, sizes, bitstream_order) = if permutated_toc {
let mut bitstream_order = vec![0usize; permutation.len()];
let (offsets, sizes, bitstream_to_original, original_to_bitstream) = if permutated_toc {
let mut bitstream_to_original = vec![0usize; permutation.len()];
let mut offsets_out = Vec::with_capacity(permutation.len());
let mut sizes_out = Vec::with_capacity(permutation.len());
for (idx, perm) in permutation.into_iter().enumerate() {
for (idx, &perm) in permutation.iter().enumerate() {
offsets_out.push(offsets[perm]);
sizes_out.push(sizes[perm]);
bitstream_order[perm] = idx;
bitstream_to_original[perm] = idx;
}
(offsets_out, sizes_out, bitstream_order)
(offsets_out, sizes_out, bitstream_to_original, permutation)
} else {
(offsets, sizes, Vec::new())
(offsets, sizes, Vec::new(), Vec::new())
};

let groups = sizes
Expand All @@ -240,7 +232,8 @@ impl Bundle<&crate::FrameHeader> for Toc {
num_lf_groups: ctx.num_lf_groups() as usize,
num_groups: num_groups as usize,
groups,
bitstream_order,
bitstream_to_original,
original_to_bitstream,
total_size,
})
}
Expand Down
Loading

0 comments on commit dcf1841

Please sign in to comment.