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: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CompressedTileBlock {
pub coordinates: TileCoordinates,
pub compressed_pixels: 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: 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: Vec<u8>,
}
use crate::io::*;
impl TileCoordinates {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
i32::write(usize_to_i32(self.tile_index.x()), write)?;
i32::write(usize_to_i32(self.tile_index.y()), write)?;
i32::write(usize_to_i32(self.level_index.x()), write)?;
i32::write(usize_to_i32(self.level_index.y()), write)?;
Ok(())
}
pub fn read(read: &mut impl Read) -> Result<Self> {
let tile_x = i32::read(read)?;
let tile_y = i32::read(read)?;
let level_x = i32::read(read)?;
let level_y = i32::read(read)?;
if level_x > 31 || level_y > 31 {
return Err(Error::invalid("level index exceeding integer maximum"));
}
Ok(TileCoordinates {
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), usize_to_i32(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::{MetaData, BlockDescription, calculate_block_size};
impl CompressedScanLineBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(self.compressed_pixels.len(), 0, "empty blocks should not be put in the file bug");
i32::write(self.y_coordinate, write)?;
u8::write_i32_sized_slice(write, &self.compressed_pixels)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let y_coordinate = i32::read(read)?;
let compressed_pixels = u8::read_i32_sized_vec(read, max_block_byte_size, Some(max_block_byte_size), "scan line block sample count")?;
Ok(CompressedScanLineBlock { y_coordinate, compressed_pixels })
}
}
impl CompressedTileBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(self.compressed_pixels.len(), 0, "empty blocks should not be put in the file bug");
self.coordinates.write(write)?;
u8::write_i32_sized_slice(write, &self.compressed_pixels)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let coordinates = TileCoordinates::read(read)?;
let compressed_pixels = u8::read_i32_sized_vec(read, max_block_byte_size, Some(max_block_byte_size), "tile block sample count")?;
Ok(CompressedTileBlock { coordinates, compressed_pixels })
}
}
impl CompressedDeepScanLineBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(self.compressed_sample_data.len(), 0, "empty blocks should not be put in the file bug");
i32::write(self.y_coordinate, write)?;
u64::write(self.compressed_pixel_offset_table.len() as u64, write)?;
u64::write(self.compressed_sample_data.len() as u64, write)?; u64::write(self.decompressed_sample_data_size as u64, write)?;
i8::write_slice(write, &self.compressed_pixel_offset_table)?;
u8::write_slice(write, &self.compressed_sample_data)?;
Ok(())
}
pub fn read(read: &mut impl Read, max_block_byte_size: usize) -> Result<Self> {
let y_coordinate = i32::read(read)?;
let compressed_pixel_offset_table_size = u64_to_usize(u64::read(read)?);
let compressed_sample_data_size = u64_to_usize(u64::read(read)?);
let decompressed_sample_data_size = u64_to_usize(u64::read(read)?);
let compressed_pixel_offset_table = i8::read_vec(
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 = u8::read_vec(
read, compressed_sample_data_size,
6 * u16::MAX as usize, Some(max_block_byte_size),
"deep scan line block sample count"
)?;
Ok(CompressedDeepScanLineBlock {
y_coordinate,
decompressed_sample_data_size,
compressed_pixel_offset_table,
compressed_sample_data,
})
}
}
impl CompressedDeepTileBlock {
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert_ne!(self.compressed_sample_data.len(), 0, "empty blocks should not be put in the file bug");
self.coordinates.write(write)?;
u64::write(self.compressed_pixel_offset_table.len() as u64, write)?;
u64::write(self.compressed_sample_data.len() as u64, write)?; u64::write(self.decompressed_sample_data_size as u64, write)?;
i8::write_slice(write, &self.compressed_pixel_offset_table)?;
u8::write_slice(write, &self.compressed_sample_data)?;
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(read)?);
let compressed_sample_data_size = u64_to_usize(u64::read(read)?); let decompressed_sample_data_size = u64_to_usize(u64::read(read)?);
let compressed_pixel_offset_table = i8::read_vec(
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 = u8::read_vec(
read, compressed_sample_data_size,
6 * u16::MAX as usize, Some(hard_max_block_byte_size),
"deep tile block sample count"
)?;
Ok(CompressedDeepTileBlock {
coordinates,
decompressed_sample_data_size,
compressed_pixel_offset_table,
compressed_sample_data,
})
}
}
use crate::error::{UnitResult, Result, Error, u64_to_usize, usize_to_i32, i32_to_usize};
use crate::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 { usize_to_i32(self.layer_index).write(write)?; }
else { assert_eq!(self.layer_index, 0, "invalid header index for single layer file"); }
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(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 = Chunk {
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)
}
}