1mod block_decode;
30pub mod cache;
31pub mod error;
32pub mod filters;
33pub mod header;
34pub mod ifd;
35pub mod io;
36mod pixel;
37pub mod source;
38pub mod strip;
39pub mod tag;
40pub mod tile;
41
42use std::path::Path;
43use std::sync::Arc;
44
45use cache::BlockCache;
46use error::{Error, Result};
47use ndarray::{ArrayD, IxDyn};
48use source::{BytesSource, MmapSource, SharedSource, TiffSource};
49
50pub use error::Error as TiffError;
51pub use header::ByteOrder;
52pub use ifd::{Ifd, RasterLayout};
53pub use tag::{Tag, TagValue};
54pub use tiff_core::constants;
55pub use tiff_core::sample::TiffSample;
56pub use tiff_core::TagType;
57pub use tiff_core::{
58 ColorMap, ColorModel, ExtraSample, InkSet, PhotometricInterpretation, YCbCrPositioning,
59};
60
61#[derive(Debug, Clone, Copy)]
63pub struct OpenOptions {
64 pub block_cache_bytes: usize,
66 pub block_cache_slots: usize,
68}
69
70impl Default for OpenOptions {
71 fn default() -> Self {
72 Self {
73 block_cache_bytes: 64 * 1024 * 1024,
74 block_cache_slots: 257,
75 }
76 }
77}
78
79pub struct TiffFile {
81 source: SharedSource,
82 header: header::TiffHeader,
83 ifds: Vec<ifd::Ifd>,
84 block_cache: Arc<BlockCache>,
85 gdal_structural_metadata: Option<GdalStructuralMetadata>,
86}
87
88#[derive(Debug, Clone, Copy)]
89pub(crate) struct GdalStructuralMetadata {
90 block_leader_size_as_u32: bool,
91 block_trailer_repeats_last_4_bytes: bool,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub(crate) struct Window {
96 pub row_off: usize,
97 pub col_off: usize,
98 pub rows: usize,
99 pub cols: usize,
100}
101
102impl Window {
103 pub(crate) fn is_empty(self) -> bool {
104 self.rows == 0 || self.cols == 0
105 }
106
107 pub(crate) fn row_end(self) -> usize {
108 self.row_off + self.rows
109 }
110
111 pub(crate) fn col_end(self) -> usize {
112 self.col_off + self.cols
113 }
114
115 pub(crate) fn output_len(self, layout: &RasterLayout) -> Result<usize> {
116 self.cols
117 .checked_mul(self.rows)
118 .and_then(|pixels| pixels.checked_mul(layout.pixel_stride_bytes()))
119 .ok_or_else(|| Error::InvalidImageLayout("window size overflows usize".into()))
120 }
121}
122
123impl GdalStructuralMetadata {
124 fn from_prefix(bytes: &[u8]) -> Option<Self> {
125 let text = std::str::from_utf8(bytes).ok()?;
126 if !text.contains("GDAL_STRUCTURAL_METADATA_SIZE=") {
127 return None;
128 }
129
130 Some(Self {
131 block_leader_size_as_u32: text.contains("BLOCK_LEADER=SIZE_AS_UINT4"),
132 block_trailer_repeats_last_4_bytes: text
133 .contains("BLOCK_TRAILER=LAST_4_BYTES_REPEATED"),
134 })
135 }
136
137 pub(crate) fn unwrap_block<'a>(
138 &self,
139 raw: &'a [u8],
140 byte_order: ByteOrder,
141 offset: u64,
142 ) -> Result<&'a [u8]> {
143 if self.block_leader_size_as_u32 {
144 if raw.len() < 4 {
145 return Ok(raw);
146 }
147 let declared_len = match byte_order {
148 ByteOrder::LittleEndian => u32::from_le_bytes(raw[..4].try_into().unwrap()),
149 ByteOrder::BigEndian => u32::from_be_bytes(raw[..4].try_into().unwrap()),
150 } as usize;
151 if let Some(payload_end) = 4usize.checked_add(declared_len) {
152 if payload_end <= raw.len() {
153 if self.block_trailer_repeats_last_4_bytes {
154 let trailer_end = payload_end.checked_add(4).ok_or_else(|| {
155 Error::InvalidImageLayout("GDAL block trailer overflows usize".into())
156 })?;
157 if trailer_end <= raw.len() {
158 let expected = &raw[payload_end - 4..payload_end];
159 let trailer = &raw[payload_end..trailer_end];
160 if expected != trailer {
161 return Err(Error::InvalidImageLayout(format!(
162 "GDAL block trailer mismatch at offset {offset}"
163 )));
164 }
165 }
166 }
167 return Ok(&raw[4..payload_end]);
168 }
169 }
170 }
171
172 if self.block_trailer_repeats_last_4_bytes && raw.len() >= 8 {
173 let split = raw.len() - 4;
174 if raw[split - 4..split] == raw[split..] {
175 return Ok(&raw[..split]);
176 }
177 }
178
179 Ok(raw)
180 }
181}
182
183pub(crate) fn read_gdal_block_payload(
184 source: &dyn TiffSource,
185 metadata: &GdalStructuralMetadata,
186 byte_order: ByteOrder,
187 offset: u64,
188 byte_count: u64,
189) -> Result<Vec<u8>> {
190 let wrapped_extra = 4u64
191 .checked_add(if metadata.block_trailer_repeats_last_4_bytes {
192 4
193 } else {
194 0
195 })
196 .ok_or_else(|| Error::InvalidImageLayout("GDAL block wrapper overflows u64".into()))?;
197
198 let mut candidates = Vec::with_capacity(2);
199 if metadata.block_leader_size_as_u32 && offset >= 4 {
200 candidates.push((
201 offset - 4,
202 byte_count.checked_add(wrapped_extra).ok_or_else(|| {
203 Error::InvalidImageLayout("GDAL wrapped block length overflows u64".into())
204 })?,
205 ));
206 }
207 candidates.push((offset, byte_count));
208
209 let mut fallback: Option<Result<Vec<u8>>> = None;
210 for (candidate_offset, candidate_len) in candidates {
211 let len = usize::try_from(candidate_len).map_err(|_| Error::OffsetOutOfBounds {
212 offset: candidate_offset,
213 length: candidate_len,
214 data_len: source.len(),
215 })?;
216 let raw = match source.read_exact_at(candidate_offset, len) {
217 Ok(raw) => raw,
218 Err(err) => {
219 if fallback.is_none() {
220 fallback = Some(Err(err));
221 }
222 continue;
223 }
224 };
225 match metadata.unwrap_block(&raw, byte_order, candidate_offset) {
226 Ok(payload) => {
227 if candidate_offset != offset
228 && payload.len() == usize::try_from(byte_count).unwrap_or(usize::MAX)
229 {
230 return Ok(payload.to_vec());
231 }
232 fallback = Some(Ok(payload.to_vec()));
233 }
234 Err(err) => {
235 if fallback.is_none() {
236 fallback = Some(Err(err));
237 }
238 }
239 }
240 }
241
242 match fallback {
243 Some(result) => result,
244 None => Ok(Vec::new()),
245 }
246}
247
248const GDAL_STRUCTURAL_METADATA_PREFIX: &str = "GDAL_STRUCTURAL_METADATA_SIZE=";
249
250impl TiffFile {
253 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
255 Self::open_with_options(path, OpenOptions::default())
256 }
257
258 pub fn open_with_options<P: AsRef<Path>>(path: P, options: OpenOptions) -> Result<Self> {
260 let source: SharedSource = Arc::new(MmapSource::open(path.as_ref())?);
261 Self::from_source_with_options(source, options)
262 }
263
264 pub fn from_bytes(data: Vec<u8>) -> Result<Self> {
266 Self::from_bytes_with_options(data, OpenOptions::default())
267 }
268
269 pub fn from_bytes_with_options(data: Vec<u8>, options: OpenOptions) -> Result<Self> {
271 let source: SharedSource = Arc::new(BytesSource::new(data));
272 Self::from_source_with_options(source, options)
273 }
274
275 pub fn from_source(source: SharedSource) -> Result<Self> {
277 Self::from_source_with_options(source, OpenOptions::default())
278 }
279
280 pub fn from_source_with_options(source: SharedSource, options: OpenOptions) -> Result<Self> {
282 let header_len = usize::try_from(source.len().min(16)).unwrap_or(16);
283 let header_bytes = source.read_exact_at(0, header_len)?;
284 let header = header::TiffHeader::parse(&header_bytes)?;
285 let gdal_structural_metadata = parse_gdal_structural_metadata(source.as_ref());
286 let ifds = ifd::parse_ifd_chain(source.as_ref(), &header)?;
287 Ok(Self {
288 source,
289 header,
290 ifds,
291 block_cache: Arc::new(BlockCache::new(
292 options.block_cache_bytes,
293 options.block_cache_slots,
294 )),
295 gdal_structural_metadata,
296 })
297 }
298
299 pub fn byte_order(&self) -> ByteOrder {
301 self.header.byte_order
302 }
303
304 pub fn is_bigtiff(&self) -> bool {
306 self.header.is_bigtiff()
307 }
308
309 pub fn ifd_count(&self) -> usize {
311 self.ifds.len()
312 }
313
314 pub fn ifd(&self, index: usize) -> Result<&Ifd> {
316 self.ifds.get(index).ok_or(Error::IfdNotFound(index))
317 }
318
319 pub fn ifds(&self) -> &[Ifd] {
321 &self.ifds
322 }
323
324 pub fn raw_bytes(&self) -> Option<&[u8]> {
326 self.source.as_slice()
327 }
328
329 pub fn source(&self) -> &dyn TiffSource {
331 self.source.as_ref()
332 }
333
334 pub fn read_ifd_at_offset(&self, offset: u64) -> Result<Ifd> {
336 ifd::parse_ifd_at(self.source.as_ref(), &self.header, offset)
337 }
338
339 pub fn read_image_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
341 let ifd = self.ifd(ifd_index)?;
342 self.read_image_bytes_from_ifd(ifd)
343 }
344
345 pub fn read_image_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
347 self.read_image_sample_bytes_from_ifd(ifd)
348 }
349
350 pub fn read_decoded_image_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
352 let ifd = self.ifd(ifd_index)?;
353 self.read_decoded_image_bytes_from_ifd(ifd)
354 }
355
356 pub fn read_decoded_image_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
359 let layout = ifd.decoded_raster_layout()?;
360 self.decode_window_pixel_bytes(
361 ifd,
362 Window {
363 row_off: 0,
364 col_off: 0,
365 rows: layout.height,
366 cols: layout.width,
367 },
368 )
369 }
370
371 pub fn read_image_sample_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
375 let ifd = self.ifd(ifd_index)?;
376 self.read_image_sample_bytes_from_ifd(ifd)
377 }
378
379 pub fn read_image_sample_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
384 let layout = ifd.raster_layout()?;
385 self.decode_window_sample_bytes(
386 ifd,
387 Window {
388 row_off: 0,
389 col_off: 0,
390 rows: layout.height,
391 cols: layout.width,
392 },
393 )
394 }
395
396 pub fn read_window_bytes(
399 &self,
400 ifd_index: usize,
401 row_off: usize,
402 col_off: usize,
403 rows: usize,
404 cols: usize,
405 ) -> Result<Vec<u8>> {
406 let ifd = self.ifd(ifd_index)?;
407 self.read_window_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
408 }
409
410 pub fn read_decoded_window_bytes(
413 &self,
414 ifd_index: usize,
415 row_off: usize,
416 col_off: usize,
417 rows: usize,
418 cols: usize,
419 ) -> Result<Vec<u8>> {
420 let ifd = self.ifd(ifd_index)?;
421 self.read_decoded_window_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
422 }
423
424 pub fn read_window_sample_bytes(
429 &self,
430 ifd_index: usize,
431 row_off: usize,
432 col_off: usize,
433 rows: usize,
434 cols: usize,
435 ) -> Result<Vec<u8>> {
436 let ifd = self.ifd(ifd_index)?;
437 self.read_window_sample_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
438 }
439
440 pub fn read_window_bytes_from_ifd(
443 &self,
444 ifd: &Ifd,
445 row_off: usize,
446 col_off: usize,
447 rows: usize,
448 cols: usize,
449 ) -> Result<Vec<u8>> {
450 self.read_window_sample_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
451 }
452
453 pub fn read_decoded_window_bytes_from_ifd(
456 &self,
457 ifd: &Ifd,
458 row_off: usize,
459 col_off: usize,
460 rows: usize,
461 cols: usize,
462 ) -> Result<Vec<u8>> {
463 let layout = ifd.decoded_raster_layout()?;
464 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
465 self.decode_window_pixel_bytes(ifd, window)
466 }
467
468 pub fn read_window_sample_bytes_from_ifd(
473 &self,
474 ifd: &Ifd,
475 row_off: usize,
476 col_off: usize,
477 rows: usize,
478 cols: usize,
479 ) -> Result<Vec<u8>> {
480 let layout = ifd.raster_layout()?;
481 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
482 self.decode_window_sample_bytes(ifd, window)
483 }
484
485 fn decode_window_sample_bytes(&self, ifd: &Ifd, window: Window) -> Result<Vec<u8>> {
486 if window.is_empty() {
487 return Ok(Vec::new());
488 }
489
490 if ifd.is_tiled() {
491 tile::read_window(
492 self.source.as_ref(),
493 ifd,
494 self.byte_order(),
495 &self.block_cache,
496 window,
497 self.gdal_structural_metadata.as_ref(),
498 )
499 } else {
500 strip::read_window(
501 self.source.as_ref(),
502 ifd,
503 self.byte_order(),
504 &self.block_cache,
505 window,
506 self.gdal_structural_metadata.as_ref(),
507 )
508 }
509 }
510
511 fn decode_window_pixel_bytes(&self, ifd: &Ifd, window: Window) -> Result<Vec<u8>> {
512 let storage_layout = ifd.raster_layout()?;
513 let sample_bytes = self.decode_window_sample_bytes(ifd, window)?;
514 let (_, pixels) = pixel::decode_pixels(
515 ifd,
516 &storage_layout,
517 window.cols,
518 window.rows,
519 &sample_bytes,
520 )?;
521 Ok(pixels)
522 }
523
524 pub fn read_window<T: TiffSample>(
529 &self,
530 ifd_index: usize,
531 row_off: usize,
532 col_off: usize,
533 rows: usize,
534 cols: usize,
535 ) -> Result<ArrayD<T>> {
536 let ifd = self.ifd(ifd_index)?;
537 self.read_window_from_ifd(ifd, row_off, col_off, rows, cols)
538 }
539
540 pub fn read_window_from_ifd<T: TiffSample>(
543 &self,
544 ifd: &Ifd,
545 row_off: usize,
546 col_off: usize,
547 rows: usize,
548 cols: usize,
549 ) -> Result<ArrayD<T>> {
550 self.read_window_samples_from_ifd(ifd, row_off, col_off, rows, cols)
551 }
552
553 pub fn read_decoded_window<T: TiffSample>(
558 &self,
559 ifd_index: usize,
560 row_off: usize,
561 col_off: usize,
562 rows: usize,
563 cols: usize,
564 ) -> Result<ArrayD<T>> {
565 let ifd = self.ifd(ifd_index)?;
566 self.read_decoded_window_from_ifd(ifd, row_off, col_off, rows, cols)
567 }
568
569 pub fn read_decoded_window_from_ifd<T: TiffSample>(
572 &self,
573 ifd: &Ifd,
574 row_off: usize,
575 col_off: usize,
576 rows: usize,
577 cols: usize,
578 ) -> Result<ArrayD<T>> {
579 let layout = ifd.decoded_raster_layout()?;
580 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
581 if !T::matches_layout(&layout) {
582 return Err(Error::TypeMismatch {
583 expected: T::type_name(),
584 actual: format!(
585 "sample_format={} bits_per_sample={}",
586 layout.sample_format, layout.bits_per_sample
587 ),
588 });
589 }
590
591 let decoded = self.decode_window_pixel_bytes(ifd, window)?;
592 let values = T::decode_many(&decoded);
593 let shape = if layout.samples_per_pixel == 1 {
594 vec![window.rows, window.cols]
595 } else {
596 vec![window.rows, window.cols, layout.samples_per_pixel]
597 };
598 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|e| {
599 Error::InvalidImageLayout(format!("failed to build ndarray from decoded raster: {e}"))
600 })
601 }
602
603 pub fn read_window_samples<T: TiffSample>(
608 &self,
609 ifd_index: usize,
610 row_off: usize,
611 col_off: usize,
612 rows: usize,
613 cols: usize,
614 ) -> Result<ArrayD<T>> {
615 let ifd = self.ifd(ifd_index)?;
616 self.read_window_samples_from_ifd(ifd, row_off, col_off, rows, cols)
617 }
618
619 pub fn read_window_samples_from_ifd<T: TiffSample>(
622 &self,
623 ifd: &Ifd,
624 row_off: usize,
625 col_off: usize,
626 rows: usize,
627 cols: usize,
628 ) -> Result<ArrayD<T>> {
629 let layout = ifd.raster_layout()?;
630 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
631 if !T::matches_layout(&layout) {
632 return Err(Error::TypeMismatch {
633 expected: T::type_name(),
634 actual: format!(
635 "sample_format={} bits_per_sample={}",
636 layout.sample_format, layout.bits_per_sample
637 ),
638 });
639 }
640
641 let decoded = self.decode_window_sample_bytes(ifd, window)?;
642 let values = T::decode_many(&decoded);
643 let shape = if layout.samples_per_pixel == 1 {
644 vec![window.rows, window.cols]
645 } else {
646 vec![window.rows, window.cols, layout.samples_per_pixel]
647 };
648 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|e| {
649 Error::InvalidImageLayout(format!("failed to build ndarray from storage raster: {e}"))
650 })
651 }
652
653 pub fn read_image<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
658 let ifd = self.ifd(ifd_index)?;
659 self.read_image_from_ifd(ifd)
660 }
661
662 pub fn read_image_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
664 self.read_image_samples_from_ifd(ifd)
665 }
666
667 pub fn read_decoded_image<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
673 let ifd = self.ifd(ifd_index)?;
674 self.read_decoded_image_from_ifd(ifd)
675 }
676
677 pub fn read_decoded_image_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
679 let layout = ifd.decoded_raster_layout()?;
680 if !T::matches_layout(&layout) {
681 return Err(Error::TypeMismatch {
682 expected: T::type_name(),
683 actual: format!(
684 "sample_format={} bits_per_sample={}",
685 layout.sample_format, layout.bits_per_sample
686 ),
687 });
688 }
689
690 self.read_decoded_window_from_ifd(ifd, 0, 0, layout.height, layout.width)
691 }
692
693 pub fn read_image_samples<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
697 let ifd = self.ifd(ifd_index)?;
698 self.read_image_samples_from_ifd(ifd)
699 }
700
701 pub fn read_image_samples_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
705 let layout = ifd.raster_layout()?;
706 if !T::matches_layout(&layout) {
707 return Err(Error::TypeMismatch {
708 expected: T::type_name(),
709 actual: format!(
710 "sample_format={} bits_per_sample={}",
711 layout.sample_format, layout.bits_per_sample
712 ),
713 });
714 }
715
716 self.read_window_samples_from_ifd(ifd, 0, 0, layout.height, layout.width)
717 }
718}
719
720fn validate_window(
721 layout: &RasterLayout,
722 row_off: usize,
723 col_off: usize,
724 rows: usize,
725 cols: usize,
726) -> Result<Window> {
727 let row_end = row_off
728 .checked_add(rows)
729 .ok_or_else(|| Error::InvalidImageLayout("window row range overflows usize".into()))?;
730 let col_end = col_off
731 .checked_add(cols)
732 .ok_or_else(|| Error::InvalidImageLayout("window column range overflows usize".into()))?;
733 if row_end > layout.height || col_end > layout.width {
734 return Err(Error::InvalidImageLayout(format!(
735 "window [{row_off}..{row_end}, {col_off}..{col_end}) exceeds raster bounds {}x{}",
736 layout.height, layout.width
737 )));
738 }
739 Ok(Window {
740 row_off,
741 col_off,
742 rows,
743 cols,
744 })
745}
746
747fn parse_gdal_structural_metadata(source: &dyn TiffSource) -> Option<GdalStructuralMetadata> {
748 let available_len = usize::try_from(source.len().checked_sub(8)?).ok()?;
749 if available_len == 0 {
750 return None;
751 }
752
753 let probe_len = available_len.min(64);
754 let probe = source.read_exact_at(8, probe_len).ok()?;
755 let total_len = parse_gdal_structural_metadata_len(&probe)?;
756 if total_len == 0 || total_len > available_len {
757 return None;
758 }
759
760 let bytes = source.read_exact_at(8, total_len).ok()?;
761 GdalStructuralMetadata::from_prefix(&bytes)
762}
763
764fn parse_gdal_structural_metadata_len(bytes: &[u8]) -> Option<usize> {
765 let text = std::str::from_utf8(bytes).ok()?;
766 let newline_index = text.find('\n')?;
767 let header = &text[..newline_index];
768 let value = header.strip_prefix(GDAL_STRUCTURAL_METADATA_PREFIX)?;
769 let digits: String = value.chars().take_while(|ch| ch.is_ascii_digit()).collect();
770 if digits.is_empty() {
771 return None;
772 }
773 let payload_len: usize = digits.parse().ok()?;
774 newline_index.checked_add(1)?.checked_add(payload_len)
775}
776
777#[cfg(test)]
778mod tests {
779 use std::collections::BTreeMap;
780 use std::sync::atomic::{AtomicUsize, Ordering};
781 use std::sync::Arc;
782
783 use super::{
784 parse_gdal_structural_metadata, parse_gdal_structural_metadata_len, GdalStructuralMetadata,
785 TiffFile, GDAL_STRUCTURAL_METADATA_PREFIX,
786 };
787 use crate::source::{BytesSource, TiffSource};
788 use flate2::{write::ZlibEncoder, Compression as FlateCompression};
789
790 fn le_u16(value: u16) -> [u8; 2] {
791 value.to_le_bytes()
792 }
793
794 fn le_u32(value: u32) -> [u8; 4] {
795 value.to_le_bytes()
796 }
797
798 fn inline_short(value: u16) -> Vec<u8> {
799 let mut bytes = [0u8; 4];
800 bytes[..2].copy_from_slice(&le_u16(value));
801 bytes.to_vec()
802 }
803
804 fn build_stripped_tiff(
805 width: u32,
806 height: u32,
807 image_data: &[u8],
808 overrides: &[(u16, u16, u32, Vec<u8>)],
809 ) -> Vec<u8> {
810 let mut entries = BTreeMap::new();
811 entries.insert(256, (4, 1, le_u32(width).to_vec()));
812 entries.insert(257, (4, 1, le_u32(height).to_vec()));
813 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
814 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
815 entries.insert(273, (4, 1, Vec::new()));
816 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
817 entries.insert(278, (4, 1, le_u32(height).to_vec()));
818 entries.insert(279, (4, 1, le_u32(image_data.len() as u32).to_vec()));
819 for &(tag, ty, count, ref value) in overrides {
820 entries.insert(tag, (ty, count, value.clone()));
821 }
822
823 let ifd_offset = 8u32;
824 let ifd_size = 2 + entries.len() * 12 + 4;
825 let mut next_data_offset = ifd_offset as usize + ifd_size;
826 let image_offset = next_data_offset as u32;
827 next_data_offset += image_data.len();
828
829 let mut data = Vec::with_capacity(next_data_offset);
830 data.extend_from_slice(b"II");
831 data.extend_from_slice(&le_u16(42));
832 data.extend_from_slice(&le_u32(ifd_offset));
833 data.extend_from_slice(&le_u16(entries.len() as u16));
834
835 let mut deferred = Vec::new();
836 for (tag, (ty, count, value)) in entries {
837 data.extend_from_slice(&le_u16(tag));
838 data.extend_from_slice(&le_u16(ty));
839 data.extend_from_slice(&le_u32(count));
840 if tag == 273 {
841 data.extend_from_slice(&le_u32(image_offset));
842 } else if value.len() <= 4 {
843 let mut inline = [0u8; 4];
844 inline[..value.len()].copy_from_slice(&value);
845 data.extend_from_slice(&inline);
846 } else {
847 let offset = next_data_offset as u32;
848 data.extend_from_slice(&le_u32(offset));
849 next_data_offset += value.len();
850 deferred.push(value);
851 }
852 }
853 data.extend_from_slice(&le_u32(0));
854 data.extend_from_slice(image_data);
855 for value in deferred {
856 data.extend_from_slice(&value);
857 }
858 data
859 }
860
861 #[allow(clippy::too_many_arguments)]
862 fn build_lerc2_header_v2(
863 width: u32,
864 height: u32,
865 valid_pixel_count: u32,
866 image_type: i32,
867 max_z_error: f64,
868 z_min: f64,
869 z_max: f64,
870 payload_len: usize,
871 ) -> Vec<u8> {
872 let blob_size = 58 + 4 + payload_len;
873 let mut bytes = Vec::with_capacity(blob_size);
874 bytes.extend_from_slice(b"Lerc2 ");
875 bytes.extend_from_slice(&2i32.to_le_bytes());
876 bytes.extend_from_slice(&height.to_le_bytes());
877 bytes.extend_from_slice(&width.to_le_bytes());
878 bytes.extend_from_slice(&valid_pixel_count.to_le_bytes());
879 bytes.extend_from_slice(&8i32.to_le_bytes());
880 bytes.extend_from_slice(&(blob_size as i32).to_le_bytes());
881 bytes.extend_from_slice(&image_type.to_le_bytes());
882 bytes.extend_from_slice(&max_z_error.to_le_bytes());
883 bytes.extend_from_slice(&z_min.to_le_bytes());
884 bytes.extend_from_slice(&z_max.to_le_bytes());
885 bytes
886 }
887
888 #[allow(clippy::too_many_arguments)]
889 fn build_lerc2_header_v4(
890 width: u32,
891 height: u32,
892 depth: u32,
893 valid_pixel_count: u32,
894 image_type: i32,
895 max_z_error: f64,
896 z_min: f64,
897 z_max: f64,
898 payload_len: usize,
899 ) -> Vec<u8> {
900 let blob_size = 66 + 4 + payload_len;
901 let mut bytes = Vec::with_capacity(blob_size);
902 bytes.extend_from_slice(b"Lerc2 ");
903 bytes.extend_from_slice(&4i32.to_le_bytes());
904 bytes.extend_from_slice(&0u32.to_le_bytes());
905 bytes.extend_from_slice(&height.to_le_bytes());
906 bytes.extend_from_slice(&width.to_le_bytes());
907 bytes.extend_from_slice(&depth.to_le_bytes());
908 bytes.extend_from_slice(&valid_pixel_count.to_le_bytes());
909 bytes.extend_from_slice(&8i32.to_le_bytes());
910 bytes.extend_from_slice(&(blob_size as i32).to_le_bytes());
911 bytes.extend_from_slice(&image_type.to_le_bytes());
912 bytes.extend_from_slice(&max_z_error.to_le_bytes());
913 bytes.extend_from_slice(&z_min.to_le_bytes());
914 bytes.extend_from_slice(&z_max.to_le_bytes());
915 bytes
916 }
917
918 fn finalize_lerc2_v4_with_checksum(mut bytes: Vec<u8>) -> Vec<u8> {
919 let blob_size = bytes.len() as i32;
920 bytes[34..38].copy_from_slice(&blob_size.to_le_bytes());
921 let checksum = fletcher32(&bytes[14..blob_size as usize]);
922 bytes[10..14].copy_from_slice(&checksum.to_le_bytes());
923 bytes
924 }
925
926 fn fletcher32(bytes: &[u8]) -> u32 {
927 let mut sum1 = 0xffffu32;
928 let mut sum2 = 0xffffu32;
929 let mut words = bytes.len() / 2;
930 let mut index = 0usize;
931
932 while words > 0 {
933 let chunk = words.min(359);
934 words -= chunk;
935 for _ in 0..chunk {
936 sum1 += (bytes[index] as u32) << 8;
937 index += 1;
938 sum2 += sum1 + bytes[index] as u32;
939 sum1 += bytes[index] as u32;
940 index += 1;
941 }
942 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
943 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
944 }
945
946 if bytes.len() & 1 != 0 {
947 sum1 += (bytes[index] as u32) << 8;
948 sum2 += sum1;
949 }
950
951 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
952 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
953 (sum2 << 16) | (sum1 & 0xffff)
954 }
955
956 fn encode_mask_rle(mask: &[u8]) -> Vec<u8> {
957 let bitset_len = mask.len().div_ceil(8);
958 let mut bitset = vec![0u8; bitset_len];
959 for (index, &value) in mask.iter().enumerate() {
960 if value != 0 {
961 bitset[index >> 3] |= 1 << (7 - (index & 7));
962 }
963 }
964
965 let mut encoded = Vec::with_capacity(bitset_len + 4);
966 encoded.extend_from_slice(&(bitset_len as i16).to_le_bytes());
967 encoded.extend_from_slice(&bitset);
968 encoded.extend_from_slice(&i16::MIN.to_le_bytes());
969 encoded
970 }
971
972 fn build_lerc_tiff(
973 width: u32,
974 height: u32,
975 image_data: &[u8],
976 bits_per_sample: u16,
977 sample_format: u16,
978 samples_per_pixel: u16,
979 lerc_parameters: Option<[u32; 2]>,
980 ) -> Vec<u8> {
981 let mut overrides = vec![
982 (258u16, 3u16, 1u32, inline_short(bits_per_sample)),
983 (259u16, 3u16, 1u32, inline_short(34887)),
984 (277u16, 3u16, 1u32, inline_short(samples_per_pixel)),
985 (279u16, 4u16, 1u32, le_u32(image_data.len() as u32).to_vec()),
986 ];
987 if sample_format != 1 {
988 overrides.push((339u16, 3u16, 1u32, inline_short(sample_format)));
989 }
990 if let Some([version, additional_compression]) = lerc_parameters {
991 overrides.push((
992 50674u16,
993 4u16,
994 2u32,
995 [version, additional_compression]
996 .into_iter()
997 .flat_map(le_u32)
998 .collect(),
999 ));
1000 }
1001 build_stripped_tiff(width, height, image_data, &overrides)
1002 }
1003
1004 fn build_tiled_tiff(
1005 width: u32,
1006 height: u32,
1007 tile_width: u32,
1008 tile_height: u32,
1009 tiles: &[&[u8]],
1010 ) -> Vec<u8> {
1011 let mut entries = BTreeMap::new();
1012 entries.insert(256, (4, 1, le_u32(width).to_vec()));
1013 entries.insert(257, (4, 1, le_u32(height).to_vec()));
1014 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
1015 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
1016 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
1017 entries.insert(322, (4, 1, le_u32(tile_width).to_vec()));
1018 entries.insert(323, (4, 1, le_u32(tile_height).to_vec()));
1019 entries.insert(
1020 325,
1021 (
1022 4,
1023 tiles.len() as u32,
1024 tiles
1025 .iter()
1026 .flat_map(|tile| le_u32(tile.len() as u32))
1027 .collect(),
1028 ),
1029 );
1030
1031 let ifd_offset = 8u32;
1032 let ifd_size = 2 + (entries.len() + 1) * 12 + 4;
1033 let mut tile_data_offset = ifd_offset as usize + ifd_size;
1034 let tile_offsets: Vec<u32> = tiles
1035 .iter()
1036 .map(|tile| {
1037 let offset = tile_data_offset as u32;
1038 tile_data_offset += tile.len();
1039 offset
1040 })
1041 .collect();
1042 entries.insert(
1043 324,
1044 (
1045 4,
1046 tile_offsets.len() as u32,
1047 tile_offsets
1048 .iter()
1049 .flat_map(|offset| le_u32(*offset))
1050 .collect(),
1051 ),
1052 );
1053
1054 let mut next_data_offset = tile_data_offset;
1055 let mut data = Vec::with_capacity(next_data_offset);
1056 data.extend_from_slice(b"II");
1057 data.extend_from_slice(&le_u16(42));
1058 data.extend_from_slice(&le_u32(ifd_offset));
1059 data.extend_from_slice(&le_u16(entries.len() as u16));
1060
1061 let mut deferred = Vec::new();
1062 for (tag, (ty, count, value)) in entries {
1063 data.extend_from_slice(&le_u16(tag));
1064 data.extend_from_slice(&le_u16(ty));
1065 data.extend_from_slice(&le_u32(count));
1066 if value.len() <= 4 {
1067 let mut inline = [0u8; 4];
1068 inline[..value.len()].copy_from_slice(&value);
1069 data.extend_from_slice(&inline);
1070 } else {
1071 let offset = next_data_offset as u32;
1072 data.extend_from_slice(&le_u32(offset));
1073 next_data_offset += value.len();
1074 deferred.push(value);
1075 }
1076 }
1077 data.extend_from_slice(&le_u32(0));
1078 for tile in tiles {
1079 data.extend_from_slice(tile);
1080 }
1081 for value in deferred {
1082 data.extend_from_slice(&value);
1083 }
1084 data
1085 }
1086
1087 fn build_multi_strip_tiff(width: u32, rows: &[&[u8]]) -> Vec<u8> {
1088 let mut entries = BTreeMap::new();
1089 entries.insert(256, (4, 1, le_u32(width).to_vec()));
1090 entries.insert(257, (4, 1, le_u32(rows.len() as u32).to_vec()));
1091 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
1092 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
1093 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
1094 entries.insert(278, (4, 1, le_u32(1).to_vec()));
1095 entries.insert(
1096 279,
1097 (
1098 4,
1099 rows.len() as u32,
1100 rows.iter()
1101 .flat_map(|row| le_u32(row.len() as u32))
1102 .collect(),
1103 ),
1104 );
1105
1106 let ifd_offset = 8u32;
1107 let ifd_size = 2 + (entries.len() + 1) * 12 + 4;
1108 let mut strip_data_offset = ifd_offset as usize + ifd_size;
1109 let strip_offsets: Vec<u32> = rows
1110 .iter()
1111 .map(|row| {
1112 let offset = strip_data_offset as u32;
1113 strip_data_offset += row.len();
1114 offset
1115 })
1116 .collect();
1117 entries.insert(
1118 273,
1119 (
1120 4,
1121 strip_offsets.len() as u32,
1122 strip_offsets
1123 .iter()
1124 .flat_map(|offset| le_u32(*offset))
1125 .collect(),
1126 ),
1127 );
1128
1129 let mut next_data_offset = strip_data_offset;
1130 let mut data = Vec::with_capacity(next_data_offset);
1131 data.extend_from_slice(b"II");
1132 data.extend_from_slice(&le_u16(42));
1133 data.extend_from_slice(&le_u32(ifd_offset));
1134 data.extend_from_slice(&le_u16(entries.len() as u16));
1135
1136 let mut deferred = Vec::new();
1137 for (tag, (ty, count, value)) in entries {
1138 data.extend_from_slice(&le_u16(tag));
1139 data.extend_from_slice(&le_u16(ty));
1140 data.extend_from_slice(&le_u32(count));
1141 if value.len() <= 4 {
1142 let mut inline = [0u8; 4];
1143 inline[..value.len()].copy_from_slice(&value);
1144 data.extend_from_slice(&inline);
1145 } else {
1146 let offset = next_data_offset as u32;
1147 data.extend_from_slice(&le_u32(offset));
1148 next_data_offset += value.len();
1149 deferred.push(value);
1150 }
1151 }
1152 data.extend_from_slice(&le_u32(0));
1153 for row in rows {
1154 data.extend_from_slice(row);
1155 }
1156 for value in deferred {
1157 data.extend_from_slice(&value);
1158 }
1159 data
1160 }
1161
1162 struct CountingSource {
1163 bytes: Vec<u8>,
1164 reads: AtomicUsize,
1165 }
1166
1167 impl CountingSource {
1168 fn new(bytes: Vec<u8>) -> Self {
1169 Self {
1170 bytes,
1171 reads: AtomicUsize::new(0),
1172 }
1173 }
1174
1175 fn reset_reads(&self) {
1176 self.reads.store(0, Ordering::SeqCst);
1177 }
1178
1179 fn reads(&self) -> usize {
1180 self.reads.load(Ordering::SeqCst)
1181 }
1182 }
1183
1184 impl TiffSource for CountingSource {
1185 fn len(&self) -> u64 {
1186 self.bytes.len() as u64
1187 }
1188
1189 fn read_exact_at(&self, offset: u64, len: usize) -> crate::error::Result<Vec<u8>> {
1190 self.reads.fetch_add(1, Ordering::SeqCst);
1191 let start =
1192 usize::try_from(offset).map_err(|_| crate::error::Error::OffsetOutOfBounds {
1193 offset,
1194 length: len as u64,
1195 data_len: self.len(),
1196 })?;
1197 let end = start
1198 .checked_add(len)
1199 .ok_or(crate::error::Error::OffsetOutOfBounds {
1200 offset,
1201 length: len as u64,
1202 data_len: self.len(),
1203 })?;
1204 if end > self.bytes.len() {
1205 return Err(crate::error::Error::OffsetOutOfBounds {
1206 offset,
1207 length: len as u64,
1208 data_len: self.len(),
1209 });
1210 }
1211 Ok(self.bytes[start..end].to_vec())
1212 }
1213 }
1214
1215 #[test]
1216 fn reads_stripped_u8_image() {
1217 let data = build_stripped_tiff(2, 2, &[1, 2, 3, 4], &[]);
1218 let file = TiffFile::from_bytes(data).unwrap();
1219 let image = file.read_image::<u8>(0).unwrap();
1220 assert_eq!(image.shape(), &[2, 2]);
1221 let (values, offset) = image.into_raw_vec_and_offset();
1222 assert_eq!(offset, Some(0));
1223 assert_eq!(values, vec![1, 2, 3, 4]);
1224 }
1225
1226 #[test]
1227 fn keeps_subbyte_palette_reads_raw_and_offers_explicit_decoded_pixels() {
1228 let mut color_map = Vec::new();
1229 color_map.extend((0u16..16).map(|value| value * 17 * 257));
1230 color_map.extend((0u16..16).map(|value| (15 - value) * 17 * 257));
1231 color_map.extend((0u16..16).map(|value| value * 8 * 257));
1232 let data = build_stripped_tiff(
1233 4,
1234 1,
1235 &[0x01, 0x23],
1236 &[
1237 (258, 3, 1, inline_short(4)),
1238 (262, 3, 1, inline_short(3)),
1239 (
1240 320,
1241 3,
1242 color_map.len() as u32,
1243 color_map.iter().flat_map(|value| le_u16(*value)).collect(),
1244 ),
1245 ],
1246 );
1247 let file = TiffFile::from_bytes(data).unwrap();
1248
1249 let image = file.read_image::<u8>(0).unwrap();
1250 assert_eq!(image.shape(), &[1, 4]);
1251 let (values, offset) = image.into_raw_vec_and_offset();
1252 assert_eq!(offset, Some(0));
1253 assert_eq!(values, vec![0, 1, 2, 3]);
1254
1255 let image = file.read_decoded_image::<u8>(0).unwrap();
1256 assert_eq!(image.shape(), &[1, 4, 3]);
1257 let (values, offset) = image.into_raw_vec_and_offset();
1258 assert_eq!(offset, Some(0));
1259 assert_eq!(
1260 values,
1261 vec![
1262 0, 255, 0, 17, 238, 8, 34, 221, 16, 51, 204, 24
1266 ]
1267 );
1268
1269 let sample_bytes = file.read_image_sample_bytes(0).unwrap();
1270 assert_eq!(sample_bytes, vec![0, 1, 2, 3]);
1271 }
1272
1273 #[test]
1274 fn keeps_subsampled_ycbcr_reads_raw_and_offers_explicit_decoded_pixels() {
1275 let data = build_stripped_tiff(
1276 2,
1277 2,
1278 &[10u8, 20, 30, 40, 128, 128],
1279 &[
1280 (
1281 258,
1282 3,
1283 3,
1284 [8u16, 8, 8].into_iter().flat_map(le_u16).collect(),
1285 ),
1286 (262, 3, 1, inline_short(6)),
1287 (277, 3, 1, inline_short(3)),
1288 (530, 3, 2, [2u16, 2].into_iter().flat_map(le_u16).collect()),
1289 ],
1290 );
1291 let file = TiffFile::from_bytes(data).unwrap();
1292
1293 let image = file.read_image::<u8>(0).unwrap();
1294 assert_eq!(image.shape(), &[2, 2, 3]);
1295 let (values, offset) = image.into_raw_vec_and_offset();
1296 assert_eq!(offset, Some(0));
1297 assert_eq!(
1298 values,
1299 vec![
1300 10, 128, 128, 20, 128, 128, 30, 128, 128, 40, 128, 128
1304 ]
1305 );
1306
1307 let image = file.read_decoded_image::<u8>(0).unwrap();
1308 assert_eq!(image.shape(), &[2, 2, 3]);
1309 let (rgb, offset) = image.into_raw_vec_and_offset();
1310 assert_eq!(offset, Some(0));
1311 assert_eq!(
1312 rgb,
1313 vec![
1314 10, 10, 10, 20, 20, 20, 30, 30, 30, 40, 40, 40
1318 ]
1319 );
1320
1321 let samples = file.read_image_samples::<u8>(0).unwrap();
1322 let (values, offset) = samples.into_raw_vec_and_offset();
1323 assert_eq!(offset, Some(0));
1324 assert_eq!(
1325 values,
1326 vec![
1327 10, 128, 128, 20, 128, 128, 30, 128, 128, 40, 128, 128
1331 ]
1332 );
1333 }
1334
1335 #[test]
1336 fn reads_horizontal_predictor_u16_strip() {
1337 let encoded = [1, 0, 1, 0, 2, 0];
1338 let data = build_stripped_tiff(
1339 3,
1340 1,
1341 &encoded,
1342 &[
1343 (258, 3, 1, [16, 0, 0, 0].to_vec()),
1344 (317, 3, 1, [2, 0, 0, 0].to_vec()),
1345 ],
1346 );
1347 let file = TiffFile::from_bytes(data).unwrap();
1348 let image = file.read_image::<u16>(0).unwrap();
1349 assert_eq!(image.shape(), &[1, 3]);
1350 let (values, offset) = image.into_raw_vec_and_offset();
1351 assert_eq!(offset, Some(0));
1352 assert_eq!(values, vec![1, 2, 4]);
1353 }
1354
1355 #[test]
1356 fn reads_lerc_f32_strip() {
1357 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1358 blob.extend_from_slice(&0u32.to_le_bytes());
1359 blob.push(1);
1360 for value in [1.0f32, 2.0, 3.0, 4.0] {
1361 blob.extend_from_slice(&value.to_le_bytes());
1362 }
1363
1364 let data = build_lerc_tiff(2, 2, &blob, 32, 3, 1, None);
1365 let file = TiffFile::from_bytes(data).unwrap();
1366 let image = file.read_image::<f32>(0).unwrap();
1367 let (values, offset) = image.into_raw_vec_and_offset();
1368 assert_eq!(offset, Some(0));
1369 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1370 }
1371
1372 #[test]
1373 fn reads_lerc_masked_f32_strip_as_nan() {
1374 let mask = [1u8, 0, 1, 1];
1375 let encoded_mask = encode_mask_rle(&mask);
1376 let mut blob =
1377 build_lerc2_header_v2(2, 2, 3, 6, 0.0, 1.0, 4.0, encoded_mask.len() + 1 + 12);
1378 blob.extend_from_slice(&(encoded_mask.len() as u32).to_le_bytes());
1379 blob.extend_from_slice(&encoded_mask);
1380 blob.push(1);
1381 for value in [1.0f32, 3.0, 4.0] {
1382 blob.extend_from_slice(&value.to_le_bytes());
1383 }
1384
1385 let data = build_lerc_tiff(2, 2, &blob, 32, 3, 1, None);
1386 let file = TiffFile::from_bytes(data).unwrap();
1387 let image = file.read_image::<f32>(0).unwrap();
1388 let (values, offset) = image.into_raw_vec_and_offset();
1389 assert_eq!(offset, Some(0));
1390 assert_eq!(values[0], 1.0);
1391 assert!(values[1].is_nan());
1392 assert_eq!(values[2], 3.0);
1393 assert_eq!(values[3], 4.0);
1394 }
1395
1396 #[test]
1397 fn reads_lerc_chunky_rgb_band_set_strip() {
1398 let mut red = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 1.0, 1.0, 0);
1399 red.extend_from_slice(&0u32.to_le_bytes());
1400 let mut green = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 2.0, 2.0, 0);
1401 green.extend_from_slice(&0u32.to_le_bytes());
1402 let mut blue = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 3.0, 3.0, 0);
1403 blue.extend_from_slice(&0u32.to_le_bytes());
1404
1405 let mut blob = red;
1406 blob.extend_from_slice(&green);
1407 blob.extend_from_slice(&blue);
1408
1409 let data = build_lerc_tiff(2, 1, &blob, 8, 1, 3, None);
1410 let file = TiffFile::from_bytes(data).unwrap();
1411 let image = file.read_image::<u8>(0).unwrap();
1412 assert_eq!(image.shape(), &[1, 2, 3]);
1413 let (values, offset) = image.into_raw_vec_and_offset();
1414 assert_eq!(offset, Some(0));
1415 assert_eq!(values, vec![1, 2, 3, 1, 2, 3]);
1416 }
1417
1418 #[test]
1419 fn reads_lerc_chunky_rgb_depth_blob_strip() {
1420 let mut blob = build_lerc2_header_v4(2, 1, 3, 2, 1, 0.0, 1.0, 6.0, 6 + 6 + 1 + 6);
1421 blob.extend_from_slice(&0u32.to_le_bytes());
1422 for value in [1u8, 2, 3] {
1423 blob.extend_from_slice(&value.to_le_bytes());
1424 }
1425 for value in [4u8, 5, 6] {
1426 blob.extend_from_slice(&value.to_le_bytes());
1427 }
1428 blob.push(1);
1429 blob.extend_from_slice(&[1, 2, 3, 4, 5, 6]);
1430 let blob = finalize_lerc2_v4_with_checksum(blob);
1431
1432 let data = build_lerc_tiff(2, 1, &blob, 8, 1, 3, Some([4, 0]));
1433 let file = TiffFile::from_bytes(data).unwrap();
1434 let image = file.read_image::<u8>(0).unwrap();
1435 assert_eq!(image.shape(), &[1, 2, 3]);
1436 let (values, offset) = image.into_raw_vec_and_offset();
1437 assert_eq!(offset, Some(0));
1438 assert_eq!(values, vec![1, 2, 3, 4, 5, 6]);
1439 }
1440
1441 #[test]
1442 fn reads_lerc_deflate_f32_strip() {
1443 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1444 blob.extend_from_slice(&0u32.to_le_bytes());
1445 blob.push(1);
1446 for value in [1.0f32, 2.0, 3.0, 4.0] {
1447 blob.extend_from_slice(&value.to_le_bytes());
1448 }
1449
1450 let mut encoder = ZlibEncoder::new(Vec::new(), FlateCompression::default());
1451 std::io::Write::write_all(&mut encoder, &blob).unwrap();
1452 let compressed = encoder.finish().unwrap();
1453
1454 let data = build_lerc_tiff(2, 2, &compressed, 32, 3, 1, Some([2, 1]));
1455 let file = TiffFile::from_bytes(data).unwrap();
1456 let image = file.read_image::<f32>(0).unwrap();
1457 let (values, offset) = image.into_raw_vec_and_offset();
1458 assert_eq!(offset, Some(0));
1459 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1460 }
1461
1462 #[cfg(feature = "zstd")]
1463 #[test]
1464 fn reads_lerc_zstd_f32_strip() {
1465 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1466 blob.extend_from_slice(&0u32.to_le_bytes());
1467 blob.push(1);
1468 for value in [1.0f32, 2.0, 3.0, 4.0] {
1469 blob.extend_from_slice(&value.to_le_bytes());
1470 }
1471
1472 let compressed = zstd::stream::encode_all(std::io::Cursor::new(blob), 0).unwrap();
1473 let data = build_lerc_tiff(2, 2, &compressed, 32, 3, 1, Some([2, 2]));
1474 let file = TiffFile::from_bytes(data).unwrap();
1475 let image = file.read_image::<f32>(0).unwrap();
1476 let (values, offset) = image.into_raw_vec_and_offset();
1477 assert_eq!(offset, Some(0));
1478 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1479 }
1480
1481 #[test]
1482 fn reads_stripped_u8_window() {
1483 let data = build_multi_strip_tiff(
1484 4,
1485 &[
1486 &[1, 2, 3, 4],
1487 &[5, 6, 7, 8],
1488 &[9, 10, 11, 12],
1489 &[13, 14, 15, 16],
1490 ],
1491 );
1492 let file = TiffFile::from_bytes(data).unwrap();
1493 let window = file.read_window::<u8>(0, 1, 1, 2, 2).unwrap();
1494 assert_eq!(window.shape(), &[2, 2]);
1495 let (values, offset) = window.into_raw_vec_and_offset();
1496 assert_eq!(offset, Some(0));
1497 assert_eq!(values, vec![6, 7, 10, 11]);
1498 }
1499
1500 #[test]
1501 fn reads_tiled_u8_window() {
1502 let data = build_tiled_tiff(
1503 4,
1504 4,
1505 2,
1506 2,
1507 &[
1508 &[1, 2, 5, 6],
1509 &[3, 4, 7, 8],
1510 &[9, 10, 13, 14],
1511 &[11, 12, 15, 16],
1512 ],
1513 );
1514 let file = TiffFile::from_bytes(data).unwrap();
1515 let window = file.read_window::<u8>(0, 1, 1, 2, 2).unwrap();
1516 assert_eq!(window.shape(), &[2, 2]);
1517 let (values, offset) = window.into_raw_vec_and_offset();
1518 assert_eq!(offset, Some(0));
1519 assert_eq!(values, vec![6, 7, 10, 11]);
1520 }
1521
1522 #[test]
1523 fn windowed_tiled_reads_only_intersecting_blocks() {
1524 let data = build_tiled_tiff(
1525 4,
1526 4,
1527 2,
1528 2,
1529 &[
1530 &[1, 2, 5, 6],
1531 &[3, 4, 7, 8],
1532 &[9, 10, 13, 14],
1533 &[11, 12, 15, 16],
1534 ],
1535 );
1536 let source = Arc::new(CountingSource::new(data));
1537 let file = TiffFile::from_source(source.clone()).unwrap();
1538 source.reset_reads();
1539
1540 let window = file.read_window::<u8>(0, 0, 0, 2, 2).unwrap();
1541 let (values, offset) = window.into_raw_vec_and_offset();
1542 assert_eq!(offset, Some(0));
1543 assert_eq!(values, vec![1, 2, 5, 6]);
1544 assert_eq!(source.reads(), 1);
1545 }
1546
1547 #[test]
1548 fn unwraps_gdal_structural_metadata_block() {
1549 let metadata = GdalStructuralMetadata::from_prefix(
1550 b"GDAL_STRUCTURAL_METADATA_SIZE=000174 bytes\nBLOCK_LEADER=SIZE_AS_UINT4\nBLOCK_TRAILER=LAST_4_BYTES_REPEATED\n",
1551 )
1552 .unwrap();
1553
1554 let payload = [1u8, 2, 3, 4];
1555 let mut block = Vec::new();
1556 block.extend_from_slice(&(payload.len() as u32).to_le_bytes());
1557 block.extend_from_slice(&payload);
1558 block.extend_from_slice(&payload[payload.len() - 4..]);
1559
1560 let unwrapped = metadata
1561 .unwrap_block(&block, crate::ByteOrder::LittleEndian, 256)
1562 .unwrap();
1563 assert_eq!(unwrapped, payload);
1564 }
1565
1566 #[test]
1567 fn rejects_gdal_structural_metadata_trailer_mismatch() {
1568 let metadata = GdalStructuralMetadata::from_prefix(
1569 b"GDAL_STRUCTURAL_METADATA_SIZE=000174 bytes\nBLOCK_LEADER=SIZE_AS_UINT4\nBLOCK_TRAILER=LAST_4_BYTES_REPEATED\n",
1570 )
1571 .unwrap();
1572
1573 let block = [
1574 4u8, 0, 0, 0, 1, 2, 3, 4, 4, 3, 2, 1,
1577 ];
1578
1579 let error = metadata
1580 .unwrap_block(&block, crate::ByteOrder::LittleEndian, 512)
1581 .unwrap_err();
1582 assert!(error.to_string().contains("GDAL block trailer mismatch"));
1583 }
1584
1585 #[test]
1586 fn parses_gdal_structural_metadata_before_binary_prefix_data() {
1587 let rest = "LAYOUT=IFDS_BEFORE_DATA\nBLOCK_ORDER=ROW_MAJOR\nBLOCK_LEADER=SIZE_AS_UINT4\nBLOCK_TRAILER=LAST_4_BYTES_REPEATED\nKNOWN_INCOMPATIBLE_EDITION=NO\n";
1588 let prefix = format!(
1589 "{GDAL_STRUCTURAL_METADATA_PREFIX}{:06} bytes\n{rest}",
1590 rest.len()
1591 );
1592
1593 let mut bytes = vec![0u8; 8];
1594 bytes.extend_from_slice(prefix.as_bytes());
1595 bytes.extend_from_slice(&[0xff, 0x00, 0x80, 0x7f]);
1596
1597 let source = BytesSource::new(bytes);
1598 let metadata = parse_gdal_structural_metadata(&source).unwrap();
1599 assert!(metadata.block_leader_size_as_u32);
1600 assert!(metadata.block_trailer_repeats_last_4_bytes);
1601 }
1602
1603 #[test]
1604 fn parses_gdal_structural_metadata_declared_length_as_header_plus_payload() {
1605 let rest = "LAYOUT=IFDS_BEFORE_DATA\nBLOCK_ORDER=ROW_MAJOR\n";
1606 let prefix = format!(
1607 "{GDAL_STRUCTURAL_METADATA_PREFIX}{:06} bytes\n{rest}",
1608 rest.len()
1609 );
1610 assert_eq!(
1611 parse_gdal_structural_metadata_len(prefix.as_bytes()),
1612 Some(prefix.len())
1613 );
1614 }
1615
1616 #[test]
1617 fn leaves_payload_only_gdal_block_unchanged() {
1618 let metadata = GdalStructuralMetadata {
1619 block_leader_size_as_u32: true,
1620 block_trailer_repeats_last_4_bytes: true,
1621 };
1622 let payload = [0x80u8, 0x1a, 0xcf, 0x68, 0x43, 0x9a, 0x11, 0x08];
1623 let unwrapped = metadata
1624 .unwrap_block(&payload, crate::ByteOrder::LittleEndian, 570)
1625 .unwrap();
1626 assert_eq!(unwrapped, payload);
1627 }
1628
1629 #[test]
1630 fn rejects_zero_rows_per_strip_without_panicking() {
1631 let data = build_stripped_tiff(2, 2, &[1, 2, 3, 4], &[(278, 4, 1, le_u32(0).to_vec())]);
1632 let file = TiffFile::from_bytes(data).unwrap();
1633 let error = file.read_image_bytes(0).unwrap_err();
1634 assert!(error.to_string().contains("RowsPerStrip"));
1635 }
1636}