Add basic thumbnail extraction to Rawkit (#2659)

* Add `CompressionValue`

* Rename `Transform` to `OrientationValue`

* Add basic thumbnail extraction

* Add `ThumbnailFormat`

* fmt + clippy fixes
This commit is contained in:
Dr George Atkinson 2025-12-21 02:08:21 +00:00 committed by GitHub
parent d441a02e72
commit c21ccf5284
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 171 additions and 80 deletions

View File

@ -1,7 +1,7 @@
use crate::tiff::Ifd; use crate::tiff::Ifd;
use crate::tiff::file::TiffRead; use crate::tiff::file::TiffRead;
use crate::tiff::tags::SonyDataOffset; use crate::tiff::tags::SonyDataOffset;
use crate::{RawImage, SubtractBlack, Transform}; use crate::{OrientationValue, RawImage, SubtractBlack};
use bitstream_io::{BE, BitRead, BitReader, Endianness}; use bitstream_io::{BE, BitRead, BitReader, Endianness};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -25,7 +25,7 @@ pub fn decode_a100<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage
#[allow(unreachable_code)] #[allow(unreachable_code)]
maximum: (1 << 12) - 1, maximum: (1 << 12) - 1,
black: SubtractBlack::None, black: SubtractBlack::None,
transform: Transform::Horizontal, orientation: OrientationValue::Horizontal,
camera_model: None, camera_model: None,
camera_white_balance: None, camera_white_balance: None,
white_balance: None, white_balance: None,

View File

@ -1,8 +1,8 @@
use crate::tiff::file::{Endian, TiffRead}; use crate::tiff::file::{Endian, TiffRead};
use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, SonyToneCurve, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels}; use crate::tiff::tags::{BitsPerSample, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, SonyToneCurve, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels};
use crate::tiff::values::CurveLookupTable; use crate::tiff::values::{CompressionValue, CurveLookupTable};
use crate::tiff::{Ifd, TiffError}; use crate::tiff::{Ifd, TiffError};
use crate::{RawImage, SubtractBlack, Transform}; use crate::{OrientationValue, RawImage, SubtractBlack};
use rawkit_proc_macros::Tag; use rawkit_proc_macros::Tag;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -26,7 +26,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len()); assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len());
assert!(ifd.strip_offsets.len() == 1); assert!(ifd.strip_offsets.len() == 1);
assert!(ifd.compression == 32767); assert!(ifd.compression == CompressionValue::Sony_ARW_Compressed);
let image_width: usize = ifd.image_width.try_into().unwrap(); let image_width: usize = ifd.image_width.try_into().unwrap();
let image_height: usize = ifd.image_height.try_into().unwrap(); let image_height: usize = ifd.image_height.try_into().unwrap();
@ -49,7 +49,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
cfa_pattern: ifd.cfa_pattern.try_into().unwrap(), cfa_pattern: ifd.cfa_pattern.try_into().unwrap(),
maximum: (1 << 14) - 1, maximum: (1 << 14) - 1,
black: SubtractBlack::CfaGrid([512, 512, 512, 512]), // TODO: Find the correct way to do this black: SubtractBlack::CfaGrid([512, 512, 512, 512]), // TODO: Find the correct way to do this
transform: Transform::Horizontal, orientation: OrientationValue::Horizontal,
camera_model: None, camera_model: None,
camera_white_balance: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)), camera_white_balance: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)),
white_balance: None, white_balance: None,

View File

@ -1,7 +1,8 @@
use crate::tiff::file::TiffRead; use crate::tiff::file::TiffRead;
use crate::tiff::tags::{BitsPerSample, BlackLevel, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels}; use crate::tiff::tags::{BitsPerSample, BlackLevel, CfaPattern, CfaPatternDim, Compression, ImageLength, ImageWidth, RowsPerStrip, StripByteCounts, StripOffsets, Tag, WhiteBalanceRggbLevels};
use crate::tiff::values::CompressionValue;
use crate::tiff::{Ifd, TiffError}; use crate::tiff::{Ifd, TiffError};
use crate::{RawImage, SubtractBlack, Transform}; use crate::{OrientationValue, RawImage, SubtractBlack};
use rawkit_proc_macros::Tag; use rawkit_proc_macros::Tag;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -26,7 +27,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len()); assert!(ifd.strip_offsets.len() == ifd.strip_byte_counts.len());
assert!(ifd.strip_offsets.len() == 1); assert!(ifd.strip_offsets.len() == 1);
assert!(ifd.compression == 1); // 1 is the value for uncompressed format assert!(ifd.compression == CompressionValue::Uncompressed);
let image_width: usize = ifd.image_width.try_into().unwrap(); let image_width: usize = ifd.image_width.try_into().unwrap();
let image_height: usize = ifd.image_height.try_into().unwrap(); let image_height: usize = ifd.image_height.try_into().unwrap();
@ -57,7 +58,7 @@ pub fn decode<R: Read + Seek>(ifd: Ifd, file: &mut TiffRead<R>) -> RawImage {
cfa_pattern: ifd.cfa_pattern.try_into().unwrap(), cfa_pattern: ifd.cfa_pattern.try_into().unwrap(),
maximum: if bits_per_sample == 16 { u16::MAX } else { (1 << bits_per_sample) - 1 }, maximum: if bits_per_sample == 16 { u16::MAX } else { (1 << bits_per_sample) - 1 },
black: SubtractBlack::CfaGrid(ifd.black_level), black: SubtractBlack::CfaGrid(ifd.black_level),
transform: Transform::Horizontal, orientation: OrientationValue::Horizontal,
camera_model: None, camera_model: None,
camera_white_balance: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)), camera_white_balance: ifd.white_balance_levels.map(|arr| arr.map(|x| x as f64)),
white_balance: None, white_balance: None,

View File

@ -12,13 +12,24 @@ use rawkit_proc_macros::Tag;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use thiserror::Error; use thiserror::Error;
use tiff::file::TiffRead; use tiff::file::TiffRead;
use tiff::tags::{Compression, ImageLength, ImageWidth, Orientation, StripByteCounts, SubIfd, Tag}; use tiff::tags::{Compression, ImageLength, ImageWidth, Orientation, StripByteCounts, SubIfd, Tag, ThumbnailLength, ThumbnailOffset};
use tiff::values::Transform; use tiff::values::{CompressionValue, OrientationValue};
use tiff::{Ifd, TiffError}; use tiff::{Ifd, TiffError};
pub(crate) const CHANNELS_IN_RGB: usize = 3; pub(crate) const CHANNELS_IN_RGB: usize = 3;
pub(crate) type Histogram = [[usize; 0x2000]; CHANNELS_IN_RGB]; pub(crate) type Histogram = [[usize; 0x2000]; CHANNELS_IN_RGB];
pub enum ThumbnailFormat {
Jpeg,
Unsupported,
}
/// A thumbnail image extracted from the raw file. This is usually a JPEG image.
pub struct ThumbnailImage {
pub data: Vec<u8>,
pub format: ThumbnailFormat,
}
/// The amount of black level to be subtracted from Raw Image. /// The amount of black level to be subtracted from Raw Image.
pub enum SubtractBlack { pub enum SubtractBlack {
/// Don't subtract any value. /// Don't subtract any value.
@ -48,7 +59,7 @@ pub struct RawImage {
pub cfa_pattern: [u8; 4], pub cfa_pattern: [u8; 4],
/// Transformation to be applied to negate the orientation of camera. /// Transformation to be applied to negate the orientation of camera.
pub transform: Transform, pub orientation: OrientationValue,
/// The maximum possible value of pixel that the camera sensor could give. /// The maximum possible value of pixel that the camera sensor could give.
pub maximum: u16, pub maximum: u16,
@ -97,8 +108,8 @@ pub struct Image<T> {
/// The transformation required to orient the image correctly. /// The transformation required to orient the image correctly.
/// ///
/// This will be [`Transform::Horizontal`] after the transform step is applied. /// This will be [`OrientationValue::Horizontal`] after the orientation step is applied.
pub transform: Transform, pub orientation: OrientationValue,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -119,7 +130,7 @@ impl RawImage {
let ifd = Ifd::new_first_ifd(&mut file)?; let ifd = Ifd::new_first_ifd(&mut file)?;
let camera_model = metadata::identify::identify_camera_model(&ifd, &mut file).unwrap(); let camera_model = metadata::identify::identify_camera_model(&ifd, &mut file).unwrap();
let transform = ifd.get_value::<Orientation, _>(&mut file)?; let orientation = ifd.get_value::<Orientation, _>(&mut file)?;
let mut raw_image = if camera_model.model == "DSLR-A100" { let mut raw_image = if camera_model.model == "DSLR-A100" {
decoder::arw1::decode_a100(ifd, &mut file) decoder::arw1::decode_a100(ifd, &mut file)
@ -127,7 +138,7 @@ impl RawImage {
let sub_ifd = ifd.get_value::<SubIfd, _>(&mut file)?; let sub_ifd = ifd.get_value::<SubIfd, _>(&mut file)?;
let arw_ifd = sub_ifd.get_value::<ArwIfd, _>(&mut file)?; let arw_ifd = sub_ifd.get_value::<ArwIfd, _>(&mut file)?;
if arw_ifd.compression == 1 { if arw_ifd.compression == CompressionValue::Uncompressed {
decoder::uncompressed::decode(sub_ifd, &mut file) decoder::uncompressed::decode(sub_ifd, &mut file)
} else if arw_ifd.strip_byte_counts[0] == arw_ifd.image_width * arw_ifd.image_height { } else if arw_ifd.strip_byte_counts[0] == arw_ifd.image_width * arw_ifd.image_height {
decoder::arw2::decode(sub_ifd, &mut file) decoder::arw2::decode(sub_ifd, &mut file)
@ -138,13 +149,38 @@ impl RawImage {
}; };
raw_image.camera_model = Some(camera_model); raw_image.camera_model = Some(camera_model);
raw_image.transform = transform; raw_image.orientation = orientation;
raw_image.calculate_conversion_matrices(); raw_image.calculate_conversion_matrices();
Ok(raw_image) Ok(raw_image)
} }
/// Extracts the thumbnail image from the raw file.
pub fn extract_thumbnail<R: Read + Seek>(reader: &mut R) -> Result<ThumbnailImage, DecoderError> {
let mut file = TiffRead::new(reader)?;
let ifd = Ifd::new_first_ifd(&mut file)?;
// TODO: ARW files Store the thumbnail offset and length in the first IFD. Add support for other file types in the future.
let thumbnail_offset = ifd.get_value::<ThumbnailOffset, _>(&mut file)?;
let thumbnail_length = ifd.get_value::<ThumbnailLength, _>(&mut file)?;
file.seek_from_start(thumbnail_offset)?;
let mut thumbnail_data = vec![0; thumbnail_length as usize];
file.read_exact(&mut thumbnail_data)?;
// Check the first two bytes to determine the format of the thumbnail.
// JPEG format starts with 0xFF, 0xD8.
if thumbnail_data[0..2] == [0xFF, 0xD8] {
Ok(ThumbnailImage {
data: thumbnail_data,
format: ThumbnailFormat::Jpeg,
})
} else {
Err(DecoderError::UnsupportedThumbnailFormat)
}
}
/// Converts the [`RawImage`] to an [`Image`] with 8 bit resolution for each channel. /// Converts the [`RawImage`] to an [`Image`] with 8 bit resolution for each channel.
/// ///
/// Applies all the processing steps to finally get RGB pixel data. /// Applies all the processing steps to finally get RGB pixel data.
@ -156,7 +192,7 @@ impl RawImage {
data: image.data.iter().map(|x| (x >> 8) as u8).collect(), data: image.data.iter().map(|x| (x >> 8) as u8).collect(),
width: image.width, width: image.width,
height: image.height, height: image.height,
transform: image.transform, orientation: image.orientation,
} }
} }
@ -174,7 +210,7 @@ impl RawImage {
let image = raw_image.demosaic_and_apply((convert_to_rgb, &mut record_histogram)); let image = raw_image.demosaic_and_apply((convert_to_rgb, &mut record_histogram));
let gamma_correction = image.gamma_correction_fn(&record_histogram.histogram); let gamma_correction = image.gamma_correction_fn(&record_histogram.histogram);
if image.transform == Transform::Horizontal { if image.orientation == OrientationValue::Horizontal {
image.apply(gamma_correction) image.apply(gamma_correction)
} else { } else {
image.transform_and_apply(gamma_correction) image.transform_and_apply(gamma_correction)
@ -211,7 +247,7 @@ impl RawImage {
data: image, data: image,
width: self.width, width: self.width,
height: self.height, height: self.height,
transform: self.transform, orientation: self.orientation,
} }
} }
} }
@ -232,7 +268,7 @@ impl Image<u16> {
pub fn transform_and_apply(self, mut transform: impl PixelTransform) -> Image<u16> { pub fn transform_and_apply(self, mut transform: impl PixelTransform) -> Image<u16> {
let mut image = vec![0; self.width * self.height * 3]; let mut image = vec![0; self.width * self.height * 3];
let (width, height, iter) = self.transform_iter(); let (width, height, iter) = self.orientation_iter();
for Pixel { values, row, column } in iter.map(|mut pixel| { for Pixel { values, row, column } in iter.map(|mut pixel| {
pixel.values = transform.apply(pixel); pixel.values = transform.apply(pixel);
pixel pixel
@ -246,7 +282,7 @@ impl Image<u16> {
data: image, data: image,
width, width,
height, height,
transform: Transform::Horizontal, orientation: OrientationValue::Horizontal,
} }
} }
} }
@ -259,4 +295,6 @@ pub enum DecoderError {
ConversionError(#[from] std::num::TryFromIntError), ConversionError(#[from] std::num::TryFromIntError),
#[error("An IO Error ocurred")] #[error("An IO Error ocurred")]
IoError(#[from] std::io::Error), IoError(#[from] std::io::Error),
#[error("The thumbnail format is unsupported")]
UnsupportedThumbnailFormat,
} }

View File

@ -1,16 +1,16 @@
use crate::{Image, Pixel, Transform}; use crate::{Image, OrientationValue, Pixel};
impl Image<u16> { impl Image<u16> {
pub fn transform_iter(&self) -> (usize, usize, impl Iterator<Item = Pixel> + use<'_>) { pub fn orientation_iter(&self) -> (usize, usize, impl Iterator<Item = Pixel> + use<'_>) {
let (final_width, final_height) = if self.transform.will_swap_coordinates() { let (final_width, final_height) = if self.orientation.will_swap_coordinates() {
(self.height, self.width) (self.height, self.width)
} else { } else {
(self.width, self.height) (self.width, self.height)
}; };
let index_0_0 = inverse_transform_index(self.transform, 0, 0, self.width, self.height); let index_0_0 = inverse_orientation_index(self.orientation, 0, 0, self.width, self.height);
let index_0_1 = inverse_transform_index(self.transform, 0, 1, self.width, self.height); let index_0_1 = inverse_orientation_index(self.orientation, 0, 1, self.width, self.height);
let index_1_0 = inverse_transform_index(self.transform, 1, 0, self.width, self.height); let index_1_0 = inverse_orientation_index(self.orientation, 1, 0, self.width, self.height);
let column_step = (index_0_1.0 - index_0_0.0, index_0_1.1 - index_0_0.1); let column_step = (index_0_1.0 - index_0_0.0, index_0_1.1 - index_0_0.1);
let row_step = (index_1_0.0 - index_0_0.0, index_1_0.1 - index_0_0.1); let row_step = (index_1_0.0 - index_0_0.0, index_1_0.1 - index_0_0.1);
@ -42,16 +42,16 @@ impl Image<u16> {
} }
} }
pub fn inverse_transform_index(transform: Transform, mut row: usize, mut column: usize, width: usize, height: usize) -> (i64, i64) { pub fn inverse_orientation_index(orientation: OrientationValue, mut row: usize, mut column: usize, width: usize, height: usize) -> (i64, i64) {
let value = match transform { let value = match orientation {
Transform::Horizontal => 0, OrientationValue::Horizontal => 0,
Transform::MirrorHorizontal => 1, OrientationValue::MirrorHorizontal => 1,
Transform::Rotate180 => 3, OrientationValue::Rotate180 => 3,
Transform::MirrorVertical => 2, OrientationValue::MirrorVertical => 2,
Transform::MirrorHorizontalRotate270 => 4, OrientationValue::MirrorHorizontalRotate270 => 4,
Transform::Rotate90 => 6, OrientationValue::Rotate90 => 6,
Transform::MirrorHorizontalRotate90 => 7, OrientationValue::MirrorHorizontalRotate90 => 7,
Transform::Rotate270 => 5, OrientationValue::Rotate270 => 5,
}; };
if value & 4 != 0 { if value & 4 != 0 {

View File

@ -26,8 +26,8 @@ pub enum TagId {
RowsPerStrip = 0x116, RowsPerStrip = 0x116,
StripByteCounts = 0x117, StripByteCounts = 0x117,
SubIfd = 0x14a, SubIfd = 0x14a,
JpegOffset = 0x201, ThumbnailOffset = 0x201,
JpegLength = 0x202, ThumbnailLength = 0x202,
SonyToneCurve = 0x7010, SonyToneCurve = 0x7010,
BlackLevel = 0x7310, BlackLevel = 0x7310,
WhiteBalanceRggbLevels = 0x7313, WhiteBalanceRggbLevels = 0x7313,
@ -88,10 +88,10 @@ impl Ifd {
} }
file.seek_from_start(offset)?; file.seek_from_start(offset)?;
let num = file.read_u16()?; let num_entries = file.read_u16()?;
let mut ifd_entries = Vec::with_capacity(num.into()); let mut ifd_entries = Vec::with_capacity(num_entries.into());
for _ in 0..num { for _ in 0..num_entries {
let tag = file.read_u16()?.into(); let tag = file.read_u16()?.into();
let the_type = file.read_u16()?.into(); let the_type = file.read_u16()?.into();
let count = file.read_u32()?; let count = file.read_u32()?;

View File

@ -1,4 +1,4 @@
use super::types::{Array, ConstArray, TagType, TypeByte, TypeIfd, TypeLong, TypeNumber, TypeOrientation, TypeSRational, TypeSShort, TypeShort, TypeSonyToneCurve, TypeString}; use super::types::{Array, ConstArray, TagType, TypeByte, TypeCompression, TypeIfd, TypeLong, TypeNumber, TypeOrientation, TypeSRational, TypeSShort, TypeShort, TypeSonyToneCurve, TypeString};
use super::{Ifd, TagId, TiffError, TiffRead}; use super::{Ifd, TagId, TiffError, TiffRead};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -22,8 +22,8 @@ pub struct SamplesPerPixel;
pub struct RowsPerStrip; pub struct RowsPerStrip;
pub struct StripByteCounts; pub struct StripByteCounts;
pub struct SubIfd; pub struct SubIfd;
pub struct JpegOffset; pub struct ThumbnailOffset;
pub struct JpegLength; pub struct ThumbnailLength;
pub struct SonyDataOffset; pub struct SonyDataOffset;
pub struct SonyToneCurve; pub struct SonyToneCurve;
pub struct BlackLevel; pub struct BlackLevel;
@ -55,7 +55,7 @@ impl SimpleTag for BitsPerSample {
} }
impl SimpleTag for Compression { impl SimpleTag for Compression {
type Type = TypeShort; type Type = TypeCompression;
const ID: TagId = TagId::Compression; const ID: TagId = TagId::Compression;
const NAME: &'static str = "Compression"; const NAME: &'static str = "Compression";
@ -124,17 +124,17 @@ impl SimpleTag for SubIfd {
const NAME: &'static str = "SubIFD"; const NAME: &'static str = "SubIFD";
} }
impl SimpleTag for JpegOffset { impl SimpleTag for ThumbnailOffset {
type Type = TypeLong; type Type = TypeLong;
const ID: TagId = TagId::JpegOffset; const ID: TagId = TagId::ThumbnailOffset;
const NAME: &'static str = "Jpeg Offset"; const NAME: &'static str = "Jpeg Offset";
} }
impl SimpleTag for JpegLength { impl SimpleTag for ThumbnailLength {
type Type = TypeLong; type Type = TypeLong;
const ID: TagId = TagId::JpegLength; const ID: TagId = TagId::ThumbnailLength;
const NAME: &'static str = "Jpeg Length"; const NAME: &'static str = "Jpeg Length";
} }

View File

@ -1,5 +1,5 @@
use super::file::TiffRead; use super::file::TiffRead;
use super::values::{CurveLookupTable, Rational, Transform}; use super::values::{CompressionValue, CurveLookupTable, OrientationValue, Rational};
use super::{Ifd, IfdTagType, TiffError}; use super::{Ifd, IfdTagType, TiffError};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -350,6 +350,7 @@ impl<T: PrimitiveType, const N: usize> TagType for ConstArray<T, N> {
} }
} }
pub struct TypeCompression;
pub struct TypeString; pub struct TypeString;
pub struct TypeSonyToneCurve; pub struct TypeSonyToneCurve;
pub struct TypeOrientation; pub struct TypeOrientation;
@ -376,19 +377,17 @@ impl TagType for TypeSonyToneCurve {
} }
impl TagType for TypeOrientation { impl TagType for TypeOrientation {
type Output = Transform; type Output = OrientationValue;
fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> { fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
Ok(match TypeShort::read(file)? { OrientationValue::try_from(TypeShort::read(file)?).map_err(|_| TiffError::InvalidValue)
1 => Transform::Horizontal, }
2 => Transform::MirrorHorizontal, }
3 => Transform::Rotate180,
4 => Transform::MirrorVertical, impl TagType for TypeCompression {
5 => Transform::MirrorHorizontalRotate270, type Output = CompressionValue;
6 => Transform::Rotate90,
7 => Transform::MirrorHorizontalRotate90, fn read<R: Read + Seek>(file: &mut TiffRead<R>) -> Result<Self::Output, TiffError> {
8 => Transform::Rotate270, CompressionValue::try_from(TypeShort::read(file)?).map_err(|_| TiffError::InvalidValue)
_ => return Err(TiffError::InvalidValue),
})
} }
} }

View File

@ -1,3 +1,5 @@
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub trait ToFloat { pub trait ToFloat {
fn to_float(&self) -> f64; fn to_float(&self) -> f64;
} }
@ -51,29 +53,80 @@ impl CurveLookupTable {
} }
} }
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Debug, IntoPrimitive, TryFromPrimitive)]
pub enum Transform { #[repr(u16)]
Horizontal, pub enum OrientationValue {
MirrorHorizontal, Horizontal = 1,
Rotate180, MirrorHorizontal = 2,
MirrorVertical, Rotate180 = 3,
MirrorHorizontalRotate270, MirrorVertical = 4,
Rotate90, MirrorHorizontalRotate270 = 5,
MirrorHorizontalRotate90, Rotate90 = 6,
Rotate270, MirrorHorizontalRotate90 = 7,
Rotate270 = 8,
} }
impl Transform { impl OrientationValue {
pub fn is_identity(&self) -> bool { pub fn is_identity(&self) -> bool {
*self == Transform::Horizontal *self == Self::Horizontal
} }
pub fn will_swap_coordinates(&self) -> bool { pub fn will_swap_coordinates(&self) -> bool {
use Transform as Tr;
match *self { match *self {
Tr::Horizontal | Tr::MirrorHorizontal | Tr::Rotate180 | Tr::MirrorVertical => false, Self::Horizontal | Self::MirrorHorizontal | Self::Rotate180 | Self::MirrorVertical => false,
Tr::MirrorHorizontalRotate270 | Tr::Rotate90 | Tr::MirrorHorizontalRotate90 | Tr::Rotate270 => true, Self::MirrorHorizontalRotate270 | Self::Rotate90 | Self::MirrorHorizontalRotate90 | Self::Rotate270 => true,
} }
} }
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
#[repr(u16)]
#[allow(non_camel_case_types)]
pub enum CompressionValue {
Uncompressed = 1,
CCITT_1D = 2,
T4 = 3,
T6 = 4,
LZW = 5,
JPEG_Old = 6,
JPEG = 7,
AdobeDeflate = 8,
JBIG_BW = 9,
JBIG_Color = 10,
KODAK_626 = 262,
Next = 32766,
Sony_ARW_Compressed = 32767,
Packed_Raw = 32769,
Samsung_SRW_Compressed = 32770,
CCIRLEW = 32771,
Samsung_SRW_Compressed_2 = 32772,
PackedBits = 32773,
Thunderscan = 32809,
Kodak_KDC_Compressed = 32867,
IT8CTPAD = 32895,
IT8LW = 32896,
IT8MP = 32897,
IT8BL = 32898,
PixarFilm = 32908,
PixarLog = 32909,
Deflate = 32946,
DCS = 32947,
AperioJPEG2K_YCbCr = 33003,
AperioJPEG2K_RGB = 33005,
JBIG = 34661,
SGILog = 34676,
SGILog24 = 34677,
JPEG2K = 34712,
NikonNEFCompressed = 34713,
JBIG2_TIFF_FX = 34715,
ESRI_Lerc = 34887,
LossyJPEG = 34892,
LZMA2 = 34925,
PNG = 34933,
JPEG_XR = 34934,
Zstd = 50000,
WebP = 50001,
JPEG_XL = 52546,
Kodak_DCR_Compressed = 65000,
Pentax_PEF_Compressed = 65535,
}