1//! Extract lines from a block of pixel bytes.
2
3use crate::math::*;
4use std::io::{Cursor};
5use crate::error::{Result, UnitResult};
6use smallvec::SmallVec;
7use std::ops::Range;
8use crate::block::{BlockIndex};
9use crate::meta::attribute::ChannelList;
10
11
12/// A single line of pixels.
13/// Use [LineRef] or [LineRefMut] for easier type names.
14#[derive(Clone, Copy, Eq, PartialEq, Debug)]
15pub struct LineSlice<T> {
16
17 // TODO also store enum SampleType, as it would always be matched in every place it is used
18
19 /// Where this line is located inside the image.
20 pub location: LineIndex,
21
22 /// The raw bytes of the pixel line, either `&[u8]` or `&mut [u8]`.
23 /// Must be re-interpreted as slice of f16, f32, or u32,
24 /// according to the channel data type.
25 pub value: T,
26}
27
28
29/// An reference to a single line of pixels.
30/// May go across the whole image or just a tile section of it.
31///
32/// This line contains an immutable slice that all samples will be read from.
33pub type LineRef<'s> = LineSlice<&'s [u8]>;
34
35/// A reference to a single mutable line of pixels.
36/// May go across the whole image or just a tile section of it.
37///
38/// This line contains a mutable slice that all samples will be written to.
39pub type LineRefMut<'s> = LineSlice<&'s mut [u8]>;
40
41
42/// Specifies where a row of pixels lies inside an image.
43/// This is a globally unique identifier which includes
44/// the layer, channel index, and pixel location.
45#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
46pub struct LineIndex {
47
48 /// Index of the layer.
49 pub layer: usize,
50
51 /// The channel index of the layer.
52 pub channel: usize,
53
54 /// Index of the mip or rip level in the image.
55 pub level: Vec2<usize>,
56
57 /// Position of the most left pixel of the row.
58 pub position: Vec2<usize>,
59
60 /// The width of the line; the number of samples in this row,
61 /// that is, the number of f16, f32, or u32 values.
62 pub sample_count: usize,
63}
64
65
66impl LineIndex {
67
68 /// Iterates the lines of this block index in interleaved fashion:
69 /// For each line in this block, this iterator steps once through each channel.
70 /// This is how lines are stored in a pixel data block.
71 ///
72 /// Does not check whether `self.layer_index`, `self.level`, `self.size` and `self.position` are valid indices.__
73 // TODO be sure this cannot produce incorrect data, as this is not further checked but only handled with panics
74 #[inline]
75 #[must_use]
76 pub fn lines_in_block(block: BlockIndex, channels: &ChannelList) -> impl Iterator<Item=(Range<usize>, LineIndex)> {
77 struct LineIter {
78 layer: usize, level: Vec2<usize>, width: usize,
79 end_y: usize, x: usize, channel_sizes: SmallVec<[usize; 8]>,
80 byte: usize, channel: usize, y: usize,
81 }
82
83 // FIXME what about sub sampling??
84
85 impl Iterator for LineIter {
86 type Item = (Range<usize>, LineIndex);
87 // TODO size hint?
88
89 fn next(&mut self) -> Option<Self::Item> {
90 if self.y < self.end_y {
91
92 // compute return value before incrementing
93 let byte_len = self.channel_sizes[self.channel];
94 let return_value = (
95 (self.byte .. self.byte + byte_len),
96 LineIndex {
97 channel: self.channel,
98 layer: self.layer,
99 level: self.level,
100 position: Vec2(self.x, self.y),
101 sample_count: self.width,
102 }
103 );
104
105 { // increment indices
106 self.byte += byte_len;
107 self.channel += 1;
108
109 if self.channel == self.channel_sizes.len() {
110 self.channel = 0;
111 self.y += 1;
112 }
113 }
114
115 Some(return_value)
116 }
117
118 else {
119 None
120 }
121 }
122 }
123
124 let channel_line_sizes: SmallVec<[usize; 8]> = channels.list.iter()
125 .map(move |channel| block.pixel_size.0 * channel.sample_type.bytes_per_sample()) // FIXME is it fewer samples per tile or just fewer tiles for sampled images???
126 .collect();
127
128 LineIter {
129 layer: block.layer,
130 level: block.level,
131 width: block.pixel_size.0,
132 x: block.pixel_position.0,
133 end_y: block.pixel_position.y() + block.pixel_size.height(),
134 channel_sizes: channel_line_sizes,
135
136 byte: 0,
137 channel: 0,
138 y: block.pixel_position.y()
139 }
140 }
141}
142
143
144
145impl<'s> LineRefMut<'s> {
146
147 /// Writes the samples (f16, f32, u32 values) into this line value reference.
148 /// Use `write_samples` if there is not slice available.
149 #[inline]
150 #[must_use]
151 pub fn write_samples_from_slice<T: crate::io::Data>(self, slice: &[T]) -> UnitResult {
152 debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width");
153 debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
154
155 T::write_slice(&mut Cursor::new(self.value), slice)
156 }
157
158 /// Iterate over all samples in this line, from left to right.
159 /// The supplied `get_line` function returns the sample value
160 /// for a given sample index within the line,
161 /// which starts at zero for each individual line.
162 /// Use `write_samples_from_slice` if you already have a slice of samples.
163 #[inline]
164 #[must_use]
165 pub fn write_samples<T: crate::io::Data>(self, mut get_sample: impl FnMut(usize) -> T) -> UnitResult {
166 debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
167
168 let mut write = Cursor::new(self.value);
169
170 for index in 0..self.location.sample_count {
171 T::write(get_sample(index), &mut write)?;
172 }
173
174 Ok(())
175 }
176}
177
178impl LineRef<'_> {
179
180 /// Read the samples (f16, f32, u32 values) from this line value reference.
181 /// Use `read_samples` if there is not slice available.
182 pub fn read_samples_into_slice<T: crate::io::Data>(self, slice: &mut [T]) -> UnitResult {
183 debug_assert_eq!(slice.len(), self.location.sample_count, "slice size does not match the line width");
184 debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
185
186 T::read_slice(&mut Cursor::new(self.value), slice)
187 }
188
189 /// Iterate over all samples in this line, from left to right.
190 /// Use `read_sample_into_slice` if you already have a slice of samples.
191 pub fn read_samples<T: crate::io::Data>(&self) -> impl Iterator<Item = Result<T>> + '_ {
192 debug_assert_eq!(self.value.len(), self.location.sample_count * T::BYTE_SIZE, "sample type size does not match line byte size");
193
194 let mut read: &[u8] = self.value; // FIXME deep data
195 (0..self.location.sample_count).map(move |_| T::read(&mut read))
196 }
197}