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, directory_chain, 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 pages: u32,
45 row: u32,
46 cached: Option<(usize, Vec<u8>)>,
52}
53
54impl TiffDecoder {
55 pub fn new<S: Source>(source: S, limits: Limits) -> Result<Self> {
64 Self::with_page(source, limits, 0)
65 }
66
67 pub fn with_page<S: Source>(mut source: S, limits: Limits, page: u32) -> Result<Self> {
79 let mut data = Vec::new();
82 let mut buffer = vec![0_u8; 256 * 1024];
83 loop {
84 let read = source.read(&mut buffer)?;
85 if read == 0 {
86 break;
87 }
88 let Some(chunk) = buffer.get(..read) else {
89 break;
90 };
91 data.extend_from_slice(chunk);
92 }
93
94 let (order, first_ifd) = parse_header(&data)?;
95 let chain = directory_chain(&data, order, first_ifd);
96 let pages = u32::try_from(chain.len()).unwrap_or(u32::MAX).max(1);
99 let offset = match page {
100 0 => first_ifd,
101 _ => chain.get(page as usize).copied().ok_or_else(|| {
102 PixelsError::invalid_argument(
103 "page",
104 format!("page {page} was asked for; the file has {pages} (0 is the first)"),
105 )
106 })?,
107 };
108 let directory = Directory::parse(&data, order, offset)?;
109 let image = TiffImage::from_directory(&directory, order, &limits)?;
110
111 Ok(Self {
112 data,
113 image,
114 pages,
115 row: 0,
116 cached: None,
117 })
118 }
119
120 #[must_use]
122 pub const fn image(&self) -> &TiffImage {
123 &self.image
124 }
125
126 #[must_use]
128 pub const fn byte_order(&self) -> ByteOrder {
129 self.image.order
130 }
131
132 fn ensure_chunk(&mut self, index: usize) -> Result<()> {
134 if matches!(&self.cached, Some((cached, _)) if *cached == index) {
135 return Ok(());
136 }
137 let decoded = self.image.read_chunk(&self.data, index)?;
138 self.cached = Some((index, decoded));
139 Ok(())
140 }
141
142 fn blit_chunk(
144 &mut self,
145 column: u32,
146 chunk_row: u32,
147 region: Region,
148 out: &mut TileMut<'_>,
149 ) -> Result<()> {
150 let across = self.image.chunks_across();
151 let index = (chunk_row * across + column) as usize;
152 if index >= self.image.offsets.len() {
153 return Err(PixelsError::malformed(
154 "tiff",
155 format!(
156 "chunk {index} is beyond the {} declared",
157 self.image.offsets.len()
158 ),
159 ));
160 }
161
162 self.ensure_chunk(index)?;
163 let Self { image, cached, .. } = self;
169 let Some((_, data)) = cached.as_ref() else {
170 return Err(PixelsError::graph("tiff chunk vanished after decoding"));
171 };
172
173 let area = image.chunk_region(column, chunk_row);
174 let (stored_width, _) = image.chunk_stored_size();
175 let stored_row_bytes = image.chunk_row_bytes();
176
177 let left = area.x.max(region.x);
179 let top = area.y.max(region.y);
180 let right = (area.x + area.width).min(region.x + region.width);
181 let bottom = (area.y + area.height).min(region.y + region.height);
182 if right <= left || bottom <= top {
183 return Ok(());
184 }
185
186 let mut expanded =
187 vec![0_u8; (right - left) as usize * image.descriptor.pixel.bytes_per_pixel()];
188 for y in top..bottom {
189 let within = (y - area.y) as usize;
190 let start = within * stored_row_bytes;
191 let Some(stored) = data.get(start..start + stored_row_bytes) else {
192 continue;
193 };
194 expand_row(
195 image,
196 stored,
197 (left - area.x) as usize,
198 (right - left) as usize,
199 stored_width as usize,
200 &mut expanded,
201 );
202 let Some(target) = out.row_mut(y) else {
203 continue;
204 };
205 let bpp = image.descriptor.pixel.bytes_per_pixel();
206 let at = (left - region.x) as usize * bpp;
207 let Some(slot) = target.get_mut(at..at + expanded.len()) else {
208 continue;
209 };
210 slot.copy_from_slice(&expanded);
211 }
212 Ok(())
213 }
214
215 fn read_region_into(&mut self, region: Region, out: &mut TileMut<'_>) -> Result<()> {
217 if region.x + region.width > self.image.descriptor.width
218 || region.y + region.height > self.image.descriptor.height
219 {
220 return Err(PixelsError::invalid_argument(
221 "region",
222 format!(
223 "{region} is outside a {}x{} image",
224 self.image.descriptor.width, self.image.descriptor.height
225 ),
226 ));
227 }
228
229 let (first_column, last_column, first_row, last_row) = self.chunks_covering(region);
230 for chunk_row in first_row..=last_row {
231 for column in first_column..=last_column {
232 self.blit_chunk(column, chunk_row, region, out)?;
233 }
234 }
235 Ok(())
236 }
237
238 fn chunks_covering(&self, region: Region) -> (u32, u32, u32, u32) {
244 match self.image.layout {
245 Layout::Strips { rows_per_strip } => {
246 let first = region.y / rows_per_strip;
247 let last = (region.y + region.height.saturating_sub(1)) / rows_per_strip;
248 (0, 0, first, last.min(self.image.chunks_down() - 1))
249 }
250 Layout::Tiles { width, height } => {
251 let first_column = region.x / width;
252 let last_column = (region.x + region.width.saturating_sub(1)) / width;
253 let first_row = region.y / height;
254 let last_row = (region.y + region.height.saturating_sub(1)) / height;
255 (
256 first_column,
257 last_column.min(self.image.chunks_across() - 1),
258 first_row,
259 last_row.min(self.image.chunks_down() - 1),
260 )
261 }
262 }
263 }
264}
265
266fn expand_row(
268 image: &TiffImage,
269 stored: &[u8],
270 from: usize,
271 count: usize,
272 stored_width: usize,
273 out: &mut [u8],
274) {
275 let bits = image.bits_per_sample as usize;
276 let channels = image.samples_per_pixel as usize;
277 let format = image.descriptor.pixel;
278 let bpp = format.bytes_per_pixel();
279 let maximum = if bits >= 16 {
280 65535_u32
281 } else {
282 (1_u32 << bits) - 1
283 };
284
285 for index in 0..count {
286 let x = from + index;
287 if x >= stored_width {
288 break;
289 }
290 let Some(target) = out.get_mut(index * bpp..(index + 1) * bpp) else {
291 break;
292 };
293
294 let mut samples = [0_u32; 4];
296 for (channel, slot) in samples.iter_mut().enumerate().take(channels.min(4)) {
297 *slot = read_sample(stored, x * channels + channel, bits, image.order);
298 }
299
300 match image.photometric {
301 Photometric::Palette => {
302 let entries = 1_usize << bits;
306 let index = samples[0] as usize;
307 for channel in 0..3 {
308 let value = image
309 .color_map
310 .get(channel * entries + index)
311 .copied()
312 .unwrap_or(0);
313 if let Some(slot) = target.get_mut(channel) {
314 *slot = (value >> 8) as u8;
317 }
318 }
319 }
320 _ => {
321 for (channel, &sample) in samples.iter().enumerate().take(channels.min(4)) {
322 let mut value = sample;
323 let is_colour = channel < 3;
327 if image.photometric == Photometric::WhiteIsZero && is_colour {
328 value = maximum.saturating_sub(value);
329 }
330 write_sample(target, channel, value, bits, maximum, format);
331 }
332 }
333 }
334 }
335}
336
337fn read_sample(data: &[u8], index: usize, bits: usize, order: ByteOrder) -> u32 {
339 match bits {
340 16 => u32::from(order.u16(data, index * 2)),
341 8 => u32::from(data.get(index).copied().unwrap_or(0)),
342 1 | 2 | 4 => {
343 let per_byte = 8 / bits;
346 let byte = data.get(index / per_byte).copied().unwrap_or(0);
347 let shift = 8 - bits * (index % per_byte + 1);
348 u32::from((byte >> shift) & ((1_u16 << bits) - 1) as u8)
349 }
350 _ => 0,
351 }
352}
353
354fn write_sample(
356 target: &mut [u8],
357 channel: usize,
358 value: u32,
359 bits: usize,
360 maximum: u32,
361 format: PixelFormat,
362) {
363 let widened = if bits >= 8 {
364 value
365 } else {
366 (value * 255 + maximum / 2) / maximum.max(1)
369 };
370 match format.sample_kind() {
371 otf_pixels_core::SampleKind::U16 => {
372 let scaled = widened as u16;
373 for (offset, byte) in scaled.to_ne_bytes().iter().enumerate() {
374 if let Some(slot) = target.get_mut(channel * 2 + offset) {
375 *slot = *byte;
376 }
377 }
378 }
379 _ => {
380 if let Some(slot) = target.get_mut(channel) {
381 *slot = widened.min(255) as u8;
382 }
383 }
384 }
385}
386
387impl Decoder for TiffDecoder {
388 fn descriptor(&self) -> ImageDescriptor {
389 self.image.descriptor
390 }
391
392 fn pages(&self) -> u32 {
393 self.pages
394 }
395
396 fn orientation(&self) -> Orientation {
397 self.image.orientation
398 }
399
400 fn icc_profile(&self) -> Option<&[u8]> {
401 self.image.icc.as_deref()
402 }
403
404 fn capability(&self) -> DecodeCapability {
405 if self.image.layout.is_random_access() {
408 DecodeCapability::Regions
409 } else {
410 DecodeCapability::Sequential
411 }
412 }
413
414 fn read_row(&mut self, out: &mut [u8]) -> Result<()> {
415 if self.row >= self.image.descriptor.height {
416 return Err(PixelsError::invalid_argument(
417 "out",
418 format!(
419 "all {} rows have already been read",
420 self.image.descriptor.height
421 ),
422 ));
423 }
424 let row_bytes = self.image.descriptor.row_bytes();
425 if out.len() != row_bytes {
426 return Err(PixelsError::invalid_argument(
427 "out",
428 format!("row buffer is {} bytes, expected {row_bytes}", out.len()),
429 ));
430 }
431
432 let region = Region::new(0, self.row, self.image.descriptor.width, 1);
437 let pixel = self.image.descriptor.pixel;
438 let mut tile = TileMut::new(region, pixel, row_bytes, out)?;
439 self.read_region_into(region, &mut tile)?;
440 self.row += 1;
441 Ok(())
442 }
443
444 fn read_region(&mut self, region: Region, out: &mut TileMut<'_>) -> Result<()> {
445 if !self.image.layout.is_random_access() {
446 return Err(PixelsError::unsupported(
447 "this TIFF is stored in strips; region decode requires tiles",
448 ));
449 }
450 self.read_region_into(region, out)
451 }
452}
453
454#[must_use]
458pub fn probe(prefix: &[u8]) -> bool {
459 probe_header(prefix)
460}
461
462#[derive(Debug, Clone, Copy, Default)]
464pub struct TiffCodec;
465
466impl Codec for TiffCodec {
467 fn format(&self) -> Format {
468 Format::Tiff
469 }
470
471 fn magic_len(&self) -> usize {
472 8
473 }
474
475 fn probe(&self, prefix: &[u8]) -> bool {
476 probe(prefix)
477 }
478}