1use otf_pixels_core::{
31 Codec, DecodeCapability, Decoder, Format, ImageDescriptor, Limits, Orientation, PixelFormat,
32 PixelsError, Region, Result, Source, TileMut,
33};
34
35use crate::ifd::{ByteOrder, Directory, parse_header, probe as probe_header};
36use crate::image::{Layout, Photometric, TiffImage};
37
38#[derive(Debug)]
40pub struct TiffDecoder {
41 data: Vec<u8>,
42 image: TiffImage,
43 row: u32,
44 cached: Option<(usize, Vec<u8>)>,
50}
51
52impl TiffDecoder {
53 pub fn new<S: Source>(mut source: S, limits: Limits) -> Result<Self> {
62 let mut data = Vec::new();
65 let mut buffer = vec![0_u8; 256 * 1024];
66 loop {
67 let read = source.read(&mut buffer)?;
68 if read == 0 {
69 break;
70 }
71 let Some(chunk) = buffer.get(..read) else {
72 break;
73 };
74 data.extend_from_slice(chunk);
75 }
76
77 let (order, first_ifd) = parse_header(&data)?;
78 let directory = Directory::parse(&data, order, first_ifd)?;
79 let image = TiffImage::from_directory(&directory, order, &limits)?;
80
81 Ok(Self {
82 data,
83 image,
84 row: 0,
85 cached: None,
86 })
87 }
88
89 #[must_use]
91 pub const fn image(&self) -> &TiffImage {
92 &self.image
93 }
94
95 #[must_use]
97 pub const fn byte_order(&self) -> ByteOrder {
98 self.image.order
99 }
100
101 fn ensure_chunk(&mut self, index: usize) -> Result<()> {
103 if matches!(&self.cached, Some((cached, _)) if *cached == index) {
104 return Ok(());
105 }
106 let decoded = self.image.read_chunk(&self.data, index)?;
107 self.cached = Some((index, decoded));
108 Ok(())
109 }
110
111 fn blit_chunk(
113 &mut self,
114 column: u32,
115 chunk_row: u32,
116 region: Region,
117 out: &mut TileMut<'_>,
118 ) -> Result<()> {
119 let across = self.image.chunks_across();
120 let index = (chunk_row * across + column) as usize;
121 if index >= self.image.offsets.len() {
122 return Err(PixelsError::malformed(
123 "tiff",
124 format!(
125 "chunk {index} is beyond the {} declared",
126 self.image.offsets.len()
127 ),
128 ));
129 }
130
131 self.ensure_chunk(index)?;
132 let Self { image, cached, .. } = self;
138 let Some((_, data)) = cached.as_ref() else {
139 return Err(PixelsError::graph("tiff chunk vanished after decoding"));
140 };
141
142 let area = image.chunk_region(column, chunk_row);
143 let (stored_width, _) = image.chunk_stored_size();
144 let stored_row_bytes = image.chunk_row_bytes();
145
146 let left = area.x.max(region.x);
148 let top = area.y.max(region.y);
149 let right = (area.x + area.width).min(region.x + region.width);
150 let bottom = (area.y + area.height).min(region.y + region.height);
151 if right <= left || bottom <= top {
152 return Ok(());
153 }
154
155 let mut expanded =
156 vec![0_u8; (right - left) as usize * image.descriptor.pixel.bytes_per_pixel()];
157 for y in top..bottom {
158 let within = (y - area.y) as usize;
159 let start = within * stored_row_bytes;
160 let Some(stored) = data.get(start..start + stored_row_bytes) else {
161 continue;
162 };
163 expand_row(
164 image,
165 stored,
166 (left - area.x) as usize,
167 (right - left) as usize,
168 stored_width as usize,
169 &mut expanded,
170 );
171 let Some(target) = out.row_mut(y) else {
172 continue;
173 };
174 let bpp = image.descriptor.pixel.bytes_per_pixel();
175 let at = (left - region.x) as usize * bpp;
176 let Some(slot) = target.get_mut(at..at + expanded.len()) else {
177 continue;
178 };
179 slot.copy_from_slice(&expanded);
180 }
181 Ok(())
182 }
183
184 fn read_region_into(&mut self, region: Region, out: &mut TileMut<'_>) -> Result<()> {
186 if region.x + region.width > self.image.descriptor.width
187 || region.y + region.height > self.image.descriptor.height
188 {
189 return Err(PixelsError::invalid_argument(
190 "region",
191 format!(
192 "{region} is outside a {}x{} image",
193 self.image.descriptor.width, self.image.descriptor.height
194 ),
195 ));
196 }
197
198 let (first_column, last_column, first_row, last_row) = self.chunks_covering(region);
199 for chunk_row in first_row..=last_row {
200 for column in first_column..=last_column {
201 self.blit_chunk(column, chunk_row, region, out)?;
202 }
203 }
204 Ok(())
205 }
206
207 fn chunks_covering(&self, region: Region) -> (u32, u32, u32, u32) {
213 match self.image.layout {
214 Layout::Strips { rows_per_strip } => {
215 let first = region.y / rows_per_strip;
216 let last = (region.y + region.height.saturating_sub(1)) / rows_per_strip;
217 (0, 0, first, last.min(self.image.chunks_down() - 1))
218 }
219 Layout::Tiles { width, height } => {
220 let first_column = region.x / width;
221 let last_column = (region.x + region.width.saturating_sub(1)) / width;
222 let first_row = region.y / height;
223 let last_row = (region.y + region.height.saturating_sub(1)) / height;
224 (
225 first_column,
226 last_column.min(self.image.chunks_across() - 1),
227 first_row,
228 last_row.min(self.image.chunks_down() - 1),
229 )
230 }
231 }
232 }
233}
234
235fn expand_row(
237 image: &TiffImage,
238 stored: &[u8],
239 from: usize,
240 count: usize,
241 stored_width: usize,
242 out: &mut [u8],
243) {
244 let bits = image.bits_per_sample as usize;
245 let channels = image.samples_per_pixel as usize;
246 let format = image.descriptor.pixel;
247 let bpp = format.bytes_per_pixel();
248 let maximum = if bits >= 16 {
249 65535_u32
250 } else {
251 (1_u32 << bits) - 1
252 };
253
254 for index in 0..count {
255 let x = from + index;
256 if x >= stored_width {
257 break;
258 }
259 let Some(target) = out.get_mut(index * bpp..(index + 1) * bpp) else {
260 break;
261 };
262
263 let mut samples = [0_u32; 4];
265 for (channel, slot) in samples.iter_mut().enumerate().take(channels.min(4)) {
266 *slot = read_sample(stored, x * channels + channel, bits, image.order);
267 }
268
269 match image.photometric {
270 Photometric::Palette => {
271 let entries = 1_usize << bits;
275 let index = samples[0] as usize;
276 for channel in 0..3 {
277 let value = image
278 .color_map
279 .get(channel * entries + index)
280 .copied()
281 .unwrap_or(0);
282 if let Some(slot) = target.get_mut(channel) {
283 *slot = (value >> 8) as u8;
286 }
287 }
288 }
289 _ => {
290 for (channel, &sample) in samples.iter().enumerate().take(channels.min(4)) {
291 let mut value = sample;
292 let is_colour = channel < 3;
296 if image.photometric == Photometric::WhiteIsZero && is_colour {
297 value = maximum.saturating_sub(value);
298 }
299 write_sample(target, channel, value, bits, maximum, format);
300 }
301 }
302 }
303 }
304}
305
306fn read_sample(data: &[u8], index: usize, bits: usize, order: ByteOrder) -> u32 {
308 match bits {
309 16 => u32::from(order.u16(data, index * 2)),
310 8 => u32::from(data.get(index).copied().unwrap_or(0)),
311 1 | 2 | 4 => {
312 let per_byte = 8 / bits;
315 let byte = data.get(index / per_byte).copied().unwrap_or(0);
316 let shift = 8 - bits * (index % per_byte + 1);
317 u32::from((byte >> shift) & ((1_u16 << bits) - 1) as u8)
318 }
319 _ => 0,
320 }
321}
322
323fn write_sample(
325 target: &mut [u8],
326 channel: usize,
327 value: u32,
328 bits: usize,
329 maximum: u32,
330 format: PixelFormat,
331) {
332 let widened = if bits >= 8 {
333 value
334 } else {
335 (value * 255 + maximum / 2) / maximum.max(1)
338 };
339 match format.sample_kind() {
340 otf_pixels_core::SampleKind::U16 => {
341 let scaled = widened as u16;
342 for (offset, byte) in scaled.to_ne_bytes().iter().enumerate() {
343 if let Some(slot) = target.get_mut(channel * 2 + offset) {
344 *slot = *byte;
345 }
346 }
347 }
348 _ => {
349 if let Some(slot) = target.get_mut(channel) {
350 *slot = widened.min(255) as u8;
351 }
352 }
353 }
354}
355
356impl Decoder for TiffDecoder {
357 fn descriptor(&self) -> ImageDescriptor {
358 self.image.descriptor
359 }
360
361 fn orientation(&self) -> Orientation {
362 self.image.orientation
363 }
364
365 fn icc_profile(&self) -> Option<&[u8]> {
366 self.image.icc.as_deref()
367 }
368
369 fn capability(&self) -> DecodeCapability {
370 if self.image.layout.is_random_access() {
373 DecodeCapability::Regions
374 } else {
375 DecodeCapability::Sequential
376 }
377 }
378
379 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
380 if self.row >= self.image.descriptor.height {
381 return Err(PixelsError::invalid_argument(
382 "out",
383 format!(
384 "all {} rows have already been read",
385 self.image.descriptor.height
386 ),
387 ));
388 }
389 let row_bytes = self.image.descriptor.row_bytes();
390 if out.len() != row_bytes {
391 return Err(PixelsError::invalid_argument(
392 "out",
393 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
394 ));
395 }
396
397 let region = Region::new(0, self.row, self.image.descriptor.width, 1);
402 let pixel = self.image.descriptor.pixel;
403 let mut tile = TileMut::new(region, pixel, row_bytes, out)?;
404 self.read_region_into(region, &mut tile)?;
405 self.row += 1;
406 Ok(())
407 }
408
409 fn read_region(&mut self, region: Region, out: &mut TileMut<'_>) -> Result<()> {
410 if !self.image.layout.is_random_access() {
411 return Err(PixelsError::unsupported(
412 "this TIFF is stored in strips; region decode requires tiles",
413 ));
414 }
415 self.read_region_into(region, out)
416 }
417}
418
419#[must_use]
423pub fn probe(prefix: &[u8]) -> bool {
424 probe_header(prefix)
425}
426
427#[derive(Debug, Clone, Copy, Default)]
429pub struct TiffCodec;
430
431impl Codec for TiffCodec {
432 fn format(&self) -> Format {
433 Format::Tiff
434 }
435
436 fn magic_len(&self) -> usize {
437 8
438 }
439
440 fn probe(&self, prefix: &[u8]) -> bool {
441 probe(prefix)
442 }
443}