use crate::prelude::common::*;
use crate::io::*;
use crate::meta::*;
use crate::meta::attribute::*;
use crate::error::{Result, UnitResult, Error};
use crate::math::*;
use std::io::{Seek, BufReader, BufWriter};
use crate::image::{OnWriteProgress, OnReadProgress, WriteOptions, ReadOptions};
use crate::block::lines::{LineRef, LineRefMut};
use crate::meta::header::Header;
#[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 tile_size: Option<Vec2<usize>>,
}
pub type Channels = SmallVec<[Channel; 5]>;
#[derive(Clone, Debug, PartialEq)]
pub struct Channel {
pub name: Text,
pub samples: Samples,
pub quantize_linearly: 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::new(layer.data_size),
layers: smallvec![ layer ],
}
}
pub fn new_from_layers(layers: Layers, display_window: IntRect) -> Self {
Self { layers, attributes: ImageAttributes::default().with_display_window(display_window) }
}
#[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::block::lines::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::block::lines::write_all_lines_to_buffered(
write, self.infer_meta_data(),
|_meta, line_mut| self.extract_line(line_mut),
options
)
}
pub fn contains_nan_pixels(&self) -> bool {
self.layers.iter().flat_map(|layer: &Layer| &layer.channels)
.any(|channel: &Channel| channel.samples.contains_nan())
}
}
impl Layer {
pub fn new(name: Text, data_size: impl Into<Vec2<usize>>, mut channels: Channels) -> Self {
let data_size: Vec2<usize> = data_size.into();
assert!(!channels.is_empty(), "at least one channel is required");
assert!(
channels.iter().all(|chan|
chan.samples.len() / chan.sampling.area() == 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,
tile_size: None,
line_order: LineOrder::Unspecified,
attributes: LayerAttributes::new(name),
}
}
pub fn with_block_format(self, tiles: Option<Vec2<usize>>, line_order: LineOrder) -> Self {
Self { tile_size: 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 color_data(name: Text, samples: Samples) -> Self {
Self { name, samples, quantize_linearly: false, sampling: Vec2(1, 1) }
}
pub fn non_color_data(name: Text, samples: Samples) -> Self {
Self { name, samples, quantize_linearly: true, sampling: Vec2(1, 1) }
}
}
impl Samples {
pub fn len(&self) -> usize {
match self {
Samples::F16(vec) => vec.len(),
Samples::F32(vec) => vec.len(),
Samples::U32(vec) => vec.len(),
}
}
pub fn contains_nan(&self) -> bool {
match self {
Samples::F16(ref values) => values.iter().any(|sample| sample.is_nan()),
Samples::F32(ref values) => values.iter().any(|sample| sample.is_nan()),
Samples::U32(_) => false,
}
}
}
impl Image {
pub fn allocate(headers: &[Header]) -> Result<Self> {
let shared_attributes = &headers.iter()
.max_by_key(|header| header.shared_attributes.custom.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) -> Headers {
self.layers.iter()
.map(|layer| layer.infer_header(&self.attributes))
.collect()
}
}
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,
tile_size: 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.x() + line.location.sample_count <= self.data_size.width(), "line index calculation bug");
debug_assert!(line.location.position.y() < self.data_size.height(), "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.x() + line.location.sample_count <= self.data_size.width(), "line index calculation bug");
debug_assert!(line.location.position.y() < self.data_size.height(), "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.tile_size {
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::attribute::ChannelInfo) -> Self {
let size = if header.deep { Vec2(0, 0) } else {
header.data_size / channel.sampling
};
Channel {
name: channel.name.clone(), quantize_linearly: channel.quantize_linearly, sampling: channel.sampling,
samples: Samples::allocate(size, channel.sample_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) -> attribute::ChannelInfo {
attribute::ChannelInfo {
sample_type: match self.samples {
Samples::F16(_) => SampleType::F16,
Samples::F32(_) => SampleType::F32,
Samples::U32(_) => SampleType::U32,
},
name: self.name.clone(),
quantize_linearly: self.quantize_linearly,
sampling: self.sampling,
}
}
}
impl Samples {
pub fn allocate(resolution: Vec2<usize>, sample_type: SampleType) -> Self {
let count = resolution.area();
debug_assert!(count < 1920*20 * 1920*20, "suspiciously large image: {} mega pixels", count / 1_000_000);
match sample_type {
SampleType::F16 => Samples::F16(vec![f16::ZERO; count ] ),
SampleType::F32 => Samples::F32(vec![0.0; count ] ),
SampleType::U32 => Samples::U32(vec![0; count ] ),
}
}
pub fn insert_line(&mut self, resolution: Vec2<usize>, line: LineRef<'_>) -> UnitResult {
if line.location.position.x() + line.location.sample_count > resolution.width() {
return Err(Error::invalid("data block x coordinate"))
}
if line.location.position.y() > resolution.height() {
return Err(Error::invalid("data block y coordinate"))
}
let start_index = line.location.position.y() * resolution.width() + line.location.position.x();
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.x() + index.sample_count <= resolution.width(), "line index calculation bug");
debug_assert!(index.position.y() < resolution.height(), "line index calculation bug");
debug_assert_ne!(resolution.0, 0, "sample size but");
let start_index = index.position.y() * resolution.width() + index.position.x();
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()),
}
}
}