// 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.
usecrate::{
error::{Error, Result},
headers::modular::WeightedHeader,
image::Image,
util::floor_log2_nonzero,
}; use num_derive::FromPrimitive; use num_traits::FromPrimitive;
impl PredictionData { #[inline] pubfn update_for_interior_row( self,
row_top: &[i32],
row_toptop: &[i32],
x: usize,
cur: i32,
needs_top: bool,
needs_toptop: bool,
) -> PredictionData {
debug_assert!(x > 1);
debug_assert!(x + 2 < row_top.len()); let left = cur; let top = self.topright; let topleft = self.top; let topright = self.toprightright; let leftleft = self.left; let toptop = if needs_toptop { row_toptop[x] } else { 0 }; let toprightright = if needs_top { row_top[x + 2] } else { 0 }; Self {
left,
top,
toptop,
topleft,
topright,
leftleft,
toprightright,
}
}
pubfn get_rows(row: &[i32], row_top: &[i32], row_toptop: &[i32], x: usize, y: usize) -> Self { let left = if x > 0 {
row[x - 1]
} elseif y > 0 {
row_top[0]
} else { 0
}; let top = if y > 0 { row_top[x] } else { left }; let topleft = if x > 0 && y > 0 { row_top[x - 1] } else { left }; let topright = if x + 1 < row.len() && y > 0 {
row_top[x + 1]
} else {
top
}; let leftleft = if x > 1 { row[x - 2] } else { left }; let toptop = if y > 1 { row_toptop[x] } else { top }; let toprightright = if x + 2 < row.len() && y > 0 {
row_top[x + 2]
} else {
topright
}; Self {
left,
top,
toptop,
topleft,
topright,
leftleft,
toprightright,
}
}
#[allow(clippy::too_many_arguments)] pubfn get_with_neighbors(
rect: &Image<i32>,
rect_left: Option<&Image<i32>>,
rect_top: Option<&Image<i32>>,
rect_top_left: Option<&Image<i32>>,
rect_right: Option<&Image<i32>>,
rect_top_right: Option<&Image<i32>>,
x: usize,
y: usize,
xsize: usize,
ysize: usize,
) -> Self { let left = if x > 0 {
rect.row(y)[x - 1]
} elseiflet Some(l) = rect_left {
l.row(y)[xsize - 1]
} elseif y > 0 {
rect.row(y - 1)[0]
} elseiflet Some(t) = rect_top {
t.row(ysize - 1)[0]
} else { 0
}; let top = if y > 0 {
rect.row(y - 1)[x]
} elseiflet Some(t) = rect_top {
t.row(ysize - 1)[x]
} else {
left
}; let topleft = if x > 0 { if y > 0 {
rect.row(y - 1)[x - 1]
} elseiflet Some(t) = rect_top {
t.row(ysize - 1)[x - 1]
} else {
left
}
} elseif y > 0 { iflet Some(l) = rect_left {
l.row(y - 1)[xsize - 1]
} else {
left
}
} elseiflet Some(tl) = rect_top_left {
tl.row(ysize - 1)[xsize - 1]
} else {
left
}; let topright = if x + 1 < rect.size().0 { if y > 0 {
rect.row(y - 1)[x + 1]
} elseiflet Some(t) = rect_top {
t.row(ysize - 1)[x + 1]
} else {
top
}
} elseif y > 0 { iflet Some(r) = rect_right {
r.row(y - 1)[0]
} else {
top
}
} elseiflet Some(tr) = rect_top_right {
tr.row(ysize - 1)[0]
} else {
top
}; let leftleft = if x > 1 {
rect.row(y)[x - 2]
} elseiflet Some(l) = rect_left {
l.row(y)[xsize + x - 2]
} else {
left
}; let toptop = if y > 1 {
rect.row(y - 2)[x]
} elseiflet Some(t) = rect_top {
t.row(ysize + y - 2)[x]
} else {
top
}; let toprightright = if x + 2 < rect.size().0 { if y > 0 {
rect.row(y - 1)[x + 2]
} elseiflet Some(t) = rect_top {
t.row(ysize - 1)[x + 2]
} else {
topright
}
} elseif y > 0 { iflet Some(r) = rect_right {
r.row(y - 1)[x + 2 - rect.size().0]
} else {
topright
}
} elseiflet Some(tr) = rect_top_right {
tr.row(ysize - 1)[x + 2 - rect.size().0]
} else {
topright
}; Self {
left,
top,
toptop,
topleft,
topright,
leftleft,
toprightright,
}
}
}
pubfn clamped_gradient(left: i64, top: i64, topleft: i64) -> i64 { // Same code/logic as libjxl. let min = left.min(top); let max = left.max(top); let grad = left + top - topleft; let grad_clamp_max = if topleft < min { max } else { grad }; if topleft > max { min } else { grad_clamp_max }
}
#[inline(always)] fn error_weight(x: u32, maxweight: u32) -> u32 { let shift = floor_log2_nonzero(x as u64 + 1) as i32 - 5; if shift < 0 { 4u32 + maxweight * DIVLOOKUP[x as usize & 63]
} else { 4u32 + ((maxweight * DIVLOOKUP[(x as usize >> shift) & 63]) >> shift)
}
}
#[inline(always)] fn weighted_average(pixels: &[i64; NUM_PREDICTORS], weights: &mut [u32; NUM_PREDICTORS]) -> i64 { let log_weight = floor_log2_nonzero(weights.iter().fold(0u64, |sum, el| sum + *el as u64)); let weight_sum = weights.iter_mut().fold(0, |sum, el| {
*el >>= log_weight - 4;
sum + *el
}); let sum = weights
.iter()
.enumerate()
.fold(((weight_sum >> 1) - 1) as i64, |sum, (i, weight)| {
sum + pixels[i] * *weight as i64
});
(sum * DIVLOOKUP[(weight_sum - 1) as usize] as i64) >> 24
}
#[derive(Debug)] pubstruct WeightedPredictorState {
prediction: [i64; NUM_PREDICTORS],
pred: i64, // Position-major layout: errors for same position are contiguous // Layout: [pos0: p0,p1,p2,p3] [pos1: p0,p1,p2,p3] ...
pred_errors_buffer: Vec<u32>,
error: Vec<i32>,
wp_header: WeightedHeader,
}
impl WeightedPredictorState { pubfn new(wp_header: &WeightedHeader, xsize: usize) -> WeightedPredictorState { let num_errors = (xsize + 2) * 2;
WeightedPredictorState {
prediction: [0; NUM_PREDICTORS],
pred: 0, // Position-major layout: errors for same position are contiguous // Layout: [pos0: p0,p1,p2,p3] [pos1: p0,p1,p2,p3] ... // This gives better cache locality when accessing all predictors for a position
pred_errors_buffer: vec![0; num_errors * NUM_PREDICTORS],
error: vec![0; num_errors],
wp_header: wp_header.clone(),
}
}
/// Get all predictor errors for a given position (contiguous in memory) #[inline(always)] fn get_errors_at_pos(&self, pos: usize) -> &[u32; NUM_PREDICTORS] { let start = pos * NUM_PREDICTORS; self.pred_errors_buffer[start..start + NUM_PREDICTORS]
.try_into()
.unwrap()
}
/// Get mutable reference to all predictor errors for a given position #[inline(always)] fn get_errors_at_pos_mut(&mutself, pos: usize) -> &mut [u32; NUM_PREDICTORS] { let start = pos * NUM_PREDICTORS;
(&mutself.pred_errors_buffer[start..start + NUM_PREDICTORS])
.try_into()
.unwrap()
}
#[inline(always)] pubfn update_errors(&mutself, correct_val: i32, pos: (usize, usize), xsize: usize) { let (cur_row, prev_row) = if pos.1 & 1 != 0 {
(0, xsize + 2)
} else {
(xsize + 2, 0)
}; let val = add_bits(correct_val); self.error[cur_row + pos.0] = (self.pred - val) as i32;
// Compute errors for all predictors letmut errs = [0u32; NUM_PREDICTORS]; for (err, &pred) in errs.iter_mut().zip(self.prediction.iter()) {
*err = (((pred - val).abs() + PREDICTION_ROUND) >> PRED_EXTRA_BITS) as u32;
}
// Write to current position (contiguous access)
*self.get_errors_at_pos_mut(cur_row + pos.0) = errs;
// Update previous row position (contiguous access) let prev_errors = self.get_errors_at_pos_mut(prev_row + pos.0 + 1); for i in0..NUM_PREDICTORS {
prev_errors[i] = prev_errors[i].wrapping_add(errs[i]);
}
}
#[inline(always)] pubfn predict_and_property(
&mutself,
pos: (usize, usize),
xsize: usize,
data: &PredictionData,
) -> (i64, i32) { let (cur_row, prev_row) = if pos.1 & 1 != 0 {
(0, xsize + 2)
} else {
(xsize + 2, 0)
}; let pos_n = prev_row + pos.0; let pos_ne = if pos.0 < xsize - 1 { pos_n + 1 } else { pos_n }; let pos_nw = if pos.0 > 0 { pos_n - 1 } else { pos_n }; // Get errors at the 3 neighboring positions (contiguous access per position) let errors_n = self.get_errors_at_pos(pos_n); let errors_ne = self.get_errors_at_pos(pos_ne); let errors_nw = self.get_errors_at_pos(pos_nw);
letmut weights = [0u32; NUM_PREDICTORS]; for i in0..NUM_PREDICTORS {
weights[i] = error_weight(
errors_n[i]
.wrapping_add(errors_ne[i])
.wrapping_add(errors_nw[i]), self.wp_header.w(i).unwrap(),
);
} let n = add_bits(data.top); let w = add_bits(data.left); let ne = add_bits(data.topright); let nw = add_bits(data.topleft); let nn = add_bits(data.toptop);
let te_w = if pos.0 == 0 { 0
} else { self.error[cur_row + pos.0 - 1] as i64
}; let te_n = self.error[pos_n] as i64; let te_nw = self.error[pos_nw] as i64; let sum_wn = te_n + te_w; let te_ne = self.error[pos_ne] as i64;
letmut p = te_w; if te_n.abs() > p.abs() {
p = te_n;
} if te_nw.abs() > p.abs() {
p = te_nw;
} if te_ne.abs() > p.abs() {
p = te_ne;
}
self.prediction[0] = w + ne - n; self.prediction[1] = n - (((sum_wn + te_ne) * self.wp_header.p1c as i64) >> 5); self.prediction[2] = w - (((sum_wn + te_nw) * self.wp_header.p2c as i64) >> 5); self.prediction[3] = n
- ((te_nw * (self.wp_header.p3ca as i64)
+ (te_n * (self.wp_header.p3cb as i64))
+ (te_ne * (self.wp_header.p3cc as i64))
+ ((nn - n) * (self.wp_header.p3cd as i64))
+ ((nw - w) * (self.wp_header.p3ce as i64)))
>> 5);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.