Add documentation and reorganize

This commit is contained in:
Birk Tjelmeland committed 2019-02-09 21:53:11 +01:00
1 parent 9ca445a5eb
commit b0d7ac9cbf
3 files changed
+127 -35

No files matched your search

+8 -4
View File
@@ -1,11 +1,15 @@
/// Trait for different colortypes that can be encoded.
pub trait ColorType { pub trait ColorType {
/// The type of each sample of thes colortype
type Inner: super::TiffValue; type Inner: super::TiffValue;
/// The value of the tiff tag `PhotometricInterpretation`
const TIFF_VALUE: u8; const TIFF_VALUE: u8;
/// The value of the tiff tag `BitsPerSample`
fn bits_per_sample() -> Vec<u16>; fn bits_per_sample() -> Vec<u16>;
} }
pub struct Grey8; pub struct Gray8;
impl ColorType for Grey8 { impl ColorType for Gray8 {
type Inner = u8; type Inner = u8;
const TIFF_VALUE: u8 = 1; const TIFF_VALUE: u8 = 1;
fn bits_per_sample() -> Vec<u16> { fn bits_per_sample() -> Vec<u16> {
@@ -13,8 +17,8 @@ impl ColorType for Grey8 {
} }
} }
pub struct Grey16; pub struct Gray16;
impl ColorType for Grey16 { impl ColorType for Gray16 {
type Inner = u16; type Inner = u16;
const TIFF_VALUE: u8 = 1; const TIFF_VALUE: u8 = 1;
fn bits_per_sample() -> Vec<u16> { fn bits_per_sample() -> Vec<u16> {
+117 -29
View File
@@ -2,7 +2,7 @@ use std::io::{Write, Seek};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use byteorder::{NativeEndian}; use byteorder::{NativeEndian};
use crate::decoder::ifd; use crate::decoder::ifd::Tag;
use crate::error::{TiffResult, TiffFormatError, TiffError}; use crate::error::{TiffResult, TiffFormatError, TiffError};
mod writer; mod writer;
@@ -11,11 +11,13 @@ pub mod colortype;
use self::writer::*; use self::writer::*;
use self::colortype::*; use self::colortype::*;
/// Type to represent tiff values of type `RATIONAL`
pub struct Rational { pub struct Rational {
pub n: u32, pub n: u32,
pub d: u32, pub d: u32,
} }
/// Trait for types that can be encoded in a tiff file
pub trait TiffValue { pub trait TiffValue {
const BYTE_LEN: u32; const BYTE_LEN: u32;
const FIELD_TYPE: u16; const FIELD_TYPE: u16;
@@ -119,9 +121,32 @@ impl TiffValue for str {
} }
} }
/// Tiff encoder.
///
/// With this type you can get a `DirectoryEncoder` or a `ImageEncoder`
/// to encode tiff ifd directories with images.
///
/// See `DirectoryEncoder` and `ImageEncoder`.
///
/// # Examples
/// ```
/// # extern crate tempfile;
/// # let mut file = tempfile::tempfile().unwrap();
/// # let mut image_data = Vec::new();
/// # for x in 0..100 {
/// # for y in 0..100u8 {
/// # let val = x + y;
/// # image_data.push(val);
/// # image_data.push(val);
/// # image_data.push(val);
/// # }
/// # }
/// use tiff::encoder::*;
///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
///
/// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap();
/// ```
pub struct TiffEncoder<W> { pub struct TiffEncoder<W> {
writer: TiffWriter<W>, writer: TiffWriter<W>,
} }
@@ -137,15 +162,18 @@ impl<W: Write + Seek> TiffEncoder<W> {
Ok(encoder) Ok(encoder)
} }
/// Create a `DirectoryEncoder` to encode an ifd directory.
pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W>> { pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<W>> {
DirectoryEncoder::new(&mut self.writer) DirectoryEncoder::new(&mut self.writer)
} }
/// Create an 'ImageEncoder' to encode an image one slice at a time.
pub fn new_image<C: ColorType>(&mut self, width: u32, height: u32) -> TiffResult<ImageEncoder<W, C>> { pub fn new_image<C: ColorType>(&mut self, width: u32, height: u32) -> TiffResult<ImageEncoder<W, C>> {
let encoder = DirectoryEncoder::new(&mut self.writer)?; let encoder = DirectoryEncoder::new(&mut self.writer)?;
ImageEncoder::new(encoder, width, height) ImageEncoder::new(encoder, width, height)
} }
/// Convenience function to write an entire image from memory.
pub fn write_image<C: ColorType>(&mut self, width: u32, height: u32, data: &[C::Inner]) -> TiffResult<()> { pub fn write_image<C: ColorType>(&mut self, width: u32, height: u32, data: &[C::Inner]) -> TiffResult<()> {
let encoder = DirectoryEncoder::new(&mut self.writer)?; let encoder = DirectoryEncoder::new(&mut self.writer)?;
let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height).unwrap(); let mut image: ImageEncoder<W, C> = ImageEncoder::new(encoder, width, height).unwrap();
@@ -160,7 +188,11 @@ impl<W: Write + Seek> TiffEncoder<W> {
} }
} }
pub struct DirectoryEncoder<'a, W> { /// Low level interface to encode ifd directories.
///
/// You should call `finish` on this when you are finished with it.
/// Encoding can silently fail while this is dropping.
pub struct DirectoryEncoder<'a, W: Write + Seek> {
writer: &'a mut TiffWriter<W>, writer: &'a mut TiffWriter<W>,
dropped: bool, dropped: bool,
// We use BTreeMap to make sure tags are written in correct order // We use BTreeMap to make sure tags are written in correct order
@@ -181,7 +213,8 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
}) })
} }
pub fn write_tag<T: TiffValue>(&mut self, tag: ifd::Tag, value: T) { /// Write a single ifd tag.
pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) {
let len = <T>::BYTE_LEN * value.count(); let len = <T>::BYTE_LEN * value.count();
let mut bytes = Vec::with_capacity(len as usize); let mut bytes = Vec::with_capacity(len as usize);
{ {
@@ -192,17 +225,6 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
self.ifd.insert(tag.to_u16(), (<T>::FIELD_TYPE, value.count(), bytes)); self.ifd.insert(tag.to_u16(), (<T>::FIELD_TYPE, value.count(), bytes));
} }
pub fn modify_tag<T: TiffValue>(&mut self, tag: ifd::Tag, offset: u64, value: T) -> TiffResult<()> {
let bytes = &mut self.ifd.get_mut(&tag.to_u16())
.ok_or(TiffError::FormatError(TiffFormatError::RequiredTagNotFound(tag)))?.2;
let mut writer = TiffWriter::new(std::io::Cursor::new(bytes));
writer.goto_offset(offset)?;
value.write(&mut writer)?;
Ok(())
}
fn write_directory(&mut self) -> TiffResult<u64> { fn write_directory(&mut self) -> TiffResult<u64> {
// Start by writing out all values // Start by writing out all values
for (_, _, ref mut bytes) in self.ifd.values_mut() { for (_, _, ref mut bytes) in self.ifd.values_mut() {
@@ -233,13 +255,16 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
Ok(offset) Ok(offset)
} }
/// Write some data to the tiff file, the offset of the data is returned.
///
/// This could be used to write tiff strips.
pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> { pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> {
let offset = self.writer.offset(); let offset = self.writer.offset();
value.write(&mut self.writer)?; value.write(&mut self.writer)?;
Ok(offset) Ok(offset)
} }
pub fn finish(mut self) -> TiffResult<()> { fn finish_internal(&mut self) -> TiffResult<()> {
let ifd_pointer = self.write_directory()?; let ifd_pointer = self.write_directory()?;
let curr_pos = self.writer.offset(); let curr_pos = self.writer.offset();
@@ -252,29 +277,68 @@ impl<'a, W: Write + Seek> DirectoryEncoder<'a, W> {
Ok(()) Ok(())
} }
/// Write out the ifd directory.
pub fn finish(mut self) -> TiffResult<()> {
self.finish_internal()
}
} }
impl<'a, W> Drop for DirectoryEncoder<'a, W> { impl<'a, W: Write + Seek> Drop for DirectoryEncoder<'a, W> {
fn drop(&mut self) { fn drop(&mut self) {
if !self.dropped { if !self.dropped {
panic!("Illegal to drop DirectoryEncoder: you should call `DirectoryEncoder::finish`") let _ = self.finish_internal();
} }
} }
} }
pub struct ImageEncoder<'a, W, T> {
/// Type to encode images strip by strip.
///
/// You should call `finish` on this when you are finished with it.
/// Encoding can silently fail while this is dropping.
///
/// # Examples
/// ```
/// # extern crate tempfile;
/// # let mut file = tempfile::tempfile().unwrap();
/// # let mut image_data = Vec::new();
/// # for x in 0..100 {
/// # for y in 0..100u8 {
/// # let val = x + y;
/// # image_data.push(val);
/// # image_data.push(val);
/// # image_data.push(val);
/// # }
/// # }
/// use tiff::encoder::*;
///
/// let mut tiff = TiffEncoder::new(&mut file).unwrap();
/// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap();
///
/// let mut idx = 0;
/// while image.next_strip_sample_count() > 0 {
/// let sample_count = image.next_strip_sample_count() as usize;
/// image.write_strip(&image_data[idx..idx+sample_count]).unwrap();
/// idx += sample_count;
/// }
/// image.finish().unwrap();
/// ```
pub struct ImageEncoder<'a, W: Write + Seek, C: ColorType> {
encoder: DirectoryEncoder<'a, W>, encoder: DirectoryEncoder<'a, W>,
strip_idx: u64, strip_idx: u64,
strip_count: u64, strip_count: u64,
row_samples: u64, row_samples: u64,
height: u32, height: u32,
rows_per_strip: u64, rows_per_strip: u64,
_phantom: std::marker::PhantomData<T>, strip_offsets: Vec<u32>,
strip_byte_count: Vec<u32>,
dropped: bool,
_phantom: std::marker::PhantomData<C>,
} }
impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> { impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
fn new(mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32) -> TiffResult<ImageEncoder<'a, W, T>> { fn new(mut encoder: DirectoryEncoder<'a, W>, width: u32, height: u32) -> TiffResult<ImageEncoder<'a, W, T>> {
use self::ifd::Tag;
let row_samples = width as u64 * <T>::bits_per_sample().len() as u64; let row_samples = width as u64 * <T>::bits_per_sample().len() as u64;
let row_bytes = row_samples * <T::Inner>::BYTE_LEN as u64; let row_bytes = row_samples * <T::Inner>::BYTE_LEN as u64;
@@ -290,8 +354,6 @@ impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
encoder.write_tag(Tag::BitsPerSample, &<T>::bits_per_sample() as &[u16]); encoder.write_tag(Tag::BitsPerSample, &<T>::bits_per_sample() as &[u16]);
encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE); encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE);
encoder.write_tag(Tag::StripOffsets, &vec![0u32; strip_count as usize] as &[u32]);
encoder.write_tag(Tag::StripByteCounts, &vec![0u32; strip_count as usize] as &[u32]);
encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32); encoder.write_tag(Tag::RowsPerStrip, rows_per_strip as u32);
encoder.write_tag(Tag::SamplesPerPixel, <T>::bits_per_sample().len() as u16); encoder.write_tag(Tag::SamplesPerPixel, <T>::bits_per_sample().len() as u16);
@@ -306,10 +368,14 @@ impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
row_samples, row_samples,
rows_per_strip, rows_per_strip,
height, height,
strip_offsets: Vec::new(),
strip_byte_count: Vec::new(),
dropped: false,
_phantom: std::marker::PhantomData, _phantom: std::marker::PhantomData,
}) })
} }
/// Number of samples the next strip should have.
pub fn next_strip_sample_count(&self) -> u64 { pub fn next_strip_sample_count(&self) -> u64 {
if self.strip_idx >= self.strip_count { if self.strip_idx >= self.strip_count {
return 0 return 0
@@ -321,17 +387,39 @@ impl<'a, W: Write + Seek, T: ColorType> ImageEncoder<'a, W, T> {
(end_row - start_row) * self.row_samples (end_row - start_row) * self.row_samples
} }
/// Write a single strip.
pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> { pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> {
// TODO: Compression // TODO: Compression
let samples = self.next_strip_sample_count();
assert_eq!(value.len() as u64, samples);
let offset = self.encoder.write_data(value)?; let offset = self.encoder.write_data(value)?;
self.encoder.modify_tag(ifd::Tag::StripOffsets, self.strip_idx as u64 * 4, offset as u32)?; self.strip_offsets.push(offset as u32);
self.encoder.modify_tag(ifd::Tag::StripByteCounts, self.strip_idx as u64 * 4, value.bytes() as u32)?; self.strip_byte_count.push(value.bytes() as u32);
self.strip_idx += 1; self.strip_idx += 1;
Ok(()) Ok(())
} }
pub fn finish(self) -> TiffResult<()> { fn finish_internal(&mut self) -> TiffResult<()> {
self.encoder.finish() self.encoder.write_tag(Tag::StripOffsets, &*self.strip_offsets);
self.encoder.write_tag(Tag::StripByteCounts, &*self.strip_byte_count);
self.dropped = true;
self.encoder.finish_internal()
}
/// Write out image and ifd directory.
pub fn finish(mut self) -> TiffResult<()> {
self.finish_internal()
} }
} }
impl<'a, W: Write + Seek, C: ColorType> Drop for ImageEncoder<'a, W, C> {
fn drop(&mut self) {
if !self.dropped {
let _ = self.finish_internal();
}
}
}
+2 -2
View File
@@ -57,7 +57,7 @@ fn test_gray_u8_roundtrip()
let mut tiff = TiffEncoder::new(&mut file).unwrap(); let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap(); let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::Grey8>(width, height, &image_data).unwrap(); tiff.write_image::<colortype::Gray8>(width, height, &image_data).unwrap();
} }
file.seek(SeekFrom::Start(0)).unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
{ {
@@ -119,7 +119,7 @@ fn test_gray_u16_roundtrip()
let mut tiff = TiffEncoder::new(&mut file).unwrap(); let mut tiff = TiffEncoder::new(&mut file).unwrap();
let (width, height) = decoder.dimensions().unwrap(); let (width, height) = decoder.dimensions().unwrap();
tiff.write_image::<colortype::Grey16>(width, height, &image_data).unwrap(); tiff.write_image::<colortype::Gray16>(width, height, &image_data).unwrap();
} }
file.seek(SeekFrom::Start(0)).unwrap(); file.seek(SeekFrom::Start(0)).unwrap();
{ {