1mod block_decode;
31pub mod cache;
32pub mod error;
33pub mod filters;
34pub mod header;
35pub mod ifd;
36pub mod io;
37mod pixel;
38pub mod source;
39pub mod strip;
40pub mod tag;
41pub mod tile;
42
43use std::path::Path;
44use std::sync::Arc;
45
46use cache::BlockCache;
47use error::{Error, Result};
48use ndarray::{ArrayD, IxDyn};
49use source::{BytesSource, MmapSource, SharedSource, TiffSource};
50
51pub use error::Error as TiffError;
52pub use header::ByteOrder;
53pub use ifd::{Ifd, RasterLayout};
54pub use tag::{Tag, TagValue};
55pub use tiff_core::constants;
56pub use tiff_core::sample::TiffSample;
57pub use tiff_core::TagType;
58pub use tiff_core::{
59 ColorMap, ColorModel, ExtraSample, InkSet, PhotometricInterpretation, YCbCrPositioning,
60};
61
62#[derive(Debug, Clone, Copy)]
64pub struct OpenOptions {
65 pub block_cache_bytes: usize,
67 pub block_cache_slots: usize,
69}
70
71impl Default for OpenOptions {
72 fn default() -> Self {
73 Self {
74 block_cache_bytes: 64 * 1024 * 1024,
75 block_cache_slots: 257,
76 }
77 }
78}
79
80pub struct TiffFile {
82 source: SharedSource,
83 header: header::TiffHeader,
84 ifds: Vec<ifd::Ifd>,
85 block_cache: Arc<BlockCache>,
86 gdal_structural_metadata: Option<GdalStructuralMetadata>,
87}
88
89#[derive(Debug, Clone, Copy)]
90pub(crate) struct GdalStructuralMetadata {
91 block_leader_size_as_u32: bool,
92 block_trailer_repeats_last_4_bytes: bool,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub(crate) struct Window {
97 pub row_off: usize,
98 pub col_off: usize,
99 pub rows: usize,
100 pub cols: usize,
101}
102
103impl Window {
104 pub(crate) fn is_empty(self) -> bool {
105 self.rows == 0 || self.cols == 0
106 }
107
108 pub(crate) fn row_end(self) -> usize {
109 self.row_off + self.rows
110 }
111
112 pub(crate) fn col_end(self) -> usize {
113 self.col_off + self.cols
114 }
115
116 pub(crate) fn output_len(self, layout: &RasterLayout) -> Result<usize> {
117 self.cols
118 .checked_mul(self.rows)
119 .and_then(|pixels| pixels.checked_mul(layout.pixel_stride_bytes()))
120 .ok_or_else(|| Error::InvalidImageLayout("window size overflows usize".into()))
121 }
122
123 pub(crate) fn band_output_len(self, layout: &RasterLayout) -> Result<usize> {
124 self.cols
125 .checked_mul(self.rows)
126 .and_then(|pixels| pixels.checked_mul(layout.bytes_per_sample))
127 .ok_or_else(|| Error::InvalidImageLayout("window band size overflows usize".into()))
128 }
129}
130
131impl GdalStructuralMetadata {
132 fn from_prefix(bytes: &[u8]) -> Option<Self> {
133 let text = std::str::from_utf8(bytes).ok()?;
134 if !text.contains("GDAL_STRUCTURAL_METADATA_SIZE=") {
135 return None;
136 }
137
138 Some(Self {
139 block_leader_size_as_u32: text.contains("BLOCK_LEADER=SIZE_AS_UINT4"),
140 block_trailer_repeats_last_4_bytes: text
141 .contains("BLOCK_TRAILER=LAST_4_BYTES_REPEATED"),
142 })
143 }
144
145 pub(crate) fn unwrap_block<'a>(
146 &self,
147 raw: &'a [u8],
148 byte_order: ByteOrder,
149 offset: u64,
150 ) -> Result<&'a [u8]> {
151 if self.block_leader_size_as_u32 {
152 if raw.len() < 4 {
153 return Ok(raw);
154 }
155 let declared_len = match byte_order {
156 ByteOrder::LittleEndian => u32::from_le_bytes(raw[..4].try_into().unwrap()),
157 ByteOrder::BigEndian => u32::from_be_bytes(raw[..4].try_into().unwrap()),
158 } as usize;
159 if let Some(payload_end) = 4usize.checked_add(declared_len) {
160 if payload_end <= raw.len() {
161 if self.block_trailer_repeats_last_4_bytes {
162 let trailer_end = payload_end.checked_add(4).ok_or_else(|| {
163 Error::InvalidImageLayout("GDAL block trailer overflows usize".into())
164 })?;
165 if trailer_end <= raw.len() {
166 let expected = &raw[payload_end - 4..payload_end];
167 let trailer = &raw[payload_end..trailer_end];
168 if expected != trailer {
169 return Err(Error::InvalidImageLayout(format!(
170 "GDAL block trailer mismatch at offset {offset}"
171 )));
172 }
173 }
174 }
175 return Ok(&raw[4..payload_end]);
176 }
177 }
178 }
179
180 if self.block_trailer_repeats_last_4_bytes && raw.len() >= 8 {
181 let split = raw.len() - 4;
182 if raw[split - 4..split] == raw[split..] {
183 return Ok(&raw[..split]);
184 }
185 }
186
187 Ok(raw)
188 }
189}
190
191pub(crate) fn read_gdal_block_payload(
192 source: &dyn TiffSource,
193 metadata: &GdalStructuralMetadata,
194 byte_order: ByteOrder,
195 offset: u64,
196 byte_count: u64,
197) -> Result<Vec<u8>> {
198 let wrapped_extra = 4u64
199 .checked_add(if metadata.block_trailer_repeats_last_4_bytes {
200 4
201 } else {
202 0
203 })
204 .ok_or_else(|| Error::InvalidImageLayout("GDAL block wrapper overflows u64".into()))?;
205
206 let mut candidates = Vec::with_capacity(2);
207 if metadata.block_leader_size_as_u32 && offset >= 4 {
208 candidates.push((
209 offset - 4,
210 byte_count.checked_add(wrapped_extra).ok_or_else(|| {
211 Error::InvalidImageLayout("GDAL wrapped block length overflows u64".into())
212 })?,
213 ));
214 }
215 candidates.push((offset, byte_count));
216
217 let mut fallback: Option<Result<Vec<u8>>> = None;
218 for (candidate_offset, candidate_len) in candidates {
219 let len = usize::try_from(candidate_len).map_err(|_| Error::OffsetOutOfBounds {
220 offset: candidate_offset,
221 length: candidate_len,
222 data_len: source.len(),
223 })?;
224 let raw = match source.read_exact_at(candidate_offset, len) {
225 Ok(raw) => raw,
226 Err(err) => {
227 if fallback.is_none() {
228 fallback = Some(Err(err));
229 }
230 continue;
231 }
232 };
233 match metadata.unwrap_block(&raw, byte_order, candidate_offset) {
234 Ok(payload) => {
235 if candidate_offset != offset
236 && payload.len() == usize::try_from(byte_count).unwrap_or(usize::MAX)
237 {
238 return Ok(payload.to_vec());
239 }
240 fallback = Some(Ok(payload.to_vec()));
241 }
242 Err(err) => {
243 if fallback.is_none() {
244 fallback = Some(Err(err));
245 }
246 }
247 }
248 }
249
250 match fallback {
251 Some(result) => result,
252 None => Ok(Vec::new()),
253 }
254}
255
256const GDAL_STRUCTURAL_METADATA_PREFIX: &str = "GDAL_STRUCTURAL_METADATA_SIZE=";
257
258impl TiffFile {
261 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
263 Self::open_with_options(path, OpenOptions::default())
264 }
265
266 pub fn open_with_options<P: AsRef<Path>>(path: P, options: OpenOptions) -> Result<Self> {
268 let source: SharedSource = Arc::new(MmapSource::open(path.as_ref())?);
269 Self::from_source_with_options(source, options)
270 }
271
272 pub fn from_bytes(data: Vec<u8>) -> Result<Self> {
274 Self::from_bytes_with_options(data, OpenOptions::default())
275 }
276
277 pub fn from_bytes_with_options(data: Vec<u8>, options: OpenOptions) -> Result<Self> {
279 let source: SharedSource = Arc::new(BytesSource::new(data));
280 Self::from_source_with_options(source, options)
281 }
282
283 pub fn from_source(source: SharedSource) -> Result<Self> {
285 Self::from_source_with_options(source, OpenOptions::default())
286 }
287
288 pub fn from_source_with_options(source: SharedSource, options: OpenOptions) -> Result<Self> {
290 let header_len = usize::try_from(source.len().min(16)).unwrap_or(16);
291 let header_bytes = source.read_exact_at(0, header_len)?;
292 let header = header::TiffHeader::parse(&header_bytes)?;
293 let gdal_structural_metadata = parse_gdal_structural_metadata(source.as_ref());
294 let ifds = ifd::parse_ifd_chain(source.as_ref(), &header)?;
295 Ok(Self {
296 source,
297 header,
298 ifds,
299 block_cache: Arc::new(BlockCache::new(
300 options.block_cache_bytes,
301 options.block_cache_slots,
302 )),
303 gdal_structural_metadata,
304 })
305 }
306
307 pub fn byte_order(&self) -> ByteOrder {
309 self.header.byte_order
310 }
311
312 pub fn is_bigtiff(&self) -> bool {
314 self.header.is_bigtiff()
315 }
316
317 pub fn ifd_count(&self) -> usize {
319 self.ifds.len()
320 }
321
322 pub fn ifd(&self, index: usize) -> Result<&Ifd> {
324 self.ifds.get(index).ok_or(Error::IfdNotFound(index))
325 }
326
327 pub fn ifds(&self) -> &[Ifd] {
329 &self.ifds
330 }
331
332 pub fn raw_bytes(&self) -> Option<&[u8]> {
334 self.source.as_slice()
335 }
336
337 pub fn source(&self) -> &dyn TiffSource {
339 self.source.as_ref()
340 }
341
342 pub fn read_ifd_at_offset(&self, offset: u64) -> Result<Ifd> {
344 ifd::parse_ifd_at(self.source.as_ref(), &self.header, offset)
345 }
346
347 pub fn read_image_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
349 let ifd = self.ifd(ifd_index)?;
350 self.read_image_bytes_from_ifd(ifd)
351 }
352
353 pub fn read_image_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
355 self.read_image_sample_bytes_from_ifd(ifd)
356 }
357
358 pub fn read_decoded_image_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
360 let ifd = self.ifd(ifd_index)?;
361 self.read_decoded_image_bytes_from_ifd(ifd)
362 }
363
364 pub fn read_decoded_image_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
367 let layout = ifd.decoded_raster_layout()?;
368 self.decode_window_pixel_bytes(
369 ifd,
370 Window {
371 row_off: 0,
372 col_off: 0,
373 rows: layout.height,
374 cols: layout.width,
375 },
376 )
377 }
378
379 pub fn read_image_sample_bytes(&self, ifd_index: usize) -> Result<Vec<u8>> {
383 let ifd = self.ifd(ifd_index)?;
384 self.read_image_sample_bytes_from_ifd(ifd)
385 }
386
387 pub fn read_image_sample_bytes_from_ifd(&self, ifd: &Ifd) -> Result<Vec<u8>> {
392 let layout = ifd.raster_layout()?;
393 self.decode_window_sample_bytes(
394 ifd,
395 Window {
396 row_off: 0,
397 col_off: 0,
398 rows: layout.height,
399 cols: layout.width,
400 },
401 )
402 }
403
404 pub fn read_window_bytes(
407 &self,
408 ifd_index: usize,
409 row_off: usize,
410 col_off: usize,
411 rows: usize,
412 cols: usize,
413 ) -> Result<Vec<u8>> {
414 let ifd = self.ifd(ifd_index)?;
415 self.read_window_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
416 }
417
418 pub fn read_decoded_window_bytes(
421 &self,
422 ifd_index: usize,
423 row_off: usize,
424 col_off: usize,
425 rows: usize,
426 cols: usize,
427 ) -> Result<Vec<u8>> {
428 let ifd = self.ifd(ifd_index)?;
429 self.read_decoded_window_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
430 }
431
432 pub fn read_window_sample_bytes(
437 &self,
438 ifd_index: usize,
439 row_off: usize,
440 col_off: usize,
441 rows: usize,
442 cols: usize,
443 ) -> Result<Vec<u8>> {
444 let ifd = self.ifd(ifd_index)?;
445 self.read_window_sample_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
446 }
447
448 pub fn read_window_bytes_from_ifd(
451 &self,
452 ifd: &Ifd,
453 row_off: usize,
454 col_off: usize,
455 rows: usize,
456 cols: usize,
457 ) -> Result<Vec<u8>> {
458 self.read_window_sample_bytes_from_ifd(ifd, row_off, col_off, rows, cols)
459 }
460
461 pub fn read_decoded_window_bytes_from_ifd(
464 &self,
465 ifd: &Ifd,
466 row_off: usize,
467 col_off: usize,
468 rows: usize,
469 cols: usize,
470 ) -> Result<Vec<u8>> {
471 let layout = ifd.decoded_raster_layout()?;
472 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
473 self.decode_window_pixel_bytes(ifd, window)
474 }
475
476 pub fn read_window_sample_bytes_from_ifd(
481 &self,
482 ifd: &Ifd,
483 row_off: usize,
484 col_off: usize,
485 rows: usize,
486 cols: usize,
487 ) -> Result<Vec<u8>> {
488 let layout = ifd.raster_layout()?;
489 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
490 self.decode_window_sample_bytes(ifd, window)
491 }
492
493 pub fn read_band_bytes(&self, ifd_index: usize, band_index: usize) -> Result<Vec<u8>> {
495 let ifd = self.ifd(ifd_index)?;
496 self.read_band_bytes_from_ifd(ifd, band_index)
497 }
498
499 pub fn read_band_bytes_from_ifd(&self, ifd: &Ifd, band_index: usize) -> Result<Vec<u8>> {
502 let layout = ifd.raster_layout()?;
503 self.read_band_window_bytes_from_ifd(ifd, band_index, 0, 0, layout.height, layout.width)
504 }
505
506 pub fn read_band_window_bytes(
509 &self,
510 ifd_index: usize,
511 band_index: usize,
512 row_off: usize,
513 col_off: usize,
514 rows: usize,
515 cols: usize,
516 ) -> Result<Vec<u8>> {
517 let ifd = self.ifd(ifd_index)?;
518 self.read_band_window_bytes_from_ifd(ifd, band_index, row_off, col_off, rows, cols)
519 }
520
521 pub fn read_band_window_bytes_from_ifd(
524 &self,
525 ifd: &Ifd,
526 band_index: usize,
527 row_off: usize,
528 col_off: usize,
529 rows: usize,
530 cols: usize,
531 ) -> Result<Vec<u8>> {
532 let layout = ifd.raster_layout()?;
533 validate_band_index(&layout, band_index)?;
534 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
535 self.decode_window_sample_band_bytes(ifd, window, band_index)
536 }
537
538 fn decode_window_sample_bytes(&self, ifd: &Ifd, window: Window) -> Result<Vec<u8>> {
539 if window.is_empty() {
540 return Ok(Vec::new());
541 }
542
543 if ifd.is_tiled() {
544 tile::read_window(
545 self.source.as_ref(),
546 ifd,
547 self.byte_order(),
548 &self.block_cache,
549 window,
550 self.gdal_structural_metadata.as_ref(),
551 )
552 } else {
553 strip::read_window(
554 self.source.as_ref(),
555 ifd,
556 self.byte_order(),
557 &self.block_cache,
558 window,
559 self.gdal_structural_metadata.as_ref(),
560 )
561 }
562 }
563
564 fn decode_window_sample_band_bytes(
565 &self,
566 ifd: &Ifd,
567 window: Window,
568 band_index: usize,
569 ) -> Result<Vec<u8>> {
570 if window.is_empty() {
571 return Ok(Vec::new());
572 }
573
574 let layout = ifd.raster_layout()?;
575 validate_band_index(&layout, band_index)?;
576 if ifd.is_tiled() {
577 tile::read_window_band(
578 self.source.as_ref(),
579 ifd,
580 self.byte_order(),
581 &self.block_cache,
582 window,
583 band_index,
584 self.gdal_structural_metadata.as_ref(),
585 )
586 } else {
587 strip::read_window_band(
588 self.source.as_ref(),
589 ifd,
590 self.byte_order(),
591 &self.block_cache,
592 window,
593 band_index,
594 self.gdal_structural_metadata.as_ref(),
595 )
596 }
597 }
598
599 fn decode_window_pixel_bytes(&self, ifd: &Ifd, window: Window) -> Result<Vec<u8>> {
600 let storage_layout = ifd.raster_layout()?;
601 let sample_bytes = self.decode_window_sample_bytes(ifd, window)?;
602 let (_, pixels) = pixel::decode_pixels(
603 ifd,
604 &storage_layout,
605 window.cols,
606 window.rows,
607 &sample_bytes,
608 )?;
609 Ok(pixels)
610 }
611
612 pub fn read_window<T: TiffSample>(
617 &self,
618 ifd_index: usize,
619 row_off: usize,
620 col_off: usize,
621 rows: usize,
622 cols: usize,
623 ) -> Result<ArrayD<T>> {
624 let ifd = self.ifd(ifd_index)?;
625 self.read_window_from_ifd(ifd, row_off, col_off, rows, cols)
626 }
627
628 pub fn read_window_from_ifd<T: TiffSample>(
631 &self,
632 ifd: &Ifd,
633 row_off: usize,
634 col_off: usize,
635 rows: usize,
636 cols: usize,
637 ) -> Result<ArrayD<T>> {
638 self.read_window_samples_from_ifd(ifd, row_off, col_off, rows, cols)
639 }
640
641 pub fn read_decoded_window<T: TiffSample>(
646 &self,
647 ifd_index: usize,
648 row_off: usize,
649 col_off: usize,
650 rows: usize,
651 cols: usize,
652 ) -> Result<ArrayD<T>> {
653 let ifd = self.ifd(ifd_index)?;
654 self.read_decoded_window_from_ifd(ifd, row_off, col_off, rows, cols)
655 }
656
657 pub fn read_decoded_window_from_ifd<T: TiffSample>(
660 &self,
661 ifd: &Ifd,
662 row_off: usize,
663 col_off: usize,
664 rows: usize,
665 cols: usize,
666 ) -> Result<ArrayD<T>> {
667 let layout = ifd.decoded_raster_layout()?;
668 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
669 if !T::matches_layout(&layout) {
670 return Err(Error::TypeMismatch {
671 expected: T::type_name(),
672 actual: format!(
673 "sample_format={} bits_per_sample={}",
674 layout.sample_format, layout.bits_per_sample
675 ),
676 });
677 }
678
679 let decoded = self.decode_window_pixel_bytes(ifd, window)?;
680 let values = T::decode_many(&decoded);
681 let shape = if layout.samples_per_pixel == 1 {
682 vec![window.rows, window.cols]
683 } else {
684 vec![window.rows, window.cols, layout.samples_per_pixel]
685 };
686 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|e| {
687 Error::InvalidImageLayout(format!("failed to build ndarray from decoded raster: {e}"))
688 })
689 }
690
691 pub fn read_window_samples<T: TiffSample>(
696 &self,
697 ifd_index: usize,
698 row_off: usize,
699 col_off: usize,
700 rows: usize,
701 cols: usize,
702 ) -> Result<ArrayD<T>> {
703 let ifd = self.ifd(ifd_index)?;
704 self.read_window_samples_from_ifd(ifd, row_off, col_off, rows, cols)
705 }
706
707 pub fn read_window_samples_from_ifd<T: TiffSample>(
710 &self,
711 ifd: &Ifd,
712 row_off: usize,
713 col_off: usize,
714 rows: usize,
715 cols: usize,
716 ) -> Result<ArrayD<T>> {
717 let layout = ifd.raster_layout()?;
718 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
719 if !T::matches_layout(&layout) {
720 return Err(Error::TypeMismatch {
721 expected: T::type_name(),
722 actual: format!(
723 "sample_format={} bits_per_sample={}",
724 layout.sample_format, layout.bits_per_sample
725 ),
726 });
727 }
728
729 let decoded = self.decode_window_sample_bytes(ifd, window)?;
730 let values = T::decode_many(&decoded);
731 let shape = if layout.samples_per_pixel == 1 {
732 vec![window.rows, window.cols]
733 } else {
734 vec![window.rows, window.cols, layout.samples_per_pixel]
735 };
736 ArrayD::from_shape_vec(IxDyn(&shape), values).map_err(|e| {
737 Error::InvalidImageLayout(format!("failed to build ndarray from storage raster: {e}"))
738 })
739 }
740
741 pub fn read_band<T: TiffSample>(
743 &self,
744 ifd_index: usize,
745 band_index: usize,
746 ) -> Result<ArrayD<T>> {
747 let ifd = self.ifd(ifd_index)?;
748 self.read_band_from_ifd(ifd, band_index)
749 }
750
751 pub fn read_band_from_ifd<T: TiffSample>(
754 &self,
755 ifd: &Ifd,
756 band_index: usize,
757 ) -> Result<ArrayD<T>> {
758 let layout = ifd.raster_layout()?;
759 self.read_band_window_from_ifd(ifd, band_index, 0, 0, layout.height, layout.width)
760 }
761
762 pub fn read_band_window<T: TiffSample>(
765 &self,
766 ifd_index: usize,
767 band_index: usize,
768 row_off: usize,
769 col_off: usize,
770 rows: usize,
771 cols: usize,
772 ) -> Result<ArrayD<T>> {
773 let ifd = self.ifd(ifd_index)?;
774 self.read_band_window_from_ifd(ifd, band_index, row_off, col_off, rows, cols)
775 }
776
777 pub fn read_band_window_from_ifd<T: TiffSample>(
780 &self,
781 ifd: &Ifd,
782 band_index: usize,
783 row_off: usize,
784 col_off: usize,
785 rows: usize,
786 cols: usize,
787 ) -> Result<ArrayD<T>> {
788 let layout = ifd.raster_layout()?;
789 validate_band_index(&layout, band_index)?;
790 let window = validate_window(&layout, row_off, col_off, rows, cols)?;
791 if !T::matches_layout(&layout) {
792 return Err(Error::TypeMismatch {
793 expected: T::type_name(),
794 actual: format!(
795 "sample_format={} bits_per_sample={}",
796 layout.sample_format, layout.bits_per_sample
797 ),
798 });
799 }
800
801 let decoded = self.decode_window_sample_band_bytes(ifd, window, band_index)?;
802 let values = T::decode_many(&decoded);
803 ArrayD::from_shape_vec(IxDyn(&[window.rows, window.cols]), values).map_err(|e| {
804 Error::InvalidImageLayout(format!("failed to build ndarray from band raster: {e}"))
805 })
806 }
807
808 pub fn read_image<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
813 let ifd = self.ifd(ifd_index)?;
814 self.read_image_from_ifd(ifd)
815 }
816
817 pub fn read_image_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
819 self.read_image_samples_from_ifd(ifd)
820 }
821
822 pub fn read_decoded_image<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
828 let ifd = self.ifd(ifd_index)?;
829 self.read_decoded_image_from_ifd(ifd)
830 }
831
832 pub fn read_decoded_image_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
834 let layout = ifd.decoded_raster_layout()?;
835 if !T::matches_layout(&layout) {
836 return Err(Error::TypeMismatch {
837 expected: T::type_name(),
838 actual: format!(
839 "sample_format={} bits_per_sample={}",
840 layout.sample_format, layout.bits_per_sample
841 ),
842 });
843 }
844
845 self.read_decoded_window_from_ifd(ifd, 0, 0, layout.height, layout.width)
846 }
847
848 pub fn read_image_samples<T: TiffSample>(&self, ifd_index: usize) -> Result<ArrayD<T>> {
852 let ifd = self.ifd(ifd_index)?;
853 self.read_image_samples_from_ifd(ifd)
854 }
855
856 pub fn read_image_samples_from_ifd<T: TiffSample>(&self, ifd: &Ifd) -> Result<ArrayD<T>> {
860 let layout = ifd.raster_layout()?;
861 if !T::matches_layout(&layout) {
862 return Err(Error::TypeMismatch {
863 expected: T::type_name(),
864 actual: format!(
865 "sample_format={} bits_per_sample={}",
866 layout.sample_format, layout.bits_per_sample
867 ),
868 });
869 }
870
871 self.read_window_samples_from_ifd(ifd, 0, 0, layout.height, layout.width)
872 }
873}
874
875fn validate_window(
876 layout: &RasterLayout,
877 row_off: usize,
878 col_off: usize,
879 rows: usize,
880 cols: usize,
881) -> Result<Window> {
882 let row_end = row_off
883 .checked_add(rows)
884 .ok_or_else(|| Error::InvalidImageLayout("window row range overflows usize".into()))?;
885 let col_end = col_off
886 .checked_add(cols)
887 .ok_or_else(|| Error::InvalidImageLayout("window column range overflows usize".into()))?;
888 if row_end > layout.height || col_end > layout.width {
889 return Err(Error::InvalidImageLayout(format!(
890 "window [{row_off}..{row_end}, {col_off}..{col_end}) exceeds raster bounds {}x{}",
891 layout.height, layout.width
892 )));
893 }
894 Ok(Window {
895 row_off,
896 col_off,
897 rows,
898 cols,
899 })
900}
901
902fn validate_band_index(layout: &RasterLayout, band_index: usize) -> Result<()> {
903 if band_index >= layout.samples_per_pixel {
904 return Err(Error::BandIndexOutOfBounds {
905 index: band_index,
906 band_count: layout.samples_per_pixel,
907 });
908 }
909 Ok(())
910}
911
912fn parse_gdal_structural_metadata(source: &dyn TiffSource) -> Option<GdalStructuralMetadata> {
913 let available_len = usize::try_from(source.len().checked_sub(8)?).ok()?;
914 if available_len == 0 {
915 return None;
916 }
917
918 let probe_len = available_len.min(64);
919 let probe = source.read_exact_at(8, probe_len).ok()?;
920 let total_len = parse_gdal_structural_metadata_len(&probe)?;
921 if total_len == 0 || total_len > available_len {
922 return None;
923 }
924
925 let bytes = source.read_exact_at(8, total_len).ok()?;
926 GdalStructuralMetadata::from_prefix(&bytes)
927}
928
929fn parse_gdal_structural_metadata_len(bytes: &[u8]) -> Option<usize> {
930 let text = std::str::from_utf8(bytes).ok()?;
931 let newline_index = text.find('\n')?;
932 let header = &text[..newline_index];
933 let value = header.strip_prefix(GDAL_STRUCTURAL_METADATA_PREFIX)?;
934 let digits: String = value.chars().take_while(|ch| ch.is_ascii_digit()).collect();
935 if digits.is_empty() {
936 return None;
937 }
938 let payload_len: usize = digits.parse().ok()?;
939 newline_index.checked_add(1)?.checked_add(payload_len)
940}
941
942#[cfg(test)]
943mod tests {
944 use std::collections::BTreeMap;
945 use std::sync::atomic::{AtomicUsize, Ordering};
946 use std::sync::Arc;
947
948 use super::{
949 parse_gdal_structural_metadata, parse_gdal_structural_metadata_len, Error,
950 GdalStructuralMetadata, TiffFile, GDAL_STRUCTURAL_METADATA_PREFIX,
951 };
952 use crate::source::{BytesSource, TiffSource};
953 use flate2::{write::ZlibEncoder, Compression as FlateCompression};
954
955 fn le_u16(value: u16) -> [u8; 2] {
956 value.to_le_bytes()
957 }
958
959 fn le_u32(value: u32) -> [u8; 4] {
960 value.to_le_bytes()
961 }
962
963 fn inline_short(value: u16) -> Vec<u8> {
964 let mut bytes = [0u8; 4];
965 bytes[..2].copy_from_slice(&le_u16(value));
966 bytes.to_vec()
967 }
968
969 fn build_stripped_tiff(
970 width: u32,
971 height: u32,
972 image_data: &[u8],
973 overrides: &[(u16, u16, u32, Vec<u8>)],
974 ) -> Vec<u8> {
975 let mut entries = BTreeMap::new();
976 entries.insert(256, (4, 1, le_u32(width).to_vec()));
977 entries.insert(257, (4, 1, le_u32(height).to_vec()));
978 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
979 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
980 entries.insert(273, (4, 1, Vec::new()));
981 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
982 entries.insert(278, (4, 1, le_u32(height).to_vec()));
983 entries.insert(279, (4, 1, le_u32(image_data.len() as u32).to_vec()));
984 for &(tag, ty, count, ref value) in overrides {
985 entries.insert(tag, (ty, count, value.clone()));
986 }
987
988 let ifd_offset = 8u32;
989 let ifd_size = 2 + entries.len() * 12 + 4;
990 let mut next_data_offset = ifd_offset as usize + ifd_size;
991 let image_offset = next_data_offset as u32;
992 next_data_offset += image_data.len();
993
994 let mut data = Vec::with_capacity(next_data_offset);
995 data.extend_from_slice(b"II");
996 data.extend_from_slice(&le_u16(42));
997 data.extend_from_slice(&le_u32(ifd_offset));
998 data.extend_from_slice(&le_u16(entries.len() as u16));
999
1000 let mut deferred = Vec::new();
1001 for (tag, (ty, count, value)) in entries {
1002 data.extend_from_slice(&le_u16(tag));
1003 data.extend_from_slice(&le_u16(ty));
1004 data.extend_from_slice(&le_u32(count));
1005 if tag == 273 {
1006 data.extend_from_slice(&le_u32(image_offset));
1007 } else if value.len() <= 4 {
1008 let mut inline = [0u8; 4];
1009 inline[..value.len()].copy_from_slice(&value);
1010 data.extend_from_slice(&inline);
1011 } else {
1012 let offset = next_data_offset as u32;
1013 data.extend_from_slice(&le_u32(offset));
1014 next_data_offset += value.len();
1015 deferred.push(value);
1016 }
1017 }
1018 data.extend_from_slice(&le_u32(0));
1019 data.extend_from_slice(image_data);
1020 for value in deferred {
1021 data.extend_from_slice(&value);
1022 }
1023 data
1024 }
1025
1026 #[allow(clippy::too_many_arguments)]
1027 fn build_lerc2_header_v2(
1028 width: u32,
1029 height: u32,
1030 valid_pixel_count: u32,
1031 image_type: i32,
1032 max_z_error: f64,
1033 z_min: f64,
1034 z_max: f64,
1035 payload_len: usize,
1036 ) -> Vec<u8> {
1037 let blob_size = 58 + 4 + payload_len;
1038 let mut bytes = Vec::with_capacity(blob_size);
1039 bytes.extend_from_slice(b"Lerc2 ");
1040 bytes.extend_from_slice(&2i32.to_le_bytes());
1041 bytes.extend_from_slice(&height.to_le_bytes());
1042 bytes.extend_from_slice(&width.to_le_bytes());
1043 bytes.extend_from_slice(&valid_pixel_count.to_le_bytes());
1044 bytes.extend_from_slice(&8i32.to_le_bytes());
1045 bytes.extend_from_slice(&(blob_size as i32).to_le_bytes());
1046 bytes.extend_from_slice(&image_type.to_le_bytes());
1047 bytes.extend_from_slice(&max_z_error.to_le_bytes());
1048 bytes.extend_from_slice(&z_min.to_le_bytes());
1049 bytes.extend_from_slice(&z_max.to_le_bytes());
1050 bytes
1051 }
1052
1053 #[allow(clippy::too_many_arguments)]
1054 fn build_lerc2_header_v4(
1055 width: u32,
1056 height: u32,
1057 depth: u32,
1058 valid_pixel_count: u32,
1059 image_type: i32,
1060 max_z_error: f64,
1061 z_min: f64,
1062 z_max: f64,
1063 payload_len: usize,
1064 ) -> Vec<u8> {
1065 let blob_size = 66 + 4 + payload_len;
1066 let mut bytes = Vec::with_capacity(blob_size);
1067 bytes.extend_from_slice(b"Lerc2 ");
1068 bytes.extend_from_slice(&4i32.to_le_bytes());
1069 bytes.extend_from_slice(&0u32.to_le_bytes());
1070 bytes.extend_from_slice(&height.to_le_bytes());
1071 bytes.extend_from_slice(&width.to_le_bytes());
1072 bytes.extend_from_slice(&depth.to_le_bytes());
1073 bytes.extend_from_slice(&valid_pixel_count.to_le_bytes());
1074 bytes.extend_from_slice(&8i32.to_le_bytes());
1075 bytes.extend_from_slice(&(blob_size as i32).to_le_bytes());
1076 bytes.extend_from_slice(&image_type.to_le_bytes());
1077 bytes.extend_from_slice(&max_z_error.to_le_bytes());
1078 bytes.extend_from_slice(&z_min.to_le_bytes());
1079 bytes.extend_from_slice(&z_max.to_le_bytes());
1080 bytes
1081 }
1082
1083 fn finalize_lerc2_v4_with_checksum(mut bytes: Vec<u8>) -> Vec<u8> {
1084 let blob_size = bytes.len() as i32;
1085 bytes[34..38].copy_from_slice(&blob_size.to_le_bytes());
1086 let checksum = fletcher32(&bytes[14..blob_size as usize]);
1087 bytes[10..14].copy_from_slice(&checksum.to_le_bytes());
1088 bytes
1089 }
1090
1091 fn fletcher32(bytes: &[u8]) -> u32 {
1092 let mut sum1 = 0xffffu32;
1093 let mut sum2 = 0xffffu32;
1094 let mut words = bytes.len() / 2;
1095 let mut index = 0usize;
1096
1097 while words > 0 {
1098 let chunk = words.min(359);
1099 words -= chunk;
1100 for _ in 0..chunk {
1101 sum1 += (bytes[index] as u32) << 8;
1102 index += 1;
1103 sum2 += sum1 + bytes[index] as u32;
1104 sum1 += bytes[index] as u32;
1105 index += 1;
1106 }
1107 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
1108 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
1109 }
1110
1111 if bytes.len() & 1 != 0 {
1112 sum1 += (bytes[index] as u32) << 8;
1113 sum2 += sum1;
1114 }
1115
1116 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
1117 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
1118 (sum2 << 16) | (sum1 & 0xffff)
1119 }
1120
1121 fn encode_mask_rle(mask: &[u8]) -> Vec<u8> {
1122 let bitset_len = mask.len().div_ceil(8);
1123 let mut bitset = vec![0u8; bitset_len];
1124 for (index, &value) in mask.iter().enumerate() {
1125 if value != 0 {
1126 bitset[index >> 3] |= 1 << (7 - (index & 7));
1127 }
1128 }
1129
1130 let mut encoded = Vec::with_capacity(bitset_len + 4);
1131 encoded.extend_from_slice(&(bitset_len as i16).to_le_bytes());
1132 encoded.extend_from_slice(&bitset);
1133 encoded.extend_from_slice(&i16::MIN.to_le_bytes());
1134 encoded
1135 }
1136
1137 fn build_lerc_tiff(
1138 width: u32,
1139 height: u32,
1140 image_data: &[u8],
1141 bits_per_sample: u16,
1142 sample_format: u16,
1143 samples_per_pixel: u16,
1144 lerc_parameters: Option<[u32; 2]>,
1145 ) -> Vec<u8> {
1146 let mut overrides = vec![
1147 (258u16, 3u16, 1u32, inline_short(bits_per_sample)),
1148 (259u16, 3u16, 1u32, inline_short(34887)),
1149 (277u16, 3u16, 1u32, inline_short(samples_per_pixel)),
1150 (279u16, 4u16, 1u32, le_u32(image_data.len() as u32).to_vec()),
1151 ];
1152 if sample_format != 1 {
1153 overrides.push((339u16, 3u16, 1u32, inline_short(sample_format)));
1154 }
1155 if let Some([version, additional_compression]) = lerc_parameters {
1156 overrides.push((
1157 50674u16,
1158 4u16,
1159 2u32,
1160 [version, additional_compression]
1161 .into_iter()
1162 .flat_map(le_u32)
1163 .collect(),
1164 ));
1165 }
1166 build_stripped_tiff(width, height, image_data, &overrides)
1167 }
1168
1169 fn build_tiled_tiff(
1170 width: u32,
1171 height: u32,
1172 tile_width: u32,
1173 tile_height: u32,
1174 tiles: &[&[u8]],
1175 ) -> Vec<u8> {
1176 let mut entries = BTreeMap::new();
1177 entries.insert(256, (4, 1, le_u32(width).to_vec()));
1178 entries.insert(257, (4, 1, le_u32(height).to_vec()));
1179 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
1180 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
1181 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
1182 entries.insert(322, (4, 1, le_u32(tile_width).to_vec()));
1183 entries.insert(323, (4, 1, le_u32(tile_height).to_vec()));
1184 entries.insert(
1185 325,
1186 (
1187 4,
1188 tiles.len() as u32,
1189 tiles
1190 .iter()
1191 .flat_map(|tile| le_u32(tile.len() as u32))
1192 .collect(),
1193 ),
1194 );
1195
1196 let ifd_offset = 8u32;
1197 let ifd_size = 2 + (entries.len() + 1) * 12 + 4;
1198 let mut tile_data_offset = ifd_offset as usize + ifd_size;
1199 let tile_offsets: Vec<u32> = tiles
1200 .iter()
1201 .map(|tile| {
1202 let offset = tile_data_offset as u32;
1203 tile_data_offset += tile.len();
1204 offset
1205 })
1206 .collect();
1207 entries.insert(
1208 324,
1209 (
1210 4,
1211 tile_offsets.len() as u32,
1212 tile_offsets
1213 .iter()
1214 .flat_map(|offset| le_u32(*offset))
1215 .collect(),
1216 ),
1217 );
1218
1219 let mut next_data_offset = tile_data_offset;
1220 let mut data = Vec::with_capacity(next_data_offset);
1221 data.extend_from_slice(b"II");
1222 data.extend_from_slice(&le_u16(42));
1223 data.extend_from_slice(&le_u32(ifd_offset));
1224 data.extend_from_slice(&le_u16(entries.len() as u16));
1225
1226 let mut deferred = Vec::new();
1227 for (tag, (ty, count, value)) in entries {
1228 data.extend_from_slice(&le_u16(tag));
1229 data.extend_from_slice(&le_u16(ty));
1230 data.extend_from_slice(&le_u32(count));
1231 if value.len() <= 4 {
1232 let mut inline = [0u8; 4];
1233 inline[..value.len()].copy_from_slice(&value);
1234 data.extend_from_slice(&inline);
1235 } else {
1236 let offset = next_data_offset as u32;
1237 data.extend_from_slice(&le_u32(offset));
1238 next_data_offset += value.len();
1239 deferred.push(value);
1240 }
1241 }
1242 data.extend_from_slice(&le_u32(0));
1243 for tile in tiles {
1244 data.extend_from_slice(tile);
1245 }
1246 for value in deferred {
1247 data.extend_from_slice(&value);
1248 }
1249 data
1250 }
1251
1252 fn build_multi_strip_tiff(width: u32, rows: &[&[u8]]) -> Vec<u8> {
1253 let mut entries = BTreeMap::new();
1254 entries.insert(256, (4, 1, le_u32(width).to_vec()));
1255 entries.insert(257, (4, 1, le_u32(rows.len() as u32).to_vec()));
1256 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
1257 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
1258 entries.insert(277, (3, 1, [1, 0, 0, 0].to_vec()));
1259 entries.insert(278, (4, 1, le_u32(1).to_vec()));
1260 entries.insert(
1261 279,
1262 (
1263 4,
1264 rows.len() as u32,
1265 rows.iter()
1266 .flat_map(|row| le_u32(row.len() as u32))
1267 .collect(),
1268 ),
1269 );
1270
1271 let ifd_offset = 8u32;
1272 let ifd_size = 2 + (entries.len() + 1) * 12 + 4;
1273 let mut strip_data_offset = ifd_offset as usize + ifd_size;
1274 let strip_offsets: Vec<u32> = rows
1275 .iter()
1276 .map(|row| {
1277 let offset = strip_data_offset as u32;
1278 strip_data_offset += row.len();
1279 offset
1280 })
1281 .collect();
1282 entries.insert(
1283 273,
1284 (
1285 4,
1286 strip_offsets.len() as u32,
1287 strip_offsets
1288 .iter()
1289 .flat_map(|offset| le_u32(*offset))
1290 .collect(),
1291 ),
1292 );
1293
1294 let mut next_data_offset = strip_data_offset;
1295 let mut data = Vec::with_capacity(next_data_offset);
1296 data.extend_from_slice(b"II");
1297 data.extend_from_slice(&le_u16(42));
1298 data.extend_from_slice(&le_u32(ifd_offset));
1299 data.extend_from_slice(&le_u16(entries.len() as u16));
1300
1301 let mut deferred = Vec::new();
1302 for (tag, (ty, count, value)) in entries {
1303 data.extend_from_slice(&le_u16(tag));
1304 data.extend_from_slice(&le_u16(ty));
1305 data.extend_from_slice(&le_u32(count));
1306 if value.len() <= 4 {
1307 let mut inline = [0u8; 4];
1308 inline[..value.len()].copy_from_slice(&value);
1309 data.extend_from_slice(&inline);
1310 } else {
1311 let offset = next_data_offset as u32;
1312 data.extend_from_slice(&le_u32(offset));
1313 next_data_offset += value.len();
1314 deferred.push(value);
1315 }
1316 }
1317 data.extend_from_slice(&le_u32(0));
1318 for row in rows {
1319 data.extend_from_slice(row);
1320 }
1321 for value in deferred {
1322 data.extend_from_slice(&value);
1323 }
1324 data
1325 }
1326
1327 fn build_planar_stripped_tiff(width: u32, height: u32, planes: &[&[u8]]) -> Vec<u8> {
1328 let mut entries = BTreeMap::new();
1329 entries.insert(256, (4, 1, le_u32(width).to_vec()));
1330 entries.insert(257, (4, 1, le_u32(height).to_vec()));
1331 entries.insert(258, (3, 1, [8, 0, 0, 0].to_vec()));
1332 entries.insert(259, (3, 1, [1, 0, 0, 0].to_vec()));
1333 entries.insert(262, (3, 1, [2, 0, 0, 0].to_vec()));
1334 entries.insert(277, (3, 1, inline_short(planes.len() as u16)));
1335 entries.insert(278, (4, 1, le_u32(height).to_vec()));
1336 entries.insert(284, (3, 1, [2, 0, 0, 0].to_vec()));
1337 entries.insert(
1338 279,
1339 (
1340 4,
1341 planes.len() as u32,
1342 planes
1343 .iter()
1344 .flat_map(|plane| le_u32(plane.len() as u32))
1345 .collect(),
1346 ),
1347 );
1348
1349 let ifd_offset = 8u32;
1350 let ifd_size = 2 + (entries.len() + 1) * 12 + 4;
1351 let mut strip_data_offset = ifd_offset as usize + ifd_size;
1352 let strip_offsets: Vec<u32> = planes
1353 .iter()
1354 .map(|plane| {
1355 let offset = strip_data_offset as u32;
1356 strip_data_offset += plane.len();
1357 offset
1358 })
1359 .collect();
1360 entries.insert(
1361 273,
1362 (
1363 4,
1364 strip_offsets.len() as u32,
1365 strip_offsets
1366 .iter()
1367 .flat_map(|offset| le_u32(*offset))
1368 .collect(),
1369 ),
1370 );
1371
1372 let mut next_data_offset = strip_data_offset;
1373 let mut data = Vec::with_capacity(next_data_offset);
1374 data.extend_from_slice(b"II");
1375 data.extend_from_slice(&le_u16(42));
1376 data.extend_from_slice(&le_u32(ifd_offset));
1377 data.extend_from_slice(&le_u16(entries.len() as u16));
1378
1379 let mut deferred = Vec::new();
1380 for (tag, (ty, count, value)) in entries {
1381 data.extend_from_slice(&le_u16(tag));
1382 data.extend_from_slice(&le_u16(ty));
1383 data.extend_from_slice(&le_u32(count));
1384 if value.len() <= 4 {
1385 let mut inline = [0u8; 4];
1386 inline[..value.len()].copy_from_slice(&value);
1387 data.extend_from_slice(&inline);
1388 } else {
1389 let offset = next_data_offset as u32;
1390 data.extend_from_slice(&le_u32(offset));
1391 next_data_offset += value.len();
1392 deferred.push(value);
1393 }
1394 }
1395 data.extend_from_slice(&le_u32(0));
1396 for plane in planes {
1397 data.extend_from_slice(plane);
1398 }
1399 for value in deferred {
1400 data.extend_from_slice(&value);
1401 }
1402 data
1403 }
1404
1405 struct CountingSource {
1406 bytes: Vec<u8>,
1407 reads: AtomicUsize,
1408 }
1409
1410 impl CountingSource {
1411 fn new(bytes: Vec<u8>) -> Self {
1412 Self {
1413 bytes,
1414 reads: AtomicUsize::new(0),
1415 }
1416 }
1417
1418 fn reset_reads(&self) {
1419 self.reads.store(0, Ordering::SeqCst);
1420 }
1421
1422 fn reads(&self) -> usize {
1423 self.reads.load(Ordering::SeqCst)
1424 }
1425 }
1426
1427 impl TiffSource for CountingSource {
1428 fn len(&self) -> u64 {
1429 self.bytes.len() as u64
1430 }
1431
1432 fn read_exact_at(&self, offset: u64, len: usize) -> crate::error::Result<Vec<u8>> {
1433 self.reads.fetch_add(1, Ordering::SeqCst);
1434 let start =
1435 usize::try_from(offset).map_err(|_| crate::error::Error::OffsetOutOfBounds {
1436 offset,
1437 length: len as u64,
1438 data_len: self.len(),
1439 })?;
1440 let end = start
1441 .checked_add(len)
1442 .ok_or(crate::error::Error::OffsetOutOfBounds {
1443 offset,
1444 length: len as u64,
1445 data_len: self.len(),
1446 })?;
1447 if end > self.bytes.len() {
1448 return Err(crate::error::Error::OffsetOutOfBounds {
1449 offset,
1450 length: len as u64,
1451 data_len: self.len(),
1452 });
1453 }
1454 Ok(self.bytes[start..end].to_vec())
1455 }
1456 }
1457
1458 #[test]
1459 fn reads_stripped_u8_image() {
1460 let data = build_stripped_tiff(2, 2, &[1, 2, 3, 4], &[]);
1461 let file = TiffFile::from_bytes(data).unwrap();
1462 let image = file.read_image::<u8>(0).unwrap();
1463 assert_eq!(image.shape(), &[2, 2]);
1464 let (values, offset) = image.into_raw_vec_and_offset();
1465 assert_eq!(offset, Some(0));
1466 assert_eq!(values, vec![1, 2, 3, 4]);
1467 }
1468
1469 #[test]
1470 fn reads_single_chunky_band_and_window() {
1471 let data = build_stripped_tiff(
1472 2,
1473 2,
1474 &[
1475 1, 10, 100, 2, 20, 110, 3, 30, 120, 4, 40, 130,
1479 ],
1480 &[
1481 (262, 3, 1, inline_short(2)),
1482 (277, 3, 1, inline_short(3)),
1483 (279, 4, 1, le_u32(12).to_vec()),
1484 ],
1485 );
1486 let file = TiffFile::from_bytes(data).unwrap();
1487
1488 let green = file.read_band::<u8>(0, 1).unwrap();
1489 assert_eq!(green.shape(), &[2, 2]);
1490 let (green_values, offset) = green.into_raw_vec_and_offset();
1491 assert_eq!(offset, Some(0));
1492 assert_eq!(green_values, vec![10, 20, 30, 40]);
1493
1494 let blue_window = file.read_band_window::<u8>(0, 2, 0, 1, 2, 1).unwrap();
1495 assert_eq!(blue_window.shape(), &[2, 1]);
1496 let (blue_values, offset) = blue_window.into_raw_vec_and_offset();
1497 assert_eq!(offset, Some(0));
1498 assert_eq!(blue_values, vec![110, 130]);
1499
1500 let err = file.read_band::<u8>(0, 3).unwrap_err();
1501 assert!(matches!(
1502 err,
1503 Error::BandIndexOutOfBounds {
1504 index: 3,
1505 band_count: 3
1506 }
1507 ));
1508 }
1509
1510 #[test]
1511 fn planar_band_reads_only_requested_plane() {
1512 let data = build_planar_stripped_tiff(
1513 2,
1514 2,
1515 &[&[1, 2, 3, 4], &[10, 20, 30, 40], &[100, 110, 120, 130]],
1516 );
1517 let source = Arc::new(CountingSource::new(data));
1518 let file = TiffFile::from_source(source.clone()).unwrap();
1519 source.reset_reads();
1520
1521 let blue = file.read_band::<u8>(0, 2).unwrap();
1522 assert_eq!(blue.shape(), &[2, 2]);
1523 let (values, offset) = blue.into_raw_vec_and_offset();
1524 assert_eq!(offset, Some(0));
1525 assert_eq!(values, vec![100, 110, 120, 130]);
1526 assert_eq!(source.reads(), 1);
1527 }
1528
1529 #[test]
1530 fn keeps_subbyte_palette_reads_raw_and_offers_explicit_decoded_pixels() {
1531 let mut color_map = Vec::new();
1532 color_map.extend((0u16..16).map(|value| value * 17 * 257));
1533 color_map.extend((0u16..16).map(|value| (15 - value) * 17 * 257));
1534 color_map.extend((0u16..16).map(|value| value * 8 * 257));
1535 let data = build_stripped_tiff(
1536 4,
1537 1,
1538 &[0x01, 0x23],
1539 &[
1540 (258, 3, 1, inline_short(4)),
1541 (262, 3, 1, inline_short(3)),
1542 (
1543 320,
1544 3,
1545 color_map.len() as u32,
1546 color_map.iter().flat_map(|value| le_u16(*value)).collect(),
1547 ),
1548 ],
1549 );
1550 let file = TiffFile::from_bytes(data).unwrap();
1551
1552 let image = file.read_image::<u8>(0).unwrap();
1553 assert_eq!(image.shape(), &[1, 4]);
1554 let (values, offset) = image.into_raw_vec_and_offset();
1555 assert_eq!(offset, Some(0));
1556 assert_eq!(values, vec![0, 1, 2, 3]);
1557
1558 let image = file.read_decoded_image::<u8>(0).unwrap();
1559 assert_eq!(image.shape(), &[1, 4, 3]);
1560 let (values, offset) = image.into_raw_vec_and_offset();
1561 assert_eq!(offset, Some(0));
1562 assert_eq!(
1563 values,
1564 vec![
1565 0, 255, 0, 17, 238, 8, 34, 221, 16, 51, 204, 24
1569 ]
1570 );
1571
1572 let sample_bytes = file.read_image_sample_bytes(0).unwrap();
1573 assert_eq!(sample_bytes, vec![0, 1, 2, 3]);
1574 }
1575
1576 #[test]
1577 fn keeps_subsampled_ycbcr_reads_raw_and_offers_explicit_decoded_pixels() {
1578 let data = build_stripped_tiff(
1579 2,
1580 2,
1581 &[10u8, 20, 30, 40, 128, 128],
1582 &[
1583 (
1584 258,
1585 3,
1586 3,
1587 [8u16, 8, 8].into_iter().flat_map(le_u16).collect(),
1588 ),
1589 (262, 3, 1, inline_short(6)),
1590 (277, 3, 1, inline_short(3)),
1591 (530, 3, 2, [2u16, 2].into_iter().flat_map(le_u16).collect()),
1592 ],
1593 );
1594 let file = TiffFile::from_bytes(data).unwrap();
1595
1596 let image = file.read_image::<u8>(0).unwrap();
1597 assert_eq!(image.shape(), &[2, 2, 3]);
1598 let (values, offset) = image.into_raw_vec_and_offset();
1599 assert_eq!(offset, Some(0));
1600 assert_eq!(
1601 values,
1602 vec![
1603 10, 128, 128, 20, 128, 128, 30, 128, 128, 40, 128, 128
1607 ]
1608 );
1609
1610 let image = file.read_decoded_image::<u8>(0).unwrap();
1611 assert_eq!(image.shape(), &[2, 2, 3]);
1612 let (rgb, offset) = image.into_raw_vec_and_offset();
1613 assert_eq!(offset, Some(0));
1614 assert_eq!(
1615 rgb,
1616 vec![
1617 10, 10, 10, 20, 20, 20, 30, 30, 30, 40, 40, 40
1621 ]
1622 );
1623
1624 let samples = file.read_image_samples::<u8>(0).unwrap();
1625 let (values, offset) = samples.into_raw_vec_and_offset();
1626 assert_eq!(offset, Some(0));
1627 assert_eq!(
1628 values,
1629 vec![
1630 10, 128, 128, 20, 128, 128, 30, 128, 128, 40, 128, 128
1634 ]
1635 );
1636 }
1637
1638 #[test]
1639 fn reads_horizontal_predictor_u16_strip() {
1640 let encoded = [1, 0, 1, 0, 2, 0];
1641 let data = build_stripped_tiff(
1642 3,
1643 1,
1644 &encoded,
1645 &[
1646 (258, 3, 1, [16, 0, 0, 0].to_vec()),
1647 (317, 3, 1, [2, 0, 0, 0].to_vec()),
1648 ],
1649 );
1650 let file = TiffFile::from_bytes(data).unwrap();
1651 let image = file.read_image::<u16>(0).unwrap();
1652 assert_eq!(image.shape(), &[1, 3]);
1653 let (values, offset) = image.into_raw_vec_and_offset();
1654 assert_eq!(offset, Some(0));
1655 assert_eq!(values, vec![1, 2, 4]);
1656 }
1657
1658 #[test]
1659 fn reads_lerc_f32_strip() {
1660 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1661 blob.extend_from_slice(&0u32.to_le_bytes());
1662 blob.push(1);
1663 for value in [1.0f32, 2.0, 3.0, 4.0] {
1664 blob.extend_from_slice(&value.to_le_bytes());
1665 }
1666
1667 let data = build_lerc_tiff(2, 2, &blob, 32, 3, 1, None);
1668 let file = TiffFile::from_bytes(data).unwrap();
1669 let image = file.read_image::<f32>(0).unwrap();
1670 let (values, offset) = image.into_raw_vec_and_offset();
1671 assert_eq!(offset, Some(0));
1672 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1673 }
1674
1675 #[test]
1676 fn reads_lerc_masked_f32_strip_as_nan() {
1677 let mask = [1u8, 0, 1, 1];
1678 let encoded_mask = encode_mask_rle(&mask);
1679 let mut blob =
1680 build_lerc2_header_v2(2, 2, 3, 6, 0.0, 1.0, 4.0, encoded_mask.len() + 1 + 12);
1681 blob.extend_from_slice(&(encoded_mask.len() as u32).to_le_bytes());
1682 blob.extend_from_slice(&encoded_mask);
1683 blob.push(1);
1684 for value in [1.0f32, 3.0, 4.0] {
1685 blob.extend_from_slice(&value.to_le_bytes());
1686 }
1687
1688 let data = build_lerc_tiff(2, 2, &blob, 32, 3, 1, None);
1689 let file = TiffFile::from_bytes(data).unwrap();
1690 let image = file.read_image::<f32>(0).unwrap();
1691 let (values, offset) = image.into_raw_vec_and_offset();
1692 assert_eq!(offset, Some(0));
1693 assert_eq!(values[0], 1.0);
1694 assert!(values[1].is_nan());
1695 assert_eq!(values[2], 3.0);
1696 assert_eq!(values[3], 4.0);
1697 }
1698
1699 #[test]
1700 fn reads_lerc_chunky_rgb_band_set_strip() {
1701 let mut red = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 1.0, 1.0, 0);
1702 red.extend_from_slice(&0u32.to_le_bytes());
1703 let mut green = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 2.0, 2.0, 0);
1704 green.extend_from_slice(&0u32.to_le_bytes());
1705 let mut blue = build_lerc2_header_v2(2, 1, 2, 1, 0.0, 3.0, 3.0, 0);
1706 blue.extend_from_slice(&0u32.to_le_bytes());
1707
1708 let mut blob = red;
1709 blob.extend_from_slice(&green);
1710 blob.extend_from_slice(&blue);
1711
1712 let data = build_lerc_tiff(2, 1, &blob, 8, 1, 3, None);
1713 let file = TiffFile::from_bytes(data).unwrap();
1714 let image = file.read_image::<u8>(0).unwrap();
1715 assert_eq!(image.shape(), &[1, 2, 3]);
1716 let (values, offset) = image.into_raw_vec_and_offset();
1717 assert_eq!(offset, Some(0));
1718 assert_eq!(values, vec![1, 2, 3, 1, 2, 3]);
1719 }
1720
1721 #[test]
1722 fn reads_lerc_chunky_rgb_depth_blob_strip() {
1723 let mut blob = build_lerc2_header_v4(2, 1, 3, 2, 1, 0.0, 1.0, 6.0, 6 + 6 + 1 + 6);
1724 blob.extend_from_slice(&0u32.to_le_bytes());
1725 for value in [1u8, 2, 3] {
1726 blob.extend_from_slice(&value.to_le_bytes());
1727 }
1728 for value in [4u8, 5, 6] {
1729 blob.extend_from_slice(&value.to_le_bytes());
1730 }
1731 blob.push(1);
1732 blob.extend_from_slice(&[1, 2, 3, 4, 5, 6]);
1733 let blob = finalize_lerc2_v4_with_checksum(blob);
1734
1735 let data = build_lerc_tiff(2, 1, &blob, 8, 1, 3, Some([4, 0]));
1736 let file = TiffFile::from_bytes(data).unwrap();
1737 let image = file.read_image::<u8>(0).unwrap();
1738 assert_eq!(image.shape(), &[1, 2, 3]);
1739 let (values, offset) = image.into_raw_vec_and_offset();
1740 assert_eq!(offset, Some(0));
1741 assert_eq!(values, vec![1, 2, 3, 4, 5, 6]);
1742 }
1743
1744 #[test]
1745 fn rejects_lerc2_blob_size_before_checksum_range_without_panicking() {
1746 let mut blob = build_lerc2_header_v4(1, 1, 1, 1, 1, 0.0, 1.0, 1.0, 0);
1747 blob[34..38].copy_from_slice(&8i32.to_le_bytes());
1748
1749 let data = build_lerc_tiff(1, 1, &blob, 8, 1, 1, Some([4, 0]));
1750 let file = TiffFile::from_bytes(data).unwrap();
1751 let error = file.read_image_bytes(0).unwrap_err();
1752 assert!(error.to_string().contains("invalid Lerc2 v4 blob size 8"));
1753 }
1754
1755 #[test]
1756 fn rejects_lerc2_header_dimensions_before_allocating_mask() {
1757 let mut blob = build_lerc2_header_v2(u32::MAX, u32::MAX, 1, 1, 0.0, 0.0, 1.0, 4);
1758 blob.extend_from_slice(&4u32.to_le_bytes());
1759 blob.extend_from_slice(&[0, 0, 0, 0]);
1760
1761 let data = build_lerc_tiff(1, 1, &blob, 8, 1, 1, None);
1762 let file = TiffFile::from_bytes(data).unwrap();
1763 let error = file.read_image_bytes(0).unwrap_err();
1764 assert!(error.to_string().contains("LERC raster dimensions"));
1765 }
1766
1767 #[test]
1768 fn rejects_truncated_lerc2_header_dimensions_before_decoder() {
1769 let blob = build_lerc2_header_v2(u32::MAX, u32::MAX, 1, 1, 0.0, 0.0, 1.0, 64);
1770
1771 let data = build_lerc_tiff(1, 1, &blob, 8, 1, 1, None);
1772 let file = TiffFile::from_bytes(data).unwrap();
1773 let error = file.read_image_bytes(0).unwrap_err();
1774 assert!(error.to_string().contains("LERC raster dimensions"));
1775 }
1776
1777 #[test]
1778 fn reads_lerc_deflate_f32_strip() {
1779 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1780 blob.extend_from_slice(&0u32.to_le_bytes());
1781 blob.push(1);
1782 for value in [1.0f32, 2.0, 3.0, 4.0] {
1783 blob.extend_from_slice(&value.to_le_bytes());
1784 }
1785
1786 let mut encoder = ZlibEncoder::new(Vec::new(), FlateCompression::default());
1787 std::io::Write::write_all(&mut encoder, &blob).unwrap();
1788 let compressed = encoder.finish().unwrap();
1789
1790 let data = build_lerc_tiff(2, 2, &compressed, 32, 3, 1, Some([2, 1]));
1791 let file = TiffFile::from_bytes(data).unwrap();
1792 let image = file.read_image::<f32>(0).unwrap();
1793 let (values, offset) = image.into_raw_vec_and_offset();
1794 assert_eq!(offset, Some(0));
1795 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1796 }
1797
1798 #[cfg(feature = "zstd")]
1799 #[test]
1800 fn reads_lerc_zstd_f32_strip() {
1801 let mut blob = build_lerc2_header_v2(2, 2, 4, 6, 0.0, 1.0, 4.0, 1 + 16);
1802 blob.extend_from_slice(&0u32.to_le_bytes());
1803 blob.push(1);
1804 for value in [1.0f32, 2.0, 3.0, 4.0] {
1805 blob.extend_from_slice(&value.to_le_bytes());
1806 }
1807
1808 let compressed = zstd::stream::encode_all(std::io::Cursor::new(blob), 0).unwrap();
1809 let data = build_lerc_tiff(2, 2, &compressed, 32, 3, 1, Some([2, 2]));
1810 let file = TiffFile::from_bytes(data).unwrap();
1811 let image = file.read_image::<f32>(0).unwrap();
1812 let (values, offset) = image.into_raw_vec_and_offset();
1813 assert_eq!(offset, Some(0));
1814 assert_eq!(values, vec![1.0, 2.0, 3.0, 4.0]);
1815 }
1816
1817 #[test]
1818 fn reads_stripped_u8_window() {
1819 let data = build_multi_strip_tiff(
1820 4,
1821 &[
1822 &[1, 2, 3, 4],
1823 &[5, 6, 7, 8],
1824 &[9, 10, 11, 12],
1825 &[13, 14, 15, 16],
1826 ],
1827 );
1828 let file = TiffFile::from_bytes(data).unwrap();
1829 let window = file.read_window::<u8>(0, 1, 1, 2, 2).unwrap();
1830 assert_eq!(window.shape(), &[2, 2]);
1831 let (values, offset) = window.into_raw_vec_and_offset();
1832 assert_eq!(offset, Some(0));
1833 assert_eq!(values, vec![6, 7, 10, 11]);
1834 }
1835
1836 #[test]
1837 fn reads_tiled_u8_window() {
1838 let data = build_tiled_tiff(
1839 4,
1840 4,
1841 2,
1842 2,
1843 &[
1844 &[1, 2, 5, 6],
1845 &[3, 4, 7, 8],
1846 &[9, 10, 13, 14],
1847 &[11, 12, 15, 16],
1848 ],
1849 );
1850 let file = TiffFile::from_bytes(data).unwrap();
1851 let window = file.read_window::<u8>(0, 1, 1, 2, 2).unwrap();
1852 assert_eq!(window.shape(), &[2, 2]);
1853 let (values, offset) = window.into_raw_vec_and_offset();
1854 assert_eq!(offset, Some(0));
1855 assert_eq!(values, vec![6, 7, 10, 11]);
1856 }
1857
1858 #[test]
1859 fn windowed_tiled_reads_only_intersecting_blocks() {
1860 let data = build_tiled_tiff(
1861 4,
1862 4,
1863 2,
1864 2,
1865 &[
1866 &[1, 2, 5, 6],
1867 &[3, 4, 7, 8],
1868 &[9, 10, 13, 14],
1869 &[11, 12, 15, 16],
1870 ],
1871 );
1872 let source = Arc::new(CountingSource::new(data));
1873 let file = TiffFile::from_source(source.clone()).unwrap();
1874 source.reset_reads();
1875
1876 let window = file.read_window::<u8>(0, 0, 0, 2, 2).unwrap();
1877 let (values, offset) = window.into_raw_vec_and_offset();
1878 assert_eq!(offset, Some(0));
1879 assert_eq!(values, vec![1, 2, 5, 6]);
1880 assert_eq!(source.reads(), 1);
1881 }
1882
1883 #[test]
1884 fn unwraps_gdal_structural_metadata_block() {
1885 let metadata = GdalStructuralMetadata::from_prefix(
1886 b"GDAL_STRUCTURAL_METADATA_SIZE=000174 bytes\nBLOCK_LEADER=SIZE_AS_UINT4\nBLOCK_TRAILER=LAST_4_BYTES_REPEATED\n",
1887 )
1888 .unwrap();
1889
1890 let payload = [1u8, 2, 3, 4];
1891 let mut block = Vec::new();
1892 block.extend_from_slice(&(payload.len() as u32).to_le_bytes());
1893 block.extend_from_slice(&payload);
1894 block.extend_from_slice(&payload[payload.len() - 4..]);
1895
1896 let unwrapped = metadata
1897 .unwrap_block(&block, crate::ByteOrder::LittleEndian, 256)
1898 .unwrap();
1899 assert_eq!(unwrapped, payload);
1900 }
1901
1902 #[test]
1903 fn rejects_gdal_structural_metadata_trailer_mismatch() {
1904 let metadata = GdalStructuralMetadata::from_prefix(
1905 b"GDAL_STRUCTURAL_METADATA_SIZE=000174 bytes\nBLOCK_LEADER=SIZE_AS_UINT4\nBLOCK_TRAILER=LAST_4_BYTES_REPEATED\n",
1906 )
1907 .unwrap();
1908
1909 let block = [
1910 4u8, 0, 0, 0, 1, 2, 3, 4, 4, 3, 2, 1,
1913 ];
1914
1915 let error = metadata
1916 .unwrap_block(&block, crate::ByteOrder::LittleEndian, 512)
1917 .unwrap_err();
1918 assert!(error.to_string().contains("GDAL block trailer mismatch"));
1919 }
1920
1921 #[test]
1922 fn parses_gdal_structural_metadata_before_binary_prefix_data() {
1923 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";
1924 let prefix = format!(
1925 "{GDAL_STRUCTURAL_METADATA_PREFIX}{:06} bytes\n{rest}",
1926 rest.len()
1927 );
1928
1929 let mut bytes = vec![0u8; 8];
1930 bytes.extend_from_slice(prefix.as_bytes());
1931 bytes.extend_from_slice(&[0xff, 0x00, 0x80, 0x7f]);
1932
1933 let source = BytesSource::new(bytes);
1934 let metadata = parse_gdal_structural_metadata(&source).unwrap();
1935 assert!(metadata.block_leader_size_as_u32);
1936 assert!(metadata.block_trailer_repeats_last_4_bytes);
1937 }
1938
1939 #[test]
1940 fn parses_gdal_structural_metadata_declared_length_as_header_plus_payload() {
1941 let rest = "LAYOUT=IFDS_BEFORE_DATA\nBLOCK_ORDER=ROW_MAJOR\n";
1942 let prefix = format!(
1943 "{GDAL_STRUCTURAL_METADATA_PREFIX}{:06} bytes\n{rest}",
1944 rest.len()
1945 );
1946 assert_eq!(
1947 parse_gdal_structural_metadata_len(prefix.as_bytes()),
1948 Some(prefix.len())
1949 );
1950 }
1951
1952 #[test]
1953 fn leaves_payload_only_gdal_block_unchanged() {
1954 let metadata = GdalStructuralMetadata {
1955 block_leader_size_as_u32: true,
1956 block_trailer_repeats_last_4_bytes: true,
1957 };
1958 let payload = [0x80u8, 0x1a, 0xcf, 0x68, 0x43, 0x9a, 0x11, 0x08];
1959 let unwrapped = metadata
1960 .unwrap_block(&payload, crate::ByteOrder::LittleEndian, 570)
1961 .unwrap();
1962 assert_eq!(unwrapped, payload);
1963 }
1964
1965 #[test]
1966 fn rejects_zero_rows_per_strip_without_panicking() {
1967 let data = build_stripped_tiff(2, 2, &[1, 2, 3, 4], &[(278, 4, 1, le_u32(0).to_vec())]);
1968 let file = TiffFile::from_bytes(data).unwrap();
1969 let error = file.read_image_bytes(0).unwrap_err();
1970 assert!(error.to_string().contains("RowsPerStrip"));
1971 }
1972}