Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe305ec24a | ||
|
|
a3ab252168 | ||
|
|
bd64e438a4 | ||
|
|
fa225e820b | ||
|
|
ea373bae00 | ||
|
|
23d1867f75 | ||
|
|
234b2ba677 | ||
|
|
a63a01319c | ||
|
|
efd2a6dbd8 | ||
|
|
d38fba4268 | ||
|
|
1732481b01 | ||
|
|
355ecc8ad9 | ||
|
|
985f3822c8 | ||
|
|
f6d95249dd |
No files matched your search
+40
-1
@@ -1,4 +1,4 @@
|
|||||||
# Version 0.10.4 (unreleased)
|
# Version 0.11.0
|
||||||
|
|
||||||
- `Directory` now implements `FromIterator<(Tag, Value)>`.
|
- `Directory` now implements `FromIterator<(Tag, Value)>`.
|
||||||
|
|
||||||
@@ -10,6 +10,45 @@ Changes:
|
|||||||
as unspecified relation. Previously, these may have been interpreted as
|
as unspecified relation. Previously, these may have been interpreted as
|
||||||
alpha by the total sample count (e.g. RgbA if 4 samples under a photometric
|
alpha by the total sample count (e.g. RgbA if 4 samples under a photometric
|
||||||
interpretation of RGB).
|
interpretation of RGB).
|
||||||
|
- The decoder handles planar data, current limited to non-subsampled channels.
|
||||||
|
The `Decoder::read_image` method return planes one-after-another depending on
|
||||||
|
the size of the buffer that was passed.
|
||||||
|
- `Decoder::read_image_to_buffer` now takes `&mut DecodingResult` and resizes
|
||||||
|
it according to the required layout. Previously, a borrowed `DecodingBuffer`
|
||||||
|
was passed which can be replaced by calling `as_bytes_mut` and
|
||||||
|
`read_image_bytes`.
|
||||||
|
- Several methods of tags are now `const`. Note that does not guarantee any
|
||||||
|
particular value when calling these methods.
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
- Fix a bug in the uncompressed encoder that could lead to short writes, i.e.
|
||||||
|
data silently dropped when the underlying writer did not accept all data in a
|
||||||
|
single write call.
|
||||||
|
- Encoding YCbCr data now writes the `ChromaSubsampling` tag as `(1, 1)` to
|
||||||
|
indicate no subsampling, instead of leaving it at its default of `(2, 2)`.
|
||||||
|
- The decoder will reject subsampled YCbCr data as there is no upsampling
|
||||||
|
routine, except for JPEG compressed images where the JPEG decoders handles
|
||||||
|
this. Since the buffer in that case indicates a full-sized plane for all
|
||||||
|
color samples any future support for the tag will upsample all planes (at
|
||||||
|
least within this major version).
|
||||||
|
|
||||||
|
Additions:
|
||||||
|
- Added support for the `CieLab` color type.
|
||||||
|
- Added `DecodingResult::resize_to` to create a buffer with a matching sample
|
||||||
|
type and dimensions.
|
||||||
|
- Added `ByteOrder::native` to access the platform's native endianness.
|
||||||
|
- Added `ByteOrder::convert` to change the byte-order of values in a byte
|
||||||
|
buffer, depending on their `Type` as described at runtime.
|
||||||
|
- Added `DirectoryOffset::new` to encode a directory whose offset is known to
|
||||||
|
the caller but that has not been written through the encoder itself.
|
||||||
|
- Added `Encoder::extra_samples` to encode images with more samples than their
|
||||||
|
color's trait implementation would otherwise suggest.
|
||||||
|
- Added `DirectoryEncoder::extend_from` to encode multiple tag entries from a
|
||||||
|
directory whose values were written to the file by means outside the
|
||||||
|
encoder's control.
|
||||||
|
- Added `Decoder::read_coding_unit_bytes` to retrieve data of corresponding
|
||||||
|
coordinates from potentially planar data, which is encoded in multiple chunks
|
||||||
|
of the file.
|
||||||
|
|
||||||
# Version 0.10.3
|
# Version 0.10.3
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.10.3"
|
version = "0.11.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
+101
-5
@@ -85,6 +85,7 @@ pub(crate) struct Image {
|
|||||||
pub tile_attributes: Option<TileAttributes>,
|
pub tile_attributes: Option<TileAttributes>,
|
||||||
pub chunk_offsets: Vec<u64>,
|
pub chunk_offsets: Vec<u64>,
|
||||||
pub chunk_bytes: Vec<u64>,
|
pub chunk_bytes: Vec<u64>,
|
||||||
|
pub chroma_subsampling: (u16, u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes how to read a tile-aligned portion of the image.
|
/// Describes how to read a tile-aligned portion of the image.
|
||||||
@@ -95,6 +96,19 @@ pub(crate) struct ReadoutLayout {
|
|||||||
pub planar_config: PlanarConfiguration,
|
pub planar_config: PlanarConfiguration,
|
||||||
|
|
||||||
/// The sample interpretation (interpret with planar_config).
|
/// The sample interpretation (interpret with planar_config).
|
||||||
|
///
|
||||||
|
/// FIXME: we should not require this here. The ability to turn out the raw bytes from the
|
||||||
|
/// sample arrays is very different from turning out interpretable color. Firstly we can always
|
||||||
|
/// readout `Multiband` but currently only use that ColorType in special circumstances (it must
|
||||||
|
/// not overlap cases where actually want to use a ColorType).
|
||||||
|
///
|
||||||
|
/// And then we have CIE Lab, which uses a tuple of `(u8, i8, i8)`, that is still filterable
|
||||||
|
/// but still not represented by any of our `DecoderResult` variants. Other color variants
|
||||||
|
/// depend on extra tags (YCbCrCoefficients/0x0211) and we don't have a good side channel to
|
||||||
|
/// tag the output with all that TIFF specific information, so arguably we should process and
|
||||||
|
/// apply those to the data so it becomes a self-contained representation.
|
||||||
|
///
|
||||||
|
/// This should be computed at a higher level, in `Decoder`, instead.
|
||||||
pub color: ColorType,
|
pub color: ColorType,
|
||||||
/// The number of bytes from one row to another.
|
/// The number of bytes from one row to another.
|
||||||
pub minimum_row_stride: usize,
|
pub minimum_row_stride: usize,
|
||||||
@@ -262,6 +276,25 @@ impl Image {
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(PlanarConfiguration::Chunky);
|
.unwrap_or(PlanarConfiguration::Chunky);
|
||||||
|
|
||||||
|
let ycbcr_subsampling = tag_reader.find_tag_uint_vec::<u16>(Tag::ChromaSubsampling)?;
|
||||||
|
|
||||||
|
let chroma_subsampling = if let Some(subsamples) = &ycbcr_subsampling {
|
||||||
|
let [a, b] = subsamples.as_slice() else {
|
||||||
|
return Err(TiffError::FormatError(TiffFormatError::InvalidCountForTag(
|
||||||
|
Tag::ChromaSubsampling,
|
||||||
|
subsamples.len(),
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// ImageWidth and ImageLength are constrained to be integer multiples of
|
||||||
|
// YCbCrSubsampleHoriz and YCbCrSubsampleVert respectively. TileWidth and TileLength
|
||||||
|
// have the same constraints. RowsPerStrip must be an integer multiple of
|
||||||
|
// YCbCrSubsampleVert.
|
||||||
|
(*a, *b)
|
||||||
|
} else {
|
||||||
|
(2, 2)
|
||||||
|
};
|
||||||
|
|
||||||
let planes = match planar_config {
|
let planes = match planar_config {
|
||||||
PlanarConfiguration::Chunky => 1,
|
PlanarConfiguration::Chunky => 1,
|
||||||
PlanarConfiguration::Planar => samples,
|
PlanarConfiguration::Planar => samples,
|
||||||
@@ -373,6 +406,7 @@ impl Image {
|
|||||||
tile_attributes,
|
tile_attributes,
|
||||||
chunk_offsets,
|
chunk_offsets,
|
||||||
chunk_bytes,
|
chunk_bytes,
|
||||||
|
chroma_subsampling,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,15 +469,60 @@ impl Image {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: this is bad we should not fail at this point
|
// ```
|
||||||
PhotometricInterpretation::RGBPalette
|
// struct IccLab /* Interpretation 9* {
|
||||||
| PhotometricInterpretation::TransparencyMask
|
// pub L: u8, // SampleFormat::Uint
|
||||||
| PhotometricInterpretation::CIELab => Err(TiffError::UnsupportedError(
|
// pub a: u8, // SampleFormat::Uint, defined as TiffLab::a + 128
|
||||||
|
// pub b: u8, // SampleFormat::Uint, defined as TiffLab::b + 128
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
PhotometricInterpretation::IccLab => match self.photometric_samples {
|
||||||
|
3 if matches!(self.sample_format, SampleFormat::Uint) => {
|
||||||
|
Ok(ColorType::Lab(self.bits_per_sample))
|
||||||
|
}
|
||||||
|
_ => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
|
self.photometric_interpretation,
|
||||||
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
// Unsupported due to inherently heterogeneous sample types. This is represented as:
|
||||||
|
// ```
|
||||||
|
// struct TiffLab /* Interpretation 8* {
|
||||||
|
// pub L: u8, // SampleFormat::Uint
|
||||||
|
// pub a: i8, // SampleFormat::Int
|
||||||
|
// pub b: i8, // SampleFormat::Int
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
PhotometricInterpretation::CIELab => Err(TiffError::UnsupportedError(
|
||||||
TiffUnsupportedError::InterpretationWithBits(
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
self.photometric_interpretation,
|
PhotometricInterpretation::CIELab,
|
||||||
vec![self.bits_per_sample; self.samples as usize],
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
|
// Unsupported due to extra unfiltering and conversion steps. We need to find the
|
||||||
|
// Decode tag (SRATIONAL; 2 * SamplesPerPixel) and apply the following conversion:
|
||||||
|
//
|
||||||
|
// L* = Decode[0] + Lsample x (Decode[1] - Decode[0]) / (2^n -1)
|
||||||
|
// …
|
||||||
|
//
|
||||||
|
// So we'll have a larger depth in the output and either worry about reducing fractions
|
||||||
|
// or turn everything into floats. That's a lot of decisions.
|
||||||
|
PhotometricInterpretation::ItuLab => Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
|
PhotometricInterpretation::CIELab,
|
||||||
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
PhotometricInterpretation::RGBPalette | PhotometricInterpretation::TransparencyMask => {
|
||||||
|
Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::InterpretationWithBits(
|
||||||
|
self.photometric_interpretation,
|
||||||
|
vec![self.bits_per_sample; self.samples as usize],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -657,6 +736,23 @@ impl Image {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only this color type interprets the tag, which is defined with a default of (2, 2)
|
||||||
|
if matches!(color, ColorType::YCbCr(_)) && self.chroma_subsampling != (1, 1) {
|
||||||
|
// The JPEG library does upsampling for us and defines its buffers correctly
|
||||||
|
// (presumably). All other compression schemes are not supported..
|
||||||
|
//
|
||||||
|
// NOTE: as explained in <fa225e820b96bef35f01bf4685654beeb4a8df0c> we may be better
|
||||||
|
// off supporting this tag by consistently upsampling, not by adjusting the buffer
|
||||||
|
// size. At least as a default this makes more sense and is much more permissive in
|
||||||
|
// case the compression stream disagrees with the tags (we would not have enough / or
|
||||||
|
// the wrong buffer layout if we only asked for subsampled planes in a planar layout).
|
||||||
|
if !matches!(self.compression_method, CompressionMethod::ModernJPEG) {
|
||||||
|
return Err(TiffError::UnsupportedError(
|
||||||
|
TiffUnsupportedError::ChromaSubsampling,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ReadoutLayout {
|
Ok(ReadoutLayout {
|
||||||
planar_config: self.planar_config,
|
planar_config: self.planar_config,
|
||||||
color,
|
color,
|
||||||
|
|||||||
+2
-1
@@ -860,6 +860,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
tile_attributes: None,
|
tile_attributes: None,
|
||||||
chunk_offsets: Vec::new(),
|
chunk_offsets: Vec::new(),
|
||||||
chunk_bytes: Vec::new(),
|
chunk_bytes: Vec::new(),
|
||||||
|
chroma_subsampling: (2, 2),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
decoder.next_image()?;
|
decoder.next_image()?;
|
||||||
@@ -1277,7 +1278,7 @@ impl<R: Read + Seek> Decoder<R> {
|
|||||||
///
|
///
|
||||||
/// It will re-allocate the buffer into the correct type and size, within the decoder's
|
/// It will re-allocate the buffer into the correct type and size, within the decoder's
|
||||||
/// configured limits, and then pass it to the underlying method. This is essentially a
|
/// configured limits, and then pass it to the underlying method. This is essentially a
|
||||||
/// type-safe wrapper around the raw [`Self::read_chunk_to_bytes`] method.
|
/// type-safe wrapper around the raw [`Self::read_chunk_bytes`] method.
|
||||||
///
|
///
|
||||||
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
/// Note that for planar images each chunk contains only one sample of the underlying data.
|
||||||
pub fn read_chunk_to_buffer(
|
pub fn read_chunk_to_buffer(
|
||||||
|
|||||||
+10
-1
@@ -11,7 +11,10 @@ use std::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
decoder::ifd::Entry,
|
decoder::ifd::Entry,
|
||||||
error::{TiffResult, UsageError},
|
error::{TiffResult, UsageError},
|
||||||
tags::{CompressionMethod, ExtraSamples, IfdPointer, ResolutionUnit, SampleFormat, Tag, Type},
|
tags::{
|
||||||
|
CompressionMethod, ExtraSamples, IfdPointer, PhotometricInterpretation, ResolutionUnit,
|
||||||
|
SampleFormat, Tag, Type,
|
||||||
|
},
|
||||||
Directory, TiffError, TiffFormatError,
|
Directory, TiffError, TiffFormatError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -565,6 +568,12 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T,
|
|||||||
|
|
||||||
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
|
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?;
|
||||||
|
|
||||||
|
if matches!(<T>::TIFF_VALUE, PhotometricInterpretation::YCbCr) {
|
||||||
|
// The default for this tag is 2,2 for subsampling but we do not support such a
|
||||||
|
// transformation. Instead all samples must be provided.
|
||||||
|
encoder.write_tag(Tag::ChromaSubsampling, &[1u16, 1u16][..])?;
|
||||||
|
}
|
||||||
|
|
||||||
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?;
|
||||||
|
|
||||||
encoder.write_tag(
|
encoder.write_tag(
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ quick_error! {
|
|||||||
InvalidTypeForTag {
|
InvalidTypeForTag {
|
||||||
display("tag has invalid type")
|
display("tag has invalid type")
|
||||||
}
|
}
|
||||||
|
InvalidCountForTag(tag: Tag, len: usize) {
|
||||||
|
display("tag `{tag:?}` with incorrect number of elements ({len}) encountered")
|
||||||
|
}
|
||||||
StripTileTagConflict {
|
StripTileTagConflict {
|
||||||
display("file should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found")
|
display("file should contain either (StripByteCounts and StripOffsets) or (TileByteCounts and TileOffsets), other combination was found")
|
||||||
}
|
}
|
||||||
@@ -156,6 +159,9 @@ quick_error! {
|
|||||||
UnsupportedInterpretation(interpretation: PhotometricInterpretation) {
|
UnsupportedInterpretation(interpretation: PhotometricInterpretation) {
|
||||||
display("unsupported photometric interpretation \"{interpretation:?}\"")
|
display("unsupported photometric interpretation \"{interpretation:?}\"")
|
||||||
}
|
}
|
||||||
|
ChromaSubsampling {
|
||||||
|
display("chroma subsampling of YCbCr color is unsupported")
|
||||||
|
}
|
||||||
MisalignedTileBoundaries {
|
MisalignedTileBoundaries {
|
||||||
display("tile rows are not aligned to byte boundaries")
|
display("tile rows are not aligned to byte boundaries")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ pub enum ColorType {
|
|||||||
/// Pixel is YCbCr
|
/// Pixel is YCbCr
|
||||||
YCbCr(u8),
|
YCbCr(u8),
|
||||||
|
|
||||||
|
/// Pixel is CIE L*a*b* (ICC LAb)
|
||||||
|
Lab(u8),
|
||||||
|
|
||||||
/// Pixel has multiple bands/channels
|
/// Pixel has multiple bands/channels
|
||||||
Multiband { bit_depth: u8, num_samples: u16 },
|
Multiband { bit_depth: u8, num_samples: u16 },
|
||||||
}
|
}
|
||||||
@@ -58,6 +61,7 @@ impl ColorType {
|
|||||||
| ColorType::CMYK(b)
|
| ColorType::CMYK(b)
|
||||||
| ColorType::CMYKA(b)
|
| ColorType::CMYKA(b)
|
||||||
| ColorType::YCbCr(b)
|
| ColorType::YCbCr(b)
|
||||||
|
| ColorType::Lab(b)
|
||||||
| ColorType::Multiband { bit_depth: b, .. } => b,
|
| ColorType::Multiband { bit_depth: b, .. } => b,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +76,7 @@ impl ColorType {
|
|||||||
ColorType::CMYK(_) => 4,
|
ColorType::CMYK(_) => 4,
|
||||||
ColorType::CMYKA(_) => 5,
|
ColorType::CMYKA(_) => 5,
|
||||||
ColorType::YCbCr(_) => 3,
|
ColorType::YCbCr(_) => 3,
|
||||||
|
ColorType::Lab(_) => 3,
|
||||||
ColorType::Multiband { num_samples, .. } => num_samples,
|
ColorType::Multiband { num_samples, .. } => num_samples,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-7
@@ -20,7 +20,7 @@ macro_rules! tags {
|
|||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
const fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
|
||||||
match n {
|
match n {
|
||||||
$( $val => Ok($name::$tag), )*
|
$( $val => Ok($name::$tag), )*
|
||||||
n => Err(n),
|
n => Err(n),
|
||||||
@@ -28,7 +28,7 @@ macro_rules! tags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn __to_inner_type(&self) -> $ty {
|
const fn __to_inner_type(&self) -> $ty {
|
||||||
match *self {
|
match *self {
|
||||||
$( $name::$tag => $val, )*
|
$( $name::$tag => $val, )*
|
||||||
$( $name::Unknown($unknown_doc) => { $unknown_doc }, )*
|
$( $name::Unknown($unknown_doc) => { $unknown_doc }, )*
|
||||||
@@ -42,19 +42,25 @@ macro_rules! tags {
|
|||||||
($name:tt, u16, $($unknown_doc:ident)*) => {
|
($name:tt, u16, $($unknown_doc:ident)*) => {
|
||||||
impl $name {
|
impl $name {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_u16(val: u16) -> Option<Self> {
|
pub const fn from_u16(val: u16) -> Option<Self> {
|
||||||
Self::__from_inner_type(val).ok()
|
match Self::__from_inner_type(val) {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_u16_exhaustive($unknown_doc: u16) -> Self {
|
pub const fn from_u16_exhaustive($unknown_doc: u16) -> Self {
|
||||||
Self::__from_inner_type($unknown_doc).unwrap_or_else(|_| $name::Unknown($unknown_doc))
|
match Self::__from_inner_type($unknown_doc) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => $name::Unknown($unknown_doc),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn to_u16(&self) -> u16 {
|
pub const fn to_u16(&self) -> u16 {
|
||||||
Self::__to_inner_type(self)
|
Self::__to_inner_type(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,6 +128,11 @@ pub enum Tag(u16) unknown(
|
|||||||
SMaxSampleValue = 341, // TODO add support
|
SMaxSampleValue = 341, // TODO add support
|
||||||
// JPEG
|
// JPEG
|
||||||
JPEGTables = 347,
|
JPEGTables = 347,
|
||||||
|
// Subsampling
|
||||||
|
#[doc(alias = "YCbCrSubsampling")]
|
||||||
|
ChromaSubsampling = 530, // TODO add support
|
||||||
|
#[doc(alias = "YCbCrPositioning")]
|
||||||
|
ChromaPositioning = 531, // TODO add support
|
||||||
// GeoTIFF
|
// GeoTIFF
|
||||||
ModelPixelScaleTag = 33550, // (SoftDesk)
|
ModelPixelScaleTag = 33550, // (SoftDesk)
|
||||||
ModelTransformationTag = 34264, // (JPL Carto Group)
|
ModelTransformationTag = 34264, // (JPL Carto Group)
|
||||||
@@ -266,6 +277,8 @@ pub enum PhotometricInterpretation(u16) {
|
|||||||
CMYK = 5,
|
CMYK = 5,
|
||||||
YCbCr = 6,
|
YCbCr = 6,
|
||||||
CIELab = 8,
|
CIELab = 8,
|
||||||
|
IccLab = 9,
|
||||||
|
ItuLab = 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user