Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace FromPrimitive with our own tag macro #63

Merged
merged 8 commits into from
Nov 12, 2019
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
2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ exclude = ["tests/images/*", "tests/fuzz_images/*"]
[dependencies]
byteorder = "1.2"
lzw = "0.10"
num-derive = "0.3"
num-traits = "0.2"
miniz_oxide = "0.3"

[dev-dependencies]
Expand Down
153 changes: 98 additions & 55 deletions src/decoder/ifd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,42 @@ use {TiffError, TiffFormatError, TiffResult, TiffUnsupportedError};
use self::Value::{Ascii, List, Rational, Unsigned, Signed, SRational};

macro_rules! tags {
{$(
$tag:ident
$val:expr;
)*} => {

/// TIFF tag
{
// Permit arbitrary meta items, which include documentation.
$( #[$enum_attr:meta] )*
$vis:vis enum $name:ident($ty:ty) $(unknown($unknown_doc:literal))* {
// Each of the `Name = Val,` permitting documentation.
$($(#[$ident_attr:meta])* $tag:ident = $val:expr,)*
}
} => {
$( #[$enum_attr] )*
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Tag {
$($tag,)*
Unknown(u16)
pub enum $name {
$($(#[$ident_attr])* $tag,)*
// FIXME: switch to non_exhaustive once stabilized and compiler requirement new enough
#[doc(hidden)]
__NonExhaustive,
$(
#[doc = $unknown_doc]
Unknown($ty),
)*
}
impl Tag {
pub fn from_u16(n: u16) -> Tag {
$(if n == $val { Tag::$tag } else)* {
Tag::Unknown(n)

impl $name {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm coming to this somewhat late so perhaps I'm missing some context, but is there a reason not to replace the methods below with impls of TryFrom<$ty> for $tag and From<$tag> for $ty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I'd minimize the public interface that comes from within the macro. It might still be a good idea to expose those.

#[inline(always)]
fn __from_inner_type(n: $ty) -> Result<Self, $ty> {
match n {
$( $val => Ok($name::$tag), )*
n => Err(n),
}
}
pub fn to_u16(&self) -> u16 {

#[inline(always)]
fn __to_inner_type(&self) -> $ty {
match *self {
$( Tag::$tag => $val, )*
Tag::Unknown(n) => n,
$( $name::$tag => $val, )*
$( $name::Unknown(n) => { $unknown_doc; n }, )*
$name::__NonExhaustive => unreachable!(),
}
}
}
Expand All @@ -39,51 +54,65 @@ macro_rules! tags {

// Note: These tags appear in the order they are mentioned in the TIFF reference
tags! {
/// TIFF tags
pub enum Tag(u16) unknown("A private or extension tag") {
// Baseline tags:
Artist 315;
Artist = 315,
// grayscale images PhotometricInterpretation 1 or 3
BitsPerSample 258;
CellLength 265; // TODO add support
CellWidth 264; // TODO add support
BitsPerSample = 258,
CellLength = 265, // TODO add support
CellWidth = 264, // TODO add support
// palette-color images (PhotometricInterpretation 3)
ColorMap 320; // TODO add support
Compression 259; // TODO add support for 2 and 32773
Copyright 33_432;
DateTime 306;
ExtraSamples 338; // TODO add support
FillOrder 266; // TODO add support
FreeByteCounts 289; // TODO add support
FreeOffsets 288; // TODO add support
GrayResponseCurve 291; // TODO add support
GrayResponseUnit 290; // TODO add support
HostComputer 316;
ImageDescription 270;
ImageLength 257;
ImageWidth 256;
Make 271;
MaxSampleValue 281; // TODO add support
MinSampleValue 280; // TODO add support
Model 272;
NewSubfileType 254; // TODO add support
Orientation 274; // TODO add support
PhotometricInterpretation 262;
PlanarConfiguration 284;
ResolutionUnit 296; // TODO add support
RowsPerStrip 278;
SamplesPerPixel 277;
Software 305;
StripByteCounts 279;
StripOffsets 273;
SubfileType 255; // TODO add support
Threshholding 263; // TODO add support
XResolution 282;
YResolution 283;
ColorMap = 320, // TODO add support
Compression = 259, // TODO add support for 2 and 32773
Copyright = 33_432,
DateTime = 306,
ExtraSamples = 338, // TODO add support
FillOrder = 266, // TODO add support
FreeByteCounts = 289, // TODO add support
FreeOffsets = 288, // TODO add support
GrayResponseCurve = 291, // TODO add support
GrayResponseUnit = 290, // TODO add support
HostComputer = 316,
ImageDescription = 270,
ImageLength = 257,
ImageWidth = 256,
Make = 271,
MaxSampleValue = 281, // TODO add support
MinSampleValue = 280, // TODO add support
Model = 272,
NewSubfileType = 254, // TODO add support
Orientation = 274, // TODO add support
PhotometricInterpretation = 262,
PlanarConfiguration = 284,
ResolutionUnit = 296, // TODO add support
RowsPerStrip = 278,
SamplesPerPixel = 277,
Software = 305,
StripByteCounts = 279,
StripOffsets = 273,
SubfileType = 255, // TODO add support
Threshholding = 263, // TODO add support
XResolution = 282,
YResolution = 283,
// Advanced tags
Predictor 317;
Predictor = 317,
}
}

impl Tag {
pub fn from_u16(val: u16) -> Self {
Self::__from_inner_type(val).unwrap_or_else(Tag::Unknown)
}

#[derive(Clone, Copy, Debug, FromPrimitive)]
pub enum Type {
pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}

tags! {
/// The type of an IFD entry (a 2 byte field).
pub enum Type(u16) {
BYTE = 1,
ASCII = 2,
SHORT = 3,
Expand All @@ -94,6 +123,18 @@ pub enum Type {
SLONG = 9,
SRATIONAL = 10,
}
}

impl Type {
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}

pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}


#[allow(unused_qualifications)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand All @@ -104,6 +145,8 @@ pub enum Value {
Rational(u32, u32),
SRational(i32, i32),
Ascii(String),
#[doc(hidden)] // Do not match against this.
__NonExhaustive,
}

impl Value {
Expand Down
78 changes: 63 additions & 15 deletions src/decoder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use num_traits::{FromPrimitive, Num};
use std::collections::HashMap;
use std::io::{self, Read, Seek};
use std::cmp;
Expand All @@ -9,6 +8,7 @@ use self::ifd::Directory;

use self::stream::{ByteOrder, EndianReader, LZWReader, DeflateReader, PackBitsReader, SmartReader};

#[macro_use]
pub mod ifd;
mod stream;

Expand Down Expand Up @@ -80,8 +80,8 @@ impl<'a> DecodingBuffer<'a> {
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
pub enum PhotometricInterpretation {
tags! {
pub enum PhotometricInterpretation(u16) {
WhiteIsZero = 0,
BlackIsZero = 1,
RGB = 2,
Expand All @@ -91,9 +91,10 @@ pub enum PhotometricInterpretation {
YCbCr = 6,
CIELab = 8,
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
pub enum CompressionMethod {
tags! {
pub enum CompressionMethod(u16) {
None = 1,
Huffman = 2,
Fax3 = 3,
Expand All @@ -104,18 +105,61 @@ pub enum CompressionMethod {
OldDeflate = 0x80B2,
PackBits = 0x8005,
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
pub enum PlanarConfiguration {
tags! {
pub enum PlanarConfiguration(u16) {
Chunky = 1,
Planar = 2,
}
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, FromPrimitive)]
enum Predictor {
tags! {
enum Predictor(u16) {
None = 1,
Horizontal = 2,
}
}

impl PhotometricInterpretation {
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}

pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}

impl CompressionMethod {
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}

pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}

impl PlanarConfiguration {
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}

pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}

impl Predictor {
pub fn from_u16(val: u16) -> Option<Self> {
Self::__from_inner_type(val).ok()
}

pub fn to_u16(&self) -> u16 {
Self::__to_inner_type(self)
}
}

#[derive(Debug)]
struct StripDecodeState {
Expand Down Expand Up @@ -191,7 +235,7 @@ impl Wrapping for u16 {

fn rev_hpredict_nsamp<T>(image: &mut [T], size: (u32, u32), samples: usize)
where
T: Num + Copy + Wrapping,
T: Copy + Wrapping,
{
let width = size.0 as usize;
let height = size.1 as usize;
Expand Down Expand Up @@ -334,8 +378,9 @@ impl<R: Read + Seek> Decoder<R> {
self.width = self.get_tag_u32(ifd::Tag::ImageWidth)?;
self.height = self.get_tag_u32(ifd::Tag::ImageLength)?;
self.strip_decoder = None;
self.photometric_interpretation = match FromPrimitive::from_u32(
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)?
// TODO: error on non-SHORT value.
birktj marked this conversation as resolved.
Show resolved Hide resolved
self.photometric_interpretation = match PhotometricInterpretation::from_u16(
self.get_tag_u32(ifd::Tag::PhotometricInterpretation)? as u16
) {
Some(val) => val,
None => {
Expand All @@ -344,8 +389,9 @@ impl<R: Read + Seek> Decoder<R> {
))
}
};
// TODO: error on non-SHORT value.
if let Some(val) = self.find_tag_u32(ifd::Tag::Compression)? {
match FromPrimitive::from_u32(val) {
match CompressionMethod::from_u16(val as u16) {
Some(method) => self.compression_method = method,
None => {
return Err(TiffError::UnsupportedError(
Expand Down Expand Up @@ -454,7 +500,7 @@ impl<R: Read + Seek> Decoder<R> {
// Value 4 bytes either a pointer the value itself
fn read_entry(&mut self) -> TiffResult<Option<(ifd::Tag, ifd::Entry)>> {
let tag = ifd::Tag::from_u16(self.read_short()?);
let type_: ifd::Type = match FromPrimitive::from_u16(self.read_short()?) {
let type_ = match ifd::Type::from_u16(self.read_short()?) {
Some(t) => t,
None => {
// Unknown type. Skip this entry according to spec.
Expand Down Expand Up @@ -720,7 +766,8 @@ impl<R: Read + Seek> Decoder<R> {
));
}
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
match FromPrimitive::from_u32(predictor) {
// TODO: error on non-SHORT value.
match Predictor::from_u16(predictor as u16) {
Some(Predictor::None) => (),
Some(Predictor::Horizontal) => {
rev_hpredict(
Expand All @@ -734,6 +781,7 @@ impl<R: Read + Seek> Decoder<R> {
predictor,
)))
}
Some(Predictor::__NonExhaustive) => unreachable!(),
}
}
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/encoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ impl<'a, W: 'a + Write + Seek> DirectoryEncoder<'a, W> {
}

self.ifd
.insert(tag.to_u16(), (<T>::FIELD_TYPE as u16, value.count(), bytes));
.insert(tag.to_u16(), (<T>::FIELD_TYPE.to_u16(), value.count(), bytes));
}

fn write_directory(&mut self) -> TiffResult<u64> {
Expand Down Expand Up @@ -565,7 +565,7 @@ impl<'a, W: 'a + Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
encoder.write_tag(Tag::Compression, 1u16);

encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE);
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE as u16);
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16());

encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);

Expand Down
3 changes: 0 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
extern crate byteorder;
extern crate lzw;
extern crate miniz_oxide;
#[macro_use]
extern crate num_derive;
extern crate num_traits;

pub mod decoder;
pub mod encoder;
Expand Down