pub mod channels;
pub mod layers;
pub mod samples;
use std::io::{BufWriter, Seek};
use crate::{
block::writer::ChunksWriter,
error::UnitResult,
image::{
ignore_progress,
write::layers::{LayersWriter, WritableLayers},
Image, IntoSample, SpecificChannels,
},
io::Write,
math::Vec2,
meta::Headers,
};
pub fn write_rgba_file<R, G, B, A>(
path: impl AsRef<std::path::Path>,
width: usize,
height: usize,
colors: impl Sync + Fn(usize, usize) -> (R, G, B, A),
) -> UnitResult
where
R: IntoSample,
G: IntoSample,
B: IntoSample,
A: IntoSample,
{
let channels = SpecificChannels::rgba(|Vec2(x, y)| colors(x, y));
Image::from_channels((width, height), channels).write().to_file(path)
}
pub fn write_rgb_file<R, G, B>(
path: impl AsRef<std::path::Path>,
width: usize,
height: usize,
colors: impl Sync + Fn(usize, usize) -> (R, G, B),
) -> UnitResult
where
R: IntoSample,
G: IntoSample,
B: IntoSample,
{
let channels = SpecificChannels::rgb(|Vec2(x, y)| colors(x, y));
Image::from_channels((width, height), channels).write().to_file(path)
}
pub trait WritableImage<'img, WritableLayers>: Sized {
fn write(self) -> WriteImageWithOptions<'img, WritableLayers, fn(f64)>;
}
impl<'img, WritableLayers> WritableImage<'img, WritableLayers> for &'img Image<WritableLayers> {
fn write(self) -> WriteImageWithOptions<'img, WritableLayers, fn(f64)> {
WriteImageWithOptions {
image: self,
check_compatibility: true,
#[cfg(not(feature = "rayon"))]
parallel: false,
#[cfg(feature = "rayon")]
parallel: true,
on_progress: ignore_progress,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WriteImageWithOptions<'img, Layers, OnProgress> {
image: &'img Image<Layers>,
on_progress: OnProgress,
check_compatibility: bool,
parallel: bool,
}
impl<'img, L, F> WriteImageWithOptions<'img, L, F>
where
L: WritableLayers<'img>,
F: FnMut(f64),
{
pub fn infer_meta_data(&self) -> Headers {
self.image.layer_data.infer_headers(&self.image.attributes)
}
pub fn non_parallel(self) -> Self {
Self {
parallel: false,
..self
}
}
pub fn skip_compatibility_checks(self) -> Self {
Self {
check_compatibility: false,
..self
}
}
pub fn on_progress<OnProgress>(
self,
on_progress: OnProgress,
) -> WriteImageWithOptions<'img, L, OnProgress>
where
OnProgress: FnMut(f64),
{
WriteImageWithOptions {
on_progress,
image: self.image,
check_compatibility: self.check_compatibility,
parallel: self.parallel,
}
}
#[inline]
#[must_use]
pub fn to_file(self, path: impl AsRef<std::path::Path>) -> UnitResult {
crate::io::attempt_delete_file_on_write_error(path.as_ref(), move |write| {
self.to_unbuffered(write)
})
}
#[inline]
#[must_use]
pub fn to_unbuffered(self, unbuffered: impl Write + Seek) -> UnitResult {
self.to_buffered(BufWriter::new(unbuffered))
}
#[must_use]
pub fn to_buffered(self, write: impl Write + Seek) -> UnitResult {
let headers = self.infer_meta_data();
let layers = self.image.layer_data.create_writer(&headers);
crate::block::write(write, headers, self.check_compatibility, move |meta, chunk_writer| {
let blocks = meta.collect_ordered_block_data(|block_index| {
layers.extract_uncompressed_block(&meta.headers, block_index)
});
let chunk_writer = chunk_writer.on_progress(self.on_progress);
if self.parallel {
#[cfg(not(feature = "rayon"))]
return Err(crate::error::Error::unsupported(
"parallel compression requires the rayon feature",
));
#[cfg(feature = "rayon")]
chunk_writer.compress_all_blocks_parallel(&meta, blocks)?;
} else {
chunk_writer.compress_all_blocks_sequential(&meta, blocks)?;
}
Ok(())
})
}
}