#![doc(hidden)]
use crate::prelude::common::*;
use smallvec::SmallVec;
use half::f16;
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::io::Data;
use crate::image::{OnWriteProgress, OnReadProgress, ReadOptions, WriteOptions};
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 size: Vec2<usize>,
pub line_order: LineOrder,
pub compression: Compression,
pub blocks: Blocks,
}
pub type Channels = SmallVec<[Channel; 5]>;
#[derive(Clone, Debug, PartialEq)]
pub struct Channel {
pub name: Text,
pub content: ChannelData,
pub quantize_linearly: 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<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.width(), image.resolution.height()
),
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<impl OnReadProgress>) -> Result<Self> {
Self::read_from_unbuffered(std::fs::File::open(path)?, options)
}
#[inline]
#[must_use]
pub fn read_from_unbuffered(unbuffered: impl Read + Send, options: ReadOptions<impl OnReadProgress>) -> Result<Self> {
Self::read_from_buffered(BufReader::new(unbuffered), options)
}
#[inline]
#[must_use]
pub fn read_from_buffered(read: impl Read + Send, options: ReadOptions<impl OnReadProgress>) -> Result<Self> {
crate::block::lines::read_all_lines_from_buffered(
read,
Image::allocate,
|image, _meta, line| Image::insert_line(image, line),
options
)
}
#[inline]
#[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.as_ref(), move |write|
self.write_to_unbuffered(write, options)
)
}
#[inline]
#[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)
}
#[inline]
#[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| match channel.content {
ChannelData::F16(ref values) => values.contains_nan_pixels(),
ChannelData::F32(ref values) => values.contains_nan_pixels(),
ChannelData::U32(ref values) => values.contains_nan_pixels(),
})
}
}
impl Image {
pub fn allocate(headers: &[Header]) -> Result<Self> {
let shared_attributes = &headers.iter()
.max_by_key(|header| header.shared_attributes.other.len())
.expect("at least one header is required").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, "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, line: LineRefMut<'_>) {
debug_assert_ne!(line.location.sample_count, 0, "line index bug");
let layer = self.layers.get(line.location.layer)
.expect("invalid layer 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 {
size: header.layer_size,
attributes: header.own_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: LineRef<'_>) -> UnitResult {
debug_assert!(line.location.position.x() + line.location.sample_count <= self.size.width(), "line index bug");
debug_assert!(line.location.position.y() < self.size.height(), "line index bug");
self.channels.get_mut(line.location.channel)
.expect("invalid channel index")
.insert_line(line)
}
pub fn extract_line(&self, line: LineRefMut<'_>) {
debug_assert!(line.location.position.x() + line.location.sample_count <= self.size.width(), "line index bug");
debug_assert!(line.location.position.y() < self.size.height(), "line index bug");
self.channels.get(line.location.channel)
.expect("invalid channel index")
.extract_line(line)
}
pub fn infer_header(&self, shared_attributes: &ImageAttributes) -> Header {
let chunk_count = compute_chunk_count(
self.compression, self.size, self.blocks
);
Header {
chunk_count,
compression: self.compression,
blocks: self.blocks,
channels: ChannelList::new(self.channels.iter().map(Channel::infer_channel_attribute).collect()),
line_order: self.line_order,
layer_size: self.size,
own_attributes: self.attributes.clone(),
shared_attributes: shared_attributes.clone(),
deep_data_version: None,
max_samples_per_pixel: None,
deep: false,
}
}
}
impl Channel {
pub fn allocate(header: &Header, channel: &attribute::ChannelInfo) -> Self {
Channel {
name: channel.name.clone(),
quantize_linearly: channel.quantize_linearly,
sampling: channel.sampling,
content: match channel.sample_type {
SampleType::F16 => ChannelData::F16(SampleMaps::allocate(header, channel)),
SampleType::F32 => ChannelData::F32(SampleMaps::allocate(header, channel)),
SampleType::U32 => ChannelData::U32(SampleMaps::allocate(header, channel)),
},
}
}
pub fn insert_line(&mut self, line: LineRef<'_>) -> UnitResult {
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, line: LineRefMut<'_>) {
match &self.content {
ChannelData::F16(maps) => maps.extract_line(line),
ChannelData::F32(maps) => maps.extract_line(line),
ChannelData::U32(maps) => maps.extract_line(line),
}
}
pub fn infer_channel_attribute(&self) -> attribute::ChannelInfo {
attribute::ChannelInfo {
sample_type: match self.content {
ChannelData::F16(_) => SampleType::F16,
ChannelData::F32(_) => SampleType::F32,
ChannelData::U32(_) => SampleType::U32,
},
name: self.name.clone(),
quantize_linearly: self.quantize_linearly,
sampling: self.sampling,
}
}
}
impl<Sample: Data + IsNan + std::fmt::Debug> SampleMaps<Sample> {
pub fn allocate(header: &Header, channel: &attribute::ChannelInfo) -> Self {
if header.deep {
SampleMaps::Deep(Levels::allocate(header, channel))
}
else {
SampleMaps::Flat(Levels::allocate(header, channel))
}
}
pub fn insert_line(&mut self, line: LineRef<'_>) -> UnitResult {
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, line: LineRefMut<'_>) {
match self {
SampleMaps::Deep(ref levels) => levels.extract_line(line),
SampleMaps::Flat(ref levels) => levels.extract_line(line),
}
}
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(),
}
}
pub fn contains_nan_pixels(&self) -> bool {
match self {
SampleMaps::Flat(levels) => levels.contains_nan_pixels(),
SampleMaps::Deep(levels) => levels.contains_nan_pixels(),
}
}
}
impl<S: Samples> Levels<S> {
pub fn allocate(header: &Header, channel: &attribute::ChannelInfo) -> Self {
let data_size = header.layer_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.width());
let level_count_y = compute_level_count(round, data_size.height());
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: LineRef<'_>) -> UnitResult {
self.get_level_mut(line.location.level)?.insert_line(line)
}
pub fn extract_line(&self, line: LineRefMut<'_>) {
self.get_level(line.location.level)
.expect("invalid level index")
.extract_line(line)
}
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.x(), level.y(), "mip map levels must be equal on x and y bug");
block.get(level.x()).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.x(), level.y(), "mip map levels must be equal on x and y bug");
block.get_mut(level.x()).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,
}
}
pub fn contains_nan_pixels(&self) -> bool {
self.as_slice().iter().any(|level| level.samples.contains_nan())
}
}
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: LineRef<'_>) -> UnitResult {
debug_assert_ne!(line.location.sample_count, 0, "line index bug");
if line.location.position.x() + line.location.sample_count > self.resolution.width() {
return Err(Error::invalid("data block x coordinate"))
}
if line.location.position.y() > self.resolution.height() {
return Err(Error::invalid("data block y coordinate"))
}
self.samples.insert_line(line, self.resolution.width())
}
pub fn extract_line(&self, line: LineRefMut<'_>) {
debug_assert!(line.location.position.x() + line.location.sample_count <= self.resolution.width(), "line index bug");
debug_assert!(line.location.position.y() < self.resolution.height(), "line index bug");
debug_assert_ne!(line.location.sample_count, 0, "line index bug");
self.samples.extract_line(line, self.resolution.width())
}
}
pub trait Samples {
fn allocate(resolution: Vec2<usize>) -> Self;
fn insert_line(&mut self, line: LineRef<'_>, image_width: usize) -> UnitResult;
fn extract_line(&self, line: LineRefMut<'_>, image_width: usize);
fn contains_nan(&self) -> bool;
}
impl<Sample: crate::io::Data + IsNan> Samples for DeepSamples<Sample> {
fn allocate(resolution: Vec2<usize>) -> Self {
debug_assert!(resolution.area() < 1920*10 * 1920*10, "suspiciously large image");
vec![
DeepLine { samples: Vec::new(), index_table: vec![0; resolution.width()] };
resolution.height()
]
}
fn insert_line(&mut self, _line: LineRef<'_>, _image_width: usize) -> UnitResult {
unimplemented!("deep data not supported yet");
}
fn extract_line(&self, _line: LineRefMut<'_>, _image_width: usize) {
debug_assert_ne!(_image_width, 0, "deep image width bug");
unimplemented!("deep data not supported yet");
}
fn contains_nan(&self) -> bool {
unimplemented!()
}
}
impl<Sample: crate::io::Data + IsNan + Default + Clone + std::fmt::Debug> Samples for FlatSamples<Sample> {
fn allocate(resolution: Vec2<usize>) -> Self {
let count = resolution.area();
debug_assert!(count < 1920*20 * 1920*20, "suspiciously large image: {} mega pixels", count / 1_000_000);
vec![Sample::default(); count]
}
fn insert_line(&mut self, line: LineRef<'_>, image_width: usize) -> UnitResult {
debug_assert_ne!(image_width, 0, "image width calculation bug");
debug_assert_ne!(line.location.sample_count, 0, "line width calculation bug");
let start_index = line.location.position.y() * image_width + line.location.position.x();
let end_index = start_index + line.location.sample_count;
line.read_samples_into_slice(&mut self[start_index .. end_index])
}
fn extract_line(&self, line: LineRefMut<'_>, image_width: usize) {
debug_assert_ne!(image_width, 0, "image width calculation bug");
let start_index = line.location.position.y() * image_width + line.location.position.x();
let end_index = start_index + line.location.sample_count;
line.write_samples_from_slice(&self[start_index .. end_index])
.expect("writing line bytes failed");
}
fn contains_nan(&self) -> bool {
self.iter().any(|value| value.is_nan())
}
}
pub trait IsNan { fn is_nan(&self) -> bool; }
impl IsNan for f16 { fn is_nan(&self) -> bool { (*self).is_nan() } }
impl IsNan for f32 { fn is_nan(&self) -> bool { (*self).is_nan() } }
impl IsNan for u32 { fn is_nan(&self) -> bool { false } }
impl<Samples> RipMaps<Samples> {
pub fn get_level_index(&self, level: Vec2<usize>) -> usize {
self.level_count.0 * level.y() + level.x()
}
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)
}
}