mod generate;
pub use generate::BuildError;
use alloc::{vec, vec::Vec};
use core::num::NonZeroU8;
use crate::util::{bytes_to_u32, read_bytes, read_u16, shift_and_mask_lower};
use crate::{ColorModel, ColorPrimaries, ParseError, TransferFunction};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Block {
Basic(Basic),
Unknown { header: BlockHeader, data: Vec<u8> },
}
impl Block {
pub(crate) fn parse(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
let (header, descriptor_block_size) = BlockHeader::from_bytes(
&bytes
.get(..BlockHeader::LENGTH)
.ok_or(ParseError::UnexpectedEnd)?
.try_into()
.unwrap(),
)?;
if descriptor_block_size == 0 {
return Err(ParseError::UnexpectedEnd);
}
let data = &bytes
.get(BlockHeader::LENGTH..descriptor_block_size)
.ok_or(ParseError::UnexpectedEnd)?;
let block = match header {
BlockHeader::BASIC => Block::Basic(Basic::parse(data)?),
_ => Block::Unknown {
header,
data: data.to_vec(),
},
};
Ok((block, descriptor_block_size))
}
pub fn serialized_length(&self) -> usize {
let data_length = match self {
Block::Basic(basic) => basic.serialized_length(),
Block::Unknown { data, .. } => data.len(),
};
BlockHeader::LENGTH + data_length
}
pub fn to_bytes(&self, output: &mut [u8]) {
assert!(
output.len() >= self.serialized_length(),
"Output buffer is too small to serialize Block: expected at least {} bytes, got {}",
self.serialized_length(),
output.len()
);
let descriptor_block_size = self.serialized_length() as u16;
let header = match self {
Block::Basic(_) => BlockHeader::BASIC,
Block::Unknown { header, .. } => *header,
};
output[..BlockHeader::LENGTH].copy_from_slice(&header.as_bytes(descriptor_block_size));
match self {
Block::Basic(basic) => {
basic.to_bytes(&mut output[BlockHeader::LENGTH..]);
}
Block::Unknown { data, .. } => {
output[BlockHeader::LENGTH..][..data.len()].copy_from_slice(data);
}
}
}
pub fn to_vec(&self) -> Vec<u8> {
let mut output = vec![0u8; self.serialized_length()];
self.to_bytes(&mut output);
output
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BlockHeader {
pub vendor_id: u32, pub descriptor_type: u32, pub version_number: u16, }
impl BlockHeader {
pub const LENGTH: usize = 8;
pub const BASIC: Self = Self {
vendor_id: 0,
descriptor_type: 0,
version_number: 2,
};
pub fn as_bytes(&self, descriptor_block_size: u16) -> [u8; Self::LENGTH] {
let mut output = [0u8; Self::LENGTH];
let first_word = (self.vendor_id & ((1 << 17) - 1)) | (self.descriptor_type << 17);
output[0..4].copy_from_slice(&first_word.to_le_bytes());
output[4..6].copy_from_slice(&self.version_number.to_le_bytes());
output[6..8].copy_from_slice(&descriptor_block_size.to_le_bytes());
output
}
pub(crate) fn from_bytes(bytes: &[u8; Self::LENGTH]) -> Result<(Self, usize), ParseError> {
let mut offset = 0;
let v = bytes_to_u32(bytes, &mut offset)?;
let vendor_id = shift_and_mask_lower(0, 17, v);
let descriptor_type = shift_and_mask_lower(17, 15, v);
let version_number = read_u16(bytes, &mut offset)?;
let descriptor_block_size = read_u16(bytes, &mut offset)?;
Ok((
Self {
vendor_id,
descriptor_type,
version_number,
},
descriptor_block_size as usize,
))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Basic {
pub color_model: Option<ColorModel>, pub color_primaries: Option<ColorPrimaries>, pub transfer_function: Option<TransferFunction>, pub flags: DataFormatFlags, pub texel_block_dimensions: [NonZeroU8; 4], pub bytes_planes: [u8; 8], pub sample_information: Vec<SampleInformation>,
}
impl Basic {
pub const FIXED_LENGTH: usize = 16;
pub fn from_format(format: crate::Format) -> Result<(Self, u32), BuildError> {
Self::from_format_with(format, false, None, None, None)
}
pub fn from_format_with(
format: crate::Format,
alpha_premultiplied: bool,
transfer_function: Option<TransferFunction>,
color_primaries: Option<ColorPrimaries>,
color_model: Option<ColorModel>,
) -> Result<(Self, u32), BuildError> {
let builder = generate::Builder::from_format(format).ok_or(BuildError::UnsupportedFormat)?;
let type_size = builder.type_size();
let dfd = builder.build(alpha_premultiplied, transfer_function, color_primaries, color_model)?;
Ok((dfd, type_size))
}
pub fn parse(bytes: &[u8]) -> Result<Self, ParseError> {
let mut offset = 0;
let [model, primaries, transfer, flags] = read_bytes(bytes, &mut offset)?;
let texel_block_dimensions = read_bytes(bytes, &mut offset)?.map(|dim| NonZeroU8::new(dim + 1).unwrap());
let bytes_planes = read_bytes(bytes, &mut offset)?;
let remaining_bytes = &bytes[Self::FIXED_LENGTH..];
let iterator = remaining_bytes.chunks_exact(SampleInformation::LENGTH);
if !iterator.remainder().is_empty() {
return Err(ParseError::UnexpectedEnd);
}
let sample_information = iterator
.map(|chunk| SampleInformation::from_bytes(chunk.try_into().unwrap()))
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
color_model: ColorModel::new(model),
color_primaries: ColorPrimaries::new(primaries),
transfer_function: TransferFunction::new(transfer),
flags: DataFormatFlags::from_bits_truncate(flags),
texel_block_dimensions,
bytes_planes,
sample_information,
})
}
pub fn serialized_length(&self) -> usize {
Self::FIXED_LENGTH + self.sample_information.len() * SampleInformation::LENGTH
}
pub fn to_bytes(&self, output: &mut [u8]) {
assert!(
output.len() >= self.serialized_length(),
"Output buffer is too small to serialize Basic block: expected at least {} bytes, got {}",
self.serialized_length(),
output.len()
);
let color_model = self.color_model.map_or(0, |c| c.value());
let color_primaries = self.color_primaries.map_or(0, |c| c.value());
let transfer_function = self.transfer_function.map_or(0, |t| t.value());
let texel_block_dimensions = self.texel_block_dimensions.map(|dim| dim.get() - 1);
output[0] = color_model;
output[1] = color_primaries;
output[2] = transfer_function;
output[3] = self.flags.bits();
output[4..8].copy_from_slice(&texel_block_dimensions);
output[8..16].copy_from_slice(&self.bytes_planes);
for (i, sample) in self.sample_information.iter().enumerate() {
let start = Self::FIXED_LENGTH + i * SampleInformation::LENGTH;
output[start..][..SampleInformation::LENGTH].copy_from_slice(&sample.as_bytes());
}
}
pub fn to_vec(&self) -> Vec<u8> {
let mut output = vec![0u8; self.serialized_length()];
self.to_bytes(&mut output);
output
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SampleInformation {
pub bit_offset: u16, pub bit_length: NonZeroU8, pub channel_type: u8, pub channel_type_qualifiers: ChannelTypeQualifiers, pub sample_positions: [u8; 4], pub lower: u32, pub upper: u32, }
impl SampleInformation {
pub const LENGTH: usize = 16;
pub fn as_bytes(&self) -> [u8; Self::LENGTH] {
let mut bytes = [0u8; Self::LENGTH];
let channel_info = self.channel_type | (self.channel_type_qualifiers.bits() << 4);
bytes[0..2].copy_from_slice(&self.bit_offset.to_le_bytes());
bytes[2] = self.bit_length.get() - 1;
bytes[3] = channel_info;
bytes[4..8].copy_from_slice(&self.sample_positions);
bytes[8..12].copy_from_slice(&self.lower.to_le_bytes());
bytes[12..16].copy_from_slice(&self.upper.to_le_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8; Self::LENGTH]) -> Result<Self, ParseError> {
let mut offset = 0;
let v = bytes_to_u32(bytes, &mut offset)?;
let bit_offset = shift_and_mask_lower(0, 16, v) as u16;
let bit_length = (shift_and_mask_lower(16, 8, v) as u8)
.checked_add(1)
.and_then(NonZeroU8::new)
.ok_or(ParseError::InvalidSampleBitLength)?;
let channel_type = shift_and_mask_lower(24, 4, v) as u8;
let channel_type_qualifiers = ChannelTypeQualifiers::from_bits_truncate(shift_and_mask_lower(28, 4, v) as u8);
let sample_positions = read_bytes(bytes, &mut offset)?;
let lower = bytes_to_u32(bytes, &mut offset)?;
let upper = bytes_to_u32(bytes, &mut offset)?;
Ok(Self {
bit_offset,
bit_length,
channel_type,
channel_type_qualifiers,
sample_positions,
lower,
upper,
})
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ChannelTypeQualifiers: u8 {
const LINEAR = (1 << 0);
const EXPONENT = (1 << 1);
const SIGNED = (1 << 2);
const FLOAT = (1 << 3);
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[repr(transparent)]
pub struct DataFormatFlags: u8 {
const STRAIGHT_ALPHA = 0;
const ALPHA_PREMULTIPLIED = (1 << 0);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn to_nonzero<const N: usize>(input: [u8; N]) -> [NonZeroU8; N] {
input.map(|n| NonZeroU8::new(n).unwrap())
}
#[test]
fn basic_dfd_header_roundtrip() {
let basic = Basic {
color_model: Some(ColorModel::LabSDA),
color_primaries: Some(ColorPrimaries::ACES),
transfer_function: Some(TransferFunction::ITU),
flags: DataFormatFlags::STRAIGHT_ALPHA,
texel_block_dimensions: to_nonzero([1, 2, 3, 4]),
bytes_planes: [5, 6, 7, 8, 9, 10, 11, 12],
sample_information: vec![],
};
let bytes = basic.to_vec();
let decoded = Basic::parse(&bytes).unwrap();
assert_eq!(basic, decoded);
}
#[test]
fn sample_information_roundtrip() {
let info = SampleInformation {
bit_offset: 234,
bit_length: NonZeroU8::new(123).unwrap(),
channel_type: 2,
channel_type_qualifiers: ChannelTypeQualifiers::LINEAR,
sample_positions: [1, 2, 3, 4],
lower: 1234,
upper: 4567,
};
let bytes = info.as_bytes();
let decoded = SampleInformation::from_bytes(&bytes).unwrap();
assert_eq!(info, decoded);
}
#[test]
fn sample_info_invalid_bit_length() {
let bytes = &[
0u8, 0, 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, ];
assert!(matches!(
SampleInformation::from_bytes(bytes),
Err(ParseError::InvalidSampleBitLength)
));
}
}