#![doc(hidden)]
use smallvec::SmallVec;
use half::f16;
use crate::io::*;
use crate::meta::*;
use crate::meta::attributes::*;
use crate::error::{Result, PassiveResult, Error};
use crate::math::*;
use std::io::{Seek, BufReader, BufWriter};
use crate::io::Data;
use crate::image::{Line, LineIndex};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WriteOptions {
pub parallel_compression: bool,
pub pedantic: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ReadOptions {
pub parallel_decompression: bool,
}
#[derive(Clone, PartialEq, Debug)]
pub struct Image {
pub layers: Layers,
pub display_window: IntRect,
pub pixel_aspect: f32,
}
pub type Layers = SmallVec<[Layer; 3]>;
#[derive(Clone, PartialEq, Debug)]
pub struct Layer {
pub name: Option<Text>,
pub attributes: Attributes,
pub data_window: IntRect,
pub screen_window_center: Vec2<f32>,
pub screen_window_width: f32,
pub line_order: LineOrder,
pub compression: Compression,
pub blocks: Blocks,
pub channels: Channels,
}
pub type Channels = SmallVec<[Channel; 5]>;
#[derive(Clone, Debug, PartialEq)]
pub struct Channel {
pub name: Text,
pub content: ChannelData,
pub is_linear: bool,
pub sampling: Vec2<usize>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ChannelData {
F16(SampleMaps<f16>),
F32(SampleMaps<f32>),
U32(SampleMaps<u32>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum SampleMaps<Sample> {
Flat (Levels<FlatSamples<Sample>>),
Deep (Levels<DeepSamples<Sample>>),
}
#[derive(Clone, PartialEq)]
pub enum Levels<Samples> {
Singular(SampleBlock<Samples>),
Mip(LevelMaps<Samples>),
Rip(RipMaps<Samples>),
}
pub type LevelMaps<Samples> = Vec<SampleBlock<Samples>>;
#[derive(Clone, PartialEq, Debug)]
pub struct RipMaps<Samples> {
pub map_data: LevelMaps<Samples>,
pub level_count: Vec2<usize>,
}
#[derive(Clone, PartialEq, Debug)]
pub struct SampleBlock<Samples> {
pub resolution: Vec2<usize>,
pub samples: Samples
}
pub type FlatSamples<Sample> = Vec<Sample>;
pub type DeepSamples<Sample> = Vec<DeepLine<Sample>>;
#[derive(Clone, Debug, PartialEq)]
pub struct DeepLine<Sample> {
pub samples: Vec<Sample>,
pub index_table: Vec<u32>,
}
impl Default for WriteOptions {
fn default() -> Self { Self::high() }
}
impl Default for ReadOptions {
fn default() -> Self { Self::high() }
}
impl WriteOptions {
pub fn higher() -> Self {
WriteOptions {
parallel_compression: true,
pedantic: false
}
}
pub fn high() -> Self {
WriteOptions {
parallel_compression: true,
pedantic: true,
}
}
pub fn low() -> Self {
WriteOptions {
parallel_compression: false,
pedantic: true,
}
}
}
impl ReadOptions {
pub fn high() -> Self { ReadOptions { parallel_decompression: true } }
pub fn low() -> Self { ReadOptions { parallel_decompression: false } }
}
impl<S> std::fmt::Debug for Levels<S> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Levels::Singular(image) => write!(
formatter, "Singular ([{}x{}])",
image.resolution.0, image.resolution.1
),
Levels::Mip(levels) => write!(
formatter, "Mip ({:?})",
levels.iter().map(|level| level.resolution).collect::<Vec<_>>(),
),
Levels::Rip(maps) => write!(
formatter, "Rip ({:?})",
maps.map_data.iter().map(|level| level.resolution).collect::<Vec<_>>()
)
}
}
}
impl Image {
#[must_use]
pub fn read_from_file(path: impl AsRef<std::path::Path>, options: ReadOptions) -> Result<Self> {
Self::read_from_unbuffered(std::fs::File::open(path)?, options)
}
#[must_use]
pub fn read_from_unbuffered(unbuffered: impl Read + Send, options: ReadOptions) -> Result<Self> {
Self::read_from_buffered(BufReader::new(unbuffered), options)
}
#[must_use]
pub fn read_from_buffered(read: impl Read + Send, options: ReadOptions) -> Result<Self> {
crate::image::read_all_lines_from_buffered(read, options.parallel_decompression, Image::allocate, Image::insert_line)
}
#[must_use]
pub fn write_to_file(&self, path: impl AsRef<std::path::Path>, options: WriteOptions) -> PassiveResult {
self.write_to_unbuffered(std::fs::File::create(path)?, options)
}
#[must_use]
pub fn write_to_unbuffered(&self, unbuffered: impl Write + Seek, options: WriteOptions) -> PassiveResult {
self.write_to_buffered(BufWriter::new(unbuffered), options)
}
#[must_use]
pub fn write_to_buffered(&self, write: impl Write + Seek, options: WriteOptions) -> PassiveResult {
crate::image::write_all_lines_to_buffered(
write, options.parallel_compression, options.pedantic, self.infer_meta_data(),
|location, write| {
self.extract_line(location, write)
}
)
}
}
impl Image {
pub fn allocate(headers: &[Header]) -> Result<Self> {
let display_window = headers.iter()
.map(|header| header.display_window)
.next().unwrap_or(IntRect::zero());
let pixel_aspect = headers.iter()
.map(|header| header.pixel_aspect)
.next().unwrap_or(1.0);
let headers : Result<_> = headers.iter().map(Layer::allocate).collect();
Ok(Image {
layers: headers?,
display_window,
pixel_aspect
})
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
debug_assert_ne!(line.location.width, 0, "linde index bug");
let layer = self.layers.get_mut(line.location.layer)
.ok_or(Error::invalid("chunk layer index"))?;
layer.insert_line(line)
}
pub fn extract_line(&self, index: LineIndex, write: &mut impl Write) {
debug_assert_ne!(index.width, 0, "line index bug");
let layer = self.layers.get(index.layer)
.expect("invalid layer index");
layer.extract_line(index, write)
}
pub fn infer_meta_data(&self) -> MetaData {
let headers: Headers = self.layers.iter()
.map(|layer| layer.infer_header(self.display_window, self.pixel_aspect))
.collect();
MetaData::new(headers)
}
}
impl Layer {
pub fn allocate(header: &Header) -> Result<Self> {
Ok(Layer {
data_window: header.data_window,
screen_window_center: header.screen_window_center,
screen_window_width: header.screen_window_width,
name: header.name.clone(),
attributes: header.custom_attributes.clone(),
channels: header.channels.list.iter().map(|channel| Channel::allocate(header, channel)).collect(),
compression: header.compression,
blocks: header.blocks,
line_order: header.line_order,
})
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
debug_assert!(line.location.position.0 + line.location.width <= self.data_window.size.0, "line index bug");
debug_assert!(line.location.position.1 < self.data_window.size.1, "line index bug");
self.channels.get_mut(line.location.channel)
.expect("invalid channel index")
.insert_line(line)
}
pub fn extract_line(&self, index: LineIndex, write: &mut impl Write) {
debug_assert!(index.position.0 + index.width <= self.data_window.size.0, "line index bug");
debug_assert!(index.position.1 < self.data_window.size.1, "line index bug");
self.channels.get(index.channel)
.expect("invalid channel index")
.extract_line(index, write)
}
pub fn infer_header(&self, display_window: IntRect, pixel_aspect: f32) -> Header {
let chunk_count = compute_chunk_count(
self.compression, self.data_window, self.blocks
);
Header {
chunk_count,
name: self.name.clone(),
data_window: self.data_window,
screen_window_center: self.screen_window_center,
screen_window_width: self.screen_window_width,
compression: self.compression,
blocks: self.blocks,
channels: ChannelList::new(self.channels.iter().map(Channel::infer_channel_attribute).collect()),
line_order: self.line_order,
custom_attributes: self.attributes.clone(),
display_window, pixel_aspect,
deep_data_version: None,
max_samples_per_pixel: None,
deep: false
}
}
}
impl Channel {
pub fn allocate(header: &Header, channel: &attributes::Channel) -> Self {
Channel {
name: channel.name.clone(),
is_linear: channel.is_linear,
sampling: channel.sampling,
content: match channel.pixel_type {
PixelType::F16 => ChannelData::F16(SampleMaps::allocate(header, channel)),
PixelType::F32 => ChannelData::F32(SampleMaps::allocate(header, channel)),
PixelType::U32 => ChannelData::U32(SampleMaps::allocate(header, channel)),
},
}
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
match &mut self.content {
ChannelData::F16(maps) => maps.insert_line(line),
ChannelData::F32(maps) => maps.insert_line(line),
ChannelData::U32(maps) => maps.insert_line(line),
}
}
pub fn extract_line(&self, index: LineIndex, block: &mut impl Write) {
match &self.content {
ChannelData::F16(maps) => maps.extract_line(index, block),
ChannelData::F32(maps) => maps.extract_line(index, block),
ChannelData::U32(maps) => maps.extract_line(index, block),
}
}
pub fn infer_channel_attribute(&self) -> attributes::Channel {
attributes::Channel {
pixel_type: match self.content {
ChannelData::F16(_) => PixelType::F16,
ChannelData::F32(_) => PixelType::F32,
ChannelData::U32(_) => PixelType::U32,
},
name: self.name.clone(),
is_linear: self.is_linear,
sampling: self.sampling,
}
}
}
impl<Sample: Data + std::fmt::Debug> SampleMaps<Sample> {
pub fn allocate(header: &Header, channel: &attributes::Channel) -> Self {
if header.deep {
SampleMaps::Deep(Levels::allocate(header, channel))
}
else {
SampleMaps::Flat(Levels::allocate(header, channel))
}
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
match self {
SampleMaps::Deep(ref mut levels) => levels.insert_line(line),
SampleMaps::Flat(ref mut levels) => levels.insert_line(line),
}
}
pub fn extract_line(&self, index: LineIndex, block: &mut impl Write) {
match self {
SampleMaps::Deep(ref levels) => levels.extract_line(index, block),
SampleMaps::Flat(ref levels) => levels.extract_line(index, block),
}
}
pub fn as_flat_samples(&self) -> Option<&Levels<FlatSamples<Sample>>> {
match self {
SampleMaps::Flat(ref levels) => Some(levels),
_ => None
}
}
pub fn as_deep_samples(&self) -> Option<&Levels<DeepSamples<Sample>>> {
match self {
SampleMaps::Deep(ref levels) => Some(levels),
_ => None
}
}
pub fn level_mode(&self) -> LevelMode {
match self {
SampleMaps::Flat(levels) => levels.level_mode(),
SampleMaps::Deep(levels) => levels.level_mode(),
}
}
}
impl<S: Samples> Levels<S> {
pub fn allocate(header: &Header, channel: &attributes::Channel) -> Self {
let data_size = header.data_window.size / channel.sampling;
if let Blocks::Tiles(tiles) = &header.blocks {
let round = tiles.rounding_mode;
match tiles.level_mode {
LevelMode::Singular => Levels::Singular(SampleBlock::allocate(data_size)),
LevelMode::MipMap => Levels::Mip(
mip_map_levels(round, data_size)
.map(|(_, level_size)| SampleBlock::allocate(level_size)).collect()
),
LevelMode::RipMap => Levels::Rip({
let level_count_x = compute_level_count(round, data_size.0);
let level_count_y = compute_level_count(round, data_size.1);
let maps = rip_map_levels(round, data_size)
.map(|(_, level_size)| SampleBlock::allocate(level_size)).collect();
RipMaps {
map_data: maps,
level_count: Vec2(level_count_x, level_count_y)
}
})
}
}
else {
Levels::Singular(SampleBlock::allocate(data_size))
}
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
self.get_level_mut(line.location.level)?.insert_line(line)
}
pub fn extract_line(&self, index: LineIndex, write: &mut impl Write) {
self.get_level(index.level)
.expect("invalid level index")
.extract_line(index, write)
}
pub fn get_level(&self, level: Vec2<usize>) -> Result<&SampleBlock<S>> {
match self {
Levels::Singular(ref block) => {
debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug");
Ok(block)
},
Levels::Mip(block) => {
debug_assert_eq!(level.0, level.1, "mip map levels must be equal on x and y bug");
block.get(level.0).ok_or(Error::invalid("block mip level index"))
},
Levels::Rip(block) => {
block.get_by_level(level).ok_or(Error::invalid("block rip level index"))
}
}
}
pub fn get_level_mut(&mut self, level: Vec2<usize>) -> Result<&mut SampleBlock<S>> {
match self {
Levels::Singular(ref mut block) => {
debug_assert_eq!(level, Vec2(0,0), "singular image cannot write leveled blocks bug");
Ok(block)
},
Levels::Mip(block) => {
debug_assert_eq!(level.0, level.1, "mip map levels must be equal on x and y bug");
block.get_mut(level.0).ok_or(Error::invalid("block mip level index"))
},
Levels::Rip(block) => {
block.get_by_level_mut(level).ok_or(Error::invalid("block rip level index"))
}
}
}
pub fn largest(&self) -> Result<&SampleBlock<S>> {
self.get_level(Vec2(0,0))
}
pub fn as_slice(&self) -> &[SampleBlock<S>] {
match self {
Levels::Singular(ref data) => std::slice::from_ref(data),
Levels::Mip(ref maps) => maps,
Levels::Rip(ref rip_map) => &rip_map.map_data,
}
}
pub fn level_mode(&self) -> LevelMode {
match self {
Levels::Singular(_) => LevelMode::Singular,
Levels::Mip(_) => LevelMode::MipMap,
Levels::Rip(_) => LevelMode::RipMap,
}
}
}
impl<S: Samples> SampleBlock<S> {
pub fn allocate(resolution: Vec2<usize>) -> Self {
SampleBlock { resolution, samples: S::allocate(resolution) }
}
pub fn insert_line(&mut self, line: Line<'_>) -> PassiveResult {
debug_assert_ne!(line.location.width, 0, "line index bug");
if line.location.position.0 + line.location.width > self.resolution.0 {
return Err(Error::invalid("data block x coordinate"))
}
if line.location.position.1 > self.resolution.1 {
return Err(Error::invalid("data block y coordinate"))
}
self.samples.insert_line(line, self.resolution.0)
}
pub fn extract_line(&self, index: LineIndex, write: &mut impl Write) {
debug_assert!(index.position.0 + index.width <= self.resolution.0, "line index bug");
debug_assert!(index.position.1 < self.resolution.1, "line index bug");
debug_assert_ne!(index.width, 0, "line index bug");
self.samples.extract_line(index, write, self.resolution.0)
}
}
pub trait Samples {
fn allocate(resolution: Vec2<usize>) -> Self;
fn insert_line(&mut self, line: Line<'_>, image_width: usize) -> PassiveResult;
fn extract_line(&self, index: LineIndex, write: &mut impl Write, image_width: usize);
}
impl<Sample: crate::io::Data> Samples for DeepSamples<Sample> {
fn allocate(resolution: Vec2<usize>) -> Self {
vec![
DeepLine { samples: Vec::new(), index_table: vec![0; resolution.0] };
resolution.1
]
}
fn insert_line(&mut self, _line: Line<'_>, _width: usize) -> PassiveResult {
unimplemented!("deep data not supported yet");
}
fn extract_line(&self, _index: LineIndex, _write: &mut impl Write, image_width: usize) {
debug_assert_ne!(image_width, 0, "deep image width bug");
unimplemented!("deep data not supported yet");
}
}
impl<Sample: crate::io::Data + Default + Clone + std::fmt::Debug> Samples for FlatSamples<Sample> {
fn allocate(resolution: Vec2<usize>) -> Self {
let resolution = (resolution.0, resolution.1);
vec![Sample::default(); resolution.0 * resolution.1]
}
fn insert_line(&mut self, line: Line<'_>, image_width: usize) -> PassiveResult {
debug_assert_ne!(image_width, 0, "image width calculation bug");
debug_assert_ne!(line.location.width, 0, "line width calculation bug");
let start_index = line.location.position.1 * image_width + line.location.position.0;
let end_index = start_index + line.location.width;
line.read_samples(&mut self[start_index .. end_index])
}
fn extract_line(&self, index: LineIndex, write: &mut impl Write, image_width: usize) {
debug_assert_ne!(image_width, 0, "image width calculation bug");
let start_index = index.position.1 * image_width + index.position.0;
let end_index = start_index + index.width;
LineIndex::write_samples(&self[start_index .. end_index], write)
.expect("writing line bytes failed");
}
}
impl<Samples> RipMaps<Samples> {
pub fn get_level_index(&self, level: Vec2<usize>) -> usize {
self.level_count.0 * level.1 + level.0
}
pub fn get_by_level(&self, level: Vec2<usize>) -> Option<&SampleBlock<Samples>> {
self.map_data.get(self.get_level_index(level))
}
pub fn get_by_level_mut(&mut self, level: Vec2<usize>) -> Option<&mut SampleBlock<Samples>> {
let index = self.get_level_index(level);
self.map_data.get_mut(index)
}
}