|
1 | 1 | use std::collections::HashMap;
|
2 | 2 | use std::io::Read;
|
3 | 3 | use std::ops::Range;
|
| 4 | +use std::sync::Arc; |
4 | 5 |
|
5 | 6 | use bytes::{buf::Buf, Bytes};
|
6 | 7 | use num_enum::TryFromPrimitive;
|
@@ -195,26 +196,24 @@ impl ImageFileDirectory {
|
195 | 196 | } else {
|
196 | 197 | cursor.read_u16().await?.into()
|
197 | 198 | };
|
198 |
| - let mut tags = HashMap::with_capacity(tag_count as usize); |
199 |
| - for _ in 0..tag_count { |
200 |
| - let (tag_name, tag_value) = read_tag(cursor, bigtiff).await?; |
201 |
| - tags.insert(tag_name, tag_value); |
202 |
| - } |
203 |
| - |
204 | 199 | // Tag 2 bytes
|
205 | 200 | // Type 2 bytes
|
206 | 201 | // Count:
|
207 | 202 | // - bigtiff: 8 bytes
|
208 | 203 | // - else: 4 bytes
|
209 | 204 | // Value:
|
210 |
| - // - bigtiff: 8 bytes either a pointer the value itself |
211 |
| - // - else: 4 bytes either a pointer the value itself |
| 205 | + // - bigtiff: 8 bytes either a pointer or the value itself |
| 206 | + // - else: 4 bytes either a pointer or the value itself |
212 | 207 | let ifd_entry_byte_size = if bigtiff { 20 } else { 12 };
|
213 |
| - // The size of `tag_count` that we read above |
214 |
| - let tag_count_byte_size = if bigtiff { 8 } else { 2 }; |
215 | 208 |
|
216 |
| - // Reset the cursor position before reading the next ifd offset |
217 |
| - cursor.seek(ifd_start + (ifd_entry_byte_size * tag_count) + tag_count_byte_size); |
| 209 | + // read all tag data into an EndianAwareReader |
| 210 | + let mut reader = cursor.read(ifd_entry_byte_size * tag_count).await?; |
| 211 | + |
| 212 | + let mut tags = HashMap::with_capacity(tag_count as usize); |
| 213 | + for _ in 0..tag_count { |
| 214 | + let (tag_name, tag_value) = read_tag(&mut reader, cursor.reader(), bigtiff).await?; |
| 215 | + tags.insert(tag_name, tag_value); |
| 216 | + } |
218 | 217 |
|
219 | 218 | let next_ifd_offset = if bigtiff {
|
220 | 219 | cursor.read_u64().await?
|
@@ -838,76 +837,80 @@ impl ImageFileDirectory {
|
838 | 837 | }
|
839 | 838 |
|
840 | 839 | /// Read a single tag from the cursor
|
841 |
| -async fn read_tag(cursor: &mut AsyncCursor, bigtiff: bool) -> AsyncTiffResult<(Tag, Value)> { |
| 840 | +async fn read_tag( |
| 841 | + cursor: &mut EndianAwareReader, |
| 842 | + file_reader: &Arc<dyn AsyncFileReader>, |
| 843 | + bigtiff: bool, |
| 844 | +) -> AsyncTiffResult<(Tag, Value)> { |
842 | 845 | // let start_cursor_position = cursor.position();
|
843 | 846 |
|
844 |
| - let tag_name = Tag::from_u16_exhaustive(cursor.read_u16().await?); |
| 847 | + let tag_name = Tag::from_u16_exhaustive(cursor.read_u16()?); |
845 | 848 |
|
846 |
| - let tag_type_code = cursor.read_u16().await?; |
| 849 | + let tag_type_code = cursor.read_u16()?; |
847 | 850 | let tag_type = Type::from_u16(tag_type_code).expect(
|
848 | 851 | "Unknown tag type {tag_type_code}. TODO: we should skip entries with unknown tag types.",
|
849 | 852 | );
|
850 | 853 | let count = if bigtiff {
|
851 |
| - cursor.read_u64().await? |
| 854 | + cursor.read_u64()? |
852 | 855 | } else {
|
853 |
| - cursor.read_u32().await?.into() |
| 856 | + cursor.read_u32()?.into() |
854 | 857 | };
|
855 | 858 |
|
856 |
| - let tag_value = read_tag_value(cursor, tag_type, count, bigtiff).await?; |
857 |
| - |
858 |
| - // TODO: better handle management of cursor state <- should be done now |
859 |
| - // let ifd_entry_size = if bigtiff { 20 } else { 12 }; |
860 |
| - // cursor.seek(start_cursor_position + ifd_entry_size); |
861 |
| - |
862 |
| - Ok((tag_name, tag_value)) |
863 |
| -} |
864 |
| - |
865 |
| -/// Read a tag's value from the cursor |
866 |
| -/// |
867 |
| -/// NOTE: this does not maintain cursor state |
868 |
| -// This is derived from the upstream tiff crate: |
869 |
| -// https://github.com/image-rs/image-tiff/blob/6dc7a266d30291db1e706c8133357931f9e2a053/src/decoder/ifd.rs#L369-L639 |
870 |
| -async fn read_tag_value( |
871 |
| - cursor: &mut AsyncCursor, |
872 |
| - tag_type: Type, |
873 |
| - count: u64, |
874 |
| - bigtiff: bool, |
875 |
| -) -> AsyncTiffResult<Value> { |
876 |
| - // Case 1: there are no values so we can return immediately. |
877 |
| - if count == 0 { |
878 |
| - return Ok(Value::List(vec![])); |
879 |
| - } |
880 |
| - |
881 | 859 | let tag_size = tag_type.size();
|
882 | 860 |
|
883 | 861 | let value_byte_length = count.checked_mul(tag_size).unwrap();
|
884 | 862 |
|
885 | 863 | // prefetch all tag data
|
886 | 864 | let mut data = if (bigtiff && value_byte_length <= 8) || value_byte_length <= 4 {
|
887 | 865 | // value fits in offset field
|
888 |
| - let res = cursor.read(value_byte_length).await?; |
| 866 | + let mut res = vec![0u8; value_byte_length as usize]; |
| 867 | + cursor.read_exact(&mut res)?; |
889 | 868 | if bigtiff {
|
890 |
| - cursor.advance(8 - value_byte_length); |
| 869 | + cursor.advance(8 - value_byte_length)?; |
891 | 870 | } else {
|
892 |
| - cursor.advance(4 - value_byte_length); |
| 871 | + cursor.advance(4 - value_byte_length)?; |
893 | 872 | }
|
894 |
| - res |
| 873 | + EndianAwareReader::new(Bytes::from_owner(res).reader(), cursor.endianness()) |
895 | 874 | } else {
|
896 |
| - // Seek cursor |
| 875 | + // fetch using file_reader |
897 | 876 | let offset = if bigtiff {
|
898 |
| - cursor.read_u64().await? |
| 877 | + cursor.read_u64()? |
899 | 878 | } else {
|
900 |
| - cursor.read_u32().await?.into() |
| 879 | + cursor.read_u32()?.into() |
901 | 880 | };
|
902 |
| - let reader = cursor |
903 |
| - .reader() |
| 881 | + let reader = file_reader |
904 | 882 | .get_bytes(offset..offset + value_byte_length)
|
905 | 883 | .await?
|
906 | 884 | .reader();
|
907 | 885 | EndianAwareReader::new(reader, cursor.endianness())
|
908 | 886 | // cursor.seek(offset);
|
909 | 887 | // cursor.read(value_byte_length).await?
|
910 | 888 | };
|
| 889 | + |
| 890 | + let tag_value = read_tag_value(&mut data, tag_type, count)?; |
| 891 | + |
| 892 | + // TODO: better handle management of cursor state <- should be done now |
| 893 | + // let ifd_entry_size = if bigtiff { 20 } else { 12 }; |
| 894 | + // cursor.seek(start_cursor_position + ifd_entry_size); |
| 895 | + |
| 896 | + Ok((tag_name, tag_value)) |
| 897 | +} |
| 898 | + |
| 899 | +/// Read a tag's value from the cursor |
| 900 | +/// |
| 901 | +/// NOTE: this does not maintain cursor state |
| 902 | +// This is derived from the upstream tiff crate: |
| 903 | +// https://github.com/image-rs/image-tiff/blob/6dc7a266d30291db1e706c8133357931f9e2a053/src/decoder/ifd.rs#L369-L639 |
| 904 | +fn read_tag_value( |
| 905 | + data: &mut EndianAwareReader, |
| 906 | + tag_type: Type, |
| 907 | + count: u64, |
| 908 | +) -> AsyncTiffResult<Value> { |
| 909 | + // Case 1: there are no values so we can return immediately. |
| 910 | + if count == 0 { |
| 911 | + return Ok(Value::List(vec![])); |
| 912 | + } |
| 913 | + |
911 | 914 | // Case 2: there is one value.
|
912 | 915 | if count == 1 {
|
913 | 916 | return Ok(match tag_type {
|
|
0 commit comments