Coverage Report

Created: 2026-09-01 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.24k
pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> {
79
7.24k
    self::reader::Reader::read_from_buffered(buffered_read, pedantic)
80
7.24k
}
exr::block::read::<std::io::cursor::Cursor<&[u8]>>
Line
Count
Source
78
7.14k
pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> {
79
7.14k
    self::reader::Reader::read_from_buffered(buffered_read, pedantic)
80
7.14k
}
Unexecuted instantiation: exr::block::read::<_>
exr::block::read::<std::io::cursor::Cursor<alloc::vec::Vec<u8>>>
Line
Count
Source
78
98
pub fn read<R: Read + Seek>(buffered_read: R, pedantic: bool) -> Result<self::reader::Reader<R>> {
79
98
    self::reader::Reader::read_from_buffered(buffered_read, pedantic)
80
98
}
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
98
pub fn write<W: Write + Seek>(
89
98
    buffered_write: W,
90
98
    headers: Headers,
91
98
    compatibility_checks: bool,
92
98
    write_chunks: impl FnOnce(MetaData, &mut self::writer::ChunkWriter<W>) -> UnitResult,
93
98
) -> UnitResult {
94
98
    self::writer::write_chunks_with(buffered_write, headers, compatibility_checks, write_chunks)
95
98
}
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
98
pub fn write<W: Write + Seek>(
89
98
    buffered_write: W,
90
98
    headers: Headers,
91
98
    compatibility_checks: bool,
92
98
    write_chunks: impl FnOnce(MetaData, &mut self::writer::ChunkWriter<W>) -> UnitResult,
93
98
) -> UnitResult {
94
98
    self::writer::write_chunks_with(buffered_write, headers, compatibility_checks, write_chunks)
95
98
}
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
98
pub fn enumerate_ordered_header_block_indices(
107
98
    headers: &[Header],
108
98
) -> impl '_ + Iterator<Item = (usize, BlockIndex)> {
109
98
    headers.iter().enumerate().flat_map(|(layer_index, header)| {
110
813k
        header.enumerate_ordered_blocks().map(move |(index_in_header, tile)| {
111
813k
            let data_indices = header
112
813k
                .get_absolute_block_pixel_coordinates(tile.location)
113
813k
                .expect("tile coordinate bug");
114
115
813k
            let block = BlockIndex {
116
813k
                layer: layer_index,
117
813k
                level: tile.location.level_index,
118
813k
                pixel_position: data_indices
119
813k
                    .position
120
813k
                    .to_usize("data indices start")
121
813k
                    .expect("data index bug"),
122
813k
                pixel_size: data_indices.size,
123
813k
            };
124
125
813k
            (index_in_header, block)
126
813k
        })
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
813k
        header.enumerate_ordered_blocks().map(move |(index_in_header, tile)| {
111
813k
            let data_indices = header
112
813k
                .get_absolute_block_pixel_coordinates(tile.location)
113
813k
                .expect("tile coordinate bug");
114
115
813k
            let block = BlockIndex {
116
813k
                layer: layer_index,
117
813k
                level: tile.location.level_index,
118
813k
                pixel_position: data_indices
119
813k
                    .position
120
813k
                    .to_usize("data indices start")
121
813k
                    .expect("data index bug"),
122
813k
                pixel_size: data_indices.size,
123
813k
            };
124
125
813k
            (index_in_header, block)
126
813k
        })
127
98
    })
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
98
    headers.iter().enumerate().flat_map(|(layer_index, header)| {
110
98
        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
98
    })
128
98
}
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
821k
    pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> {
137
821k
        let header: &Header = meta_data
138
821k
            .headers
139
821k
            .get(chunk.layer_index)
140
821k
            .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
821k
        let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?;
143
821k
        let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?;
144
145
819k
        absolute_indices.validate(Some(header.layer_size))?;
146
147
819k
        match chunk.compressed_block {
148
            CompressedBlock::Tile(CompressedTileBlock {
149
814k
                compressed_pixels_le,
150
                ..
151
            })
152
            | CompressedBlock::ScanLine(CompressedScanLineBlock {
153
5.19k
                compressed_pixels_le,
154
                ..
155
            }) => Ok(Self {
156
819k
                data: header.compression.decompress_image_section_from_le(
157
819k
                    header,
158
819k
                    compressed_pixels_le,
159
819k
                    absolute_indices,
160
819k
                    pedantic,
161
769
                )?,
162
                index: BlockIndex {
163
818k
                    layer: chunk.layer_index,
164
818k
                    pixel_position: absolute_indices.position.to_usize("data indices start")?,
165
818k
                    level: tile_data_indices.level_index,
166
818k
                    pixel_size: absolute_indices.size,
167
                },
168
            }),
169
170
0
            _ => Err(Error::unsupported("deep data not supported yet")),
171
        }
172
821k
    }
<exr::block::UncompressedBlock>::decompress_chunk
Line
Count
Source
136
8.03k
    pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> {
137
8.03k
        let header: &Header = meta_data
138
8.03k
            .headers
139
8.03k
            .get(chunk.layer_index)
140
8.03k
            .ok_or_else(|| Error::invalid("chunk layer index"))?;
141
142
8.03k
        let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?;
143
7.96k
        let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?;
144
145
5.44k
        absolute_indices.validate(Some(header.layer_size))?;
146
147
5.44k
        match chunk.compressed_block {
148
            CompressedBlock::Tile(CompressedTileBlock {
149
244
                compressed_pixels_le,
150
                ..
151
            })
152
            | CompressedBlock::ScanLine(CompressedScanLineBlock {
153
5.19k
                compressed_pixels_le,
154
                ..
155
            }) => Ok(Self {
156
5.44k
                data: header.compression.decompress_image_section_from_le(
157
5.44k
                    header,
158
5.44k
                    compressed_pixels_le,
159
5.44k
                    absolute_indices,
160
5.44k
                    pedantic,
161
769
                )?,
162
                index: BlockIndex {
163
4.67k
                    layer: chunk.layer_index,
164
4.67k
                    pixel_position: absolute_indices.position.to_usize("data indices start")?,
165
4.67k
                    level: tile_data_indices.level_index,
166
4.67k
                    pixel_size: absolute_indices.size,
167
                },
168
            }),
169
170
0
            _ => Err(Error::unsupported("deep data not supported yet")),
171
        }
172
8.03k
    }
Unexecuted instantiation: <exr::block::UncompressedBlock>::decompress_chunk
<exr::block::UncompressedBlock>::decompress_chunk
Line
Count
Source
136
813k
    pub fn decompress_chunk(chunk: Chunk, meta_data: &MetaData, pedantic: bool) -> Result<Self> {
137
813k
        let header: &Header = meta_data
138
813k
            .headers
139
813k
            .get(chunk.layer_index)
140
813k
            .ok_or_else(|| Error::invalid("chunk layer index"))?;
141
142
813k
        let tile_data_indices = header.get_block_data_indices(&chunk.compressed_block)?;
143
813k
        let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_data_indices)?;
144
145
813k
        absolute_indices.validate(Some(header.layer_size))?;
146
147
813k
        match chunk.compressed_block {
148
            CompressedBlock::Tile(CompressedTileBlock {
149
813k
                compressed_pixels_le,
150
                ..
151
            })
152
            | CompressedBlock::ScanLine(CompressedScanLineBlock {
153
0
                compressed_pixels_le,
154
                ..
155
            }) => Ok(Self {
156
813k
                data: header.compression.decompress_image_section_from_le(
157
813k
                    header,
158
813k
                    compressed_pixels_le,
159
813k
                    absolute_indices,
160
813k
                    pedantic,
161
0
                )?,
162
                index: BlockIndex {
163
813k
                    layer: chunk.layer_index,
164
813k
                    pixel_position: absolute_indices.position.to_usize("data indices start")?,
165
813k
                    level: tile_data_indices.level_index,
166
813k
                    pixel_size: absolute_indices.size,
167
                },
168
            }),
169
170
0
            _ => Err(Error::unsupported("deep data not supported yet")),
171
        }
172
813k
    }
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
813k
    pub fn compress_to_chunk(self, headers: &[Header]) -> Result<Chunk> {
179
        let Self {
180
813k
            data,
181
813k
            index,
182
813k
        } = self;
183
184
813k
        let header: &Header = headers.get(index.layer).expect("block layer index bug");
185
186
813k
        let expected_byte_size = header.channels.bytes_per_pixel * self.index.pixel_size.area(); // TODO sampling??
187
813k
        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
813k
        }
194
195
813k
        let tile_coordinates = TileCoordinates {
196
813k
            // FIXME this calculation should not be made here but elsewhere instead (in
197
813k
            // meta::header?)
198
813k
            tile_index: index.pixel_position / header.max_block_pixel_size(), // TODO sampling??
199
813k
            level_index: index.level,
200
813k
        };
201
202
813k
        let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_coordinates)?;
203
813k
        absolute_indices.validate(Some(header.layer_size))?;
204
205
813k
        if !header.compression.may_loose_data() {
206
813k
            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
813k
        let compressed_pixels_le =
226
813k
            header.compression.compress_image_section_to_le(header, data, absolute_indices)?;
227
228
        Ok(Chunk {
229
813k
            layer_index: index.layer,
230
813k
            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
813k
                BlockDescription::Tiles(_) => CompressedBlock::Tile(CompressedTileBlock {
241
813k
                    compressed_pixels_le,
242
813k
                    coordinates: tile_coordinates,
243
813k
                }),
244
            },
245
        })
246
813k
    }
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
813k
    pub fn compress_to_chunk(self, headers: &[Header]) -> Result<Chunk> {
179
        let Self {
180
813k
            data,
181
813k
            index,
182
813k
        } = self;
183
184
813k
        let header: &Header = headers.get(index.layer).expect("block layer index bug");
185
186
813k
        let expected_byte_size = header.channels.bytes_per_pixel * self.index.pixel_size.area(); // TODO sampling??
187
813k
        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
813k
        }
194
195
813k
        let tile_coordinates = TileCoordinates {
196
813k
            // FIXME this calculation should not be made here but elsewhere instead (in
197
813k
            // meta::header?)
198
813k
            tile_index: index.pixel_position / header.max_block_pixel_size(), // TODO sampling??
199
813k
            level_index: index.level,
200
813k
        };
201
202
813k
        let absolute_indices = header.get_absolute_block_pixel_coordinates(tile_coordinates)?;
203
813k
        absolute_indices.validate(Some(header.layer_size))?;
204
205
813k
        if !header.compression.may_loose_data() {
206
813k
            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
813k
        let compressed_pixels_le =
226
813k
            header.compression.compress_image_section_to_le(header, data, absolute_indices)?;
227
228
        Ok(Chunk {
229
813k
            layer_index: index.layer,
230
813k
            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
813k
                BlockDescription::Tiles(_) => CompressedBlock::Tile(CompressedTileBlock {
241
813k
                    compressed_pixels_le,
242
813k
                    coordinates: tile_coordinates,
243
813k
                }),
244
            },
245
        })
246
813k
    }
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
}