1#[cfg(feature = "tokio")]
2use crate::hdu::NormalisedImageStream;
3use crate::hdu::{HDU, ImageHDU};
4use crate::header::card::Card;
5use crate::header::{BayerPattern, Bitpix, Header, ImageType};
6#[cfg(feature = "tokio")]
7use crate::image::Normalizer;
8use crate::image::{Group, Image};
9#[cfg(feature = "tokio")]
10use futures::StreamExt;
11#[cfg(feature = "tokio")]
12use futures::stream;
13use std::error::Error;
14use std::sync::Arc;
15use std::time::Duration;
16
17#[derive(Debug, Clone)]
19pub struct SliceImageHDU {
20 header: Header,
21 data: Arc<Vec<u8>>,
23 data_offset: usize,
25 pending: Option<Vec<u8>>,
27}
28
29impl SliceImageHDU {
30 pub(crate) fn new(header: Header, data: Arc<Vec<u8>>, data_offset: usize) -> Self {
31 Self {
32 header,
33 data,
34 data_offset,
35 pending: None,
36 }
37 }
38
39 pub fn empty() -> Self {
41 Self {
42 header: Header::default(),
43 data: Arc::new(Vec::new()),
44 data_offset: 0,
45 pending: Some(Vec::new()),
46 }
47 }
48
49 pub(crate) fn data_bytes(&self) -> &[u8] {
51 if let Some(pending) = &self.pending {
52 return pending;
53 }
54
55 let len = self.header.data_bytes_len();
56 self.data
57 .get(self.data_offset..)
58 .and_then(|data| data.get(..len))
59 .unwrap_or_default()
60 }
61
62 fn is_compressed(&self) -> bool {
64 self.header.is_compressed_image()
65 }
66
67 fn dimensions(&self) -> (u32, u32) {
70 let axis = |index: usize| {
71 let length = if self.is_compressed() {
72 self.header.compressed_naxis_n(index)
73 } else {
74 self.header.naxis_n(index)
75 };
76
77 length
78 .and_then(|length| u32::try_from(length).ok())
79 .unwrap_or(0)
80 };
81
82 (axis(0), axis(1))
83 }
84
85 fn read_compressed(&self, index: usize) -> Result<Image, Box<dyn Error + Send + Sync>> {
91 let table = crate::bin_table::BinTable::from_u8(&self.header, self.data_bytes().to_vec())?;
92 let (data, image_header) = crate::image::compression::read_data(&self.header, &table)?;
93
94 let size = self.image_data_size() as usize;
95 let start = size.saturating_mul(index);
96
97 let plane = data
98 .get(start..)
99 .and_then(|rest| rest.get(..size))
100 .unwrap_or_default()
101 .to_vec();
102
103 Image::from_data_and_header(plane, &image_header)
104 }
105
106 fn image_bytes(&self, index: usize) -> &[u8] {
107 let size = self.image_data_size() as usize;
108 let start = size.saturating_mul(index);
109
110 self.data_bytes()
111 .get(start..)
112 .and_then(|data| data.get(..size))
113 .unwrap_or_default()
114 }
115
116 fn group_bytes(
118 &self,
119 index: usize,
120 len: usize,
121 ) -> Result<Vec<u8>, Box<dyn Error + Send + Sync>> {
122 let start = len * index;
123
124 Ok(self
125 .data_bytes()
126 .get(start..)
127 .and_then(|rest| rest.get(..len))
128 .unwrap_or_default()
129 .to_vec())
130 }
131
132 fn set_raw_array<T: Copy, const N: usize>(
135 &mut self,
136 bitpix: Bitpix,
137 shape: &[u32],
138 values: &[T],
139 to_be_bytes: impl Fn(T) -> [u8; N],
140 ) -> Result<(), Box<dyn Error + Send + Sync>> {
141 if shape.is_empty() {
142 return self.clear_images();
143 }
144
145 let mut expected = 1_usize;
146 for length in shape {
147 expected = expected
148 .checked_mul(*length as usize)
149 .ok_or("Array dimensions overflow the address space")?;
150 }
151
152 if values.len() != expected {
153 return Err(format!(
154 "An array of {:?} holds {} values, but {} were given",
155 shape,
156 expected,
157 values.len()
158 )
159 .into());
160 }
161
162 let mut data = Vec::with_capacity(expected * N);
163 for value in values {
164 data.extend_from_slice(&to_be_bytes(*value));
165 }
166
167 self.header.set(Card::Bitpix {
168 value: bitpix,
169 comment: None,
170 });
171 self.header.set(Card::NAxis {
172 value: shape.len() as i64,
173 comment: None,
174 });
175
176 self.header
179 .remove_prefixed(crate::header::card_keys::PREFIX_NAXIS_N);
180
181 for (index, length) in shape.iter().enumerate() {
182 self.header.set(Card::NAxisN {
183 index,
184 value: *length as i64,
185 comment: None,
186 });
187 }
188
189 self.pending = Some(data);
190
191 Ok(())
192 }
193}
194
195impl HDU for SliceImageHDU {
196 fn header(&self) -> &Header {
197 &self.header
198 }
199
200 fn header_mut(&mut self) -> &mut Header {
201 &mut self.header
202 }
203}
204
205impl ImageHDU for SliceImageHDU {
206 fn image_count(&self) -> usize {
207 if self.header.is_random_groups() {
210 return 0;
211 }
212
213 if self.is_compressed() {
214 return self.header.compressed_plane_count();
215 }
216
217 self.header.image_plane_count()
218 }
219
220 fn images_width(&self) -> u32 {
221 self.dimensions().0
222 }
223
224 fn images_height(&self) -> u32 {
225 self.dimensions().1
226 }
227
228 fn images_bayer_pattern(&self) -> Option<BayerPattern> {
229 self.header.bayer_pattern()
230 }
231
232 fn images_type(&self) -> Option<&ImageType> {
233 self.header.image_type()
234 }
235
236 fn images_exposure_time(&self) -> Option<Duration> {
237 self.header
238 .exposure()
239 .or_else(|| self.header.exposure_time())
240 }
241
242 fn read_image(&self, index: usize) -> Result<Option<Image>, Box<dyn Error + Send + Sync>> {
243 if index >= self.image_count() {
244 return Ok(None);
245 }
246
247 if self.is_compressed() {
248 return Ok(Some(self.read_compressed(index)?));
249 }
250
251 let bytes = self.image_bytes(index).to_vec();
252
253 Ok(Some(Image::from_data_and_header(bytes, &self.header)?))
254 }
255
256 fn group_count(&self) -> usize {
258 if !self.header.is_random_groups() {
259 return 0;
260 }
261
262 self.header.group_count().unwrap_or(0).max(0) as usize
263 }
264
265 fn read_group(&self, index: usize) -> Result<Option<Group>, Box<dyn Error + Send + Sync>> {
266 if index >= ImageHDU::group_count(self) {
267 return Ok(None);
268 }
269
270 let Some(bitpix) = self.header.bitpix() else {
271 return Ok(None);
272 };
273
274 let parameters = self.header.pcount().unwrap_or(0).max(0) as usize;
277 let len = (parameters + self.header.group_array_len()) * bitpix.byte_size();
278
279 let bytes = self.group_bytes(index, len)?;
280
281 Ok(crate::image::decode_group(&self.header, &bytes))
282 }
283
284 fn clear_images(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
285 self.header.set(Card::NAxis {
286 value: 0,
287 comment: None,
288 });
289 self.header
290 .remove_prefixed(crate::header::card_keys::PREFIX_NAXIS_N);
291
292 self.pending = Some(Vec::new());
293
294 Ok(())
295 }
296
297 fn set_raw_array_u8(
298 &mut self,
299 shape: &[u32],
300 values: &[u8],
301 ) -> Result<(), Box<dyn Error + Send + Sync>> {
302 self.set_raw_array(Bitpix::U8, shape, values, u8::to_be_bytes)
303 }
304
305 fn set_raw_array_i16(
306 &mut self,
307 shape: &[u32],
308 values: &[i16],
309 ) -> Result<(), Box<dyn Error + Send + Sync>> {
310 self.set_raw_array(Bitpix::I16, shape, values, i16::to_be_bytes)
311 }
312
313 fn set_raw_array_i32(
314 &mut self,
315 shape: &[u32],
316 values: &[i32],
317 ) -> Result<(), Box<dyn Error + Send + Sync>> {
318 self.set_raw_array(Bitpix::I32, shape, values, i32::to_be_bytes)
319 }
320
321 fn set_raw_array_f32(
322 &mut self,
323 shape: &[u32],
324 values: &[f32],
325 ) -> Result<(), Box<dyn Error + Send + Sync>> {
326 self.set_raw_array(Bitpix::F32, shape, values, f32::to_be_bytes)
327 }
328
329 fn set_raw_array_f64(
330 &mut self,
331 shape: &[u32],
332 values: &[f64],
333 ) -> Result<(), Box<dyn Error + Send + Sync>> {
334 self.set_raw_array(Bitpix::F64, shape, values, f64::to_be_bytes)
335 }
336
337 #[cfg(feature = "tokio")]
338 fn stream_normalised_image(
339 &self,
340 index: usize,
341 ) -> Result<Option<NormalisedImageStream<'_>>, Box<dyn Error + Send + Sync>> {
342 if index >= self.image_count() {
343 return Ok(None);
344 }
345
346 let width = self.images_width();
347 if width == 0 {
348 return Ok(None);
349 }
350
351 if self.is_compressed() {
354 let image = self.read_compressed(index)?;
355 let normalised = image.normalized();
356
357 let pixels: Vec<_> = normalised
358 .enumerate_pixels()
359 .map(|(x, y, pixel)| (x, y, pixel[0]))
360 .collect();
361
362 return Ok(Some(stream::iter(pixels).boxed()));
363 }
364
365 let bitpix = self
366 .header
367 .bitpix()
368 .ok_or("Cannot stream an image from a header without a BITPIX card")?;
369 let normalizer = Normalizer::from_header(&self.header)?;
370 let pixel_len = bitpix.byte_size();
371
372 let pixels: Vec<_> = self
375 .image_bytes(index)
376 .chunks_exact(pixel_len)
377 .enumerate()
378 .filter_map(|(index, raw)| {
379 let value = bitpix.read_be(raw)?;
380 let x = (index as u64 % width as u64) as u32;
381 let y = (index as u64 / width as u64) as u32;
382
383 Some((x, y, normalizer.normalize(value)))
384 })
385 .collect();
386
387 Ok(Some(stream::iter(pixels).boxed()))
388 }
389
390 fn image_data_size(&self) -> u64 {
391 let bitpix = if self.is_compressed() {
392 self.header.compressed_bitpix()
393 } else {
394 self.header.bitpix()
395 };
396
397 let Some(bitpix) = bitpix else {
398 return 0;
399 };
400
401 self.images_width() as u64 * self.images_height() as u64 * bitpix.byte_size() as u64
402 }
403
404 fn compress(
405 &mut self,
406 options: &crate::image::compression::CompressionOptions,
407 ) -> Result<(), Box<dyn Error + Send + Sync>> {
408 self.decompress()?;
411
412 let (header, data) =
413 crate::image::compression::compress_image(&self.header, self.data_bytes(), options)?;
414
415 self.header = header;
416 self.pending = Some(data);
417
418 Ok(())
419 }
420
421 fn decompress(&mut self) -> Result<(), Box<dyn Error + Send + Sync>> {
422 if !self.is_compressed() {
423 return Ok(());
424 }
425
426 let (header, data) =
427 crate::image::compression::decompress_image(&self.header, self.data_bytes())?;
428
429 self.header = header;
430 self.pending = Some(data);
431
432 Ok(())
433 }
434}