| 1 | //! Extract lines from a block of pixel bytes.
|
| 2 |
|
| 3 | use crate::math::*;
|
| 4 | use std::io::{Cursor};
|
| 5 | use crate::error::{Result, UnitResult};
|
| 6 | use smallvec::SmallVec;
|
| 7 | use std::ops::Range;
|
| 8 | use crate::block::{BlockIndex};
|
| 9 | use 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)]
|
| 15 | pub 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.
|
| 33 | pub 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.
|
| 39 | pub 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)]
|
| 46 | pub 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 |
|
| 66 | impl 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 |
|
| 145 | impl<'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 |
|
| 178 | impl 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 | } |