use crate::corner_table::CornerTable;
use crate::decoder_buffer::DecoderBuffer;
use crate::draco_types::DataType;
use crate::geometry_attribute::PointAttribute;
use crate::geometry_indices::{CornerIndex, PointIndex, INVALID_CORNER_INDEX};
use crate::mesh_prediction_scheme_data::MeshPredictionSchemeData;
use crate::point_cloud::PointCloud;
use crate::point_cloud_decoder::PointCloudDecoder;
use crate::prediction_scheme::{
PredictionScheme, PredictionSchemeDecoder, PredictionSchemeMethod,
PredictionSchemeTransformType,
};
use crate::prediction_scheme_constrained_multi_parallelogram::MeshPredictionSchemeConstrainedMultiParallelogramDecoder;
use crate::prediction_scheme_delta::PredictionSchemeDeltaDecoder;
use crate::prediction_scheme_geometric_normal::MeshPredictionSchemeGeometricNormalDecoder;
#[cfg(feature = "legacy_bitstream_decode")]
use crate::prediction_scheme_multi_parallelogram::MeshPredictionSchemeMultiParallelogramDecoder;
use crate::prediction_scheme_normal_octahedron_canonicalized_decoding_transform::PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform;
use crate::prediction_scheme_parallelogram::MeshPredictionSchemeParallelogramDecoder;
#[cfg(feature = "legacy_bitstream_decode")]
use crate::prediction_scheme_tex_coords_deprecated::MeshPredictionSchemeTexCoordsDeprecatedDecoder;
use crate::prediction_scheme_tex_coords_portable::MeshPredictionSchemeTexCoordsPortableDecoder;
use crate::prediction_scheme_wrap::PredictionSchemeWrapDecodingTransform;
use crate::symbol_encoding::{decode_symbols, SymbolEncodingOptions};
pub struct SequentialIntegerAttributeDecoder {
attribute: i32,
prediction_scheme: Option<Box<dyn PredictionSchemeDecoder<'static, i32, i32>>>,
}
fn build_vertex_to_data_map_from_data_to_corner_map(
corner_table: &CornerTable,
data_to_corner_map: &[u32],
vertex_to_data_map: &mut Vec<i32>,
) -> bool {
vertex_to_data_map.resize(corner_table.num_vertices(), -1);
for (data_id, &corner_u32) in data_to_corner_map.iter().enumerate() {
let corner_id = CornerIndex(corner_u32);
if corner_id == INVALID_CORNER_INDEX {
continue;
}
if corner_id.0 as usize >= corner_table.num_corners() {
return false;
}
let v = corner_table.vertex(corner_id).0 as usize;
let Some(slot) = vertex_to_data_map.get_mut(v) else {
return false;
};
*slot = data_id as i32;
}
true
}
fn run_decode_prediction_data<'a, P: PredictionSchemeDecoder<'a, i32, i32> + ?Sized>(
predictor: Option<&mut P>,
buffer: &mut DecoderBuffer,
) -> bool {
let Some(predictor) = predictor else {
debug_log!("Predictor was selected but not initialized");
return false;
};
if !predictor.decode_prediction_data(buffer) {
debug_log!("Failed to decode prediction data");
return false;
}
true
}
fn run_compute_original_values<'a, P: PredictionSchemeDecoder<'a, i32, i32> + ?Sized>(
predictor: Option<&mut P>,
corrections: &[i32],
values: &mut [i32],
num_values: usize,
num_components: usize,
entry_to_point_id_map: Option<crate::prediction_scheme::EntryToPointIdMap<'_>>,
) -> bool {
let Some(predictor) = predictor else {
debug_log!("Predictor was selected but not initialized");
return false;
};
if !predictor.compute_original_values(
corrections,
values,
num_values,
num_components,
entry_to_point_id_map,
) {
debug_log!("Failed to compute original values");
return false;
}
true
}
impl Default for SequentialIntegerAttributeDecoder {
fn default() -> Self {
Self::new()
}
}
impl SequentialIntegerAttributeDecoder {
pub fn new() -> Self {
Self {
attribute: -1,
prediction_scheme: None,
}
}
pub fn init(&mut self, _decoder: &PointCloudDecoder, attribute_id: i32) -> bool {
self.attribute = attribute_id;
true
}
pub fn attribute_id(&self) -> i32 {
self.attribute
}
pub fn set_prediction_scheme(
&mut self,
scheme: Box<dyn PredictionSchemeDecoder<'static, i32, i32>>,
) {
self.prediction_scheme = Some(scheme);
}
#[allow(clippy::too_many_arguments)]
pub fn decode_values(
&mut self,
point_cloud: &mut PointCloud,
point_ids: &[PointIndex],
in_buffer: &mut DecoderBuffer,
corner_table: Option<&CornerTable>,
data_to_corner_map_override: Option<&[u32]>,
vertex_to_data_map_override: Option<&[i32]>,
portable_attribute: Option<&mut PointAttribute>,
portable_parent_attribute: Option<&PointAttribute>,
pre_integer_decode: Option<&mut dyn FnMut(&mut DecoderBuffer<'_>) -> bool>,
) -> bool {
let att_id = self.attribute;
if att_id < 0 {
return false;
}
let num_points = point_ids.len();
if num_points == 0 {
return true;
}
let attribute = if let Some(ref pa) = portable_attribute {
&**pa
} else {
let Ok(attribute) = point_cloud.try_attribute(att_id) else {
return false;
};
attribute
};
let num_components = attribute.num_components() as usize;
let num_values = num_points * num_components;
let method_byte = match in_buffer.decode_u8() {
Ok(v) => v,
Err(_) => {
debug_log!("Failed to decode prediction method");
return false;
}
};
let selected_method = if method_byte == 0xFF || method_byte == 0xFE {
PredictionSchemeMethod::None
} else {
match PredictionSchemeMethod::try_from(method_byte) {
Ok(m) => m,
Err(_) => {
return false;
}
}
};
let mut selected_transform: Option<PredictionSchemeTransformType> = None;
if selected_method != PredictionSchemeMethod::None {
let transform_byte = match in_buffer.decode_u8() {
Ok(v) => v,
Err(_) => return false,
};
if transform_byte != 0xFF {
match PredictionSchemeTransformType::try_from(transform_byte) {
Ok(t) => selected_transform = Some(t),
Err(_) => {
return false;
}
}
}
}
if let Some(ref scheme) = self.prediction_scheme {
if scheme.get_prediction_method() != selected_method {
debug_log!(
"Prediction method mismatch. Stream: {:?}, Scheme: {:?}",
selected_method,
scheme.get_prediction_method()
);
return false;
}
}
let mut predictor_opt: Option<
PredictionSchemeDeltaDecoder<i32, i32, PredictionSchemeWrapDecodingTransform<i32>>,
> = None;
let mut predictor_normal_octa_diff_opt: Option<
PredictionSchemeDeltaDecoder<
i32,
i32,
PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform,
>,
> = None;
let mut predictor_parallelogram_opt: Option<
MeshPredictionSchemeParallelogramDecoder<
i32,
i32,
PredictionSchemeWrapDecodingTransform<i32>,
>,
> = None;
#[cfg(feature = "legacy_bitstream_decode")]
let mut predictor_multi_parallelogram_opt: Option<
MeshPredictionSchemeMultiParallelogramDecoder<
'_,
i32,
i32,
PredictionSchemeWrapDecodingTransform<i32>,
>,
> = None;
let mut predictor_constrained_multi_parallelogram_opt: Option<
MeshPredictionSchemeConstrainedMultiParallelogramDecoder<
'_,
i32,
i32,
PredictionSchemeWrapDecodingTransform<i32>,
>,
> = None;
#[cfg(feature = "legacy_bitstream_decode")]
let mut predictor_tex_coords_deprecated_opt: Option<
MeshPredictionSchemeTexCoordsDeprecatedDecoder<
'_,
PredictionSchemeWrapDecodingTransform<i32>,
>,
> = None;
let mut predictor_tex_coords_opt: Option<MeshPredictionSchemeTexCoordsPortableDecoder> =
None;
let mut predictor_geometric_normal_opt: Option<MeshPredictionSchemeGeometricNormalDecoder> =
None;
let mut vertex_to_data_map: Vec<i32> = Vec::new();
let mut data_to_corner_map: Vec<u32> = Vec::new();
match selected_method {
_ if self.prediction_scheme.is_some() => {
}
PredictionSchemeMethod::Difference => match selected_transform {
Some(PredictionSchemeTransformType::NormalOctahedronCanonicalized) => {
let transform =
PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform::new();
let predictor = PredictionSchemeDeltaDecoder::new(transform);
predictor_normal_octa_diff_opt = Some(predictor);
}
Some(PredictionSchemeTransformType::NormalOctahedron) => {
let mut transform =
PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform::new();
transform.set_canonicalized(false);
let predictor = PredictionSchemeDeltaDecoder::new(transform);
predictor_normal_octa_diff_opt = Some(predictor);
}
_ => {
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let predictor = PredictionSchemeDeltaDecoder::new(transform);
predictor_opt = Some(predictor);
}
},
PredictionSchemeMethod::MeshPredictionParallelogram => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let predictor = MeshPredictionSchemeParallelogramDecoder::new(
attribute, transform, mesh_data,
);
predictor_parallelogram_opt = Some(predictor);
} else {
debug_log!("Parallelogram prediction requires corner table");
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let predictor =
MeshPredictionSchemeMultiParallelogramDecoder::new(transform, mesh_data);
predictor_multi_parallelogram_opt = Some(predictor);
} else {
debug_log!("MultiParallelogram prediction requires corner table");
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
debug_log!("MultiParallelogram prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionConstrainedMultiParallelogram => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let predictor = MeshPredictionSchemeConstrainedMultiParallelogramDecoder::new(
transform, mesh_data,
);
predictor_constrained_multi_parallelogram_opt = Some(predictor);
} else {
debug_log!("ConstrainedMultiParallelogram prediction requires corner table");
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let mut predictor =
MeshPredictionSchemeTexCoordsDeprecatedDecoder::new(transform);
predictor.init(&mesh_data);
let pos_att_id = point_cloud.named_attribute_id(
crate::geometry_attribute::GeometryAttributeType::Position,
);
if pos_att_id >= 0 {
let pos_att = if let Some(attribute) = portable_parent_attribute {
attribute
} else {
let Ok(attribute) = point_cloud.try_attribute(pos_att_id) else {
return false;
};
attribute
};
if !predictor.set_parent_attribute(pos_att) {
debug_log!("Failed to set parent attribute for TexCoordsDeprecated");
return false;
}
} else {
debug_log!("Position attribute not found for TexCoordsDeprecated");
return false;
}
predictor_tex_coords_deprecated_opt = Some(predictor);
} else {
debug_log!("TexCoordsDeprecated prediction requires corner table");
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
debug_log!("TexCoordsDeprecated prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionTexCoordsPortable => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let transform = PredictionSchemeWrapDecodingTransform::<i32>::new();
let mut predictor =
MeshPredictionSchemeTexCoordsPortableDecoder::new(transform);
predictor.init(&mesh_data);
let pos_att_id = point_cloud.named_attribute_id(
crate::geometry_attribute::GeometryAttributeType::Position,
);
if pos_att_id >= 0 {
let pos_att = if let Some(attribute) = portable_parent_attribute {
attribute
} else {
let Ok(attribute) = point_cloud.try_attribute(pos_att_id) else {
return false;
};
attribute
};
if !predictor.set_parent_attribute(pos_att) {
debug_log!("Failed to set parent attribute for TexCoordsPortable");
return false;
}
} else {
debug_log!("Position attribute not found for TexCoordsPortable");
return false;
}
predictor_tex_coords_opt = Some(predictor);
} else {
debug_log!("TexCoordsPortable prediction requires corner table");
return false;
}
}
PredictionSchemeMethod::MeshPredictionGeometricNormal => {
if let Some(corner_table) = corner_table {
data_to_corner_map.resize(num_points, 0);
if let Some(map) = vertex_to_data_map_override {
if map.len() != corner_table.num_vertices() {
debug_log!("Invalid vertex_to_data_map_override length");
return false;
}
vertex_to_data_map.resize(map.len(), 0);
vertex_to_data_map.copy_from_slice(map);
if let Some(dcm) = data_to_corner_map_override {
if dcm.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(dcm);
}
} else if let Some(map) = data_to_corner_map_override {
if map.len() != num_points {
debug_log!("Invalid data_to_corner_map_override length");
return false;
}
data_to_corner_map.copy_from_slice(map);
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
} else {
if !build_vertex_to_data_map_from_data_to_corner_map(
corner_table,
&data_to_corner_map,
&mut vertex_to_data_map,
) {
debug_log!("Invalid data_to_corner_map corner id");
return false;
}
}
let mut mesh_data = MeshPredictionSchemeData::new();
mesh_data.set(corner_table, &data_to_corner_map, &vertex_to_data_map);
let mut transform =
PredictionSchemeNormalOctahedronCanonicalizedDecodingTransform::new();
if matches!(
selected_transform,
Some(PredictionSchemeTransformType::NormalOctahedron)
) {
transform.set_canonicalized(false);
}
let mut predictor = MeshPredictionSchemeGeometricNormalDecoder::new(transform);
predictor.init(&mesh_data);
predictor.set_entry_to_point_id_map(
crate::prediction_scheme::EntryToPointIdMap::from_point_indices(point_ids),
);
let pos_att_id = point_cloud.named_attribute_id(
crate::geometry_attribute::GeometryAttributeType::Position,
);
if pos_att_id >= 0 {
let pos_att = if let Some(attribute) = portable_parent_attribute {
attribute
} else {
let Ok(attribute) = point_cloud.try_attribute(pos_att_id) else {
return false;
};
attribute
};
if !predictor.set_parent_attribute(pos_att) {
debug_log!("Failed to set parent attribute for GeometricNormal");
return false;
}
} else {
debug_log!("Position attribute not found for GeometricNormal");
return false;
}
predictor_geometric_normal_opt = Some(predictor);
} else {
debug_log!("GeometricNormal prediction requires corner table");
return false;
}
}
PredictionSchemeMethod::None => {}
_ => {
debug_log!("Unsupported prediction method: {:?}", selected_method);
return false;
}
}
if let Some(hook) = pre_integer_decode {
if !hook(in_buffer) {
return false;
}
}
let compressed = match in_buffer.decode_u8() {
Ok(v) => v,
Err(_) => return false,
};
let are_corrections_positive = match selected_transform {
Some(PredictionSchemeTransformType::NormalOctahedron)
| Some(PredictionSchemeTransformType::NormalOctahedronCanonicalized) => true,
_ => {
if let Some(ref scheme) = self.prediction_scheme {
scheme.are_corrections_positive()
} else {
false
}
}
};
let needs_zigzag_conversion = !are_corrections_positive;
let corrections: Vec<i32> = if compressed > 0 {
let mut symbols = vec![0u32; num_values];
let options = SymbolEncodingOptions::default();
if !decode_symbols(
num_values,
num_components,
&options,
in_buffer,
&mut symbols,
) {
return false;
}
symbols_to_corrections(symbols, needs_zigzag_conversion)
} else {
let num_bytes = match in_buffer.decode_u8() {
Ok(v) => v as usize,
Err(_) => return false,
};
if num_bytes > 4 {
return false;
}
let mut raw_corrections = Vec::with_capacity(num_values);
if num_bytes == 0 {
raw_corrections.resize(num_values, 0);
} else if num_bytes == 4 {
let Some(byte_len) = num_values.checked_mul(4) else {
return false;
};
let bytes = match in_buffer.decode_slice(byte_len) {
Ok(bytes) => bytes,
Err(_) => return false,
};
for chunk in bytes.chunks_exact(4) {
let symbol = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
raw_corrections.push(symbol_to_correction(symbol, needs_zigzag_conversion));
}
} else {
for _ in 0..num_values {
let mut tmp = [0u8; 4];
if in_buffer.decode_bytes(&mut tmp[..num_bytes]).is_err() {
return false;
}
let symbol = u32::from_le_bytes(tmp);
raw_corrections.push(symbol_to_correction(symbol, needs_zigzag_conversion));
}
}
raw_corrections
};
let mut values = if selected_method == PredictionSchemeMethod::None {
Vec::new()
} else {
vec![0i32; num_values]
};
match selected_method {
_ if self.prediction_scheme.is_some() => {
if !run_decode_prediction_data(self.prediction_scheme.as_deref_mut(), in_buffer) {
return false;
}
}
PredictionSchemeMethod::Difference => {
let ok = if predictor_normal_octa_diff_opt.is_some() {
run_decode_prediction_data(predictor_normal_octa_diff_opt.as_mut(), in_buffer)
} else {
run_decode_prediction_data(predictor_opt.as_mut(), in_buffer)
};
if !ok {
return false;
}
}
PredictionSchemeMethod::MeshPredictionParallelogram => {
if !run_decode_prediction_data(predictor_parallelogram_opt.as_mut(), in_buffer) {
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
if !run_decode_prediction_data(
predictor_multi_parallelogram_opt.as_mut(),
in_buffer,
) {
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
debug_log!("MultiParallelogram prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionConstrainedMultiParallelogram => {
if !run_decode_prediction_data(
predictor_constrained_multi_parallelogram_opt.as_mut(),
in_buffer,
) {
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
if !run_decode_prediction_data(
predictor_tex_coords_deprecated_opt.as_mut(),
in_buffer,
) {
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
debug_log!("TexCoordsDeprecated prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionTexCoordsPortable => {
if !run_decode_prediction_data(predictor_tex_coords_opt.as_mut(), in_buffer) {
return false;
}
}
PredictionSchemeMethod::MeshPredictionGeometricNormal => {
if !run_decode_prediction_data(predictor_geometric_normal_opt.as_mut(), in_buffer) {
return false;
}
}
PredictionSchemeMethod::None => {}
_ => {
return false;
}
}
match selected_method {
_ if self.prediction_scheme.is_some() => {
let map_opt = match selected_method {
PredictionSchemeMethod::MeshPredictionParallelogram
| PredictionSchemeMethod::MeshPredictionMultiParallelogram
| PredictionSchemeMethod::MeshPredictionConstrainedMultiParallelogram
| PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated
| PredictionSchemeMethod::MeshPredictionTexCoordsPortable
| PredictionSchemeMethod::MeshPredictionGeometricNormal => Some(
crate::prediction_scheme::EntryToPointIdMap::from_point_indices(point_ids),
),
_ => None,
};
if !run_compute_original_values(
self.prediction_scheme.as_deref_mut(),
&corrections,
&mut values,
num_values,
num_components,
map_opt,
) {
return false;
}
}
PredictionSchemeMethod::Difference => {
let ok = if predictor_normal_octa_diff_opt.is_some() {
run_compute_original_values(
predictor_normal_octa_diff_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
None,
)
} else {
run_compute_original_values(
predictor_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
None,
)
};
if !ok {
return false;
}
}
PredictionSchemeMethod::MeshPredictionParallelogram => {
if !run_compute_original_values(
predictor_parallelogram_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
None,
) {
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
if !run_compute_original_values(
predictor_multi_parallelogram_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
None,
) {
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionMultiParallelogram => {
debug_log!("MultiParallelogram prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionConstrainedMultiParallelogram => {
if !run_compute_original_values(
predictor_constrained_multi_parallelogram_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
None,
) {
return false;
}
}
#[cfg(feature = "legacy_bitstream_decode")]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
let map = Some(
crate::prediction_scheme::EntryToPointIdMap::from_point_indices(point_ids),
);
if !run_compute_original_values(
predictor_tex_coords_deprecated_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
map,
) {
return false;
}
}
#[cfg(not(feature = "legacy_bitstream_decode"))]
PredictionSchemeMethod::MeshPredictionTexCoordsDeprecated => {
debug_log!("TexCoordsDeprecated prediction is disabled");
return false;
}
PredictionSchemeMethod::MeshPredictionTexCoordsPortable => {
let map = Some(
crate::prediction_scheme::EntryToPointIdMap::from_point_indices(point_ids),
);
if !run_compute_original_values(
predictor_tex_coords_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
map,
) {
return false;
}
}
PredictionSchemeMethod::MeshPredictionGeometricNormal => {
let map = Some(
crate::prediction_scheme::EntryToPointIdMap::from_point_indices(point_ids),
);
if !run_compute_original_values(
predictor_geometric_normal_opt.as_mut(),
&corrections,
&mut values,
num_values,
num_components,
map,
) {
return false;
}
}
PredictionSchemeMethod::None => {
values = corrections;
}
_ => {
debug_log!("Unsupported prediction method: {:?}", selected_method);
return false;
}
}
#[cfg(feature = "debug_logs")]
{
if num_points > 0 {
debug_log!(
"Sequential Decoded: Point 0 ID = {:?}, Value[0] = {}",
point_ids[0],
values[0]
);
debug_log!("DEBUG decoded values (first 25 x/y/z):");
if num_components >= 3 {
for i in 0..std::cmp::min(25, num_points) {
let x = values[i * num_components];
let y = values[i * num_components + 1];
let z = values[i * num_components + 2];
debug_log!(
" data_id={} -> point_ids[{}]={:?}: quantized({}, {}, {})",
i,
i,
point_ids[i],
x,
y,
z
);
}
}
}
}
if let Some(portable_att) = portable_attribute {
if !store_i32_values_to_attribute(portable_att, &values, num_points, num_components) {
return false;
}
} else {
let Ok(dst_attribute) = point_cloud.try_attribute_mut(att_id) else {
return false;
};
if !store_i32_values_to_attribute(dst_attribute, &values, num_points, num_components) {
return false;
}
}
true
}
}
#[inline]
fn symbol_to_correction(symbol: u32, needs_zigzag_conversion: bool) -> i32 {
if needs_zigzag_conversion {
((symbol >> 1) as i32) ^ (-((symbol & 1) as i32))
} else {
symbol as i32
}
}
#[inline]
fn symbols_to_corrections(symbols: Vec<u32>, needs_zigzag_conversion: bool) -> Vec<i32> {
symbols
.into_iter()
.map(|symbol| symbol_to_correction(symbol, needs_zigzag_conversion))
.collect()
}
#[inline]
fn store_i32_values_to_attribute(
attr: &mut PointAttribute,
values: &[i32],
num_points: usize,
num_components: usize,
) -> bool {
let Ok(byte_stride) = usize::try_from(attr.byte_stride()) else {
return false;
};
let data_type = attr.data_type();
let component_size = data_type.byte_length();
let Some(packed_row) = num_components.checked_mul(component_size) else {
return false;
};
let Some(num_values_required) = num_points.checked_mul(num_components) else {
return false;
};
if values.len() < num_values_required {
return false;
}
let Some(required) = num_points.checked_mul(byte_stride) else {
return false;
};
if attr.buffer().data_size() < required && attr.buffer_mut().try_resize(required).is_err() {
return false;
}
if (data_type == DataType::Int32 || data_type == DataType::Uint32) && byte_stride == packed_row
{
let src: &[u8] = bytemuck::cast_slice(&values[..num_values_required]);
let dst = attr.buffer_mut().data_mut();
let Some(dst) = dst.get_mut(..src.len()) else {
return false;
};
dst.copy_from_slice(src);
return true;
}
let dst_buffer = attr.buffer_mut();
for i in 0..num_points {
let Some(entry_offset) = i.checked_mul(byte_stride) else {
return false;
};
for c in 0..num_components {
let Some(component_byte_offset) = c.checked_mul(component_size) else {
return false;
};
let Some(component_offset) = entry_offset.checked_add(component_byte_offset) else {
return false;
};
if !write_value_from_i32(
dst_buffer,
component_offset,
data_type,
values[i * num_components + c],
) {
return false;
}
}
}
true
}
#[inline(always)]
fn write_value_from_i32(
buffer: &mut crate::data_buffer::DataBuffer,
offset: usize,
data_type: DataType,
val: i32,
) -> bool {
match data_type {
DataType::Int8 => buffer.try_write(offset, &(val as i8).to_le_bytes()),
DataType::Uint8 => buffer.try_write(offset, &(val as u8).to_le_bytes()),
DataType::Int16 => buffer.try_write(offset, &(val as i16).to_le_bytes()),
DataType::Uint16 => buffer.try_write(offset, &(val as u16).to_le_bytes()),
DataType::Int32 => buffer.try_write(offset, &val.to_le_bytes()),
DataType::Uint32 => buffer.try_write(offset, &(val as u32).to_le_bytes()),
_ => true,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry_attribute::{GeometryAttributeType, PointAttribute};
use crate::geometry_indices::VertexIndex;
use crate::point_cloud::PointCloud;
#[test]
fn store_i32_values_rejects_short_decoded_values() {
let mut attr = PointAttribute::new();
attr.init(GeometryAttributeType::Generic, 3, DataType::Int16, false, 2);
assert!(!store_i32_values_to_attribute(&mut attr, &[1, 2, 3], 2, 3));
}
#[test]
fn store_i32_values_rejects_impossible_required_size() {
let mut attr = PointAttribute::new();
attr.init(GeometryAttributeType::Generic, 1, DataType::Int32, false, 1);
assert!(!store_i32_values_to_attribute(
&mut attr,
&[1],
usize::MAX,
1,
));
}
#[test]
fn vertex_to_data_map_builder_accepts_valid_corners() {
let mut corner_table = CornerTable::new(1);
assert!(corner_table.init(&[[VertexIndex(0), VertexIndex(1), VertexIndex(2),]]));
let mut vertex_to_data_map = Vec::new();
assert!(build_vertex_to_data_map_from_data_to_corner_map(
&corner_table,
&[0, 1, 2],
&mut vertex_to_data_map,
));
assert_eq!(vertex_to_data_map, vec![0, 1, 2]);
}
#[test]
fn vertex_to_data_map_builder_rejects_out_of_range_corner() {
let mut corner_table = CornerTable::new(1);
assert!(corner_table.init(&[[VertexIndex(0), VertexIndex(1), VertexIndex(2),]]));
let mut vertex_to_data_map = Vec::new();
assert!(!build_vertex_to_data_map_from_data_to_corner_map(
&corner_table,
&[3],
&mut vertex_to_data_map,
));
}
#[test]
fn decode_values_rejects_invalid_attribute_id() {
let mut decoder = SequentialIntegerAttributeDecoder::new();
decoder.init(&PointCloudDecoder::new(), 0);
let mut point_cloud = PointCloud::new();
let mut buffer = DecoderBuffer::new(&[]);
let point_ids = [PointIndex(0)];
assert!(!decoder.decode_values(
&mut point_cloud,
&point_ids,
&mut buffer,
None,
None,
None,
None,
None,
None,
));
}
#[test]
fn decode_values_with_portable_attribute_allows_missing_destination_id() {
let mut decoder = SequentialIntegerAttributeDecoder::new();
decoder.init(&PointCloudDecoder::new(), 0);
let mut point_cloud = PointCloud::new();
let mut portable = PointAttribute::new();
portable.init(GeometryAttributeType::Generic, 1, DataType::Int32, false, 1);
let bytes = [0xfe, 0, 0, 0, 0];
let mut buffer = DecoderBuffer::new(&bytes);
let point_ids = [PointIndex(0)];
assert!(decoder.decode_values(
&mut point_cloud,
&point_ids,
&mut buffer,
None,
None,
None,
Some(&mut portable),
None,
None,
));
}
}