use smallvec::SmallVec;
use half::f16;
use crate::io::*;
use crate::meta::*;
use crate::meta::attributes::*;
use crate::error::{Result, UnitResult, Error};
use crate::math::*;
use std::io::{Seek, BufReader, BufWriter};
use crate::image::{LineRefMut, LineRef, OnWriteProgress, OnReadProgress, WriteOptions, ReadOptions};
#[derive(Clone, PartialEq, Debug)]
pub struct Image {
pub layers: Layers,
pub attributes: ImageAttributes,
}
pub type Layers = SmallVec<[Layer; 3]>;
#[derive(Clone, PartialEq, Debug)]
pub struct Layer {
pub channels: Channels,
pub attributes: LayerAttributes,
pub data_size: Vec2<usize>,
pub line_order: LineOrder,
pub compression: Compression,
pub tiles: Option<Vec2<usize>>,
}
pub type Channels = SmallVec<[Channel; 5]>;
#[derive(Clone, Debug, PartialEq)]
pub struct Channel {
pub name: Text,
pub samples: Samples,
pub is_linear: bool,
pub sampling: Vec2<usize>,
}
#[derive(Clone, PartialEq)]
pub enum Samples {
F16(Vec<f16>),
F32(Vec<f32>),
U32(Vec<u32>),
}
impl Image {
pub fn new_from_single_layer(layer: Layer) -> Self {
Self {
attributes: ImageAttributes {
display_window: layer.data_window(),
pixel_aspect: 1.0,
list: Vec::new()
},
layers: smallvec![ layer ],
}
}
pub fn new_from_layers(layers: Layers, display_window: IntRect) -> Self {
Self {
layers,
attributes: ImageAttributes {
display_window,
pixel_aspect: 1.0,
list: Vec::new()
}
}
}
#[must_use]
pub fn read_from_file(path: impl AsRef<std::path::Path>, options: ReadOptions<impl OnReadProgress>) -> Result<Self> {
Self::read_from_unbuffered(std::fs::File::open(path)?, options)
}
#[must_use]
pub fn read_from_unbuffered(unbuffered: impl Read + Send + Seek, options: ReadOptions<impl OnReadProgress>) -> Result<Self> { Self::read_from_buffered(BufReader::new(unbuffered), options)
}
#[must_use]
pub fn read_from_buffered(read: impl Read + Send + Seek, options: ReadOptions<impl OnReadProgress>) -> Result<Self> { let mut image: Image = crate::image::read_filtered_lines_from_buffered(
read,
Image::allocate,
|_image, header, tile_index| {
!header.deep && tile_index.location.is_largest_resolution_level()
},
|image, _meta, line| Image::insert_line(image, line),
options
)?;
{ for layer in &mut image.layers {
layer.channels.retain(|channel| channel.samples.len() > 0);
}
image.layers.retain(|layer| layer.channels.len() > 0);
}
Ok(image)
}
#[must_use]
pub fn write_to_file(&self, path: impl AsRef<std::path::Path>, options: WriteOptions<impl OnWriteProgress>) -> UnitResult {
crate::io::attempt_delete_file_on_write_error(path, |write|
self.write_to_unbuffered(write, options)
)
}
#[must_use]
pub fn write_to_unbuffered(&self, unbuffered: impl Write + Seek, options: WriteOptions<impl OnWriteProgress>) -> UnitResult {
self.write_to_buffered(BufWriter::new(unbuffered), options)
}
#[must_use]
pub fn write_to_buffered(&self, write: impl Write + Seek, options: WriteOptions<impl OnWriteProgress>) -> UnitResult {
crate::image::write_all_lines_to_buffered(
write, self.infer_meta_data(),
|_meta, line_mut| {
self.extract_line(line_mut);
Ok(()) },
options
)
}
}
impl Layer {
pub fn new(name: Text, data_size: Vec2<usize>, mut channels: Channels) -> Self {
assert!(!channels.is_empty(), "at least one channel is required");
assert!(
channels.iter().all(|chan|
chan.samples.len() / (chan.sampling.0 * chan.sampling.1) == data_size.area()
),
"channel data size must conform to data window size (scaled by channel sampling)"
);
channels.sort_by_key(|chan| chan.name.clone());
Layer {
channels,
data_size,
compression: Compression::Uncompressed,
tiles: None,
line_order: LineOrder::Unspecified,
attributes: LayerAttributes {
name: Some(name),
data_position: Vec2(0, 0),
screen_window_center: Vec2(0.0, 0.0),
screen_window_width: 1.0,
list: Vec::new(),
}
}
}
pub fn with_block_format(self, tiles: Option<Vec2<usize>>, line_order: LineOrder) -> Self {
Self { tiles, line_order, .. self }
}
pub fn with_compression(self, compression: Compression) -> Self {
Self { compression, .. self }
}
pub fn data_window(&self) -> IntRect {
IntRect::new(self.attributes.data_position, self.data_size)
}
}
impl Channel {
pub fn new(name: Text, is_linear: bool, samples: Samples) -> Self {
Self { name, samples, is_linear, sampling: Vec2(1, 1) }
}
pub fn new_linear(name: Text, samples: Samples) -> Self {
Self::new(name, true, samples)
}
}
impl Samples {
pub fn len(&self) -> usize {
match self {
Samples::F16(vec) => vec.len(),
Samples::F32(vec) => vec.len(),
Samples::U32(vec) => vec.len(),
}
}
}
impl Image {
pub fn allocate(headers: &[Header]) -> Result<Self> {
let shared_attributes = &headers.iter()
.max_by_key(|header| header.shared_attributes.list.len())
.expect("no headers found").shared_attributes;
let headers : Result<_> = headers.iter()
.map(Layer::allocate).collect();
Ok(Image {
layers: headers?,
attributes: shared_attributes.clone(),
})
}
pub fn insert_line(&mut self, line: LineRef<'_>) -> UnitResult {
debug_assert_ne!(line.location.sample_count, 0, "line width calculation bug");
let layer = self.layers.get_mut(line.location.layer)
.ok_or(Error::invalid("chunk part index"))?;
layer.insert_line(line)
}
pub fn extract_line(&self, line: LineRefMut<'_>) {
debug_assert_ne!(line.location.sample_count, 0, "line width calculation bug");
let layer = self.layers.get(line.location.layer)
.expect("invalid part index");
layer.extract_line(line)
}
pub fn infer_meta_data(&self) -> MetaData {
let headers: Headers = self.layers.iter()
.map(|layer| layer.infer_header(&self.attributes))
.collect();
MetaData::new(headers)
}
}
impl Layer {
pub fn allocate(header: &Header) -> Result<Self> {
Ok(Layer {
data_size: header.data_size,
attributes: header.own_attributes.clone(),
channels: header.channels.list.iter().map(|channel| Channel::allocate(header, channel)).collect(),
compression: header.compression,
line_order: header.line_order,
tiles: match header.blocks {
Blocks::ScanLines => None,
Blocks::Tiles(tiles) => Some(tiles.tile_size),
}
})
}
pub fn insert_line(&mut self, line: LineRef<'_>) -> UnitResult {
debug_assert!(line.location.position.0 + line.location.sample_count <= self.data_size.0, "line index calculation bug");
debug_assert!(line.location.position.1 < self.data_size.1, "line index calculation bug");
self.channels.get_mut(line.location.channel)
.expect("invalid channel index")
.insert_line(line, self.data_size)
}
pub fn extract_line(&self, line: LineRefMut<'_>) {
debug_assert!(line.location.position.0 + line.location.sample_count <= self.data_size.0, "line index calculation bug");
debug_assert!(line.location.position.1 < self.data_size.1, "line index calculation bug");
self.channels.get(line.location.channel)
.expect("invalid channel index")
.extract_line(line, self.data_size)
}
pub fn infer_header(&self, shared_attributes: &ImageAttributes) -> Header {
let blocks = match self.tiles {
Some(tiles) => Blocks::Tiles(TileDescription {
tile_size: tiles,
level_mode: LevelMode::Singular,
rounding_mode: RoundingMode::Down
}),
None => Blocks::ScanLines,
};
let channels = self.channels.iter()
.map(Channel::infer_channel_attribute).collect();
let chunk_count = compute_chunk_count(
self.compression, self.data_size, blocks
);
Header {
chunk_count,
data_size: self.data_size,
compression: self.compression,
channels: ChannelList::new(channels),
line_order: self.line_order,
own_attributes: self.attributes.clone(), shared_attributes: shared_attributes.clone(),
blocks,
deep_data_version: None,
max_samples_per_pixel: None,
deep: false,
}
}
}
impl Channel {
pub fn allocate(header: &Header, channel: &crate::meta::attributes::Channel) -> Self {
let size = if header.deep { Vec2(0, 0) } else {
header.data_size / channel.sampling
};
Channel {
name: channel.name.clone(), is_linear: channel.is_linear, sampling: channel.sampling,
samples: Samples::allocate(size, channel.pixel_type)
}
}
pub fn insert_line(&mut self, line: LineRef<'_>, resolution: Vec2<usize>) -> UnitResult {
assert_eq!(line.location.level, Vec2(0,0), "line index calculation bug");
self.samples.insert_line(resolution / self.sampling, line)
}
pub fn extract_line(&self, line: LineRefMut<'_>, resolution: Vec2<usize>) {
debug_assert_eq!(line.location.level, Vec2(0,0), "line index calculation bug");
self.samples.extract_line(line, resolution / self.sampling)
}
pub fn infer_channel_attribute(&self) -> attributes::Channel {
attributes::Channel {
pixel_type: match self.samples {
Samples::F16(_) => PixelType::F16,
Samples::F32(_) => PixelType::F32,
Samples::U32(_) => PixelType::U32,
},
name: self.name.clone(),
is_linear: self.is_linear,
sampling: self.sampling,
}
}
}
impl Samples {
pub fn allocate(resolution: Vec2<usize>, pixel_type: PixelType) -> Self {
let count = resolution.area();
match pixel_type {
PixelType::F16 => Samples::F16(vec![ f16::ZERO; count ] ),
PixelType::F32 => Samples::F32(vec![ 0.0; count ] ),
PixelType::U32 => Samples::U32(vec![ 0; count ] ),
}
}
pub fn insert_line(&mut self, resolution: Vec2<usize>, line: LineRef<'_>) -> UnitResult {
debug_assert_ne!(line.location.sample_count, 0, "line index calculation bug");
if line.location.position.0 + line.location.sample_count > resolution.0 {
return Err(Error::invalid("data block x coordinate"))
}
if line.location.position.1 > resolution.1 {
return Err(Error::invalid("data block y coordinate"))
}
debug_assert_ne!(resolution.0, 0, "sample size bug");
debug_assert_ne!(line.location.sample_count, 0, "line index calculation bug");
let start_index = line.location.position.1 * resolution.0 + line.location.position.0;
let end_index = start_index + line.location.sample_count;
match self {
Samples::F16(samples) => line.read_samples_into_slice(&mut samples[start_index .. end_index]),
Samples::F32(samples) => line.read_samples_into_slice(&mut samples[start_index .. end_index]),
Samples::U32(samples) => line.read_samples_into_slice(&mut samples[start_index .. end_index]),
}
}
pub fn extract_line(&self, line: LineRefMut<'_>, resolution: Vec2<usize>) {
let index = line.location;
debug_assert!(index.position.0 + index.sample_count <= resolution.0, "line index calculation bug");
debug_assert!(index.position.1 < resolution.1, "line index calculation bug");
debug_assert_ne!(index.sample_count, 0, "line index bug");
debug_assert_ne!(resolution.0, 0, "sample size but");
debug_assert_ne!(index.sample_count, 0, "line index bug");
let start_index = index.position.1 * resolution.0 + index.position.0;
let end_index = start_index + index.sample_count;
match &self {
Samples::F16(samples) =>
line.write_samples_from_slice(&samples[start_index .. end_index])
.expect("writing line bytes failed"),
Samples::F32(samples) =>
line.write_samples_from_slice(&samples[start_index .. end_index])
.expect("writing line bytes failed"),
Samples::U32(samples) =>
line.write_samples_from_slice(&samples[start_index .. end_index])
.expect("writing line bytes failed"),
}
}
}
impl std::fmt::Debug for Samples {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Samples::F16(vec) => write!(formatter, "[f16; {}]", vec.len()),
Samples::F32(vec) => write!(formatter, "[f32; {}]", vec.len()),
Samples::U32(vec) => write!(formatter, "[u32; {}]", vec.len()),
}
}
}