Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
[package]
name = "async-tiff"
version = "0.1.0"
version = "0.1.0-beta.1"
edition = "2021"
authors = ["Kyle Barron <[email protected]>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/developmentseed/async-tiff"
description = "Low-level asynchronous TIFF reader."
readme = "README.md"

[dependencies]
byteorder = "1"
Expand All @@ -11,6 +16,7 @@ futures = "0.3.31"
jpeg = { package = "jpeg-decoder", version = "0.3.0", default-features = false }
num_enum = "0.7.3"
# Match the version used by pyo3-object-store
# We'll upgrade to object_store 0.12 ASAP when it comes out
object_store = { git = "https://github.com/apache/arrow-rs", rev = "7a15e4b47ca97df2edef689c9f2ebd2f3888b79e" }
thiserror = "1"
tokio = { version = "1.43.0", optional = true }
Expand Down
7 changes: 4 additions & 3 deletions python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "async-tiff"
name = "py-async-tiff"
version = "0.1.0-beta.1"
authors = ["Kyle Barron <[email protected]>"]
edition = "2021"
Expand All @@ -18,12 +18,13 @@ crate-type = ["cdylib"]

[dependencies]
async-tiff = { path = "../" }
bytes = "1.8"
bytes = "1.10.1"
# We'll upgrade to object_store 0.12 ASAP
# Match the version used by pyo3-object-store
object_store = { git = "https://github.com/apache/arrow-rs", rev = "7a15e4b47ca97df2edef689c9f2ebd2f3888b79e" }
pyo3 = { version = "0.23.0", features = ["macros"] }
pyo3-async-runtimes = "0.23"
pyo3-bytes = "0.1.2"
pyo3-bytes = "0.1.3"
pyo3-object_store = { git = "https://github.com/developmentseed/obstore", rev = "28ba07a621c1c104f084fb47ae7f8d08b1eae3ea" }
rayon = "1.10.0"
tokio-rayon = "2.1.0"
Expand Down
8 changes: 4 additions & 4 deletions python/src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::sync::Arc;

use async_tiff::decoder::{Decoder, DecoderRegistry};
use async_tiff::error::AiocogeoError;
use async_tiff::error::{AsyncTiffError, AsyncTiffResult};
use async_tiff::tiff::tags::PhotometricInterpretation;
use bytes::Bytes;
use pyo3::exceptions::PyTypeError;
Expand Down Expand Up @@ -74,12 +74,12 @@ impl<'py> FromPyObject<'py> for PyDecoder {
impl Decoder for PyDecoder {
fn decode_tile(
&self,
buffer: bytes::Bytes,
buffer: Bytes,
_photometric_interpretation: PhotometricInterpretation,
_jpeg_tables: Option<&[u8]>,
) -> async_tiff::error::Result<bytes::Bytes> {
) -> AsyncTiffResult<Bytes> {
let decoded_buffer = Python::with_gil(|py| self.call(py, buffer))
.map_err(|err| AiocogeoError::General(err.to_string()))?;
.map_err(|err| AsyncTiffError::General(err.to_string()))?;
Ok(decoded_buffer.into_inner())
}
}
7 changes: 4 additions & 3 deletions python/src/tiff.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use async_tiff::{AsyncFileReader, COGReader, ObjectReader, PrefetchReader};
use async_tiff::reader::{AsyncFileReader, ObjectReader, PrefetchReader};
use async_tiff::TIFF;
use pyo3::prelude::*;
use pyo3::types::PyType;
use pyo3_async_runtimes::tokio::future_into_py;
Expand All @@ -7,7 +8,7 @@ use pyo3_object_store::PyObjectStore;
use crate::PyImageFileDirectory;

#[pyclass(name = "TIFF", frozen)]
pub(crate) struct PyTIFF(COGReader);
pub(crate) struct PyTIFF(TIFF);

#[pymethods]
impl PyTIFF {
Expand All @@ -32,7 +33,7 @@ impl PyTIFF {
} else {
Box::new(reader)
};
Ok(PyTIFF(COGReader::try_open(reader).await.unwrap()))
Ok(PyTIFF(TIFF::try_open(reader).await.unwrap()))
})?;
Ok(cog_reader)
}
Expand Down
26 changes: 14 additions & 12 deletions src/cog.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use crate::async_reader::AsyncCursor;
use crate::error::Result;
use crate::error::AsyncTiffResult;
use crate::ifd::ImageFileDirectories;
use crate::reader::{AsyncCursor, AsyncFileReader};
use crate::tiff::{TiffError, TiffFormatError};
use crate::AsyncFileReader;

#[derive(Debug)]
pub struct COGReader {
#[allow(dead_code)]
cursor: AsyncCursor,
/// A TIFF file.
#[derive(Debug, Clone)]
pub struct TIFF {
ifds: ImageFileDirectories,
}

impl COGReader {
pub async fn try_open(reader: Box<dyn AsyncFileReader>) -> Result<Self> {
impl TIFF {
/// Open a new TIFF file.
///
/// This will read all the Image File Directories (IFDs) in the file.
pub async fn try_open(reader: Box<dyn AsyncFileReader>) -> AsyncTiffResult<Self> {
let mut cursor = AsyncCursor::try_open_tiff(reader).await?;
let version = cursor.read_u16().await?;

Expand Down Expand Up @@ -44,9 +45,10 @@ impl COGReader {

let ifds = ImageFileDirectories::open(&mut cursor, first_ifd_location, bigtiff).await?;

Ok(Self { cursor, ifds })
Ok(Self { ifds })
}

/// Access the underlying Image File Directories.
pub fn ifds(&self) -> &ImageFileDirectories {
&self.ifds
}
Expand All @@ -58,7 +60,7 @@ mod test {
use std::sync::Arc;

use crate::decoder::DecoderRegistry;
use crate::ObjectReader;
use crate::reader::ObjectReader;

use super::*;
use object_store::local::LocalFileSystem;
Expand All @@ -72,7 +74,7 @@ mod test {
let store = Arc::new(LocalFileSystem::new_with_prefix(folder).unwrap());
let reader = ObjectReader::new(store, path);

let cog_reader = COGReader::try_open(Box::new(reader.clone())).await.unwrap();
let cog_reader = TIFF::try_open(Box::new(reader.clone())).await.unwrap();

let ifd = &cog_reader.ifds.as_ref()[1];
let decoder_registry = DecoderRegistry::default();
Expand Down
21 changes: 14 additions & 7 deletions src/decoder.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! Decoders for different TIFF compression methods.

use std::collections::HashMap;
use std::fmt::Debug;
use std::io::{Cursor, Read};

use bytes::Bytes;
use flate2::bufread::ZlibDecoder;

use crate::error::Result;
use crate::error::AsyncTiffResult;
use crate::tiff::tags::{CompressionMethod, PhotometricInterpretation};
use crate::tiff::{TiffError, TiffUnsupportedError};

Expand Down Expand Up @@ -46,14 +48,16 @@ impl Default for DecoderRegistry {

/// A trait to decode a TIFF tile.
pub trait Decoder: Debug + Send + Sync {
/// Decode a TIFF tile.
fn decode_tile(
&self,
buffer: Bytes,
photometric_interpretation: PhotometricInterpretation,
jpeg_tables: Option<&[u8]>,
) -> Result<Bytes>;
) -> AsyncTiffResult<Bytes>;
}

/// A decoder for the Deflate compression method.
#[derive(Debug, Clone)]
pub struct DeflateDecoder;

Expand All @@ -63,14 +67,15 @@ impl Decoder for DeflateDecoder {
buffer: Bytes,
_photometric_interpretation: PhotometricInterpretation,
_jpeg_tables: Option<&[u8]>,
) -> Result<Bytes> {
) -> AsyncTiffResult<Bytes> {
let mut decoder = ZlibDecoder::new(Cursor::new(buffer));
let mut buf = Vec::new();
decoder.read_to_end(&mut buf)?;
Ok(buf.into())
}
}

/// A decoder for the JPEG compression method.
#[derive(Debug, Clone)]
pub struct JPEGDecoder;

Expand All @@ -80,11 +85,12 @@ impl Decoder for JPEGDecoder {
buffer: Bytes,
photometric_interpretation: PhotometricInterpretation,
jpeg_tables: Option<&[u8]>,
) -> Result<Bytes> {
) -> AsyncTiffResult<Bytes> {
decode_modern_jpeg(buffer, photometric_interpretation, jpeg_tables)
}
}

/// A decoder for the LZW compression method.
#[derive(Debug, Clone)]
pub struct LZWDecoder;

Expand All @@ -94,14 +100,15 @@ impl Decoder for LZWDecoder {
buffer: Bytes,
_photometric_interpretation: PhotometricInterpretation,
_jpeg_tables: Option<&[u8]>,
) -> Result<Bytes> {
) -> AsyncTiffResult<Bytes> {
// https://github.com/image-rs/image-tiff/blob/90ae5b8e54356a35e266fb24e969aafbcb26e990/src/decoder/stream.rs#L147
let mut decoder = weezl::decode::Decoder::with_tiff_size_switch(weezl::BitOrder::Msb, 8);
let decoded = decoder.decode(&buffer).expect("failed to decode LZW data");
Ok(decoded.into())
}
}

/// A decoder for uncompressed data.
#[derive(Debug, Clone)]
pub struct UncompressedDecoder;

Expand All @@ -111,7 +118,7 @@ impl Decoder for UncompressedDecoder {
buffer: Bytes,
_photometric_interpretation: PhotometricInterpretation,
_jpeg_tables: Option<&[u8]>,
) -> Result<Bytes> {
) -> AsyncTiffResult<Bytes> {
Ok(buffer)
}
}
Expand All @@ -121,7 +128,7 @@ fn decode_modern_jpeg(
buf: Bytes,
photometric_interpretation: PhotometricInterpretation,
jpeg_tables: Option<&[u8]>,
) -> Result<Bytes> {
) -> AsyncTiffResult<Bytes> {
// Construct new jpeg_reader wrapping a SmartReader.
//
// JPEG compression in TIFF allows saving quantization and/or huffman tables in one central
Expand Down
11 changes: 9 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
//! Error handling.

use std::fmt::Debug;
use thiserror::Error;

/// Enum with all errors in this crate.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum AiocogeoError {
pub enum AsyncTiffError {
/// End of file error.
#[error("End of File: expected to read {0} bytes, got {1}")]
EndOfFile(usize, usize),

/// General error.
#[error("General error: {0}")]
General(String),

/// IO Error.
#[error(transparent)]
IOError(#[from] std::io::Error),

/// Error while decoding JPEG data.
#[error(transparent)]
JPEGDecodingError(#[from] jpeg::Error),

/// Error while fetching data using object store.
#[error(transparent)]
ObjectStore(#[from] object_store::Error),

/// An error during TIFF tag parsing.
#[error(transparent)]
InternalTIFFError(#[from] crate::tiff::TiffError),
}

/// Crate-specific result type.
pub type Result<T> = std::result::Result<T, AiocogeoError>;
pub type AsyncTiffResult<T> = std::result::Result<T, AsyncTiffError>;
7 changes: 7 additions & 0 deletions src/geo/affine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,37 @@ use crate::ImageFileDirectory;
pub struct AffineTransform(f64, f64, f64, f64, f64, f64);

impl AffineTransform {
/// Construct a new Affine Transform
pub fn new(a: f64, b: f64, xoff: f64, d: f64, e: f64, yoff: f64) -> Self {
Self(a, b, xoff, d, e, yoff)
}

/// a
pub fn a(&self) -> f64 {
self.0
}

/// b
pub fn b(&self) -> f64 {
self.1
}

/// c
pub fn c(&self) -> f64 {
self.2
}

/// d
pub fn d(&self) -> f64 {
self.3
}

/// e
pub fn e(&self) -> f64 {
self.4
}

/// f
pub fn f(&self) -> f64 {
self.5
}
Expand Down
1 change: 1 addition & 0 deletions src/geo/geo_key_directory.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(dead_code)]
#![allow(missing_docs)]

use std::collections::HashMap;

Expand Down
Loading
Loading