1//! How to write samples (a grid of `f32`, `f16` or `u32` values).
2
3use crate::meta::attribute::{LevelMode, SampleType, TileDescription};
4use crate::meta::header::Header;
5use crate::block::lines::LineRefMut;
6use crate::image::{FlatSamples, Levels, RipMaps};
7use crate::math::{Vec2, RoundingMode};
8use crate::meta::{rip_map_levels, mip_map_levels, rip_map_indices, mip_map_indices, BlockDescription};
9
10/// Enable an image with this sample grid to be written to a file.
11/// Also can contain multiple resolution levels.
12/// Usually contained within `Channels`.
13pub trait WritableSamples<'slf> {
14 // fn is_deep(&self) -> bool;
15
16 /// Generate the file meta data regarding the number type of this storage
17 fn sample_type(&self) -> SampleType;
18
19 /// Generate the file meta data regarding resolution levels
20 fn infer_level_modes(&self) -> (LevelMode, RoundingMode);
21
22 /// The type of the temporary writer for this sample storage
23 type Writer: SamplesWriter;
24
25 /// Create a temporary writer for this sample storage
26 fn create_samples_writer(&'slf self, header: &Header) -> Self::Writer;
27}
28
29/// Enable an image with this single level sample grid to be written to a file.
30/// Only contained within `Levels`.
31pub trait WritableLevel<'slf> {
32
33 /// Generate the file meta data regarding the number type of these samples
34 fn sample_type(&self) -> SampleType;
35
36 /// The type of the temporary writer for this single level of samples
37 type Writer: SamplesWriter;
38
39 /// Create a temporary writer for this single level of samples
40 fn create_level_writer(&'slf self, size: Vec2<usize>) -> Self::Writer;
41}
42
43/// A temporary writer for one or more resolution levels containing samples
44pub trait SamplesWriter: Sync {
45
46 /// Deliver a single short horizontal list of samples for a specific channel.
47 fn extract_line(&self, line: LineRefMut<'_>);
48}
49
50/// A temporary writer for a predefined non-deep sample storage
51#[derive(Debug, Copy, Clone, PartialEq)]
52pub struct FlatSamplesWriter<'samples> {
53 resolution: Vec2<usize>, // respects resolution level
54 samples: &'samples FlatSamples
55}
56
57
58
59// used if no layers are used and the flat samples are directly inside the channels
60impl<'samples> WritableSamples<'samples> for FlatSamples {
61 fn sample_type(&self) -> SampleType {
62 match self {
63 FlatSamples::F16(_) => SampleType::F16,
64 FlatSamples::F32(_) => SampleType::F32,
65 FlatSamples::U32(_) => SampleType::U32,
66 }
67 }
68
69 fn infer_level_modes(&self) -> (LevelMode, RoundingMode) { (LevelMode::Singular, RoundingMode::Down) }
70
71 type Writer = FlatSamplesWriter<'samples>; //&'s FlatSamples;
72 fn create_samples_writer(&'samples self, header: &Header) -> Self::Writer {
73 FlatSamplesWriter {
74 resolution: header.layer_size,
75 samples: self
76 }
77 }
78}
79
80// used if layers are used and the flat samples are inside the levels
81impl<'samples> WritableLevel<'samples> for FlatSamples {
82 fn sample_type(&self) -> SampleType {
83 match self {
84 FlatSamples::F16(_) => SampleType::F16,
85 FlatSamples::F32(_) => SampleType::F32,
86 FlatSamples::U32(_) => SampleType::U32,
87 }
88 }
89
90 type Writer = FlatSamplesWriter<'samples>;
91 fn create_level_writer(&'samples self, size: Vec2<usize>) -> Self::Writer {
92 FlatSamplesWriter {
93 resolution: size,
94 samples: self
95 }
96 }
97}
98
99impl<'samples> SamplesWriter for FlatSamplesWriter<'samples> {
100 fn extract_line(&self, line: LineRefMut<'_>) {
101 let image_width: usize = self.resolution.width(); // header.layer_size.width();
102 debug_assert_ne!(image_width, 0, "image width calculation bug");
103
104 let start_index: usize = line.location.position.y() * image_width + line.location.position.x();
105 let end_index: usize = start_index + line.location.sample_count;
106
107 debug_assert!(
108 start_index < end_index && end_index <= self.samples.len(),
109 "for resolution {:?}, this is an invalid line: {:?}",
110 self.resolution, line.location
111 );
112
113 match self.samples {
114 FlatSamples::F16(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
115 FlatSamples::F32(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
116 FlatSamples::U32(samples) => line.write_samples_from_slice(&samples[start_index .. end_index]),
117 }.expect(msg:"writing line bytes failed");
118 }
119}
120
121
122impl<'samples, LevelSamples> WritableSamples<'samples> for Levels<LevelSamples>
123 where LevelSamples: WritableLevel<'samples>
124{
125 fn sample_type(&self) -> SampleType {
126 let sample_type = self.levels_as_slice().first().expect("no levels found").sample_type();
127
128 debug_assert!(
129 self.levels_as_slice().iter().skip(1).all(|ty| ty.sample_type() == sample_type),
130 "sample types must be the same across all levels"
131 );
132
133 sample_type
134 }
135
136 fn infer_level_modes(&self) -> (LevelMode, RoundingMode) {
137 match self {
138 Levels::Singular(_) => (LevelMode::Singular, RoundingMode::Down),
139 Levels::Mip { rounding_mode, .. } => (LevelMode::MipMap, *rounding_mode),
140 Levels::Rip { rounding_mode, .. } => (LevelMode::RipMap, *rounding_mode),
141 }
142 }
143
144 type Writer = LevelsWriter<LevelSamples::Writer>;
145 fn create_samples_writer(&'samples self, header: &Header) -> Self::Writer {
146 let rounding = match header.blocks {
147 BlockDescription::Tiles(TileDescription { rounding_mode, .. }) => Some(rounding_mode),
148 BlockDescription::ScanLines => None,
149 };
150
151 LevelsWriter {
152 levels: match self {
153 Levels::Singular(level) => Levels::Singular(level.create_level_writer(header.layer_size)),
154 Levels::Mip { level_data, rounding_mode } => {
155 debug_assert_eq!(
156 level_data.len(),
157 mip_map_indices(rounding.expect("mip maps only with tiles"), header.layer_size).count(),
158 "invalid mip map count"
159 );
160
161 Levels::Mip { // TODO store level size in image??
162 rounding_mode: *rounding_mode,
163 level_data: level_data.iter()
164 .zip(mip_map_levels(rounding.expect("mip maps only with tiles"), header.layer_size))
165 // .map(|level| level.create_samples_writer(header))
166 .map(|(level, (_level_index, level_size))| level.create_level_writer(level_size))
167 .collect()
168 }
169 },
170 Levels::Rip { level_data, rounding_mode } => {
171 debug_assert_eq!(level_data.map_data.len(), level_data.level_count.area(), "invalid rip level count");
172 debug_assert_eq!(
173 level_data.map_data.len(),
174 rip_map_indices(rounding.expect("rip maps only with tiles"), header.layer_size).count(),
175 "invalid rip map count"
176 );
177
178 Levels::Rip {
179 rounding_mode: *rounding_mode,
180 level_data: RipMaps {
181 level_count: level_data.level_count,
182 map_data: level_data.map_data.iter()
183 .zip(rip_map_levels(rounding.expect("rip maps only with tiles"), header.layer_size))
184 .map(|(level, (_level_index, level_size))| level.create_level_writer(level_size))
185 .collect(),
186 }
187 }
188 }
189 }
190 }
191 }
192}
193
194/// A temporary writer for multiple resolution levels
195#[derive(Debug, Clone, Eq, PartialEq)]
196pub struct LevelsWriter<SamplesWriter> {
197 levels: Levels<SamplesWriter>,
198}
199
200impl<Samples> SamplesWriter for LevelsWriter<Samples> where Samples: SamplesWriter {
201 fn extract_line(&self, line: LineRefMut<'_>) {
202 self.levels.get_level(line.location.level).expect(msg:"invalid level index") // TODO compute level size from line index??
203 .extract_line(line)
204 }
205}
206