Coverage Report

Created: 2026-07-16 07:16

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.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
}