use smallvec::SmallVec;
#[derive(Debug, Clone, PartialEq)]
pub enum AttributeValue {
ChannelList(ChannelList),
Chromaticities(Chromaticities),
Compression(Compression),
EnvironmentMap(EnvironmentMap),
KeyCode(KeyCode),
LineOrder(LineOrder),
Matrix3x3(Matrix3x3),
Matrix4x4(Matrix4x4),
Preview(Preview),
Rational(Rational),
BlockType(BlockType),
TextVector(Vec<Text>),
TileDescription(TileDescription),
TimeCode(TimeCode),
Text(Text),
F64(f64),
F32(f32),
I32(i32),
IntRect(IntRect),
FloatRect(FloatRect),
IntVec2(Vec2<i32>),
FloatVec2(Vec2<f32>),
IntVec3((i32, i32, i32)),
FloatVec3((f32, f32, f32)),
Custom {
kind: Text,
bytes: Vec<u8>
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct Text {
bytes: TextBytes,
}
#[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)]
pub struct TimeCode {
time_and_flags: u32,
user_data: u32,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BlockType {
ScanLine,
Tile,
DeepScanLine,
DeepTile,
}
pub mod block_type_strings {
pub const SCAN_LINE: &'static [u8] = b"scanlineimage";
pub const TILE: &'static [u8] = b"tiledimage";
pub const DEEP_SCAN_LINE: &'static [u8] = b"deepscanline";
pub const DEEP_TILE: &'static [u8] = b"deeptile";
}
pub use crate::compression::Compression;
pub type DataWindow = IntRect;
pub type DisplayWindow = IntRect;
pub type Rational = (i32, u32);
pub type Matrix4x4 = [f32; 4*4];
pub type Matrix3x3 = [f32; 3*3];
#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
pub struct IntRect {
pub position: Vec2<i32>,
pub size: Vec2<usize>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FloatRect {
min: Vec2<f32>,
max: Vec2<f32>
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChannelList {
pub list: SmallVec<[Channel; 5]>,
pub bytes_per_pixel: usize, }
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Channel {
pub name: Text,
pub pixel_type: PixelType,
pub is_linear: bool,
pub sampling: Vec2<usize>,
}
#[derive(Clone, Debug, Eq, PartialEq, Copy)]
pub enum PixelType {
U32,
F16,
F32,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Chromaticities {
pub red: Vec2<f32>,
pub green: Vec2<f32>,
pub blue: Vec2<f32>,
pub white: Vec2<f32>
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum EnvironmentMap {
LatitudeLongitude,
Cube,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct KeyCode {
pub film_manufacturer_code: i32,
pub film_type: i32,
pub film_roll_prefix: i32,
pub count: i32,
pub perforation_offset: i32,
pub perforations_per_frame: i32,
pub perforations_per_count: i32,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum LineOrder {
Increasing,
Decreasing,
Unspecified,
}
#[derive(Clone, Eq, PartialEq)]
pub struct Preview {
pub size: Vec2<usize>,
pub pixel_data: Vec<i8>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct TileDescription {
pub tile_size: Vec2<usize>,
pub level_mode: LevelMode,
pub rounding_mode: RoundingMode,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum LevelMode {
Singular,
MipMap,
RipMap,
}
type TextBytes = SmallVec<[u8; 24]>;
use crate::io::*;
use crate::meta::sequence_end;
use crate::error::*;
use crate::math::{RoundingMode, Vec2};
use half::f16;
use std::convert::{TryFrom};
fn invalid_type() -> Error {
Error::invalid("attribute type mismatch")
}
impl Text {
pub fn bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
pub fn from(str: impl AsRef<str>) -> Option<Self> {
let vec : Option<TextBytes> = str.as_ref().chars()
.map(|character| u8::try_from(character as u64).ok())
.collect();
vec.map(Self::from_bytes_unchecked)
}
pub fn from_slice_unchecked(text: &'static [u8]) -> Self {
Self::from_bytes_unchecked(SmallVec::from_slice(text))
}
pub fn from_bytes_unchecked(bytes: TextBytes) -> Self {
Text { bytes }
}
pub fn validate(&self, null_terminated: bool, long_names: Option<bool>) -> UnitResult {
Self::validate_bytes(self.bytes(), null_terminated, long_names)
}
pub fn validate_bytes(text: &[u8], null_terminated: bool, long_names: Option<bool>) -> UnitResult {
if null_terminated && text.is_empty() {
return Err(Error::invalid("text must not be empty"));
}
if let Some(long) = long_names {
if long && text.len() >= 256 { return Err(Error::invalid("text must not be longer than 255")); }
if !long && text.len() >= 32 { return Err(Error::invalid("text must not be longer than 31")); }
}
Ok(())
}
pub fn null_terminated_byte_size(&self) -> usize {
self.bytes.len() + sequence_end::byte_size()
}
pub fn i32_sized_byte_size(&self) -> usize {
self.bytes.len() + i32::BYTE_SIZE
}
pub fn write_i32_sized<W: Write>(&self, write: &mut W) -> UnitResult {
debug_assert!(self.validate( false, None).is_ok(), "text size bug");
i32::write(usize_to_i32(self.bytes.len()), write)?;
Self::write_unsized_bytes(self.bytes.as_slice(), write)
}
fn write_unsized_bytes<W: Write>(bytes: &[u8], write: &mut W) -> UnitResult {
u8::write_slice(write, bytes)?;
Ok(())
}
pub fn read_i32_sized<R: Read>(read: &mut R, max_size: usize) -> Result<Self> {
let size = i32_to_usize(i32::read(read)?, "vector size")?;
Ok(Text::from_bytes_unchecked(SmallVec::from_vec(u8::read_vec(read, size, 1024, Some(max_size))?)))
}
pub fn read_sized<R: Read>(read: &mut R, size: usize) -> Result<Self> {
const SMALL_SIZE: usize = 24;
if size <= SMALL_SIZE {
let mut buffer = [0_u8; SMALL_SIZE];
let data = &mut buffer[..size];
read.read_exact(data)?;
Ok(Text::from_bytes_unchecked(SmallVec::from_slice(data)))
}
else {
Ok(Text::from_bytes_unchecked(SmallVec::from_vec(u8::read_vec(read, size, 1024, None)?)))
}
}
pub fn write_null_terminated<W: Write>(&self, write: &mut W) -> UnitResult {
Self::write_null_terminated_bytes(self.bytes(), write)
}
fn write_null_terminated_bytes<W: Write>(bytes: &[u8], write: &mut W) -> UnitResult {
debug_assert!(!bytes.is_empty(), "text is empty bug");
Text::write_unsized_bytes(bytes, write)?;
sequence_end::write(write)?;
Ok(())
}
pub fn read_null_terminated<R: Read>(read: &mut R, max_len: usize) -> Result<Self> {
let mut bytes = SmallVec::new();
loop {
match u8::read(read)? {
0 => break,
non_terminator => bytes.push(non_terminator),
}
if bytes.len() > max_len {
return Err(Error::invalid("text too long"))
}
}
Ok(Text { bytes })
}
fn read_vec_of_i32_sized(
read: &mut PeekRead<impl Read>,
total_byte_size: usize
) -> Result<Vec<Text>>
{
let mut result = Vec::with_capacity(2);
let mut processed_bytes = 0;
while processed_bytes < total_byte_size {
let text = Text::read_i32_sized(read, total_byte_size)?;
processed_bytes += ::std::mem::size_of::<i32>(); processed_bytes += text.bytes.len();
result.push(text);
}
if processed_bytes != total_byte_size {
return Err(Error::invalid("text array byte size"))
}
Ok(result)
}
fn write_vec_of_i32_sized_texts<W: Write>(write: &mut W, texts: &[Text]) -> UnitResult {
for text in texts {
text.write_i32_sized(write)?;
}
Ok(())
}
}
impl Into<String> for Text {
fn into(self) -> String {
self.to_string()
}
}
impl<'s> TryFrom<&'s str> for Text {
type Error = &'static str;
fn try_from(value: &'s str) -> std::result::Result<Self, Self::Error> {
Text::from(value).ok_or("exr text does not support unicode characters")
}
}
impl ::std::fmt::Debug for Text {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "exr::Text(\"{}\")", self)
}
}
impl ::std::fmt::Display for Text {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
use std::fmt::Write;
for &byte in self.bytes.iter() {
f.write_char(byte as char)?;
}
Ok(())
}
}
impl ChannelList {
pub fn new(channels: SmallVec<[Channel; 5]>) -> Self {
ChannelList {
bytes_per_pixel: channels.iter().map(|channel| channel.pixel_type.bytes_per_sample()).sum(),
list: channels,
}
}
}
impl BlockType {
const TYPE_NAME: &'static [u8] = attribute_type_names::TEXT;
pub fn parse(text: Text) -> Result<Self> {
match text.bytes() {
block_type_strings::SCAN_LINE => Ok(BlockType::ScanLine),
block_type_strings::TILE => Ok(BlockType::Tile),
block_type_strings::DEEP_SCAN_LINE => Ok(BlockType::DeepScanLine),
block_type_strings::DEEP_TILE => Ok(BlockType::DeepTile),
_ => Err(Error::invalid("block type attribute value")),
}
}
pub fn write(&self, write: &mut impl Write) -> UnitResult {
u8::write_slice(write, self.to_text_bytes())?;
Ok(())
}
pub fn to_text_bytes(&self) -> &[u8] {
match self {
BlockType::ScanLine => block_type_strings::SCAN_LINE,
BlockType::Tile => block_type_strings::TILE,
BlockType::DeepScanLine => block_type_strings::DEEP_SCAN_LINE,
BlockType::DeepTile => block_type_strings::DEEP_TILE,
}
}
pub fn byte_size(&self) -> usize {
self.to_text_bytes().len()
}
}
impl IntRect {
pub fn zero() -> Self {
Self::from_dimensions(Vec2(0, 0))
}
pub fn from_dimensions(size: Vec2<usize>) -> Self {
Self::new(Vec2(0,0), size)
}
pub fn new(start: Vec2<i32>, size: Vec2<usize>) -> Self {
Self { position: start, size }
}
pub fn end(self) -> Vec2<i32> {
self.position + self.size.to_i32() }
pub fn max(self) -> Vec2<i32> {
self.end() - Vec2(1,1)
}
pub fn validate(&self, max: Option<Vec2<usize>>) -> UnitResult {
if let Some(max) = max {
if self.size.0 > max.0 || self.size.1 > max.1 {
return Err(Error::invalid("window attribute dimension value"));
}
}
let max_int = std::i32::MAX as i64 / 2;
let self_max = Vec2(
self.position.0 as i64 + self.size.0 as i64,
self.position.1 as i64 + self.size.1 as i64,
);
if self_max.0 >= max_int || self_max.1 >= max_int
|| self.position.0 as i64 <= -max_int
|| self.position.1 as i64 <= -max_int
{
return Err(Error::invalid("window size exceeding integer maximum"));
}
Ok(())
}
pub fn byte_size() -> usize {
4 * i32::BYTE_SIZE
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
let Vec2(x_min, y_min) = self.position;
let Vec2(x_max, y_max) = self.max();
x_min.write(write)?;
y_min.write(write)?;
x_max.write(write)?;
y_max.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let x_min = i32::read(read)?;
let y_min = i32::read(read)?;
let x_max = i32::read(read)?;
let y_max = i32::read(read)?;
let min = Vec2(x_min.min(x_max), y_min.min(y_max));
let max = Vec2(x_min.max(x_max), y_min.max(y_max)); let size = Vec2(max.0 + 1 - min.0, max.1 + 1 - min.1); let size = size.to_usize("box coordinates")?;
Ok(IntRect { position: min, size })
}
pub fn with_origin(self, origin: Vec2<i32>) -> Self {
IntRect { position: self.position + origin, .. self }
}
}
impl FloatRect {
pub fn byte_size() -> usize {
4 * f32::BYTE_SIZE
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
self.min.0.write(write)?;
self.min.1.write(write)?;
self.max.0.write(write)?;
self.max.1.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let x_min = f32::read(read)?;
let y_min = f32::read(read)?;
let x_max = f32::read(read)?;
let y_max = f32::read(read)?;
Ok(FloatRect {
min: Vec2(x_min, y_min),
max: Vec2(x_max, y_max)
})
}
}
impl PixelType {
pub fn bytes_per_sample(&self) -> usize {
match self {
PixelType::F16 => f16::BYTE_SIZE,
PixelType::F32 => f32::BYTE_SIZE,
PixelType::U32 => u32::BYTE_SIZE,
}
}
pub fn byte_size() -> usize {
i32::BYTE_SIZE
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
match *self {
PixelType::U32 => 0_i32,
PixelType::F16 => 1_i32,
PixelType::F32 => 2_i32,
}.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
Ok(match i32::read(read)? {
0 => PixelType::U32,
1 => PixelType::F16,
2 => PixelType::F32,
_ => return Err(Error::invalid("pixel type attribute value")),
})
}
}
impl Channel {
pub fn new(name: Text, pixel_type: PixelType, is_linear: bool) -> Self {
Self { name, pixel_type, is_linear, sampling: Vec2(1, 1) }
}
pub fn subsampled_pixels(&self, dimensions: Vec2<usize>) -> usize {
self.subsampled_resolution(dimensions).area()
}
pub fn subsampled_resolution(&self, dimensions: Vec2<usize>) -> Vec2<usize> {
dimensions / self.sampling
}
pub fn byte_size(&self) -> usize {
self.name.null_terminated_byte_size()
+ PixelType::byte_size()
+ 1 + 3 + 2 * u32::BYTE_SIZE }
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
Text::write_null_terminated(&self.name, write)?;
self.pixel_type.write(write)?;
match self.is_linear {
false => 0_u8,
true => 1_u8,
}.write(write)?;
i8::write_slice(write, &[0_i8, 0_i8, 0_i8])?;
i32::write(usize_to_i32(self.sampling.0), write)?;
i32::write(usize_to_i32(self.sampling.1), write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let name = Text::read_null_terminated(read, 256)?;
let pixel_type = PixelType::read(read)?;
let is_linear = match u8::read(read)? {
1 => true,
0 => false,
_ => return Err(Error::invalid("channel linearity attribute value")),
};
let mut reserved = [0_i8; 3];
i8::read_slice(read, &mut reserved)?;
let x_sampling = i32_to_usize(i32::read(read)?, "x channel sampling")?;
let y_sampling = i32_to_usize(i32::read(read)?, "y channel sampling")?;
Ok(Channel {
name, pixel_type, is_linear,
sampling: Vec2(x_sampling, y_sampling),
})
}
pub fn validate(&self, allow_sampling: bool, data_window: IntRect, strict: bool) -> UnitResult {
if self.sampling.0 == 0 || self.sampling.1 == 0 {
return Err(Error::invalid("zero sampling factor"));
}
if strict && allow_sampling && self.sampling != Vec2(1,1) {
return Err(Error::invalid("sub sampling is only allowed in flat scan line images"));
}
if data_window.position.0 % self.sampling.0 as i32 != 0 || data_window.position.1 % self.sampling.1 as i32 != 0 {
return Err(Error::invalid("channel sampling factor not dividing data window position"));
}
if data_window.size.0 % self.sampling.0 != 0 || data_window.size.1 % self.sampling.1 != 0 {
return Err(Error::invalid("channel sampling factor not dividing data window size"));
}
if self.sampling != Vec2(1,1) {
return Err(Error::unsupported("channel sub sampling not supported yet"));
}
Ok(())
}
}
impl ChannelList {
pub fn byte_size(&self) -> usize {
self.list.iter().map(Channel::byte_size).sum::<usize>() + sequence_end::byte_size()
}
pub fn write(&self, write: &mut impl Write) -> UnitResult {
for channel in &self.list {
channel.write(write)?;
}
sequence_end::write(write)?;
Ok(())
}
pub fn read(read: &mut PeekRead<impl Read>) -> Result<Self> {
let mut channels = SmallVec::new();
while !sequence_end::has_come(read)? {
channels.push(Channel::read(read)?);
}
Ok(ChannelList::new(channels))
}
pub fn validate(&self, allow_sampling: bool, data_window: IntRect, strict: bool) -> UnitResult {
let mut iter = self.list.iter().map(|chan| chan.validate(allow_sampling, data_window, strict).map(|_| &chan.name));
let mut previous = iter.next().ok_or(Error::invalid("at least one channel is required"))??;
for result in iter {
let value = result?;
if strict && previous == value { return Err(Error::invalid("channel names are not unique")); }
else if previous > value { return Err(Error::invalid("channel names are not sorted alphabetically")); }
else { previous = value; }
}
Ok(())
}
}
impl TimeCode {
pub const BYTE_SIZE: usize = 2 * u32::BYTE_SIZE;
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
self.time_and_flags.write(write)?;
self.user_data.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let time_and_flags = u32::read(read)?;
let user_data = u32::read(read)?;
Ok(Self { time_and_flags, user_data })
}
}
impl Chromaticities {
pub fn byte_size() -> usize {
8 * f32::BYTE_SIZE
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
self.red.0.write(write)?;
self.red.1.write(write)?;
self.green.0.write(write)?;
self.green.1.write(write)?;
self.blue.0.write(write)?;
self.blue.1.write(write)?;
self.white.0.write(write)?;
self.white.1.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
Ok(Chromaticities {
red: Vec2(f32::read(read)?, f32::read(read)?), green: Vec2(f32::read(read)?, f32::read(read)?),
blue: Vec2(f32::read(read)?, f32::read(read)?),
white: Vec2(f32::read(read)?, f32::read(read)?),
})
}
}
impl Compression {
pub fn byte_size() -> usize { 1 }
pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
use self::Compression::*;
match self {
Uncompressed => 0_u8,
RLE => 1_u8,
ZIP1 => 2_u8,
ZIP16 => 3_u8,
PIZ => 4_u8,
PXR24 => 5_u8,
B44 => 6_u8,
B44A => 7_u8,
DWAA => 8_u8,
DWAB => 9_u8,
}.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
use self::Compression::*;
Ok(match u8::read(read)? {
0 => Uncompressed,
1 => RLE,
2 => ZIP1,
3 => ZIP16,
4 => PIZ,
5 => PXR24,
6 => B44,
7 => B44A,
8 => DWAA,
9 => DWAB,
_ => return Err(Error::unsupported("unknown compression method")),
})
}
}
impl EnvironmentMap {
pub fn byte_size() -> usize {
u32::BYTE_SIZE
}
pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
use self::EnvironmentMap::*;
match self {
LatitudeLongitude => 0_u8,
Cube => 1_u8
}.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
use self::EnvironmentMap::*;
Ok(match u8::read(read)? {
0 => LatitudeLongitude,
1 => Cube,
_ => return Err(Error::invalid("environment map attribute value")),
})
}
}
impl KeyCode {
pub fn byte_size() -> usize {
6 * i32::BYTE_SIZE
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
self.film_manufacturer_code.write(write)?;
self.film_type.write(write)?;
self.film_roll_prefix.write(write)?;
self.count.write(write)?;
self.perforation_offset.write(write)?;
self.perforations_per_count.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
Ok(KeyCode {
film_manufacturer_code: i32::read(read)?,
film_type: i32::read(read)?,
film_roll_prefix: i32::read(read)?,
count: i32::read(read)?,
perforation_offset: i32::read(read)?,
perforations_per_frame: i32::read(read)?,
perforations_per_count: i32::read(read)?,
})
}
}
impl LineOrder {
pub fn byte_size() -> usize {
u32::BYTE_SIZE
}
pub fn write<W: Write>(self, write: &mut W) -> UnitResult {
use self::LineOrder::*;
match self {
Increasing => 0_u8,
Decreasing => 1_u8,
Unspecified => 2_u8,
}.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
use self::LineOrder::*;
Ok(match u8::read(read)? {
0 => Increasing,
1 => Decreasing,
2 => Unspecified,
_ => return Err(Error::invalid("line order attribute value")),
})
}
}
impl Preview {
pub fn byte_size(&self) -> usize {
2 * u32::BYTE_SIZE + self.pixel_data.len()
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
u32::write(self.size.0 as u32, write)?;
u32::write(self.size.1 as u32, write)?;
i8::write_slice(write, &self.pixel_data)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let components_per_pixel = 4;
let width = u32::read(read)? as usize;
let height = u32::read(read)? as usize;
let pixel_data = i8::read_vec(read, width * height * components_per_pixel, 1024*1024*4, None)?;
let preview = Preview {
size: Vec2(width, height),
pixel_data,
};
Ok(preview)
}
pub fn validate(&self, strict: bool) -> UnitResult {
if strict && (self.size.0 * self.size.1 * 4 != self.pixel_data.len()) {
return Err(Error::invalid("preview dimensions do not match content length"))
}
Ok(())
}
}
impl ::std::fmt::Debug for Preview {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Preview ({}x{} px)", self.size.0, self.size.1)
}
}
impl TileDescription {
pub fn byte_size() -> usize {
2 * u32::BYTE_SIZE + 1 }
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
u32::write(self.tile_size.0 as u32, write)?;
u32::write(self.tile_size.1 as u32, write)?;
let level_mode = match self.level_mode {
LevelMode::Singular => 0_u8,
LevelMode::MipMap => 1_u8,
LevelMode::RipMap => 2_u8,
};
let rounding_mode = match self.rounding_mode {
RoundingMode::Down => 0_u8,
RoundingMode::Up => 1_u8,
};
let mode: u8 = level_mode + (rounding_mode * 16);
mode.write(write)?;
Ok(())
}
pub fn read<R: Read>(read: &mut R) -> Result<Self> {
let x_size = u32::read(read)? as usize;
let y_size = u32::read(read)? as usize;
let mode = u8::read(read)?;
let level_mode = mode & 0b00001111; let rounding_mode = mode >> 4;
let level_mode = match level_mode {
0 => LevelMode::Singular,
1 => LevelMode::MipMap,
2 => LevelMode::RipMap,
_ => return Err(Error::invalid("tile description level mode")),
};
let rounding_mode = match rounding_mode {
0 => RoundingMode::Down,
1 => RoundingMode::Up,
_ => return Err(Error::invalid("tile description rounding mode")),
};
Ok(TileDescription { tile_size: Vec2(x_size, y_size), level_mode, rounding_mode, })
}
pub fn validate(&self) -> UnitResult {
let max = std::i32::MAX as i64 / 2;
if self.tile_size.0 == 0 || self.tile_size.1 == 0 || self.tile_size.0 as i64 >= max || self.tile_size.1 as i64 >= max {
return Err(Error::invalid("tile size"))
}
Ok(())
}
}
pub fn byte_size(name: &Text, value: &AttributeValue) -> usize {
name.null_terminated_byte_size()
+ value.kind_name().len() + sequence_end::byte_size()
+ i32::BYTE_SIZE + value.byte_size()
}
pub fn write<W: Write>(name: &[u8], value: &AttributeValue, write: &mut W) -> UnitResult {
Text::write_null_terminated_bytes(name, write)?;
Text::write_null_terminated_bytes(value.kind_name(), write)?;
i32::write(value.byte_size() as i32, write)?;
value.write(write)
}
pub fn read(read: &mut PeekRead<impl Read>, max_size: usize) -> Result<(Text, AttributeValue)> {
let name = Text::read_null_terminated(read, max_size)?;
let kind = Text::read_null_terminated(read, max_size)?;
let size = i32_to_usize(i32::read(read)?, "attribute size")?;
let value = AttributeValue::read(read, kind, size)?;
Ok((name, value))
}
pub fn validate(name: &Text, value: &AttributeValue, long_names: bool, allow_sampling: bool, data_window: IntRect, strict: bool) -> UnitResult {
name.validate(true, Some(long_names))?; value.validate(allow_sampling, data_window, strict) }
impl AttributeValue {
pub fn byte_size(&self) -> usize {
use self::AttributeValue::*;
match *self {
IntRect(_) => self::IntRect::byte_size(),
FloatRect(_) => self::FloatRect::byte_size(),
I32(_) => i32::BYTE_SIZE,
F32(_) => f32::BYTE_SIZE,
F64(_) => f64::BYTE_SIZE,
Rational(_) => { i32::BYTE_SIZE + u32::BYTE_SIZE },
TimeCode(_) => self::TimeCode::BYTE_SIZE,
IntVec2(_) => { 2 * i32::BYTE_SIZE },
FloatVec2(_) => { 2 * f32::BYTE_SIZE },
IntVec3(_) => { 3 * i32::BYTE_SIZE },
FloatVec3(_) => { 3 * f32::BYTE_SIZE },
ChannelList(ref channels) => channels.byte_size(),
Chromaticities(_) => self::Chromaticities::byte_size(),
Compression(_) => self::Compression::byte_size(),
EnvironmentMap(_) => self::EnvironmentMap::byte_size(),
KeyCode(_) => self::KeyCode::byte_size(),
LineOrder(_) => self::LineOrder::byte_size(),
Matrix3x3(ref value) => value.len() * f32::BYTE_SIZE,
Matrix4x4(ref value) => value.len() * f32::BYTE_SIZE,
Preview(ref value) => value.byte_size(),
Text(ref value) => value.bytes.len(),
TextVector(ref value) => value.iter().map(self::Text::i32_sized_byte_size).sum(),
TileDescription(_) => self::TileDescription::byte_size(),
Custom { ref bytes, .. } => bytes.len(),
BlockType(ref kind) => kind.byte_size()
}
}
pub fn kind_name(&self) -> &[u8] {
use self::AttributeValue::*;
use self::attribute_type_names as ty;
match *self {
IntRect(_) => ty::I32BOX2,
FloatRect(_) => ty::F32BOX2,
I32(_) => ty::I32,
F32(_) => ty::F32,
F64(_) => ty::F64,
Rational(_) => ty::RATIONAL,
TimeCode(_) => ty::TIME_CODE,
IntVec2(_) => ty::I32VEC2,
FloatVec2(_) => ty::F32VEC2,
IntVec3(_) => ty::I32VEC3,
FloatVec3(_) => ty::F32VEC3,
ChannelList(_) => ty::CHANNEL_LIST,
Chromaticities(_) => ty::CHROMATICITIES,
Compression(_) => ty::COMPRESSION,
EnvironmentMap(_) => ty::ENVIRONMENT_MAP,
KeyCode(_) => ty::KEY_CODE,
LineOrder(_) => ty::LINE_ORDER,
Matrix3x3(_) => ty::F32MATRIX3X3,
Matrix4x4(_) => ty::F32MATRIX4X4,
Preview(_) => ty::PREVIEW,
Text(_) => ty::TEXT,
TextVector(_) => ty::TEXT_VECTOR,
TileDescription(_) => ty::TILES,
Custom { ref kind, .. } => &kind.bytes,
BlockType(_) => super::BlockType::TYPE_NAME,
}
}
pub fn write<W: Write>(&self, write: &mut W) -> UnitResult {
use self::AttributeValue::*;
match *self {
IntRect(value) => value.write(write)?,
FloatRect(value) => value.write(write)?,
I32(value) => value.write(write)?,
F32(value) => value.write(write)?,
F64(value) => value.write(write)?,
Rational((a, b)) => { a.write(write)?; b.write(write)?; },
TimeCode(codes) => { codes.write(write)?; },
IntVec2(Vec2(x, y)) => { x.write(write)?; y.write(write)?; },
FloatVec2(Vec2(x, y)) => { x.write(write)?; y.write(write)?; },
IntVec3((x, y, z)) => { x.write(write)?; y.write(write)?; z.write(write)?; },
FloatVec3((x, y, z)) => { x.write(write)?; y.write(write)?; z.write(write)?; },
ChannelList(ref channels) => channels.write(write)?,
Chromaticities(ref value) => value.write(write)?,
Compression(value) => value.write(write)?,
EnvironmentMap(value) => value.write(write)?,
KeyCode(value) => value.write(write)?,
LineOrder(value) => value.write(write)?,
Matrix3x3(mut value) => f32::write_slice(write, &mut value)?,
Matrix4x4(mut value) => f32::write_slice(write, &mut value)?,
Preview(ref value) => { value.write(write)?; },
Text(ref value) => u8::write_slice(write, value.bytes.as_slice())?,
TextVector(ref value) => self::Text::write_vec_of_i32_sized_texts(write, value)?,
TileDescription(ref value) => value.write(write)?,
Custom { ref bytes, .. } => u8::write_slice(write, &bytes)?, BlockType(kind) => kind.write(write)?
};
Ok(())
}
pub fn read(read: &mut PeekRead<impl Read>, kind: Text, byte_size: usize) -> Result<Self> {
use self::AttributeValue::*;
use self::attribute_type_names as ty;
Ok(match kind.bytes.as_slice() {
ty::I32BOX2 => IntRect(self::IntRect::read(read)?),
ty::F32BOX2 => FloatRect(self::FloatRect::read(read)?),
ty::I32 => I32(i32::read(read)?),
ty::F32 => F32(f32::read(read)?),
ty::F64 => F64(f64::read(read)?),
ty::RATIONAL => Rational({
let a = i32::read(read)?;
let b = u32::read(read)?;
(a, b)
}),
ty::TIME_CODE => TimeCode(self::TimeCode::read(read)?),
ty::I32VEC2 => IntVec2({
let a = i32::read(read)?;
let b = i32::read(read)?;
Vec2(a, b)
}),
ty::F32VEC2 => FloatVec2({
let a = f32::read(read)?;
let b = f32::read(read)?;
Vec2(a, b)
}),
ty::I32VEC3 => IntVec3({
let a = i32::read(read)?;
let b = i32::read(read)?;
let c = i32::read(read)?;
(a, b, c)
}),
ty::F32VEC3 => FloatVec3({
let a = f32::read(read)?;
let b = f32::read(read)?;
let c = f32::read(read)?;
(a, b, c)
}),
ty::CHANNEL_LIST => ChannelList(self::ChannelList::read(read)?),
ty::CHROMATICITIES => Chromaticities(self::Chromaticities::read(read)?),
ty::COMPRESSION => Compression(self::Compression::read(read)?),
ty::ENVIRONMENT_MAP => EnvironmentMap(self::EnvironmentMap::read(read)?),
ty::KEY_CODE => KeyCode(self::KeyCode::read(read)?),
ty::LINE_ORDER => LineOrder(self::LineOrder::read(read)?),
ty::F32MATRIX3X3 => Matrix3x3({
let mut result = [0.0_f32; 9];
f32::read_slice(read, &mut result)?;
result
}),
ty::F32MATRIX4X4 => Matrix4x4({
let mut result = [0.0_f32; 16];
f32::read_slice(read, &mut result)?;
result
}),
ty::PREVIEW => Preview(self::Preview::read(read)?),
ty::TEXT => Text(self::Text::read_sized(read, byte_size)?),
ty::TEXT_VECTOR => TextVector(self::Text::read_vec_of_i32_sized(read, byte_size)?),
ty::TILES => TileDescription(self::TileDescription::read(read)?),
_ => {
let mut bytes = vec![0_u8; byte_size];
u8::read_slice(read, &mut bytes)?;
Custom { kind, bytes }
}
})
}
pub fn validate(&self, allow_sampling: bool, data_window: IntRect, strict: bool) -> UnitResult {
use self::AttributeValue::*;
match *self {
ChannelList(ref channels) => channels.validate(allow_sampling, data_window, strict)?,
TileDescription(ref value) => value.validate()?,
Preview(ref value) => value.validate(strict)?,
TextVector(ref vec) => if strict && vec.is_empty() {
return Err(Error::invalid("text vector may not be empty"))
},
_ => {}
};
Ok(())
}
pub fn to_tile_description(&self) -> Result<TileDescription> {
match *self {
AttributeValue::TileDescription(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_time_code(&self) -> Result<TimeCode> {
match *self {
AttributeValue::TimeCode(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_preview(&self) -> Result<&Preview> {
match self {
AttributeValue::Preview(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn into_preview(self) -> Result<Preview> {
match self {
AttributeValue::Preview(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_i32(&self) -> Result<i32> {
match *self {
AttributeValue::I32(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_f32(&self) -> Result<f32> {
match *self {
AttributeValue::F32(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_i32_box_2(&self) -> Result<IntRect> { match *self {
AttributeValue::IntRect(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_f32_vec_2(&self) -> Result<Vec2<f32>> {
match *self {
AttributeValue::FloatVec2(vec) => Ok(vec),
_ => Err(invalid_type())
}
}
pub fn to_line_order(&self) -> Result<LineOrder> {
match *self {
AttributeValue::LineOrder(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_compression(&self) -> Result<Compression> {
match *self {
AttributeValue::Compression(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn into_text(self) -> Result<Text> {
match self {
AttributeValue::Text(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_text(&self) -> Result<&Text> {
match self {
AttributeValue::Text(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn into_block_type(self) -> Result<BlockType> {
match self {
AttributeValue::BlockType(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn into_channel_list(self) -> Result<ChannelList> {
match self {
AttributeValue::ChannelList(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_chromaticities(&self) -> Result<Chromaticities> {
match *self {
AttributeValue::Chromaticities(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_environment_map(&self) -> Result<EnvironmentMap> {
match *self {
AttributeValue::EnvironmentMap(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_key_code(&self) -> Result<KeyCode> {
match *self {
AttributeValue::KeyCode(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_rational(&self) -> Result<Rational> {
match *self {
AttributeValue::Rational(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_matrix4x4(&self) -> Result<Matrix4x4> { match *self {
AttributeValue::Matrix4x4(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_matrix3x3(&self) -> Result<Matrix3x3> {
match *self {
AttributeValue::Matrix3x3(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn into_text_vector(self) -> Result<Vec<Text>> {
match self {
AttributeValue::TextVector(value) => Ok(value),
_ => Err(invalid_type())
}
}
pub fn to_text_vector(&self) -> Result<&Vec<Text>> {
match self {
AttributeValue::TextVector(value) => Ok(value),
_ => Err(invalid_type())
}
}
}
pub mod attribute_type_names {
macro_rules! define_attribute_type_names {
( $($name: ident : $value: expr),* ) => {
$(
pub const $name: &'static [u8] = $value;
)*
};
}
define_attribute_type_names! {
I32BOX2: b"box2i",
F32BOX2: b"box2f",
I32: b"int",
F32: b"float",
F64: b"double",
RATIONAL: b"rational",
TIME_CODE: b"timecode",
I32VEC2: b"v2i",
F32VEC2: b"v2f",
I32VEC3: b"v3i",
F32VEC3: b"v3f",
CHANNEL_LIST: b"chlist",
CHROMATICITIES: b"chromaticities",
COMPRESSION: b"compression",
ENVIRONMENT_MAP:b"envmap",
KEY_CODE: b"keycode",
LINE_ORDER: b"lineOrder",
F32MATRIX3X3: b"m33f",
F32MATRIX4X4: b"m44f",
PREVIEW: b"preview",
TEXT: b"string",
TEXT_VECTOR: b"stringvector",
TILES: b"tiledesc"
}
}
pub mod required_attribute_names {
macro_rules! define_required_attribute_names {
( $($name: ident : $value: expr),* ) => {
$(
pub const $name: &'static [u8] = $value;
)*
};
}
define_required_attribute_names! {
TILES: b"tiles",
NAME: b"name",
BLOCK_TYPE: b"type",
DEEP_DATA_VERSION: b"version",
CHUNKS: b"chunkCount",
MAX_SAMPLES: b"maxSamplesPerPixel",
CHANNELS: b"channels",
COMPRESSION: b"compression",
DATA_WINDOW: b"dataWindow",
DISPLAY_WINDOW: b"displayWindow",
LINE_ORDER: b"lineOrder",
PIXEL_ASPECT: b"pixelAspectRatio",
WINDOW_CENTER: b"screenWindowCenter",
WINDOW_WIDTH: b"screenWindowWidth",
WHITE_LUMINANCE: b"whiteLuminance",
ADOPTED_NEUTRAL: b"adoptedNeutral",
RENDERING_TRANSFORM: b"renderingTransform",
LOOK_MOD_TRANSFORM: b"lookModTransform",
X_DENSITY: b"xDensity",
OWNER: b"owner",
COMMENTS: b"comments",
CAPTURE_DATE: b"capDate",
UTC_OFFSET: b"utcOffset",
LONGITUDE: b"longitude",
LATITUDE: b"latitude",
ALTITUDE: b"altitude",
FOCUS: b"focus",
EXPOSURE_TIME: b"expTime",
APERTURE: b"aperture",
ISO_SPEED: b"isoSpeed",
ENVIRONMENT_MAP: b"envmap",
KEY_CODE: b"keyCode",
TIME_CODE: b"timeCode",
WRAP_MODES: b"wrapmodes",
FRAMES_PER_SECOND: b"framesPerSecond",
MULTI_VIEW: b"multiView",
WORLD_TO_CAMERA: b"worldToCamera",
WORLD_TO_NDC: b"worldToNDC",
DEEP_IMAGE_STATE: b"deepImageState",
ORIGINAL_DATA_WINDOW: b"originalDataWindow",
DWA_COMPRESSION_LEVEL: b"dwaCompressionLevel",
PREVIEW: b"preview",
VIEW: b"view",
CHROMATICITIES: b"chromaticities"
}
}
#[cfg(test)]
mod test {
use super::*;
use ::std::io::Cursor;
#[test]
fn text_ord() {
for _ in 0..1024 {
let text1 = Text::from_bytes_unchecked((0..4).map(|_| rand::random::<u8>()).collect());
let text2 = Text::from_bytes_unchecked((0..4).map(|_| rand::random::<u8>()).collect());
assert_eq!(text1.to_string().cmp(&text2.to_string()), text1.cmp(&text2), "in text {:?} vs {:?}", text1, text2);
}
}
#[test]
fn rounding_up(){
let round_up = RoundingMode::Up;
assert_eq!(round_up.divide(10, 10), 1, "divide equal");
assert_eq!(round_up.divide(10, 2), 5, "divide even");
assert_eq!(round_up.divide(10, 5), 2, "divide even");
assert_eq!(round_up.divide(8, 5), 2, "round up");
assert_eq!(round_up.divide(10, 3), 4, "round up");
assert_eq!(round_up.divide(100, 50), 2, "divide even");
assert_eq!(round_up.divide(100, 49), 3, "round up");
}
#[test]
fn rounding_down(){
let round_down = RoundingMode::Down;
assert_eq!(round_down.divide(8, 5), 1, "round down");
assert_eq!(round_down.divide(10, 3), 3, "round down");
assert_eq!(round_down.divide(100, 50), 2, "divide even");
assert_eq!(round_down.divide(100, 49), 2, "round down");
assert_eq!(round_down.divide(100, 51), 1, "round down");
}
#[test]
fn tile_description_write_read_roundtrip(){
let tiles = [
TileDescription {
tile_size: Vec2(31, 7),
level_mode: LevelMode::MipMap,
rounding_mode: RoundingMode::Down,
},
TileDescription {
tile_size: Vec2(0, 0),
level_mode: LevelMode::Singular,
rounding_mode: RoundingMode::Up,
},
TileDescription {
tile_size: Vec2(4294967294, 4294967295),
level_mode: LevelMode::RipMap,
rounding_mode: RoundingMode::Down,
},
];
for tile in &tiles {
let mut bytes = Vec::new();
tile.write(&mut bytes).unwrap();
let new_tile = TileDescription::read(&mut Cursor::new(bytes)).unwrap();
assert_eq!(*tile, new_tile, "tile round trip");
}
}
#[test]
fn attribute_write_read_roundtrip_and_byte_size(){
let attributes = [
(
Text::from("greeting").unwrap(),
AttributeValue::Text(Text::from("hello").unwrap()),
),
(
Text::from("age").unwrap(),
AttributeValue::I32(923),
),
(
Text::from("leg count").unwrap(),
AttributeValue::F64(9.114939599234),
),
(
Text::from("rabbit area").unwrap(),
AttributeValue::FloatRect(FloatRect {
min: Vec2(23.4234, 345.23),
max: Vec2(68623.0, 3.12425926538),
}),
),
(
Text::from("tests are difficult").unwrap(),
AttributeValue::TextVector(vec![
Text::from("sdoifjpsdv").unwrap(),
Text::from("sdoifjpsdvxxxx").unwrap(),
Text::from("sdoifjasd").unwrap(),
Text::from("sdoifj").unwrap(),
Text::from("sdoifjddddddddasdasd").unwrap(),
]),
),
(
Text::from("what should we eat tonight").unwrap(),
AttributeValue::Preview(Preview {
size: Vec2(10, 30),
pixel_data: vec![31; 10 * 30 * 4],
}),
),
(
Text::from("leg count, again").unwrap(),
AttributeValue::ChannelList(ChannelList {
list: smallvec![
Channel {
name: Text::from("Green").unwrap(),
pixel_type: PixelType::F16,
is_linear: false,
sampling: Vec2(1,2)
},
Channel {
name: Text::from("Red").unwrap(),
pixel_type: PixelType::F32,
is_linear: true,
sampling: Vec2(1,2)
},
Channel {
name: Text::from("Purple").unwrap(),
pixel_type: PixelType::U32,
is_linear: false,
sampling: Vec2(0,0)
}
],
bytes_per_pixel: 10
}),
),
];
for (name, value) in &attributes {
let mut bytes = Vec::new();
super::write(name.bytes(), value, &mut bytes).unwrap();
assert_eq!(super::byte_size(name, value), bytes.len(), "attribute.byte_size() for {:?}", (name, value));
let new_attribute = super::read(&mut PeekRead::new(Cursor::new(bytes)), 300).unwrap();
assert_eq!((name.clone(), value.clone()), new_attribute, "attribute round trip");
}
{
let (name, value) = (
Text::from("asdkaspfokpaosdkfpaokswdpoakpsfokaposdkf").unwrap(),
AttributeValue::I32(0),
);
super::validate(&name, &value, false, false, IntRect::zero(), false).expect_err("name length check failed");
}
{
let (name, value) = (
Text::from("sdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfposdöksadöofkaspdolkpöasolfkcöalsod,kfcöaslodkcpöasolkfpo").unwrap(),
AttributeValue::I32(0),
);
super::validate(&name, &value, true, false, IntRect::zero(), false).expect_err("name length check failed");
}
}
}