use crate::attribute_quantization_transform::AttributeQuantizationTransform;
#[cfg(feature = "legacy_bitstream_decode")]
use crate::attribute_transform::AttributeTransform;
use crate::corner_table::CornerTable;
use crate::decoder_buffer::DecoderBuffer;
use crate::draco_types::DataType;
use crate::geometry_attribute::PointAttribute;
use crate::point_cloud::PointCloud;
use crate::point_cloud_decoder::PointCloudDecoder;
use crate::prediction_scheme::EntryToPointIdMap;
use crate::sequential_integer_attribute_decoder::{
PortableExtent, SequentialIntegerAttributeDecoder,
};
use crate::status::{DracoError, Status};
pub struct SequentialQuantizationAttributeDecoder {
base: SequentialIntegerAttributeDecoder,
transform: AttributeQuantizationTransform,
}
impl Default for SequentialQuantizationAttributeDecoder {
fn default() -> Self {
Self::new()
}
}
impl SequentialQuantizationAttributeDecoder {
pub fn new() -> Self {
Self {
base: SequentialIntegerAttributeDecoder::new(),
transform: AttributeQuantizationTransform::new(),
}
}
pub fn transform(&self) -> &AttributeQuantizationTransform {
&self.transform
}
pub fn into_transform(self) -> AttributeQuantizationTransform {
self.transform
}
pub fn init(
&mut self,
decoder: &PointCloudDecoder,
point_cloud: &PointCloud,
attribute_id: i32,
) -> Status {
self.base.init(decoder, attribute_id);
let attribute = point_cloud.try_attribute(attribute_id)?;
if attribute.data_type() != DataType::Float32 {
return Err(DracoError::general(format!(
"Quantized decoding needs float32 values, attribute {attribute_id} is {:?}",
attribute.data_type()
)));
}
Ok(())
}
#[cfg(feature = "legacy_bitstream_decode")]
fn read_inline_parameters(
&mut self,
point_cloud: &PointCloud,
attribute_id: i32,
buffer: &mut DecoderBuffer,
) -> Result<usize, DracoError> {
let saved_pos = buffer.position();
let method_byte = buffer
.decode_u8()
.map_err(|_| DracoError::general("Failed to read prediction method".to_string()))?;
let carries_transform = crate::point_cloud_decoder::carries_transform_byte(method_byte);
if carries_transform {
buffer
.decode_u8()
.map_err(|_| DracoError::general("Failed to read transform type".to_string()))?;
}
let original = point_cloud.try_attribute(attribute_id)?;
self.transform
.decode_parameters(original, buffer)
.map_err(|e| {
DracoError::general(format!(
"Failed to decode quantization parameters (v<2.0): {e}"
))
})?;
let bytes_consumed = buffer.position() - saved_pos;
let header_bytes = if carries_transform { 2 } else { 1 };
buffer
.set_position(saved_pos)
.map_err(|_| DracoError::general("Failed to reset buffer position".to_string()))?;
Ok(bytes_consumed - header_bytes)
}
#[allow(clippy::too_many_arguments)]
pub fn decode_values(
&mut self,
point_cloud: &mut PointCloud,
point_ids: EntryToPointIdMap<'_>,
buffer: &mut DecoderBuffer,
bitstream_version: u16,
extent: PortableExtent,
corner_table: Option<&CornerTable>,
data_to_corner_map: Option<&[u32]>,
vertex_to_data_map: Option<&[i32]>,
portable_parent_attribute: Option<&PointAttribute>,
) -> Result<PointAttribute, DracoError> {
let attribute_id = self.base.attribute_id();
let (attribute_type, num_components) = {
let original = point_cloud.try_attribute(attribute_id)?;
(original.attribute_type(), original.num_components())
};
let mut portable = PointAttribute::default();
extent.init(
&mut portable,
attribute_type,
num_components,
DataType::Uint32,
false,
)?;
let skip_bytes = if bitstream_version < 0x0200 {
#[cfg(not(feature = "legacy_bitstream_decode"))]
{
return Err(DracoError::bitstream_version_unsupported());
}
#[cfg(feature = "legacy_bitstream_decode")]
{
self.read_inline_parameters(point_cloud, attribute_id, buffer)?
}
} else {
0
};
let mut skip = move |buf: &mut DecoderBuffer<'_>| -> bool {
skip_bytes == 0 || buf.try_advance(skip_bytes).is_ok()
};
let hook: Option<&mut dyn FnMut(&mut DecoderBuffer<'_>) -> bool> = if skip_bytes > 0 {
Some(&mut skip)
} else {
None
};
self.base.decode_values(
point_cloud,
point_ids,
buffer,
corner_table,
data_to_corner_map,
vertex_to_data_map,
Some(&mut portable),
portable_parent_attribute,
hook,
)?;
Ok(portable)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry_attribute::GeometryAttributeType;
#[test]
fn init_refuses_an_attribute_quantization_cannot_describe() {
let mut point_cloud = PointCloud::new();
point_cloud.set_num_points(1);
let mut attribute = PointAttribute::new();
attribute.init(
GeometryAttributeType::Position,
3,
DataType::Uint32,
false,
1,
);
point_cloud.add_attribute(attribute);
let error = SequentialQuantizationAttributeDecoder::new()
.init(&PointCloudDecoder::new(), &point_cloud, 0)
.expect_err("integers are not quantized");
assert!(
error.to_string().contains("float32"),
"unexpected message: {error}"
);
}
}