1use async_trait::async_trait;
22use bytes::Bytes;
23
24use crate::error::TiffError;
25use crate::io::RangeReader;
26use crate::slide::SlideReader;
27
28use super::jpeg::prepare_tile_jpeg;
29use super::tiff::{
30 validate_pyramid, PyramidLevel, TiffHeader, TiffPyramid, TileData, ValidationResult,
31};
32
33#[derive(Debug, Clone)]
39pub struct GenericTiffLevelData {
40 pub level: PyramidLevel,
42
43 pub tile_data: TileData,
45}
46
47impl GenericTiffLevelData {
48 pub fn get_tile_location(&self, tile_x: u32, tile_y: u32) -> Option<(u64, u64)> {
50 let tile_index = self.level.tile_index(tile_x, tile_y)?;
51 self.tile_data.get_tile_location(tile_index)
52 }
53
54 pub fn jpeg_tables(&self) -> Option<&Bytes> {
56 self.tile_data.jpeg_tables.as_ref()
57 }
58}
59
60#[derive(Debug)]
69pub struct GenericTiffReader {
70 pyramid: TiffPyramid,
72
73 levels: Vec<GenericTiffLevelData>,
75
76 warnings: Vec<String>,
78}
79
80impl GenericTiffReader {
81 pub async fn open<R: RangeReader>(reader: &R) -> Result<Self, TiffError> {
94 let pyramid = TiffPyramid::parse(reader).await?;
96
97 let validation = validate_pyramid(&pyramid);
99 if !validation.is_valid {
100 return Err(validation.into_result().unwrap_err());
101 }
102
103 let warnings = validation.warnings;
105
106 let mut levels = Vec::with_capacity(pyramid.levels.len());
108 for level in &pyramid.levels {
109 let tile_data = TileData::load(reader, level, &pyramid.header).await?;
110 levels.push(GenericTiffLevelData {
111 level: level.clone(),
112 tile_data,
113 });
114 }
115
116 Ok(GenericTiffReader {
117 pyramid,
118 levels,
119 warnings,
120 })
121 }
122
123 pub async fn open_with_validation<R: RangeReader>(
128 reader: &R,
129 ) -> Result<(Self, ValidationResult), TiffError> {
130 let pyramid = TiffPyramid::parse(reader).await?;
132
133 let validation = validate_pyramid(&pyramid);
135 if !validation.is_valid {
136 return Err(validation.clone().into_result().unwrap_err());
137 }
138
139 let mut levels = Vec::with_capacity(pyramid.levels.len());
141 for level in &pyramid.levels {
142 let tile_data = TileData::load(reader, level, &pyramid.header).await?;
143 levels.push(GenericTiffLevelData {
144 level: level.clone(),
145 tile_data,
146 });
147 }
148
149 let reader = GenericTiffReader {
150 pyramid,
151 levels,
152 warnings: validation.warnings.clone(),
153 };
154
155 Ok((reader, validation))
156 }
157
158 pub fn header(&self) -> &TiffHeader {
160 &self.pyramid.header
161 }
162
163 pub fn warnings(&self) -> &[String] {
167 &self.warnings
168 }
169
170 pub fn level_count(&self) -> usize {
172 self.levels.len()
173 }
174
175 pub fn get_level(&self, level: usize) -> Option<&GenericTiffLevelData> {
177 self.levels.get(level)
178 }
179
180 pub fn dimensions(&self) -> Option<(u32, u32)> {
182 self.levels.first().map(|l| (l.level.width, l.level.height))
183 }
184
185 pub fn level_dimensions(&self, level: usize) -> Option<(u32, u32)> {
187 self.levels
188 .get(level)
189 .map(|l| (l.level.width, l.level.height))
190 }
191
192 pub fn level_downsample(&self, level: usize) -> Option<f64> {
194 self.levels.get(level).map(|l| l.level.downsample)
195 }
196
197 pub fn tile_size(&self, level: usize) -> Option<(u32, u32)> {
199 self.levels
200 .get(level)
201 .map(|l| (l.level.tile_width, l.level.tile_height))
202 }
203
204 pub fn tile_count(&self, level: usize) -> Option<(u32, u32)> {
206 self.levels
207 .get(level)
208 .map(|l| (l.level.tiles_x, l.level.tiles_y))
209 }
210
211 pub async fn read_raw_tile<R: RangeReader>(
215 &self,
216 reader: &R,
217 level: usize,
218 tile_x: u32,
219 tile_y: u32,
220 ) -> Result<Bytes, TiffError> {
221 let level_data = self.levels.get(level).ok_or(TiffError::InvalidTagValue {
222 tag: "level",
223 message: format!("level {} out of range (max {})", level, self.levels.len()),
224 })?;
225
226 let (offset, size) =
227 level_data
228 .get_tile_location(tile_x, tile_y)
229 .ok_or(TiffError::InvalidTagValue {
230 tag: "tile",
231 message: format!(
232 "tile ({}, {}) out of range for level {}",
233 tile_x, tile_y, level
234 ),
235 })?;
236
237 let data = reader.read_exact_at(offset, size as usize).await?;
238 Ok(data)
239 }
240
241 pub async fn read_tile<R: RangeReader>(
255 &self,
256 reader: &R,
257 level: usize,
258 tile_x: u32,
259 tile_y: u32,
260 ) -> Result<Bytes, TiffError> {
261 let raw_data = self.read_raw_tile(reader, level, tile_x, tile_y).await?;
263
264 let level_data = self.levels.get(level).ok_or(TiffError::InvalidTagValue {
266 tag: "level",
267 message: format!("level {} out of range", level),
268 })?;
269
270 let tables = level_data.jpeg_tables();
271
272 let jpeg_data = prepare_tile_jpeg(tables.map(|t| t.as_ref()), &raw_data);
274
275 Ok(jpeg_data)
276 }
277
278 pub fn best_level_for_downsample(&self, downsample: f64) -> Option<usize> {
282 self.pyramid
283 .best_level_for_downsample(downsample)
284 .map(|l| l.level_index)
285 }
286}
287
288#[async_trait]
293impl SlideReader for GenericTiffReader {
294 fn level_count(&self) -> usize {
295 self.levels.len()
296 }
297
298 fn dimensions(&self) -> Option<(u32, u32)> {
299 self.levels.first().map(|l| (l.level.width, l.level.height))
300 }
301
302 fn level_dimensions(&self, level: usize) -> Option<(u32, u32)> {
303 self.levels
304 .get(level)
305 .map(|l| (l.level.width, l.level.height))
306 }
307
308 fn level_downsample(&self, level: usize) -> Option<f64> {
309 self.levels.get(level).map(|l| l.level.downsample)
310 }
311
312 fn tile_size(&self, level: usize) -> Option<(u32, u32)> {
313 self.levels
314 .get(level)
315 .map(|l| (l.level.tile_width, l.level.tile_height))
316 }
317
318 fn tile_count(&self, level: usize) -> Option<(u32, u32)> {
319 self.levels
320 .get(level)
321 .map(|l| (l.level.tiles_x, l.level.tiles_y))
322 }
323
324 fn best_level_for_downsample(&self, downsample: f64) -> Option<usize> {
325 GenericTiffReader::best_level_for_downsample(self, downsample)
326 }
327
328 async fn read_tile<R: RangeReader>(
329 &self,
330 reader: &R,
331 level: usize,
332 tile_x: u32,
333 tile_y: u32,
334 ) -> Result<Bytes, TiffError> {
335 GenericTiffReader::read_tile(self, reader, level, tile_x, tile_y).await
336 }
337}
338
339#[cfg(test)]
344mod tests {
345 use super::*;
346 use crate::error::IoError;
347 use crate::format::tiff::{FieldType, Ifd, IfdEntry, TiffTag};
348 use crate::io::RangeReader;
349 use async_trait::async_trait;
350 use std::collections::HashMap;
351
352 #[allow(dead_code)]
354 struct MockTiffReader {
355 data: Vec<u8>,
357 }
358
359 #[allow(dead_code)]
360 impl MockTiffReader {
361 fn new_valid_tiff() -> Self {
362 let mut data = vec![0u8; 1024];
365
366 data[0] = 0x49; data[1] = 0x49; data[2] = 0x2A; data[3] = 0x00;
371 data[4] = 0x08; data[5] = 0x00;
373 data[6] = 0x00;
374 data[7] = 0x00;
375
376 data[8] = 0x07;
379 data[9] = 0x00;
380
381 MockTiffReader { data }
385 }
386 }
387
388 #[async_trait]
389 impl RangeReader for MockTiffReader {
390 async fn read_exact_at(&self, offset: u64, len: usize) -> Result<Bytes, IoError> {
391 let start = offset as usize;
392 let end = start + len;
393 if end > self.data.len() {
394 return Err(IoError::RangeOutOfBounds {
395 offset,
396 requested: len as u64,
397 size: self.data.len() as u64,
398 });
399 }
400 Ok(Bytes::copy_from_slice(&self.data[start..end]))
401 }
402
403 fn size(&self) -> u64 {
404 self.data.len() as u64
405 }
406
407 fn identifier(&self) -> &str {
408 "mock://test.tif"
409 }
410 }
411
412 fn make_mock_level() -> GenericTiffLevelData {
417 let ifd = Ifd {
418 entries: vec![],
419 entries_by_tag: HashMap::new(),
420 next_ifd_offset: 0,
421 };
422
423 let level = PyramidLevel {
424 level_index: 0,
425 ifd_index: 0,
426 width: 1000,
427 height: 800,
428 tile_width: 256,
429 tile_height: 256,
430 tiles_x: 4,
431 tiles_y: 4,
432 tile_count: 16,
433 downsample: 1.0,
434 compression: 7,
435 ifd,
436 tile_offsets_entry: Some(IfdEntry {
437 tag_id: TiffTag::TileOffsets.as_u16(),
438 field_type: Some(FieldType::Long),
439 field_type_raw: 4,
440 count: 16,
441 value_offset_bytes: vec![0, 0, 0, 0],
442 is_inline: false,
443 }),
444 tile_byte_counts_entry: Some(IfdEntry {
445 tag_id: TiffTag::TileByteCounts.as_u16(),
446 field_type: Some(FieldType::Long),
447 field_type_raw: 4,
448 count: 16,
449 value_offset_bytes: vec![0, 0, 0, 0],
450 is_inline: false,
451 }),
452 jpeg_tables_entry: None,
453 };
454
455 let tile_data = TileData {
456 offsets: vec![
457 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000, 13000,
458 14000, 15000, 16000,
459 ],
460 byte_counts: vec![500; 16],
461 jpeg_tables: None,
462 };
463
464 GenericTiffLevelData { level, tile_data }
465 }
466
467 #[test]
468 fn test_get_tile_location() {
469 let level_data = make_mock_level();
470
471 assert_eq!(level_data.get_tile_location(0, 0), Some((1000, 500)));
473
474 assert_eq!(level_data.get_tile_location(1, 0), Some((2000, 500)));
476
477 assert_eq!(level_data.get_tile_location(0, 1), Some((5000, 500)));
479
480 assert_eq!(level_data.get_tile_location(10, 0), None);
482 assert_eq!(level_data.get_tile_location(0, 10), None);
483 }
484
485 #[test]
486 fn test_jpeg_tables_none() {
487 let level_data = make_mock_level();
488 assert!(level_data.jpeg_tables().is_none());
489 }
490
491 #[test]
492 fn test_jpeg_tables_present() {
493 let mut level_data = make_mock_level();
494 level_data.tile_data.jpeg_tables = Some(Bytes::from_static(&[0xFF, 0xD8, 0xFF, 0xD9]));
495
496 let tables = level_data.jpeg_tables();
497 assert!(tables.is_some());
498 assert_eq!(tables.unwrap().len(), 4);
499 }
500}