diff options
Diffstat (limited to 'vendor/exr/src/image/write/mod.rs')
-rw-r--r-- | vendor/exr/src/image/write/mod.rs | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/vendor/exr/src/image/write/mod.rs b/vendor/exr/src/image/write/mod.rs new file mode 100644 index 0000000..3c20060 --- /dev/null +++ b/vendor/exr/src/image/write/mod.rs @@ -0,0 +1,184 @@ + +//! Write an exr image to a file. +//! +//! First, call `my_image.write()`. The resulting value can be customized, like this: +//! ```no_run +//! use exr::prelude::*; +//! # let my_image: FlatImage = unimplemented!(); +//! +//! my_image.write() +//! .on_progress(|progress| println!("progress: {:.1}", progress*100.0)) +//! .to_file("image.exr").unwrap(); +//! ``` +//! + +pub mod layers; +pub mod samples; +pub mod channels; + + + +use crate::meta::Headers; +use crate::error::UnitResult; +use std::io::{Seek, BufWriter}; +use crate::io::Write; +use crate::image::{Image, ignore_progress, SpecificChannels, IntoSample}; +use crate::image::write::layers::{WritableLayers, LayersWriter}; +use crate::math::Vec2; +use crate::block::writer::ChunksWriter; + +/// An oversimplified function for "just write the damn file already" use cases. +/// Have a look at the examples to see how you can write an image with more flexibility (it's not that hard). +/// Use `write_rgb_file` if you do not need an alpha channel. +/// +/// Each of `R`, `G`, `B` and `A` can be either `f16`, `f32`, `u32`, or `Sample`. +// TODO explain pixel tuple f32,f16,u32 +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) +} + +/// An oversimplified function for "just write the damn file already" use cases. +/// Have a look at the examples to see how you can write an image with more flexibility (it's not that hard). +/// Use `write_rgb_file` if you do not need an alpha channel. +/// +/// Each of `R`, `G`, and `B` can be either `f16`, `f32`, `u32`, or `Sample`. +// TODO explain pixel tuple f32,f16,u32 +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) +} + + + +/// Enables an image to be written to a file. Call `image.write()` where this trait is implemented. +pub trait WritableImage<'img, WritableLayers>: Sized { + + /// Create a temporary writer which can be configured and used to write the image to a file. + 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, + parallel: true, + on_progress: ignore_progress + } + } +} + +/// A temporary writer which can be configured and used to write an image to a file. +// temporary writer with options +#[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) +{ + /// Generate file meta data for this image. The meta data structure is close to the data in the file. + pub fn infer_meta_data(&self) -> Headers { // TODO this should perform all validity checks? and none after that? + self.image.layer_data.infer_headers(&self.image.attributes) + } + + /// Do not compress multiple pixel blocks on multiple threads at once. + /// Might use less memory and synchronization, but will be slower in most situations. + pub fn non_parallel(self) -> Self { Self { parallel: false, ..self } } + + /// Skip some checks that ensure a file can be opened by other exr software. + /// For example, it is no longer checked that no two headers or two attributes have the same name, + /// which might be an expensive check for images with an exorbitant number of headers. + /// + /// If you write an uncompressed file and need maximum speed, it might save a millisecond to disable the checks, + /// if you know that your file is not invalid any ways. I do not recommend this though, + /// as the file might not be readably by any other exr library after that. + /// __You must care for not producing an invalid file yourself.__ + pub fn skip_compatibility_checks(self) -> Self { Self { check_compatibility: false, ..self } } + + /// Specify a function to be called regularly throughout the writing process. + /// Replaces all previously specified progress functions in this reader. + 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 + } + } + + /// Write the exr image to a file. + /// Use `to_unbuffered` instead, if you do not have a file. + /// If an error occurs, attempts to delete the partially written file. + #[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) + ) + } + + /// Buffer the writer and then write the exr image to it. + /// Use `to_buffered` instead, if your writer is an in-memory buffer. + /// Use `to_file` instead, if you have a file path. + /// If your writer cannot seek, you can write to an in-memory vector of bytes first, using `to_buffered`. + #[inline] + #[must_use] + pub fn to_unbuffered(self, unbuffered: impl Write + Seek) -> UnitResult { + self.to_buffered(BufWriter::new(unbuffered)) + } + + /// Write the exr image to a writer. + /// Use `to_file` instead, if you have a file path. + /// Use `to_unbuffered` instead, if this is not an in-memory writer. + /// If your writer cannot seek, you can write to an in-memory vector of bytes first. + #[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 { chunk_writer.compress_all_blocks_parallel(&meta, blocks)?; } + else { chunk_writer.compress_all_blocks_sequential(&meta, blocks)?; } + /*let blocks_writer = chunk_writer.as_blocks_writer(&meta); + + // TODO propagate send requirement further upwards + if self.parallel { + blocks_writer.compress_all_blocks_parallel(blocks)?; + } + else { + blocks_writer.compress_all_blocks_sequential(blocks)?; + }*/ + + Ok(()) + } + ) + } +} + |