use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use crate::{
bit_reader::BitReader,
entropy_coding::decode::Histograms,
entropy_coding::decode::SymbolReader,
error::{Error, Result},
features::blending::perform_blending,
frame::{DecoderState, ReferenceFrame},
headers::extra_channels::ExtraChannelInfo,
util::{MemoryTracker, NewWithCapacity, SmallVec, tracing_wrappers::*},
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(usize)]
pub enum PatchContext {
NumRefPatch = 0,
ReferenceFrame = 1,
PatchSize = 2,
PatchReferencePosition = 3,
PatchPosition = 4,
PatchBlendMode = 5,
PatchOffset = 6,
PatchCount = 7,
PatchAlphaChannel = 8,
PatchClamp = 9,
}
impl PatchContext {
const NUM: usize = 10;
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum PatchBlendMode {
None = 0,
Replace = 1,
Add = 2,
Mul = 3,
BlendAbove = 4,
BlendBelow = 5,
AlphaWeightedAddAbove = 6,
AlphaWeightedAddBelow = 7,
}
impl PatchBlendMode {
pub const NUM_BLEND_MODES: u8 = 8;
#[cfg(test)]
fn try_from(i: u8) -> Result<PatchBlendMode> {
match i {
0 => Ok(PatchBlendMode::None),
1 => Ok(PatchBlendMode::Replace),
2 => Ok(PatchBlendMode::Add),
3 => Ok(PatchBlendMode::Mul),
4 => Ok(PatchBlendMode::BlendAbove),
5 => Ok(PatchBlendMode::BlendBelow),
6 => Ok(PatchBlendMode::AlphaWeightedAddAbove),
7 => Ok(PatchBlendMode::AlphaWeightedAddBelow),
_ => Err(Error::PatchesInvalidBlendMode(
i,
PatchBlendMode::NUM_BLEND_MODES,
)),
}
}
#[cfg(test)]
fn random<R: rand::Rng>(rng: &mut R) -> Self {
use rand::distr::{Distribution, Uniform};
Self::try_from(
Uniform::new_inclusive(
PatchBlendMode::None as u8,
PatchBlendMode::AlphaWeightedAddBelow as u8,
)
.unwrap()
.sample(rng),
)
.unwrap()
}
pub fn uses_alpha(self) -> bool {
matches!(
self,
Self::BlendAbove
| Self::BlendBelow
| Self::AlphaWeightedAddAbove
| Self::AlphaWeightedAddBelow
)
}
pub fn uses_clamp(self) -> bool {
self.uses_alpha() || self == Self::Mul
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatchBlending {
pub mode: PatchBlendMode,
pub alpha_channel: usize,
pub clamp: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatchReferencePosition {
reference: usize,
x0: usize,
y0: usize,
xsize: usize,
ysize: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PatchPosition {
x: usize,
y: usize,
ref_pos_idx: usize,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
struct PatchTreeNode {
left_child: isize,
right_child: isize,
y_center: usize,
start: usize,
num: usize,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct PatchesDictionary {
pub positions: Vec<PatchPosition>,
pub ref_positions: Vec<PatchReferencePosition>,
blendings: Vec<PatchBlending>,
blendings_stride: usize,
patch_tree: Vec<PatchTreeNode>,
num_patches: Vec<usize>,
sorted_patches_y0: Vec<(usize, usize)>,
sorted_patches_y1: Vec<(usize, usize)>,
}
impl PatchesDictionary {
#[cfg(test)]
pub fn random<R: rand::Rng>(
size: (usize, usize),
num_extra_channels: usize,
alpha_channel: usize,
reference_frames: usize,
rng: &mut R,
) -> Self {
use rand::distr::{Distribution, Uniform};
let width_dist = Uniform::new_inclusive(0, size.0 - 1).unwrap();
let height_dist = Uniform::new_inclusive(0, size.1 - 1).unwrap();
let num_refs = Uniform::new_inclusive(1, 5).unwrap().sample(rng);
let ref_dist = Uniform::new_inclusive(0, num_refs - 1).unwrap();
let ref_frame_dist = Uniform::new_inclusive(0, reference_frames - 1).unwrap();
let num_patches = Uniform::new_inclusive(num_refs, 10).unwrap().sample(rng);
let mut result = PatchesDictionary {
positions: (0..num_patches)
.map(|_| PatchPosition {
x: width_dist.sample(rng),
y: height_dist.sample(rng),
ref_pos_idx: ref_dist.sample(rng),
})
.collect(),
ref_positions: (0..num_refs)
.map(|_| {
let mut result = PatchReferencePosition {
reference: ref_frame_dist.sample(rng),
x0: width_dist.sample(rng),
y0: height_dist.sample(rng),
xsize: 0,
ysize: 0,
};
result.xsize = Uniform::new_inclusive(1, size.0 - result.x0)
.unwrap()
.sample(rng);
result.ysize = Uniform::new_inclusive(1, size.1 - result.y0)
.unwrap()
.sample(rng);
result
})
.collect(),
blendings: (0..num_patches)
.map(|_| PatchBlending {
mode: PatchBlendMode::random(rng),
alpha_channel,
clamp: Uniform::new_inclusive(0, 1).unwrap().sample(rng) == 0,
})
.collect(),
blendings_stride: num_extra_channels + 1,
patch_tree: vec![],
num_patches: vec![],
sorted_patches_y0: vec![],
sorted_patches_y1: vec![],
};
result.compute_patch_tree().unwrap();
result
}
fn compute_patch_tree(&mut self) -> Result<()> {
#[derive(Debug, Clone, Copy)]
struct PatchInterval {
idx: usize,
y0: usize,
y1: usize,
}
self.patch_tree.clear();
self.num_patches.clear();
self.sorted_patches_y0.clear();
self.sorted_patches_y1.clear();
if self.positions.is_empty() {
return Ok(());
}
let mut intervals: Vec<PatchInterval> = Vec::new_with_capacity(self.positions.len())?;
for (i, pos) in self.positions.iter().enumerate() {
let ref_pos = self.ref_positions[pos.ref_pos_idx];
if ref_pos.xsize > 0 && ref_pos.ysize > 0 {
intervals.push(PatchInterval {
idx: i,
y0: pos.y,
y1: pos.y + self.ref_positions[pos.ref_pos_idx].ysize,
});
}
}
let intervals_len = intervals.len();
let sort_by_y0 = |intervals: &mut Vec<PatchInterval>, start: usize, end: usize| {
intervals[start..end].sort_unstable_by_key(|i| i.y0);
};
let sort_by_y1 = |intervals: &mut Vec<PatchInterval>, start: usize, end: usize| {
intervals[start..end].sort_unstable_by_key(|i| i.y1);
};
sort_by_y1(&mut intervals, 0, intervals_len);
self.num_patches
.resize(intervals.last().map_or(0, |iv| iv.y1), 0); for iv in &intervals {
for y in iv.y0..iv.y1 {
self.num_patches[y] += 1;
}
}
let root = PatchTreeNode {
start: 0,
num: intervals.len(),
..Default::default()
};
self.patch_tree.push(root);
let mut next = 0;
while next < self.patch_tree.len() {
let node = &mut self.patch_tree[next]; let start = node.start;
let end = node.start + node.num;
sort_by_y0(&mut intervals, start, end);
let middle_idx = start + node.num / 2;
node.y_center = intervals[middle_idx].y0;
let mut right_start = middle_idx;
while right_start < end && intervals[right_start].y0 == node.y_center {
right_start += 1;
}
sort_by_y1(&mut intervals, start, right_start);
let mut left_end = right_start;
while left_end > start && intervals[left_end - 1].y1 > node.y_center {
left_end -= 1;
}
node.num = right_start - left_end;
node.start = self.sorted_patches_y0.len();
self.sorted_patches_y1
.try_reserve(right_start.saturating_sub(left_end))?;
self.sorted_patches_y0
.try_reserve(right_start.saturating_sub(left_end))?;
for i in (left_end..right_start).rev() {
self.sorted_patches_y1
.push((intervals[i].y1, intervals[i].idx));
}
sort_by_y0(&mut intervals, left_end, right_start);
for interval in intervals.iter().take(right_start).skip(left_end) {
self.sorted_patches_y0.push((interval.y0, interval.idx));
}
self.patch_tree[next].left_child = -1;
self.patch_tree[next].right_child = -1;
if left_end > start {
let mut left = PatchTreeNode::default();
left.start = start;
left.num = left_end - left.start;
self.patch_tree[next].left_child = self.patch_tree.len() as isize;
self.patch_tree.try_reserve(1)?;
self.patch_tree.push(left);
}
if right_start < end {
let mut right = PatchTreeNode::default();
right.start = right_start;
right.num = end - right.start;
self.patch_tree[next].right_child = self.patch_tree.len() as isize;
self.patch_tree.try_reserve(1)?;
self.patch_tree.push(right);
}
next += 1;
}
Ok(())
}
#[instrument(level = "debug", skip(br, memory_tracker), ret, err)]
pub fn read(
br: &mut BitReader,
xsize: usize,
ysize: usize,
num_extra_channels: usize,
reference_frames: &[Option<ReferenceFrame>],
max_patches_limit: Option<usize>,
memory_tracker: &MemoryTracker,
) -> Result<PatchesDictionary> {
let blendings_stride = num_extra_channels + 1;
let patches_histograms = Histograms::decode(PatchContext::NUM, br, true)?;
let mut patches_reader = SymbolReader::new(&patches_histograms, br, None)?;
let num_ref_patch = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::NumRefPatch as usize,
) as usize;
let num_pixels = xsize
.checked_mul(ysize)
.ok_or(Error::ImageSizeTooLarge(xsize, ysize))?;
let max_ref_patches = 1024usize
.checked_add(num_pixels / 4)
.ok_or(Error::ArithmeticOverflow)?;
let max_patches_calculated = max_ref_patches
.checked_mul(4)
.ok_or(Error::ArithmeticOverflow)?;
let max_patches = match max_patches_limit {
Some(limit) => max_patches_calculated.min(limit),
None => max_patches_calculated,
};
let max_blending_infos = max_patches
.checked_mul(4)
.ok_or(Error::ArithmeticOverflow)?;
if num_ref_patch > max_ref_patches {
return Err(Error::PatchesTooMany(
"reference patches".to_string(),
num_ref_patch,
max_ref_patches,
));
}
let mut total_patches = 0;
let mut next_size = 1;
let mut positions: Vec<PatchPosition> = Vec::new();
let mut blendings = Vec::new();
memory_tracker
.check_alloc((num_ref_patch * std::mem::size_of::<PatchReferencePosition>()) as u64)?;
let mut ref_positions = Vec::new_with_capacity(num_ref_patch)?;
for _ in 0..num_ref_patch {
let reference = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::ReferenceFrame as usize,
) as usize;
if reference >= DecoderState::MAX_STORED_FRAMES {
return Err(Error::PatchesRefTooLarge(
reference,
DecoderState::MAX_STORED_FRAMES,
));
}
let x0 = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchReferencePosition as usize,
) as usize;
let y0 = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchReferencePosition as usize,
) as usize;
let ref_pos_xsize = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchSize as usize,
) as usize
+ 1;
let ref_pos_ysize = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchSize as usize,
) as usize
+ 1;
let reference_frame = &reference_frames[reference];
match reference_frame {
None => return Err(Error::PatchesInvalidReference(reference)),
Some(reference) => {
if !reference.saved_before_color_transform {
return Err(Error::PatchesPostColorTransform());
}
if x0 + ref_pos_xsize > reference.frame[0].size().0 {
return Err(Error::PatchesInvalidPosition(
"x".to_string(),
x0,
ref_pos_xsize,
reference.frame[0].size().0,
));
}
if y0 + ref_pos_ysize > reference.frame[0].size().1 {
return Err(Error::PatchesInvalidPosition(
"y".to_string(),
y0,
ref_pos_ysize,
reference.frame[0].size().1,
));
}
}
}
let id_count = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchCount as usize,
) as usize
+ 1;
if id_count > max_patches + 1 {
return Err(Error::PatchesTooMany(
"patches".to_string(),
id_count,
max_patches,
));
}
total_patches += id_count;
if total_patches > max_patches {
return Err(Error::PatchesTooMany(
"patches".to_string(),
total_patches,
max_patches,
));
}
if next_size < total_patches {
next_size *= 2;
next_size = std::cmp::min(next_size, max_patches);
}
if next_size.saturating_mul(blendings_stride) > max_blending_infos {
return Err(Error::PatchesTooMany(
"blending_info".to_string(),
total_patches,
max_patches,
));
}
positions.try_reserve(next_size.saturating_sub(positions.len()))?;
let blendings_needed = next_size
.checked_mul(PatchBlendMode::NUM_BLEND_MODES as usize)
.ok_or(Error::ArithmeticOverflow)?;
blendings.try_reserve(blendings_needed.saturating_sub(blendings.len()))?;
for i in 0..id_count {
let mut pos = PatchPosition {
x: 0,
y: 0,
ref_pos_idx: ref_positions.len(),
};
if i == 0 {
pos.x = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchPosition as usize,
) as usize;
pos.y = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchPosition as usize,
) as usize;
} else {
let delta_x = patches_reader.read_signed(
&patches_histograms,
br,
PatchContext::PatchOffset as usize,
);
if delta_x < 0 && (-delta_x as usize) > positions.last().unwrap().x {
return Err(Error::PatchesInvalidDelta(
"x".to_string(),
positions.last().unwrap().x,
delta_x,
));
}
pos.x = (positions.last().unwrap().x as i32 + delta_x) as usize;
let delta_y = patches_reader.read_signed(
&patches_histograms,
br,
PatchContext::PatchOffset as usize,
);
if delta_y < 0 && (-delta_y as usize) > positions.last().unwrap().y {
return Err(Error::PatchesInvalidDelta(
"y".to_string(),
positions.last().unwrap().y,
delta_y,
));
}
pos.y = (positions.last().unwrap().y as i32 + delta_y) as usize;
}
if pos.x + ref_pos_xsize > xsize {
return Err(Error::PatchesOutOfBounds(
"x".to_string(),
pos.x,
ref_pos_xsize,
xsize,
));
}
if pos.y + ref_pos_ysize > ysize {
return Err(Error::PatchesOutOfBounds(
"y".to_string(),
pos.y,
ref_pos_ysize,
ysize,
));
}
for _ in 0..blendings_stride {
let mut alpha_channel = 0;
let mut clamp = false;
let maybe_blend_mode = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchBlendMode as usize,
) as u8;
let blend_mode = match PatchBlendMode::from_u8(maybe_blend_mode) {
None => {
return Err(Error::PatchesInvalidBlendMode(
maybe_blend_mode,
PatchBlendMode::NUM_BLEND_MODES,
));
}
Some(blend_mode) => blend_mode,
};
if PatchBlendMode::uses_alpha(blend_mode) && blendings_stride > 2 {
alpha_channel = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchAlphaChannel as usize,
) as usize;
if alpha_channel >= num_extra_channels {
return Err(Error::PatchesInvalidAlphaChannel(
alpha_channel,
num_extra_channels,
));
}
}
if PatchBlendMode::uses_clamp(blend_mode) {
clamp = patches_reader.read_unsigned(
&patches_histograms,
br,
PatchContext::PatchClamp as usize,
) != 0;
}
blendings.push(PatchBlending {
mode: blend_mode,
alpha_channel,
clamp,
});
}
positions.push(pos);
}
ref_positions.push(PatchReferencePosition {
reference,
x0,
y0,
xsize: ref_pos_xsize,
ysize: ref_pos_ysize,
})
}
let mut patches_dict = PatchesDictionary {
positions,
blendings,
ref_positions,
blendings_stride,
num_patches: vec![],
sorted_patches_y0: vec![],
sorted_patches_y1: vec![],
patch_tree: vec![],
};
patches_dict.compute_patch_tree()?;
Ok(patches_dict)
}
pub fn set_patches_for_row(&self, y: usize, patches_for_row_result: &mut Vec<usize>) {
patches_for_row_result.clear();
if self.num_patches.len() <= y || self.num_patches[y] == 0 {
return;
}
let mut tree_idx: isize = 0;
loop {
if tree_idx == -1 {
break;
}
let node = self.patch_tree.get(tree_idx as usize).unwrap_or_else(|| {
panic!("Invalid tree_idx: {tree_idx}");
});
if y <= node.y_center {
for i in 0..node.num {
let p = self.sorted_patches_y0[node.start + i];
if y < p.0 {
break;
}
patches_for_row_result.push(p.1);
}
tree_idx = if y < node.y_center {
node.left_child
} else {
-1
};
} else {
for i in 0..node.num {
let p = self.sorted_patches_y1[node.start + i];
if y >= p.0 {
break;
}
patches_for_row_result.push(p.1);
}
tree_idx = node.right_child;
}
}
patches_for_row_result.sort();
}
pub fn add_one_row(
&self,
row: &mut [&mut [f32]],
row_pos: (usize, usize),
xsize: usize,
extra_channel_info: &[ExtraChannelInfo],
reference_frames: &[Option<ReferenceFrame>],
patches_for_row_result: &mut Vec<usize>,
) {
let num_ec = extra_channel_info.len();
assert!(num_ec + 1 == self.blendings_stride);
let num_channels = 3 + num_ec;
let dummy_fg = [0f32];
let mut fg: SmallVec<[&[f32]; 8]> = SmallVec::new();
for _ in 0..num_channels {
fg.push(&dummy_fg[..]);
}
self.set_patches_for_row(row_pos.1, &mut *patches_for_row_result);
for pos_idx in patches_for_row_result.iter() {
let pos = &self.positions[*pos_idx];
assert!(row_pos.1 >= pos.y); if pos.x >= row_pos.0 + xsize {
continue;
}
let ref_pos = &self.ref_positions[pos.ref_pos_idx];
assert!(pos.y + ref_pos.ysize > row_pos.1); if pos.x + ref_pos.xsize < row_pos.0 {
continue;
}
let (ref_x0, out_x0, ref_xsize) = if pos.x < row_pos.0 {
(
ref_pos.x0 + row_pos.0 - pos.x,
0,
ref_pos.xsize + pos.x - row_pos.0,
)
} else {
(ref_pos.x0, pos.x - row_pos.0, ref_pos.xsize)
};
let (ref_x1, out_x1) = if xsize - out_x0 < ref_xsize {
(ref_x0 + xsize - out_x0, xsize)
} else {
(ref_x0 + ref_xsize, out_x0 + ref_xsize)
};
let ref_pos_y = ref_pos.y0 + row_pos.1 - pos.y;
let ref_frame = reference_frames[ref_pos.reference].as_ref().unwrap();
for (c, fg_ptr) in fg.iter_mut().enumerate().take(3) {
*fg_ptr = &ref_frame.frame[c].row(ref_pos_y)[ref_x0..ref_x1];
}
for i in 0..num_ec {
fg[3 + i] = &ref_frame.frame[3 + i].row(ref_pos_y)[ref_x0..ref_x1];
}
let blending_idx = pos_idx * self.blendings_stride;
let mut bg: SmallVec<[&mut [f32]; 8]> =
row.iter_mut().map(|s| &mut s[out_x0..out_x1]).collect();
perform_blending(
&mut bg,
&fg,
&self.blendings[blending_idx],
&self.blendings[blending_idx + 1..],
extra_channel_info,
);
}
}
}
#[cfg(test)]
mod tests {
mod read_patches_tests {
use super::super::*;
use crate::util::MemoryTracker;
use test_log::test;
#[test]
fn read_single_patch_dict() -> Result<()> {
let mut br = BitReader::new(&[0x12, 0x4a, 0x8c, 0x63, 0x13, 0x01, 0xa6, 0x53, 0x01]);
let got_dict = PatchesDictionary::read(
&mut br,
1024,
1024,
0,
&[Some(ReferenceFrame::blank(1024, 1024, 1, true).unwrap())],
None,
&MemoryTracker::default(),
)?;
let want_dict = PatchesDictionary {
positions: vec![PatchPosition {
x: 10,
y: 20,
ref_pos_idx: 0,
}],
ref_positions: vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 1,
}],
blendings: vec![PatchBlending {
mode: PatchBlendMode::Add,
alpha_channel: 0,
clamp: false,
}],
blendings_stride: 1,
num_patches: vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
],
patch_tree: vec![PatchTreeNode {
left_child: -1,
right_child: -1,
y_center: 20,
start: 0,
num: 1,
}],
sorted_patches_y0: vec![(20, 0)],
sorted_patches_y1: vec![(21, 0)],
};
assert_eq!(got_dict, want_dict);
Ok(())
}
#[test]
fn read_multi_patch_dict() -> Result<()> {
let mut br = BitReader::new(&[
0x12, 0xc6, 0x26, 0x3f, 0x08, 0x4e, 0xb6, 0x0d, 0xf2, 0xde, 0xb6, 0x6d,
]);
let got_dict = PatchesDictionary::read(
&mut br,
1024,
1024,
2,
&[Some(ReferenceFrame::blank(1024, 1024, 1, true).unwrap())],
None,
&MemoryTracker::default(),
)?;
let want_dict = PatchesDictionary {
positions: vec![
PatchPosition {
x: 0,
y: 0,
ref_pos_idx: 0,
},
PatchPosition {
x: 5,
y: 5,
ref_pos_idx: 1,
},
],
ref_positions: vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 2,
ysize: 1,
},
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 2,
},
],
blendings: vec![
PatchBlending {
mode: PatchBlendMode::BlendAbove,
alpha_channel: 1,
clamp: false,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
},
],
blendings_stride: 3,
num_patches: vec![1, 0, 0, 0, 0, 1, 1],
patch_tree: vec![
PatchTreeNode {
left_child: 1,
right_child: -1,
y_center: 5,
start: 0,
num: 1,
},
PatchTreeNode {
left_child: -1,
right_child: -1,
y_center: 0,
start: 1,
num: 1,
},
],
sorted_patches_y0: vec![(5, 1), (0, 0)],
sorted_patches_y1: vec![(7, 1), (1, 0)],
};
assert_eq!(got_dict, want_dict);
Ok(())
}
#[test]
fn read_large_patch_dict() -> Result<()> {
let mut br = BitReader::new(&[
0x12, 0x4e, 0x50, 0x76, 0xeb, 0x41, 0x0d, 0x7e, 0xe5, 0x8e, 0xd2, 0x5d, 0x01,
]);
let got_dict = PatchesDictionary::read(
&mut br,
1024,
1024,
1,
&[Some(ReferenceFrame::blank(1024, 1024, 1, true).unwrap())],
None,
&MemoryTracker::default(),
)?;
let want_dict = PatchesDictionary {
positions: vec![PatchPosition {
x: 2,
y: 3,
ref_pos_idx: 0,
}],
ref_positions: vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 300,
ysize: 200,
}],
blendings: vec![
PatchBlending {
mode: PatchBlendMode::AlphaWeightedAddBelow,
alpha_channel: 0,
clamp: false,
},
PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: false,
},
],
blendings_stride: 2,
num_patches: vec![
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
],
patch_tree: vec![PatchTreeNode {
left_child: -1,
right_child: -1,
y_center: 3,
start: 0,
num: 1,
}],
sorted_patches_y0: vec![(3, 0)],
sorted_patches_y1: vec![(203, 0)],
};
assert_eq!(got_dict, want_dict);
Ok(())
}
#[test]
fn read_clamped_patch_dict() -> Result<()> {
let mut br = BitReader::new(&[0x12, 0xc6, 0x26, 0x1f, 0x70, 0xce, 0x06]);
let got_dict = PatchesDictionary::read(
&mut br,
1024,
1024,
0,
&[Some(ReferenceFrame::blank(1024, 1024, 1, true).unwrap())],
None,
&MemoryTracker::default(),
)?;
let want_dict = PatchesDictionary {
positions: vec![PatchPosition {
x: 4,
y: 4,
ref_pos_idx: 0,
}],
ref_positions: vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 1,
}],
blendings: vec![PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: true,
}],
blendings_stride: 1,
num_patches: vec![0, 0, 0, 0, 1],
patch_tree: vec![PatchTreeNode {
left_child: -1,
right_child: -1,
y_center: 4,
start: 0,
num: 1,
}],
sorted_patches_y0: vec![(4, 0)],
sorted_patches_y1: vec![(5, 0)],
};
assert_eq!(got_dict, want_dict);
Ok(())
}
#[test]
fn read_dup_patch_dict() -> Result<()> {
let mut br = BitReader::new(&[0x12, 0x0a, 0x8d, 0x88, 0x03, 0x31, 0xd7, 0x35]);
let got_dict = PatchesDictionary::read(
&mut br,
1024,
1024,
0,
&[Some(ReferenceFrame::blank(1024, 1024, 1, true).unwrap())],
None,
&MemoryTracker::default(),
)?;
let want_dict = PatchesDictionary {
positions: vec![
PatchPosition {
x: 0,
y: 0,
ref_pos_idx: 0,
},
PatchPosition {
x: 5,
y: 5,
ref_pos_idx: 0,
},
],
ref_positions: vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 1,
}],
blendings: vec![
PatchBlending {
mode: PatchBlendMode::Add,
alpha_channel: 0,
clamp: false,
},
PatchBlending {
mode: PatchBlendMode::Add,
alpha_channel: 0,
clamp: false,
},
],
blendings_stride: 1,
num_patches: vec![1, 0, 0, 0, 0, 1],
patch_tree: vec![
PatchTreeNode {
left_child: 1,
right_child: -1,
y_center: 5,
start: 0,
num: 1,
},
PatchTreeNode {
left_child: -1,
right_child: -1,
y_center: 0,
start: 1,
num: 1,
},
],
sorted_patches_y0: vec![(5, 1), (0, 0)],
sorted_patches_y1: vec![(6, 1), (1, 0)],
};
assert_eq!(got_dict, want_dict);
Ok(())
}
}
mod set_patches_for_row_tests {
use super::super::*;
use test_log::test;
fn create_dictionary(
positions: Vec<PatchPosition>,
ref_positions: Vec<PatchReferencePosition>,
) -> PatchesDictionary {
let mut dict = PatchesDictionary {
positions,
ref_positions,
..Default::default()
};
dict.compute_patch_tree().unwrap();
dict
}
#[test]
fn test_no_patches() {
let dict = create_dictionary(vec![], vec![]);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(0, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_single_patch_hit() {
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 5,
}];
let positions = vec![PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 0,
}];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]); dict.set_patches_for_row(12, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]); dict.set_patches_for_row(14, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]); }
#[test]
fn test_single_patch_miss() {
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 5,
}]; let positions = vec![PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 0,
}];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(9, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>); dict.set_patches_for_row(15, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>); }
#[test]
fn test_single_patch_height_one() {
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 1,
}]; let positions = vec![PatchPosition {
x: 0,
y: 5,
ref_pos_idx: 0,
}];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(4, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(5, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]);
dict.set_patches_for_row(6, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_multiple_patches_non_overlapping() {
let ref_positions = vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 3,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 2,
}, ];
let positions = vec![
PatchPosition {
x: 0,
y: 5,
ref_pos_idx: 0,
},
PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 1,
},
];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(4, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(5, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]);
dict.set_patches_for_row(7, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]);
dict.set_patches_for_row(8, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>); dict.set_patches_for_row(9, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>); dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(11, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(12, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_multiple_patches_overlapping() {
let ref_positions = vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 5,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 4,
}, ];
let positions = vec![
PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 0,
}, PatchPosition {
x: 0,
y: 12,
ref_pos_idx: 1,
}, ];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]); dict.set_patches_for_row(11, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]); dict.set_patches_for_row(12, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 1]); dict.set_patches_for_row(13, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 1]); dict.set_patches_for_row(14, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 1]); dict.set_patches_for_row(15, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]); dict.set_patches_for_row(16, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_multiple_patches_adjacent() {
let ref_positions = vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 2,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 3,
}, ];
let positions = vec![
PatchPosition {
x: 0,
y: 5,
ref_pos_idx: 0,
},
PatchPosition {
x: 0,
y: 7,
ref_pos_idx: 1,
},
];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(4, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(5, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]);
dict.set_patches_for_row(6, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0]);
dict.set_patches_for_row(7, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]); dict.set_patches_for_row(8, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(9, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_multiple_patches_same_start_different_heights() {
let ref_positions = vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 2,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 10,
ysize: 4,
}, ];
let positions = vec![
PatchPosition {
x: 0,
y: 3,
ref_pos_idx: 0,
}, PatchPosition {
x: 0,
y: 3,
ref_pos_idx: 1,
}, ];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(2, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(3, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 1]); dict.set_patches_for_row(4, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 1]); dict.set_patches_for_row(5, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]); dict.set_patches_for_row(6, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]); dict.set_patches_for_row(7, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
#[test]
fn test_patches_out_of_order_definition() {
let ref_positions = vec![
PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 5,
ysize: 3,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 5,
ysize: 3,
}, PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 5,
ysize: 3,
}, ];
let positions = vec![
PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 0,
}, PatchPosition {
x: 0,
y: 5,
ref_pos_idx: 1,
}, PatchPosition {
x: 0,
y: 10,
ref_pos_idx: 2,
}, ];
let dict = create_dictionary(positions, ref_positions);
let mut patches_for_row_result = vec![];
dict.set_patches_for_row(4, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(5, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(6, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(7, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![1]);
dict.set_patches_for_row(8, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(9, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
dict.set_patches_for_row(10, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 2]); dict.set_patches_for_row(11, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 2]);
dict.set_patches_for_row(12, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![0, 2]);
dict.set_patches_for_row(13, &mut patches_for_row_result);
assert_eq!(patches_for_row_result, vec![] as Vec<usize>);
}
}
mod add_one_row_tests {
use super::super::*;
use crate::{
headers::{bit_depth::BitDepth, extra_channels::ExtraChannel},
image::Image,
util::test::assert_all_almost_abs_eq,
};
const MAX_ABS_DELTA: f32 = 1e-6;
fn create_reference_frame(
width: usize,
height: usize,
channel_values: &[f32],
) -> Result<Option<ReferenceFrame>> {
let mut frame_channels = Vec::new();
for v in channel_values.iter() {
let img = Image::new_with_value((width, height), *v)?;
frame_channels.push(img);
}
Ok(Some(ReferenceFrame {
frame: frame_channels,
saved_before_color_transform: true,
}))
}
fn create_reference_frame_single_row<const N: usize>(
rows: &[[f32; N]],
) -> Result<Option<ReferenceFrame>> {
let mut frame_channels = Vec::new();
for v in rows.iter() {
let mut img = Image::new((N, 1))?;
img.row_mut(0).copy_from_slice(v);
frame_channels.push(img);
}
Ok(Some(ReferenceFrame {
frame: frame_channels,
saved_before_color_transform: true,
}))
}
#[test]
fn test_add_one_row_simple_replace() -> Result<()> {
let xsize = 10;
let ref_frames = vec![create_reference_frame(xsize, 1, &[1.0; 3])?];
let extra_channel_info: Vec<ExtraChannelInfo> = Vec::new();
let ref_positions = vec![PatchReferencePosition {
reference: 0, x0: 2,
y0: 0,
xsize: 3,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 2,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![PatchBlending {
mode: PatchBlendMode::Replace,
alpha_channel: 0,
clamp: false, }];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1 + extra_channel_info.len(),
patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let mut r_data: Vec<f32> = vec![0.0; xsize];
let mut g_data: Vec<f32> = vec![0.0; xsize];
let mut b_data: Vec<f32> = vec![0.0; xsize];
let mut row_slices: Vec<&mut [f32]> = vec![&mut r_data, &mut g_data, &mut b_data];
let expected_r = vec![0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0];
patches_dict.add_one_row(
&mut row_slices,
(0, 0),
xsize,
&extra_channel_info,
&ref_frames, &mut vec![],
);
assert_all_almost_abs_eq(&r_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &expected_r, MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_simple_add() -> Result<()> {
let xsize = 10;
let y_coord = 0;
let ref_frames = vec![create_reference_frame(xsize, 1, &[0.2; 3])?];
let extra_channel_info: Vec<ExtraChannelInfo> = Vec::new();
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 2,
y0: 0,
xsize: 3,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 2,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![PatchBlending {
mode: PatchBlendMode::Add,
alpha_channel: 0,
clamp: false, }];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1 + extra_channel_info.len(),
patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let mut r_data: Vec<f32> = vec![0.5; xsize];
let mut g_data: Vec<f32> = vec![0.5; xsize];
let mut b_data: Vec<f32> = vec![0.5; xsize];
let mut row_slices: Vec<&mut [f32]> = vec![&mut r_data, &mut g_data, &mut b_data];
let mut expected_r: Vec<f32> = vec![0.5; xsize];
for r in expected_r.iter_mut().take(5).skip(2) {
*r = 0.5 + 0.2
}
patches_dict.add_one_row(
&mut row_slices,
(0, y_coord),
xsize,
&extra_channel_info,
&ref_frames,
&mut vec![],
);
assert_all_almost_abs_eq(&r_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &expected_r, MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_overlapping_replace() -> Result<()> {
let xsize = 10;
let y_coord = 0;
let main_ref_frame1 = create_reference_frame(xsize, 1, &[1.0; 3])?;
let main_ref_frame2 = create_reference_frame(xsize, 1, &[2.0; 3])?;
let ref_frames = vec![main_ref_frame1, main_ref_frame2];
let extra_channel_info: Vec<ExtraChannelInfo> = Vec::new();
let ref_positions = vec![
PatchReferencePosition {
reference: 0, x0: 0,
y0: 0,
xsize: 4,
ysize: 1,
},
PatchReferencePosition {
reference: 1, x0: 0,
y0: 0,
xsize: 3,
ysize: 1,
},
];
let positions = vec![
PatchPosition {
x: 2,
y: 0,
ref_pos_idx: 0,
}, PatchPosition {
x: 4,
y: 0,
ref_pos_idx: 1,
}, ];
let blendings = vec![
PatchBlending {
mode: PatchBlendMode::Replace,
alpha_channel: 0,
clamp: false, }, PatchBlending {
mode: PatchBlendMode::Replace,
alpha_channel: 0,
clamp: false, }, ];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1 + extra_channel_info.len(),
patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let mut r_data: Vec<f32> = vec![0.0; xsize];
let mut g_data: Vec<f32> = vec![0.0; xsize];
let mut b_data: Vec<f32> = vec![0.0; xsize];
let mut row_slices: Vec<&mut [f32]> = vec![&mut r_data, &mut g_data, &mut b_data];
let expected_r: Vec<f32> = vec![0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0];
patches_dict.add_one_row(
&mut row_slices,
(0, y_coord),
xsize,
&extra_channel_info,
&ref_frames,
&mut vec![],
);
assert_all_almost_abs_eq(&r_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &expected_r, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &expected_r, MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_blend_above_ec_alpha_non_associated() -> Result<()> {
let xsize = 1;
let y_coord = 0;
let initial_color_val = 0.1;
let initial_ec0_alpha = 0.4;
let ref_color_val = 0.8;
let ref_ec0_alpha_val = 0.5;
let ec_info = vec![ExtraChannelInfo::new(
true,
ExtraChannel::Alpha,
BitDepth::f32(),
0,
"AlphaEC".to_string(),
false, None,
None,
)];
let main_ref_frame = create_reference_frame(
xsize,
1,
&[
ref_color_val,
ref_color_val,
ref_color_val,
ref_ec0_alpha_val,
],
)?;
let ref_frames = vec![main_ref_frame];
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 0,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![
PatchBlending {
mode: PatchBlendMode::BlendAbove,
alpha_channel: 0, clamp: false, }, PatchBlending {
mode: PatchBlendMode::BlendAbove,
alpha_channel: 0, clamp: false, }, ];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1 + ec_info.len(), patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let mut r_data = vec![initial_color_val; xsize];
let mut g_data = vec![initial_color_val; xsize];
let mut b_data = vec![initial_color_val; xsize];
let mut ec0_data = vec![initial_ec0_alpha; xsize];
let mut row_slices: Vec<&mut [f32]> =
vec![&mut r_data, &mut g_data, &mut b_data, &mut ec0_data];
let canvas_alpha_val = initial_ec0_alpha; let patch_alpha_val = ref_ec0_alpha_val;
let expected_ec0 = canvas_alpha_val + patch_alpha_val * (1.0 - canvas_alpha_val);
let canvas_color_val = initial_color_val; let patch_color_val = ref_color_val;
let expected_color = if expected_ec0.abs() < 1e-5 {
0.0
} else {
(canvas_color_val * canvas_alpha_val * (1.0 - patch_alpha_val)
+ patch_color_val * patch_alpha_val)
/ expected_ec0
};
patches_dict.add_one_row(
&mut row_slices,
(0, y_coord),
xsize,
&ec_info,
&ref_frames,
&mut vec![],
);
assert_all_almost_abs_eq(&r_data, &vec![expected_color], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &vec![expected_color], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &vec![expected_color], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&ec0_data, &vec![expected_ec0], MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_blend_above_ec_alpha_associated() -> Result<()> {
let xsize = 1;
let y_coord = 0;
let initial_color_val = 0.1; let initial_ec0_alpha = 0.4; let ref_color_val = 0.8; let ref_ec0_alpha_val = 0.5;
let ec_info = vec![ExtraChannelInfo::new(
true,
ExtraChannel::Alpha,
BitDepth::f32(),
0,
"AlphaEC".to_string(),
true, None,
None,
)];
let main_ref_frame = create_reference_frame(
xsize,
1,
&[
ref_color_val,
ref_color_val,
ref_color_val,
ref_ec0_alpha_val,
],
)?;
let ref_frames = vec![main_ref_frame];
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 1,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 0,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![
PatchBlending {
mode: PatchBlendMode::BlendAbove,
alpha_channel: 0,
clamp: false, }, PatchBlending {
mode: PatchBlendMode::BlendAbove,
alpha_channel: 0,
clamp: false, }, ];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1 + ec_info.len(),
patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let mut r_data = vec![initial_color_val; xsize];
let mut g_data = vec![initial_color_val; xsize];
let mut b_data = vec![initial_color_val; xsize];
let mut ec0_data = vec![initial_ec0_alpha; xsize];
let mut row_slices: Vec<&mut [f32]> =
vec![&mut r_data, &mut g_data, &mut b_data, &mut ec0_data];
let expected_ec0 = ref_ec0_alpha_val + initial_ec0_alpha * (1.0 - ref_ec0_alpha_val);
let expected_color = ref_color_val + initial_color_val * (1.0 - ref_ec0_alpha_val);
patches_dict.add_one_row(
&mut row_slices,
(0, y_coord),
xsize,
&ec_info,
&ref_frames,
&mut vec![],
);
assert_all_almost_abs_eq(&ec0_data, &vec![expected_ec0], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&r_data, &vec![expected_color], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &vec![expected_color], MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &vec![expected_color], MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_mul_blend() -> Result<()> {
let xsize = 2;
let y_coord = 0;
let initial_vals = [0.5, 2.0];
let ref_vals = [0.8, 0.7];
let main_ref_frame = create_reference_frame_single_row(&[ref_vals; 3])?;
let dummy_ref_frame1 = create_reference_frame_single_row(&[[0.0; 2]; 3])?;
let dummy_ref_frame2 = create_reference_frame_single_row(&[[0.0; 2]; 3])?;
let ref_frames = vec![main_ref_frame, dummy_ref_frame1, dummy_ref_frame2];
let extra_channel_info: Vec<ExtraChannelInfo> = Vec::new();
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 0,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![PatchBlending {
mode: PatchBlendMode::Mul,
alpha_channel: 0,
clamp: false, }];
let mut dict = PatchesDictionary {
positions: positions.clone(),
ref_positions: ref_positions.clone(),
blendings,
blendings_stride: 1, patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
dict.compute_patch_tree()?;
let mut r_data = initial_vals;
let mut g_data = initial_vals;
let mut b_data = initial_vals;
let mut slices: Vec<&mut [f32]> = vec![&mut r_data, &mut g_data, &mut b_data];
dict.add_one_row(
&mut slices,
(0, y_coord),
xsize,
&extra_channel_info,
&ref_frames,
&mut vec![],
);
let expected_vals = [0.5 * 0.8, 2.0 * 0.7]; assert_all_almost_abs_eq(&r_data, &expected_vals, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &expected_vals, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &expected_vals, MAX_ABS_DELTA);
Ok(())
}
#[test]
fn test_add_one_row_none_blend() -> Result<()> {
let xsize = 5;
let y_coord = 0;
let main_ref_frame = create_reference_frame(xsize, 1, &[100.0; 3])?;
let dummy_ref_frame1 = create_reference_frame(xsize, 1, &[0.0; 3])?;
let dummy_ref_frame2 = create_reference_frame(xsize, 1, &[0.0; 3])?;
let ref_frames = vec![main_ref_frame, dummy_ref_frame1, dummy_ref_frame2];
let extra_channel_info: Vec<ExtraChannelInfo> = Vec::new();
let ref_positions = vec![PatchReferencePosition {
reference: 0,
x0: 0,
y0: 0,
xsize: 3,
ysize: 1,
}];
let positions = vec![PatchPosition {
x: 1,
y: 0,
ref_pos_idx: 0,
}];
let blendings = vec![PatchBlending {
mode: PatchBlendMode::None,
alpha_channel: 0,
clamp: false, }];
let mut patches_dict = PatchesDictionary {
positions,
ref_positions,
blendings,
blendings_stride: 1, patch_tree: Vec::new(),
num_patches: Vec::new(),
sorted_patches_y0: Vec::new(),
sorted_patches_y1: Vec::new(),
};
patches_dict.compute_patch_tree()?;
let initial_data: Vec<f32> = (0..xsize).map(|i| i as f32 * 0.1 + 0.05).collect();
let mut r_data = initial_data.clone();
let mut g_data = initial_data.clone();
let mut b_data = initial_data.clone();
let mut row_slices: Vec<&mut [f32]> = vec![&mut r_data, &mut g_data, &mut b_data];
patches_dict.add_one_row(
&mut row_slices,
(0, y_coord),
xsize,
&extra_channel_info,
&ref_frames,
&mut vec![],
);
assert_all_almost_abs_eq(&r_data, &initial_data, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&g_data, &initial_data, MAX_ABS_DELTA);
assert_all_almost_abs_eq(&b_data, &initial_data, MAX_ABS_DELTA);
Ok(())
}
}
}