/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tiff-0.10.3/src/encoder/mod.rs
Line | Count | Source |
1 | | pub use tiff_value::*; |
2 | | |
3 | | use std::{ |
4 | | cmp, |
5 | | io::{self, Seek, Write}, |
6 | | marker::PhantomData, |
7 | | mem, |
8 | | num::{NonZeroU64, TryFromIntError}, |
9 | | }; |
10 | | |
11 | | use crate::{ |
12 | | decoder::ifd::Entry, |
13 | | error::{TiffResult, UsageError}, |
14 | | tags::{CompressionMethod, IfdPointer, ResolutionUnit, SampleFormat, Tag, Type}, |
15 | | Directory, TiffError, TiffFormatError, |
16 | | }; |
17 | | |
18 | | pub mod colortype; |
19 | | pub mod compression; |
20 | | mod tiff_value; |
21 | | mod writer; |
22 | | |
23 | | use self::colortype::*; |
24 | | use self::compression::Compression as Comp; |
25 | | use self::compression::*; |
26 | | use self::writer::*; |
27 | | |
28 | | /// Type of prediction to prepare the image with. |
29 | | /// |
30 | | /// Image data can be very unpredictable, and thus very hard to compress. Predictors are simple |
31 | | /// passes ran over the image data to prepare it for compression. This is mostly used for LZW |
32 | | /// compression, where using [Predictor::Horizontal] we see a 35% improvement in compression |
33 | | /// ratio over the unpredicted compression ! |
34 | | /// |
35 | | /// [Predictor::FloatingPoint] is currently not supported. |
36 | | pub type Predictor = crate::tags::Predictor; |
37 | | #[cfg(feature = "deflate")] |
38 | | pub type DeflateLevel = compression::DeflateLevel; |
39 | | |
40 | | #[derive(Clone, Copy, PartialEq)] |
41 | | pub enum Compression { |
42 | | Uncompressed, |
43 | | #[cfg(feature = "lzw")] |
44 | | Lzw, |
45 | | #[cfg(feature = "deflate")] |
46 | | Deflate(DeflateLevel), |
47 | | Packbits, |
48 | | } |
49 | | |
50 | | impl Default for Compression { |
51 | 0 | fn default() -> Self { |
52 | 0 | Self::Uncompressed |
53 | 0 | } |
54 | | } |
55 | | |
56 | | impl Compression { |
57 | 0 | fn tag(&self) -> CompressionMethod { |
58 | 0 | match self { |
59 | 0 | Compression::Uncompressed => CompressionMethod::None, |
60 | | #[cfg(feature = "lzw")] |
61 | 0 | Compression::Lzw => CompressionMethod::LZW, |
62 | | #[cfg(feature = "deflate")] |
63 | 0 | Compression::Deflate(_) => CompressionMethod::Deflate, |
64 | 0 | Compression::Packbits => CompressionMethod::PackBits, |
65 | | } |
66 | 0 | } |
67 | | |
68 | 0 | fn get_algorithm(&self) -> Compressor { |
69 | 0 | match self { |
70 | 0 | Compression::Uncompressed => compression::Uncompressed {}.get_algorithm(), |
71 | | #[cfg(feature = "lzw")] |
72 | 0 | Compression::Lzw => compression::Lzw {}.get_algorithm(), |
73 | | #[cfg(feature = "deflate")] |
74 | 0 | Compression::Deflate(level) => compression::Deflate::with_level(*level).get_algorithm(), |
75 | 0 | Compression::Packbits => compression::Packbits {}.get_algorithm(), |
76 | | } |
77 | 0 | } |
78 | | } |
79 | | |
80 | | /// Encoder for Tiff and BigTiff files. |
81 | | /// |
82 | | /// With this type you can get a `DirectoryEncoder` or a `ImageEncoder` |
83 | | /// to encode Tiff/BigTiff ifd directories with images. |
84 | | /// |
85 | | /// See `DirectoryEncoder` and `ImageEncoder`. |
86 | | /// |
87 | | /// # Examples |
88 | | /// ``` |
89 | | /// # extern crate tiff; |
90 | | /// # fn main() { |
91 | | /// # let mut file = std::io::Cursor::new(Vec::new()); |
92 | | /// # let image_data = vec![0; 100*100*3]; |
93 | | /// use tiff::encoder::*; |
94 | | /// |
95 | | /// // create a standard Tiff file |
96 | | /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); |
97 | | /// tiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap(); |
98 | | /// |
99 | | /// // create a BigTiff file |
100 | | /// let mut bigtiff = TiffEncoder::new_big(&mut file).unwrap(); |
101 | | /// bigtiff.write_image::<colortype::RGB8>(100, 100, &image_data).unwrap(); |
102 | | /// |
103 | | /// # } |
104 | | /// ``` |
105 | | pub struct TiffEncoder<W, K: TiffKind = TiffKindStandard> { |
106 | | writer: TiffWriter<W>, |
107 | | kind: PhantomData<K>, |
108 | | predictor: Predictor, |
109 | | compression: Compression, |
110 | | /// The offset of the last main image directory's `next` field. |
111 | | last_ifd_chain: NonZeroU64, |
112 | | } |
113 | | |
114 | | /// Constructor functions to create standard Tiff files. |
115 | | impl<W: Write + Seek> TiffEncoder<W> { |
116 | | /// Creates a new encoder for standard Tiff files. |
117 | | /// |
118 | | /// To create BigTiff files, use [`new_big`][TiffEncoder::new_big] or |
119 | | /// [`new_generic`][TiffEncoder::new_generic]. |
120 | 0 | pub fn new(writer: W) -> TiffResult<TiffEncoder<W, TiffKindStandard>> { |
121 | 0 | TiffEncoder::new_generic(writer) |
122 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffEncoder<_>>::new Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new |
123 | | } |
124 | | |
125 | | /// Constructor functions to create BigTiff files. |
126 | | impl<W: Write + Seek> TiffEncoder<W, TiffKindBig> { |
127 | | /// Creates a new encoder for BigTiff files. |
128 | | /// |
129 | | /// To create standard Tiff files, use [`new`][TiffEncoder::new] or |
130 | | /// [`new_generic`][TiffEncoder::new_generic]. |
131 | 0 | pub fn new_big(writer: W) -> TiffResult<Self> { |
132 | 0 | TiffEncoder::new_generic(writer) |
133 | 0 | } |
134 | | } |
135 | | |
136 | | /// Generic functions that are available for both Tiff and BigTiff encoders. |
137 | | impl<W: Write + Seek, K: TiffKind> TiffEncoder<W, K> { |
138 | | /// Creates a new Tiff or BigTiff encoder, inferred from the return type. |
139 | 0 | pub fn new_generic(writer: W) -> TiffResult<Self> { |
140 | 0 | let mut writer = TiffWriter::new(writer); |
141 | 0 | K::write_header(&mut writer)?; |
142 | | |
143 | 0 | let last_ifd_chain = NonZeroU64::new(writer.previous_ifd_pointer::<K>()) |
144 | 0 | .expect("Header is at a non-zero offset"); |
145 | | |
146 | 0 | Ok(TiffEncoder { |
147 | 0 | writer, |
148 | 0 | kind: PhantomData, |
149 | 0 | predictor: Predictor::None, |
150 | 0 | compression: Compression::Uncompressed, |
151 | 0 | last_ifd_chain, |
152 | 0 | }) |
153 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffEncoder<_, _>>::new_generic Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::new_generic |
154 | | |
155 | | /// Set the predictor to use |
156 | | /// |
157 | | /// A predictor is used to simplify the file before writing it. This is very |
158 | | /// useful when writing a file compressed using LZW as it can improve efficiency |
159 | 0 | pub fn with_predictor(mut self, predictor: Predictor) -> Self { |
160 | 0 | self.predictor = predictor; |
161 | | |
162 | 0 | self |
163 | 0 | } |
164 | | |
165 | | /// Set the compression method to use |
166 | 0 | pub fn with_compression(mut self, compression: Compression) -> Self { |
167 | 0 | self.compression = compression; |
168 | | |
169 | 0 | self |
170 | 0 | } |
171 | | |
172 | | /// Create a [`DirectoryEncoder`] to encode an ifd directory. |
173 | | #[deprecated = "`image_directory` replaced the old behavior and clarifies the intent"] |
174 | | #[doc(hidden)] |
175 | 0 | pub fn new_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> { |
176 | 0 | Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain) |
177 | 0 | } |
178 | | |
179 | | /// Create a [`DirectoryEncoder`] to encode an ifd directory. |
180 | | /// |
181 | | /// The caller is responsible for ensuring that the directory is a valid image in the main TIFF |
182 | | /// IFD sequence. To encode additional directories that are not linked into the sequence, use |
183 | | /// [`Self::extra_directory`][TiffEncoder::extra_directory]. |
184 | 0 | pub fn image_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> { |
185 | 0 | Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain) |
186 | 0 | } |
187 | | |
188 | | /// Create a [`DirectoryEncoder`] to encode an ifd directory. |
189 | | /// |
190 | | /// The directory is not linked into the sequence of directories. For instance, encode Exif |
191 | | /// directories or SubIfd directories with this method. |
192 | 0 | pub fn extra_directory(&mut self) -> TiffResult<DirectoryEncoder<'_, W, K>> { |
193 | 0 | Self::unchained_directory(&mut self.writer) |
194 | 0 | } |
195 | | |
196 | | /// Create an [`ImageEncoder`] to encode an image one slice at a time. |
197 | 0 | pub fn new_image<C: ColorType>( |
198 | 0 | &mut self, |
199 | 0 | width: u32, |
200 | 0 | height: u32, |
201 | 0 | ) -> TiffResult<ImageEncoder<'_, W, C, K>> { |
202 | 0 | let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?; |
203 | 0 | ImageEncoder::new(encoder, width, height, self.compression, self.predictor) |
204 | 0 | } |
205 | | |
206 | | /// Convenience function to write an entire image from memory. |
207 | 0 | pub fn write_image<C: ColorType>( |
208 | 0 | &mut self, |
209 | 0 | width: u32, |
210 | 0 | height: u32, |
211 | 0 | data: &[C::Inner], |
212 | 0 | ) -> TiffResult<()> |
213 | 0 | where |
214 | 0 | [C::Inner]: TiffValue, |
215 | | { |
216 | 0 | let encoder = Self::chain_directory(&mut self.writer, &mut self.last_ifd_chain)?; |
217 | 0 | let image: ImageEncoder<W, C, K> = |
218 | 0 | ImageEncoder::new(encoder, width, height, self.compression, self.predictor)?; |
219 | 0 | image.write_data(data) |
220 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffEncoder<_, _>>::write_image::<_> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGB32Float> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGBA32Float> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGB8> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::Gray8> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGB16> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGBA8> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::Gray16> Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::write_image::<tiff::encoder::colortype::RGBA16> |
221 | | |
222 | 0 | fn chain_directory<'lt>( |
223 | 0 | writer: &'lt mut TiffWriter<W>, |
224 | 0 | last_ifd_chain: &'lt mut NonZeroU64, |
225 | 0 | ) -> TiffResult<DirectoryEncoder<'lt, W, K>> { |
226 | 0 | let last_ifd = *last_ifd_chain; |
227 | 0 | DirectoryEncoder::new(writer, Some(last_ifd), Some(last_ifd_chain)) |
228 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffEncoder<_, _>>::chain_directory Unexecuted instantiation: <tiff::encoder::TiffEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>>>::chain_directory |
229 | | |
230 | 0 | fn unchained_directory(writer: &mut TiffWriter<W>) -> TiffResult<DirectoryEncoder<'_, W, K>> { |
231 | 0 | DirectoryEncoder::new(writer, None, None) |
232 | 0 | } |
233 | | } |
234 | | |
235 | | /// Low level interface to encode ifd directories. |
236 | | /// |
237 | | /// You should call `finish` on this when you are finished with it. |
238 | | /// Encoding can silently fail while this is dropping. |
239 | | pub struct DirectoryEncoder<'a, W: 'a + Write + Seek, K: TiffKind> { |
240 | | writer: &'a mut TiffWriter<W>, |
241 | | /// The position of the previous directory's `next` field, if any. |
242 | | chained_ifd_pos: Option<NonZeroU64>, |
243 | | /// An output to write the `next` field offset on completion. |
244 | | write_chain: Option<&'a mut NonZeroU64>, |
245 | | kind: PhantomData<K>, |
246 | | // We use BTreeMap to make sure tags are written in correct order |
247 | | directory: Directory, |
248 | | dropped: bool, |
249 | | } |
250 | | |
251 | | /// The offset of an encoded directory in the file. |
252 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
253 | | pub struct DirectoryOffset<K: TiffKind> { |
254 | | /// The start of the directory as a Tiff value. |
255 | | /// |
256 | | /// This is a bit of a wart in the strongly typed design of the encoder. The value _type_ must |
257 | | /// itself know how to be represented in the Tiff file but that may differ based on endianess |
258 | | /// as well as the offset size (`u32` or BigTIFF's `u64`). Thankfully we're allowed to |
259 | | /// represent offsets with `LONG` or `IFD` in the usual case. |
260 | | pub offset: K::OffsetType, |
261 | | /// The start of the directory as a pure offset. |
262 | | pub pointer: IfdPointer, |
263 | | /// The offset of its sequence link field, in our private representation. |
264 | | ifd_chain: NonZeroU64, |
265 | | /// The kind of Tiff file the offset is for. |
266 | | kind: PhantomData<K>, |
267 | | } |
268 | | |
269 | | impl<'a, W: 'a + Write + Seek, K: TiffKind> DirectoryEncoder<'a, W, K> { |
270 | | /// Construct a directory writer appending to data, assuming the writer is currently positioned |
271 | | /// immediately after a previously written IFD to which to append. |
272 | 0 | fn new( |
273 | 0 | writer: &'a mut TiffWriter<W>, |
274 | 0 | chained_ifd_pos: Option<NonZeroU64>, |
275 | 0 | chain_into: Option<&'a mut NonZeroU64>, |
276 | 0 | ) -> TiffResult<Self> { |
277 | 0 | writer.pad_word_boundary()?; // TODO: Do we need to adjust this for BigTiff? |
278 | 0 | Ok(Self { |
279 | 0 | writer, |
280 | 0 | chained_ifd_pos, |
281 | 0 | write_chain: chain_into, |
282 | 0 | kind: PhantomData, |
283 | 0 | directory: Directory::empty(), |
284 | 0 | dropped: false, |
285 | 0 | }) |
286 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::new Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::new |
287 | | |
288 | | /// Write a single ifd tag. |
289 | 0 | pub fn write_tag<T: TiffValue>(&mut self, tag: Tag, value: T) -> TiffResult<()> { |
290 | | // Encodes the value if necessary. In the current bytes all integers are taken as native |
291 | | // endian and thus transparent but this keeps the interface generic. |
292 | 0 | let mut bytes = Vec::with_capacity(value.bytes()); |
293 | | { |
294 | 0 | let mut writer = TiffWriter::new(&mut bytes); |
295 | 0 | value.write(&mut writer)?; |
296 | | } |
297 | | |
298 | 0 | let entry = Self::write_value( |
299 | 0 | self.writer, |
300 | | &DirectoryEntry { |
301 | | data_type: <T>::FIELD_TYPE, |
302 | 0 | count: value.count().try_into()?, |
303 | 0 | data: bytes, |
304 | | }, |
305 | 0 | )?; |
306 | | |
307 | 0 | self.directory.extend([(tag, entry)]); |
308 | | |
309 | 0 | Ok(()) |
310 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::write_tag::<_> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_tag::<tiff::encoder::tiff_value::Rational> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_tag::<&[u32]> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_tag::<&[u16]> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_tag::<u32> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_tag::<u16> |
311 | | |
312 | | /// Write some data to the tiff file, the offset of the data is returned. |
313 | | /// |
314 | | /// This could be used to write tiff strips. |
315 | 0 | pub fn write_data<T: TiffValue>(&mut self, value: T) -> TiffResult<u64> { |
316 | 0 | let offset = self.writer.offset(); |
317 | 0 | value.write(self.writer)?; |
318 | 0 | Ok(offset) |
319 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::write_data::<_> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_data::<&[f32]> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_data::<&[u8]> Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_data::<&[u16]> |
320 | | |
321 | | /// Define the parent directory. |
322 | | /// |
323 | | /// Each directory has an offset based link to its successor, forming a linked list in the |
324 | | /// file. The encoder writes its own offset into the parent's field when [`Self::finish`] is |
325 | | /// called or it is dropped. This redefines the parent. An offset representation of the parent |
326 | | /// must be acquired by finishing it with [`Self::finish_with_offsets`]. |
327 | 0 | pub fn set_parent(&mut self, offset: &DirectoryOffset<K>) { |
328 | 0 | self.chained_ifd_pos = Some(offset.ifd_chain); |
329 | 0 | } |
330 | | |
331 | | /// Write out the ifd directory itself. |
332 | 0 | pub fn finish(mut self) -> TiffResult<()> { |
333 | 0 | self.finish_internal()?; |
334 | 0 | Ok(()) |
335 | 0 | } |
336 | | |
337 | 0 | pub fn finish_with_offsets(mut self) -> TiffResult<DirectoryOffset<K>> { |
338 | 0 | self.finish_internal() |
339 | 0 | } |
340 | | |
341 | 0 | fn write_directory(&mut self) -> TiffResult<u64> { |
342 | | // Start by turning all buffered unwritten values into entries. |
343 | 0 | let offset = self.writer.offset(); |
344 | 0 | K::write_entry_count(self.writer, self.directory.len())?; |
345 | | |
346 | 0 | let offset_bytes = mem::size_of::<K::OffsetType>(); |
347 | 0 | for (tag, entry) in self.directory.iter() { |
348 | 0 | self.writer.write_u16(tag.to_u16())?; |
349 | 0 | self.writer.write_u16(entry.field_type().to_u16())?; |
350 | 0 | let count = K::convert_offset(entry.count())?; |
351 | 0 | count.write(self.writer)?; |
352 | 0 | self.writer.write_bytes(&entry.offset()[..offset_bytes])?; |
353 | | } |
354 | | |
355 | 0 | Ok(offset) |
356 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::write_directory Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_directory |
357 | | |
358 | 0 | fn write_value( |
359 | 0 | writer: &mut TiffWriter<W>, |
360 | 0 | value: &DirectoryEntry<K::OffsetType>, |
361 | 0 | ) -> TiffResult<Entry> { |
362 | | let &DirectoryEntry { |
363 | 0 | data: ref bytes, |
364 | 0 | ref count, |
365 | 0 | data_type, |
366 | 0 | } = value; |
367 | | |
368 | 0 | let in_entry_bytes = mem::size_of::<K::OffsetType>(); |
369 | 0 | let mut offset_bytes = [0; 8]; |
370 | | |
371 | 0 | if bytes.len() > in_entry_bytes { |
372 | 0 | let offset = writer.offset(); |
373 | 0 | writer.write_bytes(bytes)?; |
374 | | |
375 | 0 | let offset = K::convert_offset(offset)?; |
376 | 0 | offset_bytes[..offset.bytes()].copy_from_slice(&offset.data()); |
377 | 0 | } else { |
378 | 0 | // Note: we have indicated our native byte order in the header, hence this |
379 | 0 | // corresponds to our byte order no matter the value type. |
380 | 0 | offset_bytes[..bytes.len()].copy_from_slice(bytes); |
381 | 0 | } |
382 | | |
383 | | // Undoing some hidden type. Offset is either u32 or u64. Due to the trait API being public |
384 | | // and some oversight, we can not clone the `count: K::OffsetType` and thus not convert it. |
385 | | // Instead, write it to a buffer... |
386 | 0 | let mut count_bytes = [0; 8]; |
387 | | // Nominally Cow but we only expect `Cow::Borrowed`. |
388 | 0 | count_bytes[..count.bytes()].copy_from_slice(&count.data()); |
389 | | |
390 | 0 | Ok(if in_entry_bytes == 4 { |
391 | 0 | let count = u32::from_ne_bytes(count_bytes[..4].try_into().unwrap()); |
392 | 0 | Entry::new(data_type, count, offset_bytes[..4].try_into().unwrap()) |
393 | | } else { |
394 | 0 | debug_assert_eq!(in_entry_bytes, 8); |
395 | 0 | let count = u64::from_ne_bytes(count_bytes); |
396 | 0 | Entry::new_u64(data_type, count, offset_bytes) |
397 | | }) |
398 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::write_value Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::write_value |
399 | | |
400 | | /// Provides the number of bytes written by the underlying TiffWriter during the last call. |
401 | 0 | fn last_written(&self) -> u64 { |
402 | 0 | self.writer.last_written() |
403 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::last_written Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::last_written |
404 | | |
405 | 0 | fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> { |
406 | 0 | let ifd_pointer = self.write_directory()?; |
407 | 0 | let offset = K::convert_offset(ifd_pointer)?; |
408 | | |
409 | 0 | if let Some(prior) = self.chained_ifd_pos { |
410 | 0 | let curr_pos = self.writer.offset(); |
411 | | |
412 | 0 | self.writer.goto_offset(prior.get())?; |
413 | | // Note how we are not writing the `offset` type itself here as doing so would need to |
414 | | // go through the `TiffValue` trait—the type is not constrained by much. But for the |
415 | | // trait we need a `TiffWriter` which comes with a bunch of additional state such as |
416 | | // compressor etc. that we have no need for. |
417 | 0 | K::write_offset(self.writer, ifd_pointer)?; |
418 | | |
419 | 0 | self.writer.goto_offset(curr_pos)?; |
420 | 0 | } |
421 | | |
422 | 0 | K::write_offset(self.writer, 0)?; |
423 | | |
424 | 0 | let ifd_chain = NonZeroU64::new(self.writer.previous_ifd_pointer::<K>()) |
425 | 0 | .expect("IFD chain field is at a non-zero offset"); |
426 | | |
427 | 0 | if let Some(prior) = self.write_chain.take() { |
428 | 0 | *prior = ifd_chain; |
429 | 0 | } |
430 | | |
431 | 0 | self.dropped = true; |
432 | | |
433 | 0 | Ok(DirectoryOffset { |
434 | 0 | pointer: IfdPointer(ifd_pointer), |
435 | 0 | offset, |
436 | 0 | ifd_chain, |
437 | 0 | kind: PhantomData, |
438 | 0 | }) |
439 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _>>::finish_internal Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard>>::finish_internal |
440 | | } |
441 | | |
442 | | impl<'a, W: Write + Seek, K: TiffKind> Drop for DirectoryEncoder<'a, W, K> { |
443 | 0 | fn drop(&mut self) { |
444 | 0 | if !self.dropped { |
445 | 0 | let _ = self.finish_internal(); |
446 | 0 | } |
447 | 0 | } Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<_, _> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::DirectoryEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop |
448 | | } |
449 | | |
450 | | /// Type to encode images strip by strip. |
451 | | /// |
452 | | /// You should call `finish` on this when you are finished with it. |
453 | | /// Encoding can silently fail while this is dropping. |
454 | | /// |
455 | | /// # Examples |
456 | | /// ``` |
457 | | /// # extern crate tiff; |
458 | | /// # fn main() { |
459 | | /// # let mut file = std::io::Cursor::new(Vec::new()); |
460 | | /// # let image_data = vec![0; 100*100*3]; |
461 | | /// use tiff::encoder::*; |
462 | | /// use tiff::tags::Tag; |
463 | | /// |
464 | | /// let mut tiff = TiffEncoder::new(&mut file).unwrap(); |
465 | | /// let mut image = tiff.new_image::<colortype::RGB8>(100, 100).unwrap(); |
466 | | /// |
467 | | /// // You can encode tags here |
468 | | /// image.encoder().write_tag(Tag::Artist, "Image-tiff").unwrap(); |
469 | | /// |
470 | | /// // Strip size can be configured before writing data |
471 | | /// image.rows_per_strip(2).unwrap(); |
472 | | /// |
473 | | /// let mut idx = 0; |
474 | | /// while image.next_strip_sample_count() > 0 { |
475 | | /// let sample_count = image.next_strip_sample_count() as usize; |
476 | | /// image.write_strip(&image_data[idx..idx+sample_count]).unwrap(); |
477 | | /// idx += sample_count; |
478 | | /// } |
479 | | /// image.finish().unwrap(); |
480 | | /// # } |
481 | | /// ``` |
482 | | /// You can also call write_data function wich will encode by strip and finish |
483 | | pub struct ImageEncoder<'a, W: 'a + Write + Seek, C: ColorType, K: TiffKind> { |
484 | | encoder: DirectoryEncoder<'a, W, K>, |
485 | | strip_idx: u64, |
486 | | strip_count: u64, |
487 | | row_samples: u64, |
488 | | width: u32, |
489 | | height: u32, |
490 | | rows_per_strip: u64, |
491 | | strip_offsets: Vec<K::OffsetType>, |
492 | | strip_byte_count: Vec<K::OffsetType>, |
493 | | dropped: bool, |
494 | | compression: Compression, |
495 | | predictor: Predictor, |
496 | | _phantom: ::std::marker::PhantomData<C>, |
497 | | } |
498 | | |
499 | | impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, K> { |
500 | 0 | fn sanity_check(compression: Compression, predictor: Predictor) -> TiffResult<()> { |
501 | 0 | match (predictor, compression, T::SAMPLE_FORMAT[0]) { |
502 | | (Predictor::Horizontal, _, SampleFormat::IEEEFP | SampleFormat::Void) => { |
503 | 0 | Err(TiffError::UsageError(UsageError::PredictorIncompatible)) |
504 | | } |
505 | | (Predictor::FloatingPoint, _, _) => { |
506 | 0 | Err(TiffError::UsageError(UsageError::PredictorUnavailable)) |
507 | | } |
508 | 0 | _ => Ok(()), |
509 | | } |
510 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::sanity_check Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::sanity_check |
511 | | |
512 | 0 | fn new( |
513 | 0 | mut encoder: DirectoryEncoder<'a, W, K>, |
514 | 0 | width: u32, |
515 | 0 | height: u32, |
516 | 0 | compression: Compression, |
517 | 0 | predictor: Predictor, |
518 | 0 | ) -> TiffResult<Self> { |
519 | 0 | if width == 0 || height == 0 { |
520 | 0 | return Err(TiffError::FormatError(TiffFormatError::InvalidDimensions( |
521 | 0 | width, height, |
522 | 0 | ))); |
523 | 0 | } |
524 | | |
525 | 0 | Self::sanity_check(compression, predictor)?; |
526 | | |
527 | 0 | let row_samples = u64::from(width) * u64::try_from(<T>::BITS_PER_SAMPLE.len())?; |
528 | 0 | let row_bytes = row_samples * u64::from(<T::Inner>::BYTE_LEN); |
529 | | |
530 | | // Limit the strip size to prevent potential memory and security issues. |
531 | | // Also keep the multiple strip handling 'oiled' |
532 | 0 | let rows_per_strip = { |
533 | 0 | match compression.tag() { |
534 | 0 | CompressionMethod::PackBits => 1, // Each row must be packed separately. Do not compress across row boundaries |
535 | 0 | _ => 1_000_000_u64.div_ceil(row_bytes), |
536 | | } |
537 | | }; |
538 | | |
539 | 0 | let strip_count = u64::from(height).div_ceil(rows_per_strip); |
540 | | |
541 | 0 | encoder.write_tag(Tag::ImageWidth, width)?; |
542 | 0 | encoder.write_tag(Tag::ImageLength, height)?; |
543 | 0 | encoder.write_tag(Tag::Compression, compression.tag().to_u16())?; |
544 | 0 | encoder.write_tag(Tag::Predictor, predictor.to_u16())?; |
545 | | |
546 | 0 | encoder.write_tag(Tag::BitsPerSample, <T>::BITS_PER_SAMPLE)?; |
547 | 0 | let sample_format: Vec<_> = <T>::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect(); Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::new::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::new::{closure#0} |
548 | 0 | encoder.write_tag(Tag::SampleFormat, &sample_format[..])?; |
549 | 0 | encoder.write_tag(Tag::PhotometricInterpretation, <T>::TIFF_VALUE.to_u16())?; |
550 | | |
551 | 0 | encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; |
552 | | |
553 | 0 | encoder.write_tag( |
554 | 0 | Tag::SamplesPerPixel, |
555 | 0 | u16::try_from(<T>::BITS_PER_SAMPLE.len())?, |
556 | 0 | )?; |
557 | 0 | encoder.write_tag(Tag::XResolution, Rational { n: 1, d: 1 })?; |
558 | 0 | encoder.write_tag(Tag::YResolution, Rational { n: 1, d: 1 })?; |
559 | 0 | encoder.write_tag(Tag::ResolutionUnit, ResolutionUnit::None.to_u16())?; |
560 | | |
561 | 0 | Ok(ImageEncoder { |
562 | 0 | encoder, |
563 | 0 | strip_count, |
564 | 0 | strip_idx: 0, |
565 | 0 | row_samples, |
566 | 0 | rows_per_strip, |
567 | 0 | width, |
568 | 0 | height, |
569 | 0 | strip_offsets: Vec::new(), |
570 | 0 | strip_byte_count: Vec::new(), |
571 | 0 | dropped: false, |
572 | 0 | compression, |
573 | 0 | predictor, |
574 | 0 | _phantom: ::std::marker::PhantomData, |
575 | 0 | }) |
576 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::new Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::new |
577 | | |
578 | | /// Number of samples the next strip should have. |
579 | 0 | pub fn next_strip_sample_count(&self) -> u64 { |
580 | 0 | if self.strip_idx >= self.strip_count { |
581 | 0 | return 0; |
582 | 0 | } |
583 | | |
584 | 0 | let raw_start_row = self.strip_idx * self.rows_per_strip; |
585 | 0 | let start_row = cmp::min(u64::from(self.height), raw_start_row); |
586 | 0 | let end_row = cmp::min(u64::from(self.height), raw_start_row + self.rows_per_strip); |
587 | | |
588 | 0 | (end_row - start_row) * self.row_samples |
589 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::next_strip_sample_count Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::next_strip_sample_count |
590 | | |
591 | | /// Write a single strip. |
592 | 0 | pub fn write_strip(&mut self, value: &[T::Inner]) -> TiffResult<()> |
593 | 0 | where |
594 | 0 | [T::Inner]: TiffValue, |
595 | | { |
596 | 0 | let samples = self.next_strip_sample_count(); |
597 | 0 | if u64::try_from(value.len())? != samples { |
598 | 0 | return Err(io::Error::new( |
599 | 0 | io::ErrorKind::InvalidData, |
600 | 0 | "Slice is wrong size for strip", |
601 | 0 | ) |
602 | 0 | .into()); |
603 | 0 | } |
604 | | |
605 | | // Write the (possible compressed) data to the encoder. |
606 | 0 | let offset = match self.predictor { |
607 | 0 | Predictor::None => self.encoder.write_data(value)?, |
608 | | Predictor::Horizontal => { |
609 | 0 | let mut row_result = Vec::with_capacity(value.len()); |
610 | 0 | for row in value.chunks_exact(self.row_samples as usize) { |
611 | 0 | T::horizontal_predict(row, &mut row_result); |
612 | 0 | } |
613 | 0 | self.encoder.write_data(row_result.as_slice())? |
614 | | } |
615 | 0 | _ => unimplemented!(), |
616 | | }; |
617 | | |
618 | 0 | let byte_count = self.encoder.last_written() as usize; |
619 | | |
620 | 0 | self.strip_offsets.push(K::convert_offset(offset)?); |
621 | 0 | self.strip_byte_count.push(byte_count.try_into()?); |
622 | | |
623 | 0 | self.strip_idx += 1; |
624 | 0 | Ok(()) |
625 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::write_strip Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::write_strip |
626 | | |
627 | | /// Write strips from data |
628 | 0 | pub fn write_data(mut self, data: &[T::Inner]) -> TiffResult<()> |
629 | 0 | where |
630 | 0 | [T::Inner]: TiffValue, |
631 | | { |
632 | 0 | let num_pix = usize::try_from(self.width)? |
633 | 0 | .checked_mul(usize::try_from(self.height)?) |
634 | 0 | .ok_or_else(|| { |
635 | 0 | io::Error::new( |
636 | 0 | io::ErrorKind::InvalidInput, |
637 | | "Image width * height exceeds usize", |
638 | | ) |
639 | 0 | })?; Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::write_data::{closure#0}Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::write_data::{closure#0} |
640 | 0 | if data.len() < num_pix { |
641 | 0 | return Err(io::Error::new( |
642 | 0 | io::ErrorKind::InvalidData, |
643 | 0 | "Input data slice is undersized for provided dimensions", |
644 | 0 | ) |
645 | 0 | .into()); |
646 | 0 | } |
647 | | |
648 | 0 | self.encoder |
649 | 0 | .writer |
650 | 0 | .set_compression(self.compression.get_algorithm()); |
651 | | |
652 | 0 | let mut idx = 0; |
653 | 0 | while self.next_strip_sample_count() > 0 { |
654 | 0 | let sample_count = usize::try_from(self.next_strip_sample_count())?; |
655 | 0 | self.write_strip(&data[idx..idx + sample_count])?; |
656 | 0 | idx += sample_count; |
657 | | } |
658 | | |
659 | 0 | self.encoder.writer.reset_compression(); |
660 | 0 | self.finish()?; |
661 | 0 | Ok(()) |
662 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::write_data Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::write_data |
663 | | |
664 | | /// Set image resolution |
665 | 0 | pub fn resolution(&mut self, unit: ResolutionUnit, value: Rational) { |
666 | 0 | self.encoder |
667 | 0 | .write_tag(Tag::ResolutionUnit, unit.to_u16()) |
668 | 0 | .unwrap(); |
669 | 0 | self.encoder |
670 | 0 | .write_tag(Tag::XResolution, value.clone()) |
671 | 0 | .unwrap(); |
672 | 0 | self.encoder.write_tag(Tag::YResolution, value).unwrap(); |
673 | 0 | } |
674 | | |
675 | | /// Set image resolution unit |
676 | 0 | pub fn resolution_unit(&mut self, unit: ResolutionUnit) { |
677 | 0 | self.encoder |
678 | 0 | .write_tag(Tag::ResolutionUnit, unit.to_u16()) |
679 | 0 | .unwrap(); |
680 | 0 | } |
681 | | |
682 | | /// Set image x-resolution |
683 | 0 | pub fn x_resolution(&mut self, value: Rational) { |
684 | 0 | self.encoder.write_tag(Tag::XResolution, value).unwrap(); |
685 | 0 | } |
686 | | |
687 | | /// Set image y-resolution |
688 | 0 | pub fn y_resolution(&mut self, value: Rational) { |
689 | 0 | self.encoder.write_tag(Tag::YResolution, value).unwrap(); |
690 | 0 | } |
691 | | |
692 | | /// Set image number of lines per strip |
693 | | /// |
694 | | /// This function needs to be called before any calls to `write_data` or |
695 | | /// `write_strip` and will return an error otherwise. |
696 | 0 | pub fn rows_per_strip(&mut self, value: u32) -> TiffResult<()> { |
697 | 0 | if self.strip_idx != 0 { |
698 | 0 | return Err(io::Error::new( |
699 | 0 | io::ErrorKind::InvalidInput, |
700 | 0 | "Cannot change strip size after data was written", |
701 | 0 | ) |
702 | 0 | .into()); |
703 | 0 | } |
704 | | // Write tag as 32 bits |
705 | 0 | self.encoder.write_tag(Tag::RowsPerStrip, value)?; |
706 | | |
707 | 0 | let value: u64 = value as u64; |
708 | 0 | self.strip_count = (self.height as u64).div_ceil(value); |
709 | 0 | self.rows_per_strip = value; |
710 | | |
711 | 0 | Ok(()) |
712 | 0 | } |
713 | | |
714 | 0 | fn finish_internal(&mut self) -> TiffResult<DirectoryOffset<K>> { |
715 | 0 | self.encoder |
716 | 0 | .write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?; |
717 | 0 | self.encoder.write_tag( |
718 | 0 | Tag::StripByteCounts, |
719 | 0 | K::convert_slice(&self.strip_byte_count), |
720 | 0 | )?; |
721 | 0 | self.dropped = true; |
722 | | |
723 | 0 | self.encoder.finish_internal() |
724 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::finish_internal Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::finish_internal |
725 | | |
726 | | /// Get a reference of the underlying `DirectoryEncoder` |
727 | 0 | pub fn encoder(&mut self) -> &mut DirectoryEncoder<'a, W, K> { |
728 | 0 | &mut self.encoder |
729 | 0 | } |
730 | | |
731 | | /// Write out image and ifd directory. |
732 | 0 | pub fn finish(mut self) -> TiffResult<()> { |
733 | 0 | self.finish_internal()?; |
734 | 0 | Ok(()) |
735 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard>>::finish Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard>>::finish |
736 | | } |
737 | | |
738 | | impl<'a, W: Write + Seek, C: ColorType, K: TiffKind> Drop for ImageEncoder<'a, W, C, K> { |
739 | 0 | fn drop(&mut self) { |
740 | 0 | if !self.dropped { |
741 | 0 | let _ = self.finish_internal(); |
742 | 0 | } |
743 | 0 | } Unexecuted instantiation: <tiff::encoder::ImageEncoder<_, _, _> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB32Float, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA32Float, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB8, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray8, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGB16, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA8, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::Gray16, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop Unexecuted instantiation: <tiff::encoder::ImageEncoder<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>, tiff::encoder::colortype::RGBA16, tiff::encoder::TiffKindStandard> as core::ops::drop::Drop>::drop |
744 | | } |
745 | | |
746 | | struct DirectoryEntry<S> { |
747 | | data_type: Type, |
748 | | count: S, |
749 | | data: Vec<u8>, |
750 | | } |
751 | | |
752 | | /// Trait to abstract over Tiff/BigTiff differences. |
753 | | /// |
754 | | /// Implemented for [`TiffKindStandard`] and [`TiffKindBig`]. |
755 | | pub trait TiffKind { |
756 | | /// The type of offset fields, `u32` for normal Tiff, `u64` for BigTiff. |
757 | | type OffsetType: TryFrom<usize, Error = TryFromIntError> + Into<u64> + TiffValue; |
758 | | |
759 | | /// Needed for the `convert_slice` method. |
760 | | type OffsetArrayType: ?Sized + TiffValue; |
761 | | |
762 | | /// Write the (Big)Tiff header. |
763 | | fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()>; |
764 | | |
765 | | /// Convert a file offset to `Self::OffsetType`. |
766 | | /// |
767 | | /// This returns an error for normal Tiff if the offset is larger than `u32::MAX`. |
768 | | fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType>; |
769 | | |
770 | | /// Write an offset value to the given writer. |
771 | | /// |
772 | | /// Like `convert_offset`, this errors if `offset > u32::MAX` for normal Tiff. |
773 | | fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()>; |
774 | | |
775 | | /// Write the IFD entry count field with the given `count` value. |
776 | | /// |
777 | | /// The entry count field is an `u16` for normal Tiff and `u64` for BigTiff. Errors |
778 | | /// if the given `usize` is larger than the representable values. |
779 | | fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()>; |
780 | | |
781 | | /// Internal helper method for satisfying Rust's type checker. |
782 | | /// |
783 | | /// The `TiffValue` trait is implemented for both primitive values (e.g. `u8`, `u32`) and |
784 | | /// slices of primitive values (e.g. `[u8]`, `[u32]`). However, this is not represented in |
785 | | /// the type system, so there is no guarantee that that for all `T: TiffValue` there is also |
786 | | /// an implementation of `TiffValue` for `[T]`. This method works around that problem by |
787 | | /// providing a conversion from `[T]` to some value that implements `TiffValue`, thereby |
788 | | /// making all slices of `OffsetType` usable with `write_tag` and similar methods. |
789 | | /// |
790 | | /// Implementations of this trait should always set `OffsetArrayType` to `[OffsetType]`. |
791 | | fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType; |
792 | | } |
793 | | |
794 | | /// Create a standard Tiff file. |
795 | | pub struct TiffKindStandard; |
796 | | |
797 | | impl TiffKind for TiffKindStandard { |
798 | | type OffsetType = u32; |
799 | | type OffsetArrayType = [u32]; |
800 | | |
801 | 0 | fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> { |
802 | 0 | write_tiff_header(writer)?; |
803 | | // blank the IFD offset location |
804 | 0 | writer.write_u32(0)?; |
805 | | |
806 | 0 | Ok(()) |
807 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_header::<_> Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_header::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> |
808 | | |
809 | 0 | fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> { |
810 | 0 | Ok(Self::OffsetType::try_from(offset)?) |
811 | 0 | } |
812 | | |
813 | 0 | fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> { |
814 | 0 | writer.write_u32(u32::try_from(offset)?)?; |
815 | 0 | Ok(()) |
816 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_offset::<_> Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_offset::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> |
817 | | |
818 | 0 | fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> { |
819 | 0 | writer.write_u16(u16::try_from(count)?)?; |
820 | | |
821 | 0 | Ok(()) |
822 | 0 | } Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_entry_count::<_> Unexecuted instantiation: <tiff::encoder::TiffKindStandard as tiff::encoder::TiffKind>::write_entry_count::<&mut std::io::cursor::Cursor<alloc::vec::Vec<u8>>> |
823 | | |
824 | 0 | fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType { |
825 | 0 | slice |
826 | 0 | } |
827 | | } |
828 | | |
829 | | /// Create a BigTiff file. |
830 | | pub struct TiffKindBig; |
831 | | |
832 | | impl TiffKind for TiffKindBig { |
833 | | type OffsetType = u64; |
834 | | type OffsetArrayType = [u64]; |
835 | | |
836 | 0 | fn write_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> { |
837 | 0 | write_bigtiff_header(writer)?; |
838 | | // blank the IFD offset location |
839 | 0 | writer.write_u64(0)?; |
840 | | |
841 | 0 | Ok(()) |
842 | 0 | } |
843 | | |
844 | 0 | fn convert_offset(offset: u64) -> TiffResult<Self::OffsetType> { |
845 | 0 | Ok(offset) |
846 | 0 | } |
847 | | |
848 | 0 | fn write_offset<W: Write>(writer: &mut TiffWriter<W>, offset: u64) -> TiffResult<()> { |
849 | 0 | writer.write_u64(offset)?; |
850 | 0 | Ok(()) |
851 | 0 | } |
852 | | |
853 | 0 | fn write_entry_count<W: Write>(writer: &mut TiffWriter<W>, count: usize) -> TiffResult<()> { |
854 | 0 | writer.write_u64(u64::try_from(count)?)?; |
855 | 0 | Ok(()) |
856 | 0 | } |
857 | | |
858 | 0 | fn convert_slice(slice: &[Self::OffsetType]) -> &Self::OffsetArrayType { |
859 | 0 | slice |
860 | 0 | } |
861 | | } |