Skip to content

Commit 28d729a

Browse files
authored
feat: Include Endianness as property of TIFF struct (#149)
1 parent d803d0f commit 28d729a

File tree

9 files changed

+68
-13
lines changed

9 files changed

+68
-13
lines changed

python/python/async_tiff/_tiff.pyi

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from typing import Protocol
2-
from ._tile import Tile
3-
from ._ifd import ImageFileDirectory
4-
from .store import ObjectStore
52

63
# Fix exports
74
from obspec._get import GetRangeAsync, GetRangesAsync
85

6+
from ._ifd import ImageFileDirectory
7+
from ._tile import Tile
8+
from .enums import Endianness
9+
from .store import ObjectStore
10+
911
class ObspecInput(GetRangeAsync, GetRangesAsync, Protocol):
1012
"""Supported obspec input to reader."""
1113

@@ -33,6 +35,10 @@ class TIFF:
3335
Returns:
3436
A TIFF instance.
3537
"""
38+
39+
@property
40+
def endianness(self) -> Endianness:
41+
"""The endianness of this TIFF file."""
3642
@property
3743
def ifds(self) -> list[ImageFileDirectory]:
3844
"""Access the underlying IFDs of this TIFF.

python/python/async_tiff/enums.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ class CompressionMethod(IntEnum):
2121
PackBits = 0x8005
2222

2323

24+
class Endianness(IntEnum):
25+
LittleEndian = 0
26+
BigEndian = 1
27+
28+
2429
class PhotometricInterpretation(IntEnum):
2530
WhiteIsZero = 0
2631
BlackIsZero = 1

python/src/enums.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use async_tiff::reader::Endianness;
12
use async_tiff::tiff::tags::{
23
CompressionMethod, PhotometricInterpretation, PlanarConfiguration, Predictor, ResolutionUnit,
34
SampleFormat,
@@ -39,6 +40,30 @@ impl<'py> IntoPyObject<'py> for PyCompressionMethod {
3940
}
4041
}
4142

43+
pub(crate) struct PyEndianness(Endianness);
44+
45+
impl From<Endianness> for PyEndianness {
46+
fn from(value: Endianness) -> Self {
47+
Self(value)
48+
}
49+
}
50+
51+
impl<'py> IntoPyObject<'py> for PyEndianness {
52+
type Target = PyAny;
53+
type Output = Bound<'py, PyAny>;
54+
type Error = PyErr;
55+
56+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
57+
let enums_mod = py.import(intern!(py, "async_tiff.enums"))?;
58+
let endianness_enum = enums_mod.getattr(intern!(py, "Endianness"))?;
59+
60+
match self.0 {
61+
Endianness::LittleEndian => endianness_enum.getattr("LittleEndian"),
62+
Endianness::BigEndian => endianness_enum.getattr("BigEndian"),
63+
}
64+
}
65+
}
66+
4267
pub(crate) struct PyPhotometricInterpretation(PhotometricInterpretation);
4368

4469
impl From<PhotometricInterpretation> for PyPhotometricInterpretation {
@@ -132,6 +157,7 @@ impl<'py> IntoPyObject<'py> for PySampleFormat {
132157
to_py_enum_variant(py, intern!(py, "SampleFormat"), self.0.to_u16())
133158
}
134159
}
160+
135161
fn to_py_enum_variant<'py>(
136162
py: Python<'py>,
137163
enum_name: &Bound<'py, PyString>,

python/src/tiff.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use pyo3::prelude::*;
99
use pyo3::types::PyType;
1010
use pyo3_async_runtimes::tokio::future_into_py;
1111

12+
use crate::enums::PyEndianness;
1213
use crate::error::PyAsyncTiffResult;
1314
use crate::reader::StoreInput;
1415
use crate::tile::PyTile;
@@ -29,8 +30,7 @@ async fn open(
2930
.with_initial_size(prefetch)
3031
.with_multiplier(multiplier);
3132
let mut metadata_reader = TiffMetadataReader::try_open(&metadata_fetch).await?;
32-
let ifds = metadata_reader.read_all_ifds(&metadata_fetch).await?;
33-
let tiff = TIFF::new(ifds);
33+
let tiff = metadata_reader.read(&metadata_fetch).await?;
3434
Ok(PyTIFF { tiff, reader })
3535
}
3636

@@ -56,6 +56,11 @@ impl PyTIFF {
5656
Ok(cog_reader)
5757
}
5858

59+
#[getter]
60+
fn endianness(&self) -> PyEndianness {
61+
self.tiff.endianness().into()
62+
}
63+
5964
#[getter]
6065
fn ifds(&self) -> Vec<PyImageFileDirectory> {
6166
let ifds = self.tiff.ifds();

python/tests/test_cog.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ async def test_cog_s3():
1313
store = S3Store("sentinel-cogs", region="us-west-2", skip_signature=True)
1414
tiff = await TIFF.open(path=path, store=store)
1515

16+
assert tiff.endianness == enums.Endianness.LittleEndian
17+
1618
ifds = tiff.ifds
1719
assert len(ifds) == 5
1820

src/cog.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
use crate::ifd::ImageFileDirectory;
2+
use crate::reader::Endianness;
23

34
/// A TIFF file.
45
#[derive(Debug, Clone)]
56
pub struct TIFF {
7+
endianness: Endianness,
68
ifds: Vec<ImageFileDirectory>,
79
}
810

911
impl TIFF {
1012
/// Create a new TIFF from existing IFDs.
11-
pub fn new(ifds: Vec<ImageFileDirectory>) -> Self {
12-
Self { ifds }
13+
pub fn new(ifds: Vec<ImageFileDirectory>, endianness: Endianness) -> Self {
14+
Self { ifds, endianness }
1315
}
1416

1517
/// Access the underlying Image File Directories.
1618
pub fn ifds(&self) -> &[ImageFileDirectory] {
1719
&self.ifds
1820
}
21+
22+
/// Get the endianness of the TIFF file.
23+
pub fn endianness(&self) -> Endianness {
24+
self.endianness
25+
}
1926
}
2027

2128
#[cfg(test)]
@@ -41,7 +48,7 @@ mod test {
4148
let cached_reader = ReadaheadMetadataCache::new(reader.clone());
4249
let mut metadata_reader = TiffMetadataReader::try_open(&cached_reader).await.unwrap();
4350
let ifds = metadata_reader.read_all_ifds(&cached_reader).await.unwrap();
44-
let tiff = TIFF::new(ifds);
51+
let tiff = TIFF::new(ifds, metadata_reader.endianness());
4552

4653
let ifd = &tiff.ifds[1];
4754
let tile = ifd.fetch_tile(0, 0, reader.as_ref()).await.unwrap();

src/metadata/reader.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::metadata::MetadataFetch;
99
use crate::reader::Endianness;
1010
use crate::tiff::tags::{Tag, Type};
1111
use crate::tiff::{TiffError, TiffFormatError, Value};
12-
use crate::ImageFileDirectory;
12+
use crate::{ImageFileDirectory, TIFF};
1313

1414
/// Entry point to reading TIFF metadata.
1515
///
@@ -135,6 +135,12 @@ impl TiffMetadataReader {
135135
}
136136
Ok(ifds)
137137
}
138+
139+
/// Read all IFDs from the file and return a complete TIFF structure.
140+
pub async fn read<F: MetadataFetch>(&mut self, fetch: &F) -> AsyncTiffResult<TIFF> {
141+
let ifds = self.read_all_ifds(fetch).await?;
142+
Ok(TIFF::new(ifds, self.endianness))
143+
}
138144
}
139145

140146
/// Reads the [`ImageFileDirectory`] metadata.

tests/image_tiff/util.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,5 @@ pub(crate) async fn open_tiff(filename: &str) -> TIFF {
1414
let reader = Arc::new(ObjectReader::new(store.clone(), path.as_str().into()))
1515
as Arc<dyn AsyncFileReader>;
1616
let mut metadata_reader = TiffMetadataReader::try_open(&reader).await.unwrap();
17-
let ifds = metadata_reader.read_all_ifds(&reader).await.unwrap();
18-
TIFF::new(ifds)
17+
metadata_reader.read(&reader).await.unwrap()
1918
}

tests/ome_tiff.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ async fn open_remote_tiff(url: &str) -> TIFF {
1616
let reader = Arc::new(ObjectReader::new(Arc::new(store), path)) as Arc<dyn AsyncFileReader>;
1717
let cached_reader = ReadaheadMetadataCache::new(reader.clone());
1818
let mut metadata_reader = TiffMetadataReader::try_open(&cached_reader).await.unwrap();
19-
let ifds = metadata_reader.read_all_ifds(&cached_reader).await.unwrap();
20-
TIFF::new(ifds)
19+
metadata_reader.read(&cached_reader).await.unwrap()
2120
}
2221

2322
#[tokio::test]

0 commit comments

Comments
 (0)