Skip to main content

tiff/encoder/
writer.rs

1use crate::encoder::compression::*;
2use crate::error::TiffResult;
3use std::io::{self, Seek, SeekFrom, Write};
4
5pub fn write_tiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
6    #[cfg(target_endian = "little")]
7    let boi: u8 = 0x49;
8    #[cfg(not(target_endian = "little"))]
9    let boi: u8 = 0x4d;
10
11    writer.writer.write_all(&[boi, boi])?;
12    writer.writer.write_all(&42u16.to_ne_bytes())?;
13    writer.offset += 4;
14
15    Ok(())
16}
17
18/// Writes a BigTiff header, excluding the IFD offset field.
19///
20/// Writes the byte order, version number, offset byte size, and zero constant fields. Does
21// _not_ write the offset to the first IFD, this should be done by the caller.
22pub fn write_bigtiff_header<W: Write>(writer: &mut TiffWriter<W>) -> TiffResult<()> {
23    #[cfg(target_endian = "little")]
24    let boi: u8 = 0x49;
25    #[cfg(not(target_endian = "little"))]
26    let boi: u8 = 0x4d;
27
28    // byte order indication
29    writer.writer.write_all(&[boi, boi])?;
30    // version number
31    writer.writer.write_all(&43u16.to_ne_bytes())?;
32    // bytesize of offsets (pointer size)
33    writer.writer.write_all(&8u16.to_ne_bytes())?;
34    // always 0
35    writer.writer.write_all(&0u16.to_ne_bytes())?;
36
37    // we wrote 8 bytes, so set the internal offset accordingly
38    writer.offset += 8;
39
40    Ok(())
41}
42
43pub struct TiffWriter<W> {
44    writer: W,
45    offset: u64,
46    byte_count: u64,
47    compressor: Compressor,
48}
49
50impl<W: Write> TiffWriter<W> {
51    pub fn new(writer: W) -> Self {
52        Self {
53            writer,
54            offset: 0,
55            byte_count: 0,
56            compressor: Compressor::default(),
57        }
58    }
59
60    pub fn set_compression(&mut self, compressor: Compressor) {
61        self.compressor = compressor;
62    }
63
64    pub fn reset_compression(&mut self) {
65        self.compressor = Compressor::default();
66    }
67
68    pub fn offset(&self) -> u64 {
69        self.offset
70    }
71
72    pub(crate) fn previous_ifd_pointer<K: super::TiffKind>(&self) -> u64 {
73        self.offset() - core::mem::size_of::<K::OffsetType>() as u64
74    }
75
76    pub fn last_written(&self) -> u64 {
77        self.byte_count
78    }
79
80    pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), io::Error> {
81        self.byte_count = self.compressor.write_to(&mut self.writer, bytes)?;
82        self.offset += self.byte_count;
83        Ok(())
84    }
85
86    pub fn write_u8(&mut self, n: u8) -> Result<(), io::Error> {
87        self.byte_count = self
88            .compressor
89            .write_to(&mut self.writer, &n.to_ne_bytes())?;
90        self.offset += self.byte_count;
91        Ok(())
92    }
93
94    pub fn write_i8(&mut self, n: i8) -> Result<(), io::Error> {
95        self.byte_count = self
96            .compressor
97            .write_to(&mut self.writer, &n.to_ne_bytes())?;
98        self.offset += self.byte_count;
99        Ok(())
100    }
101
102    pub fn write_u16(&mut self, n: u16) -> Result<(), io::Error> {
103        self.byte_count = self
104            .compressor
105            .write_to(&mut self.writer, &n.to_ne_bytes())?;
106        self.offset += self.byte_count;
107
108        Ok(())
109    }
110
111    pub fn write_i16(&mut self, n: i16) -> Result<(), io::Error> {
112        self.byte_count = self
113            .compressor
114            .write_to(&mut self.writer, &n.to_ne_bytes())?;
115        self.offset += self.byte_count;
116
117        Ok(())
118    }
119
120    pub fn write_u32(&mut self, n: u32) -> Result<(), io::Error> {
121        self.byte_count = self
122            .compressor
123            .write_to(&mut self.writer, &n.to_ne_bytes())?;
124        self.offset += self.byte_count;
125
126        Ok(())
127    }
128
129    pub fn write_i32(&mut self, n: i32) -> Result<(), io::Error> {
130        self.byte_count = self
131            .compressor
132            .write_to(&mut self.writer, &n.to_ne_bytes())?;
133        self.offset += self.byte_count;
134
135        Ok(())
136    }
137
138    pub fn write_u64(&mut self, n: u64) -> Result<(), io::Error> {
139        self.byte_count = self
140            .compressor
141            .write_to(&mut self.writer, &n.to_ne_bytes())?;
142        self.offset += self.byte_count;
143
144        Ok(())
145    }
146
147    pub fn write_i64(&mut self, n: i64) -> Result<(), io::Error> {
148        self.byte_count = self
149            .compressor
150            .write_to(&mut self.writer, &n.to_ne_bytes())?;
151        self.offset += self.byte_count;
152
153        Ok(())
154    }
155
156    pub fn write_f32(&mut self, n: f32) -> Result<(), io::Error> {
157        self.byte_count = self
158            .compressor
159            .write_to(&mut self.writer, &u32::to_ne_bytes(n.to_bits()))?;
160        self.offset += self.byte_count;
161
162        Ok(())
163    }
164
165    pub fn write_f64(&mut self, n: f64) -> Result<(), io::Error> {
166        self.byte_count = self
167            .compressor
168            .write_to(&mut self.writer, &u64::to_ne_bytes(n.to_bits()))?;
169        self.offset += self.byte_count;
170
171        Ok(())
172    }
173
174    pub fn pad_word_boundary(&mut self) -> Result<(), io::Error> {
175        if self.offset % 4 != 0 {
176            let padding = [0, 0, 0];
177            let padd_len = 4 - (self.offset % 4);
178            self.writer.write_all(&padding[..padd_len as usize])?;
179            self.offset += padd_len;
180        }
181
182        Ok(())
183    }
184}
185
186impl<W: Seek> TiffWriter<W> {
187    pub fn goto_offset(&mut self, offset: u64) -> Result<(), io::Error> {
188        self.offset = offset;
189        self.writer.seek(SeekFrom::Start(offset))?;
190        Ok(())
191    }
192}
193
194#[test]
195fn encoder_on_short_writes() {
196    struct ShortWriter<T> {
197        inner: T,
198    }
199
200    impl<T: std::io::Write> std::io::Write for ShortWriter<T> {
201        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
202            let lower_bound = buf.len().min(1);
203            let lower = buf.len().saturating_sub(1).max(lower_bound);
204
205            self.inner.write(&buf[..lower])
206        }
207
208        fn flush(&mut self) -> std::io::Result<()> {
209            self.inner.flush()
210        }
211    }
212
213    impl<T: std::io::Seek> std::io::Seek for ShortWriter<T> {
214        fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
215            self.inner.seek(pos)
216        }
217    }
218
219    let mut write = ShortWriter {
220        inner: std::io::Cursor::new(vec![0u8; 0]),
221    };
222
223    let imgdata = vec![42u8; 100 * 100];
224    let mut data = crate::decoder::DecodingResult::U8(vec![]);
225
226    for comp in [
227        super::Compression::Uncompressed,
228        super::Compression::Packbits,
229    ] {
230        write.inner.set_position(0);
231        write.inner.get_mut().clear();
232
233        {
234            let mut tiff_writer = super::TiffEncoder::new(&mut write)
235                .unwrap()
236                .with_compression(comp);
237
238            tiff_writer
239                .write_image::<super::Gray8>(100, 100, &imgdata)
240                .unwrap();
241        }
242
243        write.inner.set_position(0);
244        let mut tiff_reader = crate::decoder::Decoder::new(&mut write.inner).unwrap();
245        let _layout = tiff_reader.read_image_to_buffer(&mut data).unwrap();
246
247        assert!(
248            matches!(&data, crate::decoder::DecodingResult::U8(v) if *v == imgdata),
249            "{data:?} @ {}",
250            data.as_buffer(0).byte_len()
251        );
252    }
253}