use crate::meta::attribute::IntegerBounds;
#[derive(Debug, Clone)]
pub struct Chunk {
pub layer_index: usize,
pub compressed_block: CompressedBlock,
}
#[derive(Debug, Clone)]
pub enum CompressedBlock {
ScanLine(CompressedScanLineBlock),
Tile(CompressedTileBlock),
DeepScanLine(CompressedDeepScanLineBlock),
DeepTile(CompressedDeepTileBlock),
}
#[derive(Debug, Clone)]
pub struct CompressedScanLineBlock {
pub y_coordinate: i32,
pub compressed_pixels_le: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CompressedTileBlock {
pub coordinates: TileCoordinates,
pub compressed_pixels_le: Vec<u8>,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct TileCoordinates {
pub tile_index: Vec2<usize>,
pub level_index: Vec2<usize>,
}
#[derive(Debug, Clone)]
pub struct CompressedDeepScanLineBlock {
pub y_coordinate: i32,
pub decompressed_sample_data_size: usize,
pub compressed_pixel_offset_table: Vec<i8>,
pub compressed_sample_data_le: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CompressedDeepTileBlock {
pub coordinates: TileCoordinates,
pub decompressed_sample_data_size: usize,
pub compressed_pixel_offset_table: Vec<i8>,
pub compressed_sample_data_le: Vec<u8>,
}
use crate::io::*;
impl TileCoordinates {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
i32::write_le(usize_to_i32(self.tile_index.x(), "tile x")?, write)?;
i32::write_le(usize_to_i32(self.tile_index.y(), "tile y")?, write)?;
i32::write_le(usize_to_i32(self.level_index.x(), "level x")?, write)?;
i32::write_le(usize_to_i32(self.level_index.y(), "level y")?, write)?;
Ok(())
}
pub fn read(read: &mut impl Read) -> Result<Self> {
let tile_x = i32::read_le(read)?;
let tile_y = i32::read_le(read)?;
let level_x = i32::read_le(read)?;
let level_y = i32::read_le(read)?;
if level_x > 31 || level_y > 31 {
return Err(Error::invalid("level index exceeding integer maximum"));
}
Ok(Self {
tile_index: Vec2(tile_x, tile_y).to_usize("tile coordinate index")?,
level_index: Vec2(level_x, level_y).to_usize("tile coordinate level")?,
})
}
pub fn to_data_indices(
&self,
tile_size: Vec2<usize>,
max: Vec2<usize>,
) -> Result<IntegerBounds> {
let x = self.tile_index.x() * tile_size.width();
let y = self.tile_index.y() * tile_size.height();
if x >= max.x() || y >= max.y() {
Err(Error::invalid("tile index"))
} else {
Ok(IntegerBounds {
position: Vec2(usize_to_i32(x, "tile x")?, usize_to_i32(y, "tile y")?),
size: Vec2(
calculate_block_size(max.x(), tile_size.width(), x)?,
calculate_block_size(max.y(), tile_size.height(), y)?,
),
})
}
}
pub fn to_absolute_indices(
&self,
tile_size: Vec2<usize>,
data_window: IntegerBounds,
) -> Result<IntegerBounds> {
let data = self.to_data_indices(tile_size, data_window.size)?;
Ok(data.with_origin(data_window.position))
}
pub fn is_largest_resolution_level(&self) -> bool {
self.level_index == Vec2(0, 0)
}
}
use crate::meta::{calculate_block_size, BlockDescription, MetaData};
impl CompressedScanLineBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(
self.compressed_pixels_le.len(),
0,
"empty blocks should not be put in the file bug"
);
i32::write_le(self.y_coordinate, write)?;
u8::write_i32_sized_slice_le(write, &self.compressed_pixels_le)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let y_coordinate = i32::read_le(read)?;
let compressed_pixels_le = u8::read_i32_sized_vec_le(
read,
max_block_byte_size,
Some(max_block_byte_size),
"scan line block sample count",
)?;
Ok(Self {
y_coordinate,
compressed_pixels_le,
})
}
}
impl CompressedTileBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(
self.compressed_pixels_le.len(),
0,
"empty blocks should not be put in the file bug"
);
self.coordinates.write(write)?;
u8::write_i32_sized_slice_le(write, &self.compressed_pixels_le)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let coordinates = TileCoordinates::read(read)?;
let compressed_pixels_le = u8::read_i32_sized_vec_le(
read,
max_block_byte_size,
Some(max_block_byte_size),
"tile block sample count",
)?;
Ok(Self {
coordinates,
compressed_pixels_le,
})
}
}
impl CompressedDeepScanLineBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(
self.compressed_sample_data_le.len(),
0,
"empty blocks should not be put in the file bug"
);
i32::write_le(self.y_coordinate, write)?;
u64::write_le(self.compressed_pixel_offset_table.len() as u64, write)?;
u64::write_le(self.compressed_sample_data_le.len() as u64, write)?; u64::write_le(self.decompressed_sample_data_size as u64, write)?;
i8::write_slice_le(write, &self.compressed_pixel_offset_table)?;
u8::write_slice_le(write, &self.compressed_sample_data_le)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let y_coordinate = i32::read_le(read)?;
let compressed_pixel_offset_table_size =
u64_to_usize(u64::read_le(read)?, "deep table size")?;
let compressed_sample_data_size = u64_to_usize(u64::read_le(read)?, "deep size")?;
let decompressed_sample_data_size = u64_to_usize(u64::read_le(read)?, "raw deep size")?;
let compressed_pixel_offset_table = i8::read_vec_le(
read,
compressed_pixel_offset_table_size,
6 * u16::MAX as usize,
Some(max_block_byte_size),
"deep scan line block table size",
)?;
let compressed_sample_data_le = u8::read_vec_le(
read,
compressed_sample_data_size,
6 * u16::MAX as usize,
Some(max_block_byte_size),
"deep scan line block sample count",
)?;
Ok(Self {
y_coordinate,
decompressed_sample_data_size,
compressed_pixel_offset_table,
compressed_sample_data_le,
})
}
}
impl CompressedDeepTileBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(
self.compressed_sample_data_le.len(),
0,
"empty blocks should not be put in the file bug"
);
self.coordinates.write(write)?;
u64::write_le(self.compressed_pixel_offset_table.len() as u64, write)?;
u64::write_le(self.compressed_sample_data_le.len() as u64, write)?; u64::write_le(self.decompressed_sample_data_size as u64, write)?;
i8::write_slice_le(write, &self.compressed_pixel_offset_table)?;
u8::write_slice_le(write, &self.compressed_sample_data_le)?;
Ok(())
}
pub fn read(read: &mut impl Read, hard_max_block_byte_size: usize) -> Result<Self> {
let coordinates = TileCoordinates::read(read)?;
let compressed_pixel_offset_table_size =
u64_to_usize(u64::read_le(read)?, "deep table size")?;
let compressed_sample_data_size = u64_to_usize(u64::read_le(read)?, "deep size")?; let decompressed_sample_data_size = u64_to_usize(u64::read_le(read)?, "raw deep size")?;
let compressed_pixel_offset_table = i8::read_vec_le(
read,
compressed_pixel_offset_table_size,
6 * u16::MAX as usize,
Some(hard_max_block_byte_size),
"deep tile block table size",
)?;
let compressed_sample_data_le = u8::read_vec_le(
read,
compressed_sample_data_size,
6 * u16::MAX as usize,
Some(hard_max_block_byte_size),
"deep tile block sample count",
)?;
Ok(Self {
coordinates,
decompressed_sample_data_size,
compressed_pixel_offset_table,
compressed_sample_data_le,
})
}
}
use crate::{
error::{i32_to_usize, u64_to_usize, usize_to_i32, Error, Result, UnitResult},
math::Vec2,
};
impl Chunk {
pub fn write(&self, write: &mut impl Write, header_count: usize) -> UnitResult {
debug_assert!(self.layer_index < header_count, "layer index bug");
if header_count == 1 {
assert_eq!(self.layer_index, 0, "invalid header index for single layer file");
} else {
usize_to_i32(self.layer_index, "layer index")?.write_le(write)?;
}
match self.compressed_block {
CompressedBlock::ScanLine(ref value) => value.write(write),
CompressedBlock::Tile(ref value) => value.write(write),
CompressedBlock::DeepScanLine(ref value) => value.write(write),
CompressedBlock::DeepTile(ref value) => value.write(write),
}
}
pub fn read(read: &mut impl Read, meta_data: &MetaData) -> Result<Self> {
let layer_number = i32_to_usize(
if meta_data.requirements.is_multilayer() {
i32::read_le(read)?
}
else {
0_i32
}, "chunk data part number",
)?;
if layer_number >= meta_data.headers.len() {
return Err(Error::invalid("chunk data part number"));
}
let header = &meta_data.headers[layer_number];
let max_block_byte_size = header.max_block_byte_size();
let chunk = Self {
layer_index: layer_number,
compressed_block: match header.blocks {
BlockDescription::ScanLines if !header.deep => CompressedBlock::ScanLine(
CompressedScanLineBlock::read(read, max_block_byte_size)?,
),
BlockDescription::Tiles(_) if !header.deep => {
CompressedBlock::Tile(CompressedTileBlock::read(read, max_block_byte_size)?)
}
BlockDescription::ScanLines => CompressedBlock::DeepScanLine(
CompressedDeepScanLineBlock::read(read, max_block_byte_size)?,
),
BlockDescription::Tiles(_) => CompressedBlock::DeepTile(
CompressedDeepTileBlock::read(read, max_block_byte_size)?,
),
},
};
Ok(chunk)
}
}