// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. use super::common::precompute_references; use crate::{ bit_reader::BitReader, entropy_coding::decode::{Histograms, SymbolReader}, error::Result, frame::modular::{ IMAGE_OFFSET, IMAGE_PADDING, ModularChannel, Tree, decode::{ common::make_pixel, specialized_trees::{TreeSpecialCase, specialize_tree}, }, predict::{PredictionData, WeightedPredictorState}, tree::{NUM_NONREF_PROPERTIES, PROPERTIES_PER_PREVCHAN, predict}, }, headers::modular::GroupHeader, image::Image, util::tracing_wrappers::*, }; const SMALL_CHANNEL_THRESHOLD: usize = 64; // General case decoder, for small buffers for which it's not worth trying to detect tree special cases. #[inline(never)] fn decode_modular_channel_small( buffers: &mut [&mut ModularChannel], chan: usize, stream_id: usize, header: &GroupHeader, tree: &Tree, reader: &mut SymbolReader, br: &mut BitReader, ) -> Result<()> { let size = buffers[chan].data.size(); let mut wp_state = WeightedPredictorState::new(&header.wp_header, size.0); let mut num_ref_props = tree .max_property_count() .saturating_sub(NUM_NONREF_PROPERTIES); // The precompute_references function stores 4 values per reference property (offset + 0,1,2,3) num_ref_props = num_ref_props.div_ceil(PROPERTIES_PER_PREVCHAN) * PROPERTIES_PER_PREVCHAN; let mut references = Image::::new((num_ref_props, size.0))?; let num_properties = NUM_NONREF_PROPERTIES + num_ref_props; const { assert!(IMAGE_OFFSET.1 == 2) }; for y in 0..size.1 { precompute_references(buffers, chan, y, &mut references); let mut property_buffer: Vec = vec![0; num_properties]; property_buffer[0] = chan as i32; property_buffer[1] = stream_id as i32; let [row, row_top, row_toptop] = buffers[chan].data.distinct_full_rows_mut([y + 2, y + 1, y]); let row = &mut row[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; let row_top = &mut row_top[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; let row_toptop = &mut row_toptop[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; for x in 0..size.0 { let prediction_data = PredictionData::get_rows(row, row_top, row_toptop, x, y); let prediction_result = predict( &tree.nodes, prediction_data, size.0, Some(&mut wp_state), x, y, &references, &mut property_buffer, ); let dec = reader.read_signed(&tree.histograms, br, prediction_result.context as usize); let val = make_pixel(dec, prediction_result.multiplier, prediction_result.guess); row[x] = val; wp_state.update_errors(val, (x, y), size.0); } } Ok(()) } pub(super) trait ModularChannelDecoder { const NEEDS_TOP: bool; const NEEDS_TOPTOP: bool; fn init_row(&mut self, buffers: &mut [&mut ModularChannel], chan: usize, y: usize); fn decode_one( &mut self, prediction_data: PredictionData, pos: (usize, usize), xsize: usize, reader: &mut SymbolReader, br: &mut BitReader, histograms: &Histograms, ) -> i32; } #[inline(never)] fn decode_modular_channel_impl( buffers: &mut [&mut ModularChannel], chan: usize, mut decoder: D, reader: &mut SymbolReader, br: &mut BitReader, histograms: &Histograms, ) -> Result<()> { let size = buffers[chan].data.size(); debug_assert!(size.0 >= 4); debug_assert!(size.1 >= 2); const { assert!(IMAGE_OFFSET.1 == 2) }; // Let the compiler decide whether inlining in the borders is worth it. let do_decode_cold = |decoder: &mut D, prediction_data, pos, reader: &mut SymbolReader, br: &mut BitReader| { decoder.decode_one(prediction_data, pos, size.0, reader, br, histograms) }; for y in 0..size.1 { decoder.init_row(buffers, chan, y); let [row, row_top, row_toptop] = buffers[chan].data.distinct_full_rows_mut([y + 2, y + 1, y]); let row = &mut row[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; let row_top = &mut row_top[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; let row_toptop = &mut row_toptop[IMAGE_OFFSET.0..IMAGE_OFFSET.0 + size.0]; let mut last = 0; let mut prediction_data = PredictionData::default(); for x in 0..2 { prediction_data = PredictionData::get_rows(row, row_top, row_toptop, x, y); let val = do_decode_cold(&mut decoder, prediction_data, (x, y), reader, br); row[x] = val; last = val; } if y < 2 { for x in 2..size.0 - 2 { let prediction_data = PredictionData::get_rows(row, row_top, row_toptop, x, y); let val = do_decode_cold(&mut decoder, prediction_data, (x, y), reader, br); row[x] = val; } } else { for (x, r) in row.iter_mut().enumerate().skip(2).take(size.0 - 4) { prediction_data = prediction_data.update_for_interior_row( row_top, row_toptop, x, last, D::NEEDS_TOP, D::NEEDS_TOPTOP, ); let val = decoder.decode_one(prediction_data, (x, y), size.0, reader, br, histograms); *r = val; last = val; } } for x in size.0 - 2..size.0 { prediction_data = PredictionData::get_rows(row, row_top, row_toptop, x, y); let val = do_decode_cold(&mut decoder, prediction_data, (x, y), reader, br); row[x] = val; } } Ok(()) } #[allow(clippy::too_many_arguments)] #[instrument(level = "debug", skip(buffers, reader, tree))] pub(super) fn decode_modular_channel( buffers: &mut [&mut ModularChannel], chan: usize, stream_id: usize, header: &GroupHeader, tree: &Tree, reader: &mut SymbolReader, br: &mut BitReader, ) -> Result<()> { debug!("reading channel"); let size = buffers[chan].data.size(); if size.0 <= IMAGE_PADDING.0 || size.1 <= IMAGE_PADDING.1 || size.0 * size.1 <= SMALL_CHANNEL_THRESHOLD { return decode_modular_channel_small(buffers, chan, stream_id, header, tree, reader, br); } assert_eq!(buffers[chan].data.padding().1, IMAGE_PADDING.1); assert!(buffers[chan].data.padding().0 >= IMAGE_PADDING.0); assert_eq!(buffers[chan].data.offset(), IMAGE_OFFSET); // We now know the channel has size at least IMAGE_PADDING. let special_tree = specialize_tree(tree, chan, stream_id, size.0, header)?; match special_tree { TreeSpecialCase::NoWp(t) => { decode_modular_channel_impl(buffers, chan, t, reader, br, &tree.histograms) } TreeSpecialCase::WpOnly(t) => { decode_modular_channel_impl(buffers, chan, t, reader, br, &tree.histograms) } TreeSpecialCase::GradientLookup(t) => { decode_modular_channel_impl(buffers, chan, t, reader, br, &tree.histograms) } TreeSpecialCase::SingleGradientOnly(t) => { decode_modular_channel_impl(buffers, chan, t, reader, br, &tree.histograms) } TreeSpecialCase::General(t) => { decode_modular_channel_impl(buffers, chan, t, reader, br, &tree.histograms) } } }