/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/block/mod.rs
Line | Count | Source |
1 | | //! This is the low-level interface for the raw blocks of an image. |
2 | | //! See `exr::image` module for a high-level interface. |
3 | | //! |
4 | | //! Handle compressed and uncompressed pixel byte blocks. Includes compression |
5 | | //! and decompression, and reading a complete image into blocks. |
6 | | //! |
7 | | //! Start with the `block::read(...)` |
8 | | //! and `block::write(...)` functions. |
9 | | |
10 | | pub mod reader; |
11 | | pub mod writer; |
12 | | |
13 | | pub mod chunk; |
14 | | pub mod lines; |
15 | | pub mod samples; |
16 | | |
17 | | use std::io::{Read, Seek, Write}; |
18 | | |
19 | | use crate::{ |
20 | | block::{ |
21 | | chunk::{ |
22 | | Chunk, CompressedBlock, CompressedScanLineBlock, CompressedTileBlock, TileCoordinates, |
23 | | }, |
24 | | lines::{LineIndex, LineRef, LineRefMut, LineSlice}, |
25 | | }, |
26 | | compression::ByteVec, |
27 | | error::{usize_to_i32, Error, Result, UnitResult}, |
28 | | math::Vec2, |
29 | | meta::{attribute::ChannelList, header::Header, BlockDescription, Headers, MetaData}, |
30 | | }; |
31 | | |
32 | | /// Specifies where a block of pixel data should be placed in the actual image. |
33 | | /// This is a globally unique identifier which |
34 | | /// includes the layer, level index, and pixel location. |
35 | | #[derive(Clone, Copy, Eq, Hash, PartialEq, Debug)] |
36 | | pub struct BlockIndex { |
37 | | /// Index of the layer. |
38 | | pub layer: usize, |
39 | | |
40 | | /// Index of the top left pixel from the block within the data window. |
41 | | pub pixel_position: Vec2<usize>, |
42 | | |
43 | | /// Number of pixels in this block, extending to the right and downwards. |
44 | | /// Stays the same across all resolution levels. |
45 | | pub pixel_size: Vec2<usize>, |
46 | | |
47 | | /// Index of the mip or rip level in the image. |
48 | | pub level: Vec2<usize>, |
49 | | } |
50 | | |
51 | | /// Contains a block of pixel data and where that data should be placed in the |
52 | | /// actual image. |
53 | | /// |
54 | | /// The bytes must be encoded in native-endian format. |
55 | | /// The conversion to little-endian format happens when converting to chunks |
56 | | /// (potentially in parallel). |
57 | | #[derive(Clone, Eq, PartialEq, Debug)] |
58 | | pub struct UncompressedBlock { |
59 | | /// Location of the data inside the image. |
60 | | pub index: BlockIndex, |
61 | | |
62 | | /// Uncompressed pixel values of the whole block. |
63 | | /// One or more scan lines may be stored together as a scan line block. |
64 | | /// This byte vector contains all pixel rows, one after another. |
65 | | /// For each line in the tile, for each channel, the row values are |
66 | | /// contiguous. Stores all samples of the first channel, then all |
67 | | /// samples of the second channel, and so on. This data is in |
68 | | /// native-endian format. |
69 | | pub data: ByteVec, |
70 | | } |
71 | | |
72 | | /// Immediately reads the meta data from the file. |
73 | | /// |
74 | | /// Then, returns a reader that can be used to read all pixel blocks. |
75 | | /// From the reader, you can pull each compressed chunk from the file. |
76 | | /// Alternatively, you can create a decompressor, and pull the uncompressed data |
77 | | /// from it. The reader is assumed to be buffered. |
78 | 7.08k | pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> { |
79 | 7.08k | self::reader::Reader::read_from_buffered(buffered_read, pedantic) |
80 | 7.08k | } exr::block::read::<std::io::cursor::Cursor<&[u8]>> Line | Count | Source | 78 | 7.00k | pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> { | 79 | 7.00k | self::reader::Reader::read_from_buffered(buffered_read, pedantic) | 80 | 7.00k | } |
Unexecuted instantiation: exr::block::read::<_> exr::block::read::<std::io::cursor::Cursor<alloc::vec::Vec<u8>>> Line | Count | Source | 78 | 83 | pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> { | 79 | 83 | self::reader::Reader::read_from_buffered(buffered_read, pedantic) | 80 | 83 | } |
|
81 | | |
82 | | /// Immediately writes the meta data to the file. |
83 | | /// |
84 | | /// Then, calls a closure with a writer that can be used to write all pixel |
85 | | /// blocks. In the closure, you can push compressed chunks directly into the |
86 | | /// writer. Alternatively, you can create a compressor, wrapping the writer, and |
87 | | /// push the uncompressed data to it. The writer is assumed to be buffered. |
88 | 83 | pub fn write<W: Write + Seek>( |
89 | 83 | buffered_write: W, |
90 | 83 | headers: Headers, |
91 | 83 | compatibility_checks: bool, |
92 | 83 | write_chunks: impl FnOnce(MetaData, &mut self::writer::ChunkWriter<W>) -> UnitResult, |
93 | 83 | ) -> UnitResult { |
94 | 83 | self::writer::write_chunks_with(buffered_write, headers, compatibility_checks, write_chunks) |
95 | 83 | } Unexecuted instantiation: exr::block::write::<_, _> Unexecuted instantiation: exr::block::write::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}>Unexecuted instantiation: exr::block::write::<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut &mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>::{closure#0}>Unexecuted instantiation: exr::block::write::<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}>exr::block::write::<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>, <exr::image::write::WriteImageWithOptions<exr::image::Layer<exr::image::SpecificChannels<image::codecs::openexr::write_buffer<std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#1}, (exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription, exr::meta::attribute::ChannelDescription)>>, fn(f64)>>::to_buffered<&mut std::io::cursor::Cursor<&mut alloc::vec::Vec<u8>>>::{closure#0}>Line | Count | Source | 88 | 83 | pub fn write<W: Write + Seek>( | 89 | 83 | buffered_write: W, | 90 | 83 | headers: Headers, | 91 | 83 | compatibility_checks: bool, | 92 | 83 | write_chunks: impl FnOnce(MetaData, &mut self::writer::ChunkWriter<W>) -> UnitResult, | 93 | 83 | ) -> UnitResult { | 94 | 83 | self::writer::write_chunks_with(buffered_write, headers, compatibility_checks, write_chunks) | 95 | 83 | } |
|
96 | | |
97 | | /// This iterator tells you the block indices of all blocks that must be in the |
98 | | /// image. |
99 | | /// |
100 | | /// The order of the blocks depends on the `LineOrder` attribute |
101 | | /// (unspecified line order is treated the same as increasing line order). |
102 | | /// The blocks written to the file must be exactly in this order, |
103 | | /// except for when the `LineOrder` is unspecified. |
104 | | /// The index represents the block index, in increasing line order, within the |
105 | | /// header. |
106 | 83 | pub fn enumerate_ordered_header_block_indices( |
107 | 83 | headers: &[Header], |
108 | 83 | ) -> impl '_ + Iterator<Item = (usize, BlockIndex)> { |
109 | 83 | headers.iter().enumerate().flat_map(|(layer_index, header)| { |
110 | 757k | header.enumerate_ordered_blocks().map(move |(index_in_header, tile)| { |
111 | 757k | let data_indices = header |
112 | 757k | .get_absolute_block_pixel_coordinates(tile.location) |
113 | 757k | .expect("tile coordinate bug"); |
114 | | |
115 | 757k | let block = BlockIndex { |
116 | 757k | layer: layer_index, |
117 | 757k | level: tile.location.level_index, |
118 | 757k | pixel_position: data_indices |
119 | 757k | .position |
120 | 757k | .to_usize("data indices start") |
121 | 757k | .expect("data index bug"), |
122 | 757k | pixel_size: data_indices.size, |
123 | 757k | }; |
124 | | |
125 | 757k | (index_in_header, block) |
126 | 757k | }) Unexecuted instantiation: exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}Unexecuted instantiation: exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}exr::block::enumerate_ordered_header_block_indices::{closure#0}::{closure#0}Line | Count | Source | 110 | 757k | header.enumerate_ordered_blocks().map(move |(index_in_header, tile)| { | 111 | 757k | let data_indices = header | 112 | 757k | .get_absolute_block_pixel_coordinates(tile.location) | 113 | 757k | .expect("tile coordinate bug"); | 114 | | | 115 | 757k | let block = BlockIndex { | 116 | 757k | layer: layer_index, | 117 | 757k | level: tile.location.level_index, | 118 | 757k | pixel_position: data_indices | 119 | 757k | .position | 120 | 757k | .to_usize("data indices start") | 121 | 757k | .expect("data index bug"), | 122 | 757k | pixel_size: data_indices.size, | 123 | 757k | }; | 124 | | | 125 | 757k | (index_in_header, block) | 126 | 757k | }) |
|
127 | 83 | }) Unexecuted instantiation: exr::block::enumerate_ordered_header_block_indices::{closure#0}Unexecuted instantiation: exr::block::enumerate_ordered_header_block_indices::{closure#0}exr::block::enumerate_ordered_header_block_indices::{closure#0}Line | Count | Source | 109 | 83 | headers.iter().enumerate().flat_map(|(layer_index, header)| { | 110 | 83 | header.enumerate_ordered_blocks().map(move |(index_in_header, tile)| { | 111 | | let data_indices = header | 112 | | .get_absolute_block_pixel_coordinates(tile.location) | 113 | | .expect("tile coordinate bug"); | 114 | | | 115 | | let block = BlockIndex { | 116 | | layer: layer_index, | 117 | | level: tile.location.level_index, | 118 | | pixel_position: data_indices | 119 | | .position | 120 | | .to_usize("data indices start") | 121 | | .expect("data index bug"), | 122 | | pixel_size: data_indices.size, | 123 | | }; | 124 | | | 125 | | (index_in_header, block) | 126 | | }) | 127 | 83 | }) |
|
128 | 83 | } |
129 | | |
130 | | impl UncompressedBlock { |
131 | | /// Decompress the possibly compressed chunk and returns an |
132 | | /// `UncompressedBlock`. |
133 | | // for uncompressed data, the ByteVec in the chunk is moved all the way |
134 | | #[inline] |
135 | | #[must_use] |
136 | 764k | pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> { |
137 | 764k | let header: &Header = meta_data |
138 | 764k | .headers |
139 | 764k | .get(chunk.layer_index) |
140 | 764k | .ok_or_else(|| Error::invalid("chunk layer index"))?;Unexecuted instantiation: <exr::block::UncompressedBlock>::decompress_chunk::{closure#0}Unexecuted instantiation: <exr::block::UncompressedBlock>::decompress_chunk::{closure#0} |
141 | | |
142 | 764k | let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?; |
143 | 763k | let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?; |
144 | | |
145 | 761k | absolute_indices.validate(Some(header.layer_size))?; |
146 | | |
147 | 761k | match chunk.compressed_block { |
148 | | CompressedBlock::Tile(CompressedTileBlock { |
149 | 757k | compressed_pixels_le, |
150 | | .. |
151 | | }) |
152 | | | CompressedBlock::ScanLine(CompressedScanLineBlock { |
153 | 3.71k | compressed_pixels_le, |
154 | | .. |
155 | | }) => Ok(Self { |
156 | 761k | data: header.compression.decompress_image_section_from_le( |
157 | 761k | header, |
158 | 761k | compressed_pixels_le, |
159 | 761k | absolute_indices, |
160 | 761k | pedantic, |
161 | 529 | )?, |
162 | | index: BlockIndex { |
163 | 761k | layer: chunk.layer_index, |
164 | 761k | pixel_position: absolute_indices.position.to_usize("data indices start")?, |
165 | 761k | level: tile_data_indices.level_index, |
166 | 761k | pixel_size: absolute_indices.size, |
167 | | }, |
168 | | }), |
169 | | |
170 | 0 | _ => Err(Error::unsupported("deep data not supported yet")), |
171 | | } |
172 | 764k | } <exr::block::UncompressedBlock>::decompress_chunk Line | Count | Source | 136 | 6.43k | pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> { | 137 | 6.43k | let header: &Header = meta_data | 138 | 6.43k | .headers | 139 | 6.43k | .get(chunk.layer_index) | 140 | 6.43k | .ok_or_else(|| Error::invalid("chunk layer index"))?; | 141 | | | 142 | 6.43k | let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?; | 143 | 6.34k | let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?; | 144 | | | 145 | 3.92k | absolute_indices.validate(Some(header.layer_size))?; | 146 | | | 147 | 3.92k | match chunk.compressed_block { | 148 | | CompressedBlock::Tile(CompressedTileBlock { | 149 | 212 | compressed_pixels_le, | 150 | | .. | 151 | | }) | 152 | | | CompressedBlock::ScanLine(CompressedScanLineBlock { | 153 | 3.71k | compressed_pixels_le, | 154 | | .. | 155 | | }) => Ok(Self { | 156 | 3.92k | data: header.compression.decompress_image_section_from_le( | 157 | 3.92k | header, | 158 | 3.92k | compressed_pixels_le, | 159 | 3.92k | absolute_indices, | 160 | 3.92k | pedantic, | 161 | 529 | )?, | 162 | | index: BlockIndex { | 163 | 3.39k | layer: chunk.layer_index, | 164 | 3.39k | pixel_position: absolute_indices.position.to_usize("data indices start")?, | 165 | 3.39k | level: tile_data_indices.level_index, | 166 | 3.39k | pixel_size: absolute_indices.size, | 167 | | }, | 168 | | }), | 169 | | | 170 | 0 | _ => Err(Error::unsupported("deep data not supported yet")), | 171 | | } | 172 | 6.43k | } |
Unexecuted instantiation: <exr::block::UncompressedBlock>::decompress_chunk <exr::block::UncompressedBlock>::decompress_chunk Line | Count | Source | 136 | 757k | pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> { | 137 | 757k | let header: &Header = meta_data | 138 | 757k | .headers | 139 | 757k | .get(chunk.layer_index) | 140 | 757k | .ok_or_else(|| Error::invalid("chunk layer index"))?; | 141 | | | 142 | 757k | let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?; | 143 | 757k | let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?; | 144 | | | 145 | 757k | absolute_indices.validate(Some(header.layer_size))?; | 146 | | | 147 | 757k | match chunk.compressed_block { | 148 | | CompressedBlock::Tile(CompressedTileBlock { | 149 | 757k | compressed_pixels_le, | 150 | | .. | 151 | | }) | 152 | | | CompressedBlock::ScanLine(CompressedScanLineBlock { | 153 | 0 | compressed_pixels_le, | 154 | | .. | 155 | | }) => Ok(Self { | 156 | 757k | data: header.compression.decompress_image_section_from_le( | 157 | 757k | header, | 158 | 757k | compressed_pixels_le, | 159 | 757k | absolute_indices, | 160 | 757k | pedantic, | 161 | 0 | )?, | 162 | | index: BlockIndex { | 163 | 757k | layer: chunk.layer_index, | 164 | 757k | pixel_position: absolute_indices.position.to_usize("data indices start")?, | 165 | 757k | level: tile_data_indices.level_index, | 166 | 757k | pixel_size: absolute_indices.size, | 167 | | }, | 168 | | }), | 169 | | | 170 | 0 | _ => Err(Error::unsupported("deep data not supported yet")), | 171 | | } | 172 | 757k | } |
|
173 | | |
174 | | /// Consume this block by compressing it, returning a `Chunk`. |
175 | | // for uncompressed data, the ByteVec in the chunk is moved all the way |
176 | | #[inline] |
177 | | #[must_use] |
178 | 757k | pub fn compress_to_chunk(self, headers: &[Header]) -> Result<Chunk> { |
179 | | let Self { |
180 | 757k | data, |
181 | 757k | index, |
182 | 757k | } = self; |
183 | | |
184 | 757k | let header: &Header = headers.get(index.layer).expect("block layer index bug"); |
185 | | |
186 | 757k | let expected_byte_size = header.channels.bytes_per_pixel * self.index.pixel_size.area(); // TODO sampling?? |
187 | 757k | if expected_byte_size != data.len() { |
188 | 0 | return Err(Error::invalid(format!( |
189 | 0 | "decompressed block byte size mismatch: expected {} bytes but got {} bytes", |
190 | 0 | expected_byte_size, |
191 | 0 | data.len() |
192 | 0 | ))); |
193 | 757k | } |
194 | | |
195 | 757k | let tile_coordinates = TileCoordinates { |
196 | 757k | // FIXME this calculation should not be made here but elsewhere instead (in |
197 | 757k | // meta::header?) |
198 | 757k | tile_index: index.pixel_position / header.max_block_pixel_size(), // TODO sampling?? |
199 | 757k | level_index: index.level, |
200 | 757k | }; |
201 | | |
202 | 757k | let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_coordinates)?; |
203 | 757k | absolute_indices.validate(Some(header.layer_size))?; |
204 | | |
205 | 757k | if !header.compression.may_loose_data() { |
206 | 757k | debug_assert_eq!( |
207 | 0 | &header |
208 | 0 | .compression |
209 | 0 | .decompress_image_section_from_le( |
210 | 0 | header, |
211 | 0 | header.compression.compress_image_section_to_le( |
212 | 0 | header, |
213 | 0 | data.clone(), |
214 | 0 | absolute_indices |
215 | 0 | )?, |
216 | 0 | absolute_indices, |
217 | | true |
218 | | ) |
219 | 0 | .unwrap(), |
220 | 0 | &data, |
221 | 0 | "compression method not round trippin'" |
222 | | ); |
223 | 0 | } |
224 | | |
225 | 757k | let compressed_pixels_le = |
226 | 757k | header.compression.compress_image_section_to_le(header, data, absolute_indices)?; |
227 | | |
228 | | Ok(Chunk { |
229 | 757k | layer_index: index.layer, |
230 | 757k | compressed_block: match header.blocks { |
231 | | BlockDescription::ScanLines => CompressedBlock::ScanLine(CompressedScanLineBlock { |
232 | 0 | compressed_pixels_le, |
233 | | |
234 | | // FIXME this calculation should not be made here but elsewhere instead (in |
235 | | // meta::header?) |
236 | 0 | y_coordinate: usize_to_i32(index.pixel_position.y(), "pixel index")? |
237 | 0 | + header.own_attributes.layer_position.y(), // TODO sampling?? |
238 | | }), |
239 | | |
240 | 757k | BlockDescription::Tiles(_) => CompressedBlock::Tile(CompressedTileBlock { |
241 | 757k | compressed_pixels_le, |
242 | 757k | coordinates: tile_coordinates, |
243 | 757k | }), |
244 | | }, |
245 | | }) |
246 | 757k | } Unexecuted instantiation: <exr::block::UncompressedBlock>::compress_to_chunk Unexecuted instantiation: <exr::block::UncompressedBlock>::compress_to_chunk <exr::block::UncompressedBlock>::compress_to_chunk Line | Count | Source | 178 | 757k | pub fn compress_to_chunk(self, headers: &[Header]) -> Result<Chunk> { | 179 | | let Self { | 180 | 757k | data, | 181 | 757k | index, | 182 | 757k | } = self; | 183 | | | 184 | 757k | let header: &Header = headers.get(index.layer).expect("block layer index bug"); | 185 | | | 186 | 757k | let expected_byte_size = header.channels.bytes_per_pixel * self.index.pixel_size.area(); // TODO sampling?? | 187 | 757k | if expected_byte_size != data.len() { | 188 | 0 | return Err(Error::invalid(format!( | 189 | 0 | "decompressed block byte size mismatch: expected {} bytes but got {} bytes", | 190 | 0 | expected_byte_size, | 191 | 0 | data.len() | 192 | 0 | ))); | 193 | 757k | } | 194 | | | 195 | 757k | let tile_coordinates = TileCoordinates { | 196 | 757k | // FIXME this calculation should not be made here but elsewhere instead (in | 197 | 757k | // meta::header?) | 198 | 757k | tile_index: index.pixel_position / header.max_block_pixel_size(), // TODO sampling?? | 199 | 757k | level_index: index.level, | 200 | 757k | }; | 201 | | | 202 | 757k | let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_coordinates)?; | 203 | 757k | absolute_indices.validate(Some(header.layer_size))?; | 204 | | | 205 | 757k | if !header.compression.may_loose_data() { | 206 | 757k | debug_assert_eq!( | 207 | 0 | &header | 208 | 0 | .compression | 209 | 0 | .decompress_image_section_from_le( | 210 | 0 | header, | 211 | 0 | header.compression.compress_image_section_to_le( | 212 | 0 | header, | 213 | 0 | data.clone(), | 214 | 0 | absolute_indices | 215 | 0 | )?, | 216 | 0 | absolute_indices, | 217 | | true | 218 | | ) | 219 | 0 | .unwrap(), | 220 | 0 | &data, | 221 | 0 | "compression method not round trippin'" | 222 | | ); | 223 | 0 | } | 224 | | | 225 | 757k | let compressed_pixels_le = | 226 | 757k | header.compression.compress_image_section_to_le(header, data, absolute_indices)?; | 227 | | | 228 | | Ok(Chunk { | 229 | 757k | layer_index: index.layer, | 230 | 757k | compressed_block: match header.blocks { | 231 | | BlockDescription::ScanLines => CompressedBlock::ScanLine(CompressedScanLineBlock { | 232 | 0 | compressed_pixels_le, | 233 | | | 234 | | // FIXME this calculation should not be made here but elsewhere instead (in | 235 | | // meta::header?) | 236 | 0 | y_coordinate: usize_to_i32(index.pixel_position.y(), "pixel index")? | 237 | 0 | + header.own_attributes.layer_position.y(), // TODO sampling?? | 238 | | }), | 239 | | | 240 | 757k | BlockDescription::Tiles(_) => CompressedBlock::Tile(CompressedTileBlock { | 241 | 757k | compressed_pixels_le, | 242 | 757k | coordinates: tile_coordinates, | 243 | 757k | }), | 244 | | }, | 245 | | }) | 246 | 757k | } |
|
247 | | |
248 | | /// Iterate all the lines in this block. |
249 | | /// Each line contains the all samples for one of the channels. |
250 | 0 | pub fn lines(&self, channels: &ChannelList) -> impl Iterator<Item = LineRef<'_>> { |
251 | 0 | LineIndex::lines_in_block(self.index, channels).map(move |(bytes, line)| LineSlice { |
252 | 0 | location: line, |
253 | 0 | value: &self.data[bytes], |
254 | 0 | }) |
255 | 0 | } |
256 | | |
257 | | // TODO pub fn lines_mut<'s>(&'s mut self, header: &Header) -> impl 's + |
258 | | // Iterator<Item=LineRefMut<'s>> { LineIndex::lines_in_block(self.index, |
259 | | // &header.channels) .map(move |(bytes, line)| LineSlice { location: line, |
260 | | // value: &mut self.data[bytes] }) } |
261 | | |
262 | | // // TODO make iterator |
263 | | // Call a closure for each line of samples in this uncompressed block. |
264 | | // pub fn for_lines( |
265 | | // &self, header: &Header, |
266 | | // mut accept_line: impl FnMut(LineRef<'_>) -> UnitResult |
267 | | // ) -> UnitResult { |
268 | | // for (bytes, line) in LineIndex::lines_in_block(self.index, &header.channels) |
269 | | // { let line_ref = LineSlice { location: line, value: &self.data[bytes] }; |
270 | | // accept_line(line_ref)?; |
271 | | // } |
272 | | // |
273 | | // Ok(()) |
274 | | // } |
275 | | |
276 | | // TODO from iterator?? |
277 | | /// Create an uncompressed block byte vector by requesting one line of |
278 | | /// samples after another. |
279 | 0 | pub fn collect_block_data_from_lines( |
280 | 0 | channels: &ChannelList, |
281 | 0 | block_index: BlockIndex, |
282 | 0 | mut extract_line: impl FnMut(LineRefMut<'_>), |
283 | 0 | ) -> Vec<u8> { |
284 | 0 | let byte_count = block_index.pixel_size.area() * channels.bytes_per_pixel; |
285 | 0 | let mut block_bytes = vec![0_u8; byte_count]; |
286 | | |
287 | 0 | for (byte_range, line_index) in LineIndex::lines_in_block(block_index, channels) { |
288 | 0 | extract_line(LineRefMut { |
289 | 0 | // TODO subsampling |
290 | 0 | value: &mut block_bytes[byte_range], |
291 | 0 | location: line_index, |
292 | 0 | }); |
293 | 0 | } |
294 | | |
295 | 0 | block_bytes |
296 | 0 | } |
297 | | |
298 | | /// Create an uncompressed block by requesting one line of samples after |
299 | | /// another. |
300 | 0 | pub fn from_lines( |
301 | 0 | channels: &ChannelList, |
302 | 0 | block_index: BlockIndex, |
303 | 0 | extract_line: impl FnMut(LineRefMut<'_>), |
304 | 0 | ) -> Self { |
305 | 0 | Self { |
306 | 0 | index: block_index, |
307 | 0 | data: Self::collect_block_data_from_lines(channels, block_index, extract_line), |
308 | 0 | } |
309 | 0 | } |
310 | | } |