Merge pull request #1 from Robzz/port_image_code

Import tiff code from image crate
This commit is contained in:
Sven Nilsen authored and GitHub committed 2018-06-17 23:57:52 +02:00
commit a8f4aebf64
25 files changed
+1147

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
/target/
**/*.rs.bk
Cargo.lock
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "tiff"
version = "0.1.0"
authors = [
"ccgn",
"bvssvni <bvssvni@gmail.com>",
"nwin",
"TyOverby <ty@pre-alpha.com>",
"HeroicKatora",
"Calum",
"CensoredUsername <cens.username@gmail.com>",
"Robzz"]
[dependencies]
byteorder = "1.2.*"
lzw = "0.10.*"
num-derive = "0.2.*"
num-traits = "0.2.*"
+187
View File
@@ -0,0 +1,187 @@
//! Function for reading TIFF tags
use std::io::{self, Read, Seek};
use std::collections::{HashMap};
use super::stream::{ByteOrder, SmartReader, EndianReader};
use ::{TiffError, TiffResult};
use self::Value::{Unsigned, List};
macro_rules! tags {
{$(
$tag:ident
$val:expr;
)*} => {
/// TIFF tag
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Tag {
$($tag,)*
Unknown(u16)
}
impl Tag {
pub fn from_u16(n: u16) -> Tag {
$(if n == $val { Tag::$tag } else)* {
Tag::Unknown(n)
}
}
}
}
}
// Note: These tags appear in the order they are mentioned in the TIFF reference
tags!{
// Baseline tags:
Artist 315; // TODO add support
// grayscale images PhotometricInterpretation 1 or 3
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; // TODO add support
DateTime 306; // TODO add support
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; // TODO add support
ImageDescription 270; // TODO add support
ImageLength 257;
ImageWidth 256;
Make 271; // TODO add support
MaxSampleValue 281; // TODO add support
MinSampleValue 280; // TODO add support
Model 272; // TODO add support
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;
}
#[derive(Clone, Copy, Debug, FromPrimitive)]
pub enum Type {
BYTE = 1,
ASCII = 2,
SHORT = 3,
LONG = 4,
RATIONAL = 5,
}
#[allow(unused_qualifications)]
#[derive(Debug)]
pub enum Value {
//Signed(i32),
Unsigned(u32),
List(Vec<Value>)
}
impl Value {
pub fn into_u32(self) -> TiffResult<u32> {
match self {
Unsigned(val) => Ok(val),
val => Err(TiffError::FormatError(format!(
"Expected unsigned integer, {:?} found.", val
)))
}
}
pub fn into_u32_vec(self) -> TiffResult<Vec<u32>> {
match self {
List(vec) => {
let mut new_vec = Vec::with_capacity(vec.len());
for v in vec {
new_vec.push(v.into_u32()?)
}
Ok(new_vec)
},
Unsigned(val) => Ok(vec![val]),
//_ => Err(::image::FormatError("Tag data malformed.".to_string()))
}
}
}
pub struct Entry {
type_: Type,
count: u32,
offset: [u8; 4],
}
impl ::std::fmt::Debug for Entry {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
fmt.write_str(&format!("Entry {{ type_: {:?}, count: {:?}, offset: {:?} }}",
self.type_,
self.count,
&self.offset
))
}
}
impl Entry {
pub fn new(type_: Type, count: u32, offset: [u8; 4]) -> Entry {
Entry { type_, count, offset }
}
/// Returns a mem_reader for the offset/value field
fn r(&self, byte_order: ByteOrder) -> SmartReader<io::Cursor<Vec<u8>>> {
SmartReader::wrap(
io::Cursor::new(self.offset.to_vec()),
byte_order
)
}
pub fn val<R: Read + Seek>(&self, decoder: &mut super::Decoder<R>)
-> TiffResult<Value> {
let bo = decoder.byte_order();
match (self.type_, self.count) {
// TODO check if this could give wrong results
// at a different endianess of file/computer.
(Type::BYTE, 1) => Ok(Unsigned(u32::from(self.offset[0]))),
(Type::SHORT, 1) => Ok(Unsigned(u32::from(self.r(bo).read_u16()?))),
(Type::SHORT, 2) => {
let mut r = self.r(bo);
Ok(List(vec![
Unsigned(u32::from(r.read_u16()?)),
Unsigned(u32::from(r.read_u16()?))
]))
},
(Type::SHORT, n) => {
let mut v = Vec::with_capacity(n as usize);
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
for _ in 0 .. n {
v.push(Unsigned(u32::from(decoder.read_short()?)))
}
Ok(List(v))
},
(Type::LONG, 1) => Ok(Unsigned(try!(self.r(bo).read_u32()))),
(Type::LONG, n) => {
let mut v = Vec::with_capacity(n as usize);
try!(decoder.goto_offset(try!(self.r(bo).read_u32())));
for _ in 0 .. n {
v.push(Unsigned(try!(decoder.read_long())))
}
Ok(List(v))
}
_ => Err(TiffError::UnsupportedError("Unsupported data type.".to_string()))
}
}
}
/// Type representing an Image File Directory
pub type Directory = HashMap<Tag, Entry>;
+541
View File
@@ -0,0 +1,541 @@
use std::io::{self, Read, Seek};
use std::mem;
use num_traits::{FromPrimitive, Num};
use std::collections::HashMap;
use ::{ColorType, TiffError, TiffResult};
use self::ifd::Directory;
use self::stream::{
ByteOrder,
EndianReader,
SmartReader,
LZWReader,
PackBitsReader
};
mod ifd;
mod stream;
/// Result of a decoding process
#[derive(Debug)]
pub enum DecodingResult {
/// A vector of unsigned bytes
U8(Vec<u8>),
/// A vector of unsigned words
U16(Vec<u16>)
}
// A buffer for image decoding
enum DecodingBuffer<'a> {
/// A slice of unsigned bytes
U8(&'a mut [u8]),
/// A slice of unsigned words
U16(&'a mut [u16])
}
#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
enum PhotometricInterpretation {
WhiteIsZero = 0,
BlackIsZero = 1,
RGB = 2,
RGBPalette = 3,
TransparencyMask = 4,
CMYK = 5,
YCbCr = 6,
CIELab = 8,
}
#[derive(Clone, Copy, Debug, FromPrimitive)]
enum CompressionMethod {
None = 1,
Huffman = 2,
Fax3 = 3,
Fax4 = 4,
LZW = 5,
JPEG = 6,
PackBits = 0x8005
}
#[derive(Clone, Copy, Debug, FromPrimitive)]
enum PlanarConfiguration {
Chunky = 1,
Planar = 2
}
#[derive(Clone, Copy, Debug, FromPrimitive)]
enum Predictor {
None = 1,
Horizontal = 2
}
/// The representation of a TIFF decoder
///
/// Currently does not support decoding of interlaced images
#[derive(Debug)]
pub struct Decoder<R> where R: Read + Seek {
reader: SmartReader<R>,
byte_order: ByteOrder,
next_ifd: Option<u32>,
ifd: Option<Directory>,
width: u32,
height: u32,
bits_per_sample: Vec<u8>,
samples: u8,
photometric_interpretation: PhotometricInterpretation,
compression_method: CompressionMethod
}
trait Wrapping {
fn wrapping_add(&self, other: Self) -> Self;
}
impl Wrapping for u8 {
fn wrapping_add(&self, other: Self) -> Self {
u8::wrapping_add(*self, other)
}
}
impl Wrapping for u16 {
fn wrapping_add(&self, other: Self) -> Self {
u16::wrapping_add(*self, other)
}
}
fn rev_hpredict_nsamp<T>(mut image: Vec<T>,
size: (u32, u32),
samples: usize)
-> Vec<T>
where T: Num + Copy + Wrapping {
let width = size.0 as usize;
let height = size.1 as usize;
for row in 0..height {
for col in samples..width * samples {
let prev_pixel = image[(row * width * samples + col - samples)];
let pixel = &mut image[(row * width * samples + col)];
*pixel = pixel.wrapping_add(prev_pixel);
}
}
image
}
fn rev_hpredict(image: DecodingResult, size: (u32, u32), color_type: ColorType) -> TiffResult<DecodingResult> {
let samples = match color_type {
ColorType::Gray(8) | ColorType::Gray(16) => 1,
ColorType::RGB(8) | ColorType::RGB(16) => 3,
ColorType::RGBA(8) | ColorType::RGBA(16) => 4,
_ => return Err(TiffError::UnsupportedError(format!(
"Horizontal predictor for {:?} is unsupported.", color_type
)))
};
Ok(match image {
DecodingResult::U8(buf) => {
DecodingResult::U8(rev_hpredict_nsamp(buf, size, samples))
},
DecodingResult::U16(buf) => {
DecodingResult::U16(rev_hpredict_nsamp(buf, size, samples))
}
})
}
impl<R: Read + Seek> Decoder<R> {
/// Create a new decoder that decodes from the stream ```r```
pub fn new(r: R) -> TiffResult<Decoder<R>> {
Decoder {
reader: SmartReader::wrap(r, ByteOrder::LittleEndian),
byte_order: ByteOrder::LittleEndian,
next_ifd: None,
ifd: None,
width: 0,
height: 0,
bits_per_sample: vec![1],
samples: 1,
photometric_interpretation: PhotometricInterpretation::BlackIsZero,
compression_method: CompressionMethod::None
}.init()
}
pub fn dimensions(&mut self) -> TiffResult<(u32, u32)> {
Ok((self.width, self.height))
}
pub fn colortype(&mut self) -> TiffResult<ColorType> {
match self.photometric_interpretation {
// TODO: catch also [ 8, 8, 8, _] this does not work due to a bug in rust atm
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8, 8] => Ok(ColorType::RGBA(8)),
PhotometricInterpretation::RGB if self.bits_per_sample == [8, 8, 8] => Ok(ColorType::RGB(8)),
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16, 16] => Ok(ColorType::RGBA(16)),
PhotometricInterpretation::RGB if self.bits_per_sample == [16, 16, 16] => Ok(ColorType::RGB(16)),
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero
if self.bits_per_sample.len() == 1 => Ok(ColorType::Gray(self.bits_per_sample[0])),
_ => Err(TiffError::UnsupportedError(format!(
"{:?} with {:?} bits per sample is unsupported", self.photometric_interpretation, self.bits_per_sample
))) // TODO: this is bad we should not fail at this point}
}
}
fn read_header(&mut self) -> TiffResult<()> {
let mut endianess = Vec::with_capacity(2);
try!(self.reader.by_ref().take(2).read_to_end(&mut endianess));
match &*endianess {
b"II" => {
self.byte_order = ByteOrder::LittleEndian;
self.reader.byte_order = ByteOrder::LittleEndian; },
b"MM" => {
self.byte_order = ByteOrder::BigEndian;
self.reader.byte_order = ByteOrder::BigEndian; },
_ => return Err(TiffError::FormatError(
"TIFF signature not found.".to_string()
))
}
if try!(self.read_short()) != 42 {
return Err(TiffError::FormatError("TIFF signature invalid.".to_string()))
}
self.next_ifd = match try!(self.read_long()) {
0 => None,
n => Some(n)
};
Ok(())
}
/// Initializes the decoder.
pub fn init(mut self) -> TiffResult<Decoder<R>> {
try!(self.read_header());
self.next_image()
}
/// Reads in the next image.
/// If there is no further image in the TIFF file a format error is returned.
/// To determine whether there are more images call `TIFFDecoder::more_images` instead.
pub fn next_image(mut self) -> TiffResult<Decoder<R>> {
self.ifd = Some(try!(self.read_ifd()));
self.width = try!(self.get_tag_u32(ifd::Tag::ImageWidth));
self.height = try!(self.get_tag_u32(ifd::Tag::ImageLength));
self.photometric_interpretation = match FromPrimitive::from_u32(
try!(self.get_tag_u32(ifd::Tag::PhotometricInterpretation))
) {
Some(val) => val,
None => return Err(TiffError::UnsupportedError(
"The image is using an unknown photometric interpretation.".to_string()
))
};
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::Compression)) {
match FromPrimitive::from_u32(val) {
Some(method) => {
self.compression_method = method
},
None => return Err(TiffError::UnsupportedError(
"Unknown compression method.".to_string()
))
}
}
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::SamplesPerPixel)) {
self.samples = val as u8
}
match self.samples {
1 => {
if let Some(val) = try!(self.find_tag_u32(ifd::Tag::BitsPerSample)) {
self.bits_per_sample = vec![val as u8]
}
}
3 | 4 => {
if let Some(val) = try!(self.find_tag_u32_vec(ifd::Tag::BitsPerSample)) {
self.bits_per_sample = val.iter().map(|&v| v as u8).collect()
}
}
_ => return Err(TiffError::UnsupportedError(
format!("{} samples per pixel is supported.", self.samples)
))
}
Ok(self)
}
/// Returns `true` if there is at least one more image available.
pub fn more_images(&self) -> bool {
match self.next_ifd {
Some(_) => true,
None => false
}
}
/// Returns the byte_order
pub fn byte_order(&self) -> ByteOrder {
self.byte_order
}
/// Reads a TIFF short value
#[inline]
pub fn read_short(&mut self) -> Result<u16, io::Error> {
self.reader.read_u16()
}
/// Reads a TIFF long value
#[inline]
pub fn read_long(&mut self) -> Result<u32, io::Error> {
self.reader.read_u32()
}
/// Reads a TIFF IFA offset/value field
#[inline]
pub fn read_offset(&mut self) -> Result<[u8; 4], io::Error> {
let mut val = [0; 4];
try!(self.reader.read_exact(&mut val));
Ok(val)
}
/// Moves the cursor to the specified offset
#[inline]
pub fn goto_offset(&mut self, offset: u32) -> io::Result<()> {
self.reader.seek(io::SeekFrom::Start(u64::from(offset))).map(|_| ())
}
/// Reads a IFD entry.
// An IFD entry has four fields:
//
// Tag 2 bytes
// Type 2 bytes
// Count 4 bytes
// 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(try!(self.read_short()));
let type_: ifd::Type = match FromPrimitive::from_u16(try!(self.read_short())) {
Some(t) => t,
None => {
// Unknown type. Skip this entry according to spec.
try!(self.read_long());
try!(self.read_long());
return Ok(None)
}
};
Ok(Some((tag, ifd::Entry::new(
type_,
try!(self.read_long()), // count
try!(self.read_offset()) // offset
))))
}
/// Reads the next IFD
fn read_ifd(&mut self) -> TiffResult<Directory> {
let mut dir: Directory = HashMap::new();
match self.next_ifd {
None => return Err(TiffError::FormatError(
"Image file directory not found.".to_string())
),
Some(offset) => try!(self.goto_offset(offset))
}
for _ in 0..try!(self.read_short()) {
let (tag, entry) = match try!(self.read_entry()) {
Some(val) => val,
None => continue // Unknown data type in tag, skip
};
dir.insert(tag, entry);
}
self.next_ifd = match try!(self.read_long()) {
0 => None,
n => Some(n)
};
Ok(dir)
}
/// Tries to retrieve a tag.
/// Return `Ok(None)` if the tag is not present.
fn find_tag(&mut self, tag: ifd::Tag) -> TiffResult<Option<ifd::Value>> {
let ifd: &Directory = unsafe {
let ifd = self.ifd.as_ref().unwrap(); // Ok to fail
// Get a immutable borrow of self
// This is ok because entry val only changes the stream
// but not the directory.
mem::transmute_copy(&ifd)
};
match ifd.get(&tag) {
None => Ok(None),
Some(entry) => Ok(Some(try!(entry.val(self))))
}
}
/// Tries to retrieve a tag and convert it to the desired type.
fn find_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<Option<u32>> {
match self.find_tag(tag)? {
Some(val) => val.into_u32().map(Some),
None => Ok(None)
}
}
/// Tries to retrieve a tag and convert it to the desired type.
fn find_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Option<Vec<u32>>> {
match self.find_tag(tag)? {
Some(val) => val.into_u32_vec().map(Some),
None => Ok(None)
}
}
/// Tries to retrieve a tag.
/// Returns an error if the tag is not present
fn get_tag(&mut self, tag: ifd::Tag) -> TiffResult<ifd::Value> {
match try!(self.find_tag(tag)) {
Some(val) => Ok(val),
None => Err(TiffError::FormatError(format!(
"Required tag `{:?}` not found.", tag
)))
}
}
/// Tries to retrieve a tag and convert it to the desired type.
fn get_tag_u32(&mut self, tag: ifd::Tag) -> TiffResult<u32> {
self.get_tag(tag)?.into_u32()
}
/// Tries to retrieve a tag and convert it to the desired type.
fn get_tag_u32_vec(&mut self, tag: ifd::Tag) -> TiffResult<Vec<u32>> {
self.get_tag(tag)?.into_u32_vec()
}
/// Decompresses the strip into the supplied buffer.
/// Returns the number of bytes read.
fn expand_strip<'a>(&mut self, buffer: DecodingBuffer<'a>, offset: u32, length: u32, max_uncompressed_length: usize) -> TiffResult<usize> {
let color_type = try!(self.colortype());
try!(self.goto_offset(offset));
let (bytes, mut reader): (usize, Box<EndianReader>) = match self.compression_method {
CompressionMethod::None => {
let order = self.reader.byte_order;
(length as usize, Box::new(SmartReader::wrap(&mut self.reader, order)))
},
CompressionMethod::LZW => {
let (bytes, reader) = try!(LZWReader::new(&mut self.reader, length as usize, max_uncompressed_length));
(bytes, Box::new(reader))
},
CompressionMethod::PackBits => {
let order = self.reader.byte_order;
let (bytes, reader) = try!(PackBitsReader::new(&mut self.reader, order, length as usize));
(bytes, Box::new(reader))
},
method => return Err(TiffError::UnsupportedError(format!(
"Compression method {:?} is unsupported", method
)))
};
Ok(match (color_type, buffer) {
(ColorType:: RGB(8), DecodingBuffer::U8(ref mut buffer)) |
(ColorType::RGBA(8), DecodingBuffer::U8(ref mut buffer)) => {
try!(reader.read(&mut buffer[..bytes]))
}
(ColorType::RGBA(16), DecodingBuffer::U16(ref mut buffer)) |
(ColorType:: RGB(16), DecodingBuffer::U16(ref mut buffer)) => {
for datum in buffer[..bytes/2].iter_mut() {
*datum = try!(reader.read_u16())
}
bytes/2
}
(ColorType::Gray(16), DecodingBuffer::U16(ref mut buffer)) => {
for datum in buffer[..bytes/2].iter_mut() {
*datum = try!(reader.read_u16());
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
*datum = 0xffff - *datum
}
}
bytes/2
}
(ColorType::Gray(n), DecodingBuffer::U8(ref mut buffer)) if n <= 8 => {
try!(reader.read_exact(&mut buffer[..bytes]));
if self.photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
for byte in buffer[..bytes].iter_mut() {
*byte = 0xff - *byte
}
}
bytes
}
(type_, _) => return Err(TiffError::UnsupportedError(format!(
"Color type {:?} is unsupported", type_
)))
})
}
/// Decodes the entire image and return it as a Vector
pub fn read_image(&mut self) -> TiffResult<DecodingResult> {
let bits_per_pixel: u8 = self.bits_per_sample.iter().cloned().sum();
let scanline_size_bits = self.width as usize * bits_per_pixel as usize;
let scanline_size = (scanline_size_bits + 7) / 8;
let rows_per_strip = self.get_tag_u32(ifd::Tag::RowsPerStrip)
.unwrap_or(self.height) as usize;
let buffer_size =
self.width as usize
* self.height as usize
* self.bits_per_sample.iter().count();
let mut result = match self.bits_per_sample.iter().cloned().max().unwrap_or(8) {
n if n <= 8 => DecodingResult::U8(Vec::with_capacity(buffer_size)),
n if n <= 16 => DecodingResult::U16(Vec::with_capacity(buffer_size)),
n => return Err(
TiffError::UnsupportedError(
format!("{} bits per channel not supported", n)
)
)
};
if let Ok(config) = self.get_tag_u32(ifd::Tag::PlanarConfiguration) {
match FromPrimitive::from_u32(config) {
Some(PlanarConfiguration::Chunky) => {},
config => return Err(TiffError::UnsupportedError(
format!("Unsupported planar configuration “{:?}”.", config)
))
}
}
// Safe since the uninitialized values are never read.
match result {
DecodingResult::U8(ref mut buffer) =>
unsafe { buffer.set_len(buffer_size) },
DecodingResult::U16(ref mut buffer) =>
unsafe { buffer.set_len(buffer_size) },
}
let mut units_read = 0;
for (i, (&offset, &byte_count)) in try!(self.get_tag_u32_vec(ifd::Tag::StripOffsets))
.iter().zip(try!(self.get_tag_u32_vec(ifd::Tag::StripByteCounts)).iter()).enumerate() {
let uncompressed_strip_size = scanline_size
* (self.height as usize - i * rows_per_strip);
units_read += match result {
DecodingResult::U8(ref mut buffer) => {
try!(self.expand_strip(
DecodingBuffer::U8(&mut buffer[units_read..]),
offset, byte_count, uncompressed_strip_size
))
},
DecodingResult::U16(ref mut buffer) => {
try!(self.expand_strip(
DecodingBuffer::U16(&mut buffer[units_read..]),
offset, byte_count, uncompressed_strip_size
))
},
};
if units_read == buffer_size {
break
}
}
// Shrink length such that the uninitialized memory is not exposed.
if units_read < buffer_size {
match result {
DecodingResult::U8(ref mut buffer) =>
unsafe { buffer.set_len(units_read) },
DecodingResult::U16(ref mut buffer) =>
unsafe { buffer.set_len(units_read) },
}
}
if let Ok(predictor) = self.get_tag_u32(ifd::Tag::Predictor) {
result = match FromPrimitive::from_u32(predictor) {
Some(Predictor::None) => result,
Some(Predictor::Horizontal) => {
try!(rev_hpredict(
result,
try!(self.dimensions()),
try!(self.colortype())
))
},
None => return Err(TiffError::FormatError(
format!("Unknown predictor “{}” encountered", predictor)
))
}
}
Ok(result)
}
}
+201
View File
@@ -0,0 +1,201 @@
//! All IO functionality needed for TIFF decoding
use std::io::{self, Read, Seek};
use byteorder::{ReadBytesExt, BigEndian, LittleEndian};
use lzw;
/// Byte order of the TIFF file.
#[derive(Clone, Copy, Debug)]
pub enum ByteOrder {
/// little endian byte order
LittleEndian,
/// big endian byte order
BigEndian
}
/// Reader that is aware of the byte order.
pub trait EndianReader: Read {
/// Byte order that should be adhered to
fn byte_order(&self) -> ByteOrder;
/// Reads an u16
#[inline(always)]
fn read_u16(&mut self) -> Result<u16, io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u16::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u16::<BigEndian>(self)
}
}
/// Reads an u32
#[inline(always)]
fn read_u32(&mut self) -> Result<u32, io::Error> {
match self.byte_order() {
ByteOrder::LittleEndian => <Self as ReadBytesExt>::read_u32::<LittleEndian>(self),
ByteOrder::BigEndian => <Self as ReadBytesExt>::read_u32::<BigEndian>(self)
}
}
}
/// Reader that decompresses LZW streams
pub struct LZWReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder
}
impl LZWReader {
/// Wraps a reader
pub fn new<R>(reader: &mut SmartReader<R>, compressed_length: usize, max_uncompressed_length: usize) -> io::Result<(usize, LZWReader)> where R: Read + Seek {
let order = reader.byte_order;
let mut compressed = vec![0; compressed_length as usize];
try!(reader.read_exact(&mut compressed[..]));
let mut uncompressed = Vec::with_capacity(max_uncompressed_length);
let mut decoder = lzw::DecoderEarlyChange::new(lzw::MsbReader::new(), 8);
let mut bytes_read = 0;
while bytes_read < compressed_length && uncompressed.len() < max_uncompressed_length {
let (len, bytes) = try!(decoder.decode_bytes(&compressed[bytes_read..]));
bytes_read += len;
uncompressed.extend_from_slice(bytes);
}
let bytes = uncompressed.len();
Ok((bytes, LZWReader {
buffer: io::Cursor::new(uncompressed),
byte_order: order
}))
}
}
impl Read for LZWReader {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
impl EndianReader for LZWReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
/// Reader that unpacks Apple's `PackBits` format
pub struct PackBitsReader {
buffer: io::Cursor<Vec<u8>>,
byte_order: ByteOrder
}
impl PackBitsReader {
/// Wraps a reader
pub fn new<R: Read + Seek>(mut reader: R, byte_order: ByteOrder, length: usize) -> io::Result<(usize, PackBitsReader)> {
let mut buffer = Vec::new();
let mut read: usize = 0;
while read < length {
read += try!(read_packbits_run(&mut reader, &mut buffer));
}
Ok((buffer.len(), PackBitsReader {
buffer: io::Cursor::new(buffer),
byte_order
}))
}
}
fn read_packbits_run<R: Read + Seek>(reader: &mut R, buffer: &mut Vec<u8>) -> io::Result<usize> {
let mut header: [u8; 1] = [0];
let bytes = try!(reader.read(&mut header));
match bytes {
0 => Ok(0),
_ => match header[0] as i8 {
-128 => Ok(1),
h if h >= -127 && h <= -1 => {
let new_len = buffer.len() + (1 - h as isize) as usize;
try!(reader.read_exact(&mut header));
buffer.resize(new_len, header[0]);
Ok(2)
},
h => {
let num_vals = h as usize + 1;
let start = buffer.len();
buffer.resize(start + num_vals, 0);
try!(reader.read_exact(&mut buffer[start..]));
Ok(num_vals + 1)
}
}
}
}
impl Read for PackBitsReader {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.buffer.read(buf)
}
}
impl EndianReader for PackBitsReader {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
/// Reader that is aware of the byte order.
#[derive(Debug)]
pub struct SmartReader<R> where R: Read + Seek {
reader: R,
pub byte_order: ByteOrder
}
impl<R> SmartReader<R> where R: Read + Seek {
/// Wraps a reader
pub fn wrap(reader: R, byte_order: ByteOrder) -> SmartReader<R> {
SmartReader { reader, byte_order }
}
}
impl<R> EndianReader for SmartReader<R> where R: Read + Seek {
#[inline(always)]
fn byte_order(&self) -> ByteOrder {
self.byte_order
}
}
impl<R: Read + Seek> Read for SmartReader<R> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.reader.read(buf)
}
}
impl<R: Read + Seek> Seek for SmartReader<R> {
#[inline]
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.reader.seek(pos)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_packbits() {
let encoded = vec![0xFE, 0xAA, 0x02, 0x80, 0x00, 0x2A, 0xFD, 0xAA,
0x03, 0x80, 0x00, 0x2A, 0x22, 0xF7, 0xAA];
let encoded_len = encoded.len();
let buff = io::Cursor::new(encoded);
let (_, mut decoder) = PackBitsReader::new(buff, ByteOrder::LittleEndian, encoded_len).unwrap();
let mut decoded = Vec::new();
decoder.read_to_end(&mut decoded).unwrap();
let expected = vec![0xAA, 0xAA, 0xAA, 0x80, 0x00, 0x2A, 0xAA, 0xAA,
0xAA, 0xAA, 0x80, 0x00, 0x2A, 0x22, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA];
assert_eq!(decoded, expected);
}
}
+53
View File
@@ -0,0 +1,53 @@
use std::error::Error;
use std::fmt;
use std::io;
/// Tiff error kinds.
#[derive(Debug)]
pub enum TiffError {
/// The Image is not formatted properly
FormatError(String),
/// The Decoder does not support this image format
UnsupportedError(String),
/// An I/O Error occurred while decoding the image
IoError(io::Error),
}
impl fmt::Display for TiffError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
TiffError::FormatError(ref e) => write!(fmt, "Format error: {}", e),
TiffError::UnsupportedError(ref f) => write!(fmt, "The Decoder does not support the \
image format `{}`", f),
TiffError::IoError(ref e) => e.fmt(fmt),
}
}
}
impl Error for TiffError {
fn description (&self) -> &str {
match *self {
TiffError::FormatError(..) => "Format error",
TiffError::UnsupportedError(..) => "Unsupported error",
TiffError::IoError(..) => "IO error",
}
}
fn cause (&self) -> Option<&Error> {
match *self {
TiffError::IoError(ref e) => Some(e),
_ => None
}
}
}
impl From<io::Error> for TiffError {
fn from(err: io::Error) -> TiffError {
TiffError::IoError(err)
}
}
/// Result of an image decoding/encoding process
pub type TiffResult<T> = Result<T, TiffError>;
+36
View File
@@ -0,0 +1,36 @@
//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification
extern crate byteorder;
extern crate lzw;
#[macro_use] extern crate num_derive;
extern crate num_traits;
pub mod decoder;
mod error;
pub use self::error::{TiffError, TiffResult};
/// An enumeration over supported color types and their bit depths
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
pub enum ColorType {
/// Pixel is grayscale
Gray(u8),
/// Pixel contains R, G and B channels
RGB(u8),
/// Pixel is an index into a color palette
Palette(u8),
/// Pixel is grayscale with an alpha channel
GrayA(u8),
/// Pixel is RGB with an alpha channel
RGBA(u8),
}
+57
View File
@@ -0,0 +1,57 @@
extern crate tiff;
use tiff::ColorType;
use tiff::decoder::{Decoder, DecodingResult};
use std::fs::File;
#[test]
fn test_gray_u8()
{
let img_file = File::open("./tests/images/minisblack-1c-8b.tiff").expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(8));
let img_res = decoder.read_image();
assert!(img_res.is_ok());
}
#[test]
fn test_rgb_u8()
{
let img_file = File::open("./tests/images/rgb-3c-8b.tiff").expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(8));
let img_res = decoder.read_image();
assert!(img_res.is_ok());
}
#[test]
fn test_gray_u16()
{
let img_file = File::open("./tests/images/minisblack-1c-16b.tiff").expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), ColorType::Gray(16));
let img_res = decoder.read_image();
assert!(img_res.is_ok());
}
#[test]
fn test_rgb_u16()
{
let img_file = File::open("./tests/images/rgb-3c-16b.tiff").expect("Cannot find test image!");
let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
assert_eq!(decoder.colortype().unwrap(), ColorType::RGB(16));
let img_res = decoder.read_image();
assert!(img_res.is_ok());
}
// TODO: GrayA support
//#[test]
//fn test_gray_alpha_u8()
//{
//let img_file = File::open("./tests/images/minisblack-2c-8b-alpha.tiff").expect("Cannot find test image!");
//let mut decoder = Decoder::new(img_file).expect("Cannot create decoder");
//assert_eq!(decoder.colortype().unwrap(), ColorType::GrayA(8));
//let img_res = decoder.read_image();
//assert!(img_res.is_ok());
//}
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) 1988-1997 Sam Leffler
Copyright (c) 1991-1997 Silicon Graphics, Inc.
Permission to use, copy, modify, distribute, and sell this software and
its documentation for any purpose is hereby granted without fee, provided
that (i) the above copyright notices and this permission notice appear in
all copies of the software and related documentation, and (ii) the names of
Sam Leffler and Silicon Graphics may not be used in any advertising or
publicity relating to the software without the specific, prior written
permission of Sam Leffler and Silicon Graphics.
THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
OF THIS SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
The test files in this directory mostly have dimension 157x151.
The naming convention is
photometric-channels-bits.tiff
minisblack-1c-16b.tiff
minisblack-1c-8b.tiff
miniswhite-1c-1b.tiff
palette-1c-1b.tiff
palette-1c-4b.tiff
palette-1c-8b.tiff
rgb-3c-16b.tiff
rgb-3c-8b.tiff
logluv-3c-16b.tiff: logluv compression/photometric interp
minisblack-2c-8b-alpha.tiff: grey+alpha
BMP files (anchient BMPv2 since v3 does not work):
palette-1c-8b.bmp
rgb-3c-8b.bmp
GIF files (anchient GIF '87 since modern GIF does not work):
palette-1c-8b.gif
PNM files:
minisblack-1c-8b.pgm
miniswhite-1c-1b.pbm
rgb-3c-8b.ppm
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.