-
Notifications
You must be signed in to change notification settings - Fork 709
OpenEXR basic support #1475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
197g
merged 47 commits into
image-rs:master
from
johannesvollmer:openexr-initial-support
Jul 14, 2021
Merged
OpenEXR basic support #1475
Changes from 2 commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
0ad1732
first ever exr prototype (not even compiled once)
johannesvollmer 2e4e73a
use u16 for now (can easily be changed back to f32 again)
johannesvollmer 11a8c2a
the minorest improvements (minor cleanup)
johannesvollmer 958a4ac
Merge branch 'master' of https://github.com/image-rs/image into opene…
johannesvollmer 2d6cf83
use newer exr library version with relaxed reader and writer bounds
johannesvollmer f0cccc0
update supported formats documentation (add exr, use camel case)
johannesvollmer 307f7be
use f32 in writer and invert y
johannesvollmer 5e90731
make it compile, with new exr version. also do not require seekable w…
johannesvollmer c7a0ef7
fix panic triggering for unsupported color types
johannesvollmer 4367914
add tests to openexr module, load alpha only if present in file or re…
johannesvollmer 57e6d8e
improve documentation: mention that writer argument should be buffered
johannesvollmer 5487eb1
cosmetic improvements
johannesvollmer 7ea5607
try adding an exr fuzzing script, rename exr test functions
johannesvollmer 9b3f3b7
add test images (lol forgot that)
johannesvollmer a9073d1
add openexr feature flag to rust ci workflow
johannesvollmer dc534ac
try fix fuzzer, update readme format table from lib.rs,
johannesvollmer dccc0fe
add exr to other existing tests
johannesvollmer ad151b4
add exr to even more existing tests and documentation
johannesvollmer 6d43853
remove exr from protected workflows file
johannesvollmer bf08b3b
remove exr error from public api
johannesvollmer 6712173
implement original_color_type, add encoder trait implementation, remo…
johannesvollmer 4df8dc2
use alpha flag in constructor instead of mutating the flag (avoiding …
johannesvollmer 9ee062b
add docs to encoder wrapper
johannesvollmer 724cc0c
remove commented-out functions
johannesvollmer fa1bf36
documentation improvements
johannesvollmer 072fcfd
simplify imports, clarify reader buffering in documentation and remov…
johannesvollmer a3b2bda
rename exr to openexr, move ImageBuffer type alias definitions to the…
johannesvollmer 465f927
remove outdated todo comment
johannesvollmer 7198c5a
use display window instead of loading only the layer data
johannesvollmer 6f3ea90
allow aligned buffers, make progress pub(crate), make image type alia…
johannesvollmer 4276b44
implement trivial review suggestions
johannesvollmer 61784de
Merge branch 'master' of https://github.com/image-rs/image into opene…
johannesvollmer 6d7c802
bridge disconnected docs sections
johannesvollmer b18339d
make helper functions private (plus re-implement the helper methods c…
johannesvollmer 495e990
re-implement more helper methods copy&paste for external fuzzer test …
johannesvollmer b3b349e
re-implement even more helper methods copy&paste for external fuzzer …
johannesvollmer 9838118
simplify helper methodsfor external fuzzer test script
johannesvollmer 5126f1b
fix unused crate import error
johannesvollmer d0c6872
fix more compiler errrors
johannesvollmer 83148a3
fix fuzzer script compile errors
johannesvollmer d67a08b
fix fuzzer script compile errors
johannesvollmer 6d729c9
unfix removing "unused imports"
johannesvollmer 57a83b5
use overflow-safe size checks
johannesvollmer ab811e5
also check buffer dimensions in decoder, not only encoder
johannesvollmer 9c6d68f
panic in decoder instead of returning an error
johannesvollmer 4710fd7
when checking decoder buffer size, use strict equality, as declared i…
johannesvollmer 899431f
re-trigger CI
johannesvollmer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| //! Decoding of OpenEXR (.exr) Images | ||
| //! | ||
| //! OpenEXR is an image format that is widely used, especially in VFX, | ||
| //! because it supports lossless and lossy compression for float data. | ||
| //! | ||
| //! # Related Links | ||
| //! * <https://www.openexr.com/documentation.html> - The OpenEXR reference. | ||
|
|
||
| // # ROADMAP | ||
| // - [] ImageDecoder | ||
| // - [] ImageEncoder | ||
| // - [] GenericImageView? | ||
| // - [] GenericImage? | ||
| // - [] Progress | ||
|
|
||
| // # MAYBE SOON | ||
| // - [] ImageDecoderExt::read_rect_with_progress | ||
| // - [] Layers -> Animation? | ||
|
|
||
|
|
||
| extern crate exr; | ||
| use exr::prelude::*; | ||
|
|
||
| use crate::{ImageDecoder, ImageResult, ColorType, Progress, image, ImageError, ImageFormat}; | ||
| use std::io::{Write, Seek, BufRead, SeekFrom, Cursor}; | ||
| use crate::error::{DecodingError, ImageFormatHint}; | ||
| use self::exr::meta::header::Header; | ||
|
|
||
| /// An OpenEXR decoder | ||
| #[derive(Debug)] | ||
| pub struct ExrDecoder<R> { | ||
| source: R, | ||
| header: exr::meta::header::Header, | ||
| } | ||
|
|
||
|
|
||
|
|
||
| impl<R: BufRead + Seek + Send> ExrDecoder<R> { | ||
|
|
||
| /// Create a decoder. Consumes the first few bytes of the source to extract image dimensions. | ||
| pub fn read(mut source: R) -> ImageResult<Self> { | ||
|
|
||
| // read meta data, then go back to the start of the file | ||
| let mut meta_data = { | ||
| let start_position = source.stream_position()?; | ||
| let meta: MetaData = exr::meta::MetaData::read_from_buffered(&mut source, true)?; | ||
| source.seek(SeekFrom::Start(start_position))?; | ||
| meta | ||
| }; | ||
|
|
||
| let header: Header = meta_data.headers.into_iter() | ||
| .filter(|header|{ | ||
| let has_rgb = ["R","G","B"].iter().all( | ||
| |required| header.channels.list.iter().find(|chan| chan.name.eq(required)).is_some() // TODO eq_lowercase only if exrs supports it | ||
| ); | ||
|
|
||
| !header.deep && has_rgb | ||
| }) | ||
| .next() | ||
| .ok_or_else(|| ImageError::Decoding(DecodingError::new( | ||
| ImageFormatHint::Exact(ImageFormat::Exr), | ||
| "image does not contain non-deep rgb channels" | ||
| )))?; | ||
|
|
||
| Ok(Self { source, header }) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| impl<'a, R: 'a + BufRead + Seek + Send> ImageDecoder<'a> for ExrDecoder<R> { | ||
| type Reader = Cursor<Vec<u8>>; | ||
|
|
||
| fn dimensions(&self) -> (u32, u32) { | ||
| let s = self.header.layer_size; | ||
| (s.width() as u32, s.height() as u32) | ||
| } | ||
|
|
||
| // TODO fn original_color_type(&self) -> ExtendedColorType {} | ||
|
|
||
| fn color_type(&self) -> ColorType { | ||
| // TODO adapt to actual exr color type | ||
| // TODO f32 ColorType::Rgba32 | ||
| ColorType::Rgba16 | ||
| } | ||
|
|
||
| /// Use `read_image` if possible, | ||
| /// as this method creates a whole new buffer to contain the entire image. | ||
| fn into_reader(self) -> ImageResult<Self::Reader> { | ||
| Ok(Cursor::new(image::decoder_to_vec(self)?)) // TODO no vec? | ||
| } | ||
|
|
||
| fn scanline_bytes(&self) -> u64 { | ||
| // we cannot always read individual scan lines, | ||
| // as the tiles or lines in the file could be in random order. | ||
| // therefore we currently read all lines at once | ||
| // Todo: respect exr.line_order | ||
| self.total_bytes() | ||
| } | ||
|
|
||
| fn read_image(mut self, target: &mut [u8]) -> ImageResult<()> { | ||
| self.read_image_with_progress(target, |_|{}) | ||
| } | ||
|
|
||
| fn read_image_with_progress<F: Fn(Progress)>(self, target: &mut [u8], progress_callback: F) -> ImageResult<()> { | ||
| // TODO reuse meta-data from earlier | ||
|
|
||
| let result = exr::prelude::read() | ||
| .no_deep_data().largest_resolution_level() | ||
| .rgba_channels( | ||
| // FIXME no alloc+copy, write into target slice directly! | ||
| |size, _channels| ( | ||
| size, vec![0_u16; size.area()*4] | ||
| ), | ||
|
|
||
| |(size, buffer), position, (r,g,b,a_or_1): (f32,f32,f32,f32)| { | ||
| // TODO white point chromaticities + srgb/linear conversion? | ||
| let first_f32_index = position.flat_index_for_size(*size); | ||
| let to_u16 = |v:f32| (v.clamp(0.0, 1.0) * u16::MAX as f32) as u16; // TODO remove, always use 32 | ||
|
|
||
| buffer[first_f32_index*4 .. (first_f32_index + 1)*4] | ||
| .copy_from_slice(&[to_u16(r), to_u16(g), to_u16(b), to_u16(a_or_1)]); | ||
| } | ||
| ) | ||
| .first_valid_layer().all_attributes() | ||
| .on_progress(move |progress| { | ||
| let mut prog = progress_callback; | ||
| prog(Progress::new((progress*100.0) as u64, 100_u64)); | ||
| }) | ||
| .from_buffered(self.source)?; | ||
|
|
||
| target.copy_from_slice(bytemuck::cast_slice(result.layer_data.channel_data.pixels.1.as_slice())); | ||
| // TODO keep meta data? | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
|
||
| pub fn write_image(mut out: impl Write, bytes: &[u8], width: u32, height: u32, color_type: ColorType) -> ImageResult<()> { | ||
| let pixels: &[u16] = bytemuck::try_cast_slice(bytes).expect("byte buffer must be aligned to u16"); | ||
|
|
||
| let mut seekable_write = Cursor::new(Vec::<u8>::with_capacity(bytes.len())); // TODO make parameter +seek | ||
| let as_f32 = |v:u16| (v as f32) / u16::MAX as f32; // TODO remove, always use f32 | ||
|
|
||
| match color_type { | ||
| ColorType::Rgb16 => { | ||
| exr::prelude::Image::from_channels( | ||
| (width as usize, height as usize), | ||
| SpecificChannels::rgb(|pixel: Vec2<usize>| { | ||
| let pixel_index = 3 * pixel.flat_index_for_size(Vec2(width as usize, height as usize)); | ||
| (as_f32(pixels[pixel_index]), as_f32(pixels[pixel_index+1]), as_f32(pixels[pixel_index+2])) | ||
| }) | ||
| ).write().to_buffered(&mut seekable_write)?; | ||
| } | ||
|
|
||
| ColorType::Rgba16 => { | ||
| exr::prelude::Image::from_channels( | ||
| (width as usize, height as usize), | ||
| SpecificChannels::rgba(|pixel: Vec2<usize>| { | ||
| let pixel_index = 4 * pixel.flat_index_for_size(Vec2(width as usize, height as usize)); | ||
| (as_f32(pixels[pixel_index]), as_f32(pixels[pixel_index+1]), as_f32(pixels[pixel_index+2]), as_f32(pixels[pixel_index+3])) | ||
| }) | ||
| ).write().to_buffered(&mut seekable_write)?; | ||
| } | ||
|
|
||
| _ => todo!() | ||
| } | ||
|
|
||
| out.write_all(seekable_write.into_inner().as_slice())?; | ||
| Ok(()) | ||
| } | ||
|
|
||
|
|
||
| impl From<exr::error::Error> for ImageError { | ||
| fn from(exr_error: Error) -> Self { | ||
| ImageError::Decoding(DecodingError::new( | ||
| ImageFormatHint::Exact(ImageFormat::Exr), | ||
| exr_error | ||
| )) | ||
| } | ||
| } | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.