1use core::ffi::CStr;
27use std::fmt;
28use std::io::BufRead;
29
30use image::error::{
31 DecodingError, ImageError, ImageFormatHint, ImageResult, LimitError, LimitErrorKind,
32};
33use image::{ColorType, ExtendedColorType, ImageDecoder, LimitSupport, Limits};
34
35const HEADER_FULL_LENGTH: usize = 512;
37
38#[derive(Clone, Copy)]
46struct SgiRgbHeaderInfo {
47 xsize: u16,
48 ysize: u16,
49 color_type: ColorType,
50 is_rle: bool,
51}
52
53#[derive(Debug)]
55enum SgiRgbDecodeError {
56 BadMagic,
57 HeaderError,
58 RLERowInvalid(u32, u32), UnexpectedColormap(u32),
60 UnexpectedZSize(u16),
61 ZeroSize(u16, u16, u16),
62 ScanlineOverflow,
63 ScanlineUnderflow,
64 ScanlineTooShort,
65 ScanlineTooLong,
66 InvalidName,
67 EarlyEOF,
68}
69
70impl fmt::Display for SgiRgbDecodeError {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::BadMagic => f.write_str("Incorrect magic bytes, not an SGI image file"),
74 Self::HeaderError => f.write_str("Error parsing header"),
75 Self::UnexpectedColormap(code) => {
76 f.write_fmt(format_args!("Unexpected color map value ({})", code))
77 }
78 Self::UnexpectedZSize(dim) => {
79 f.write_fmt(format_args!("Unexpected Z dimension is >= 5 ({})", dim))
80 }
81 Self::ZeroSize(x, y, z) => f.write_fmt(format_args!(
82 "Image has zero size: x*y*z={}*{}*{}=0",
83 x, y, z
84 )),
85 Self::RLERowInvalid(offset, length) => {
86 f.write_fmt(format_args!("Bad RLE row info (offset={}, length={}); empty or not in image data region", offset, length))
87 }
88 Self::InvalidName => {
89 f.write_str("Invalid name field (not null terminated or not ASCII)")
90 }
91 Self::ScanlineOverflow => {
92 f.write_str("An RLE-encoded scanline contained more data than the image width")
93 }
94 Self::ScanlineUnderflow => {
95 f.write_str("An RLE-encoded scanline contained less data than the image width")
96 }
97 Self::ScanlineTooShort => {
98 f.write_str("An RLE-encoded scanline stopped (reached a zero counter) before its stated length")
99 }
100 Self::ScanlineTooLong => {
101 f.write_str("An RLE-encoded scanline did not stop at its stated length (missing trailing zero counter).")
102 }
103 Self::EarlyEOF => {
104 f.write_str("File ended before all scanlines were read.")
105 }
106 }
107 }
108}
109
110impl std::error::Error for SgiRgbDecodeError {}
111
112impl From<SgiRgbDecodeError> for ImageError {
113 fn from(e: SgiRgbDecodeError) -> ImageError {
114 ImageError::Decoding(DecodingError::new(ImageFormatHint::Name("SGI".into()), e))
115 }
116}
117
118fn parse_header(
120 header: &[u8; HEADER_FULL_LENGTH],
121) -> Result<(SgiRgbHeaderInfo, &CStr), SgiRgbDecodeError> {
122 if header[..2] != *b"\x01\xda" {
123 return Err(SgiRgbDecodeError::BadMagic);
124 }
125 let storage = header[2];
126 let bpc = header[3];
127 let dimension = u16::from_be_bytes(header[4..6].try_into().unwrap());
128 let xsize = u16::from_be_bytes(header[6..8].try_into().unwrap());
129 let ysize = u16::from_be_bytes(header[8..10].try_into().unwrap());
130 let zsize = u16::from_be_bytes(header[10..12].try_into().unwrap());
131 let _pixmin = u32::from_be_bytes(header[12..16].try_into().unwrap());
132 let _pixmax = u32::from_be_bytes(header[16..20].try_into().unwrap());
133 let _dummy1 = u32::from_be_bytes(header[20..24].try_into().unwrap());
136 let imagename: &[u8] = &header[24..104];
137 let colormap = u32::from_be_bytes(header[104..108].try_into().unwrap());
138 let _dummy2 = &header[108..];
145 if storage >= 2 {
146 return Err(SgiRgbDecodeError::HeaderError);
147 }
148 if bpc == 0 {
149 return Err(SgiRgbDecodeError::HeaderError);
150 }
151 if colormap != 0 {
152 return Err(SgiRgbDecodeError::UnexpectedColormap(colormap));
153 }
154 let (xsize, ysize, zsize) = match dimension {
156 0 | 4.. => {
157 return Err(SgiRgbDecodeError::HeaderError);
158 }
159 1 => (xsize, 1, 1),
160 2 => (xsize, ysize, 1),
161 3 => (xsize, ysize, zsize),
162 };
163 if xsize == 0 || ysize == 0 || zsize == 0 {
164 return Err(SgiRgbDecodeError::ZeroSize(xsize, ysize, zsize));
165 }
166 let name = CStr::from_bytes_until_nul(imagename).map_err(|_| SgiRgbDecodeError::InvalidName)?;
167 if name.to_bytes().iter().any(|x| !x.is_ascii()) {
168 return Err(SgiRgbDecodeError::InvalidName);
169 }
170
171 let color_type = match (bpc, zsize) {
172 (1, 1) => ColorType::L8,
173 (1, 2) => ColorType::La8,
174 (1, 3) => ColorType::Rgb8,
175 (1, 4) => ColorType::Rgba8,
176 (2, 1) => ColorType::L16,
177 (2, 2) => ColorType::La16,
178 (2, 3) => ColorType::Rgb16,
179 (2, 4) => ColorType::Rgba16,
180 _ => {
181 if zsize >= 5 {
182 return Err(SgiRgbDecodeError::UnexpectedZSize(zsize));
183 } else {
184 return Err(SgiRgbDecodeError::HeaderError);
185 }
186 }
187 };
188 Ok((
189 SgiRgbHeaderInfo {
190 is_rle: storage == 1,
191 xsize,
192 ysize,
193 color_type,
194 },
195 name,
196 ))
197}
198
199pub struct SgiDecoder<R> {
201 info: SgiRgbHeaderInfo,
202 reader: R,
203}
204
205impl<R> SgiDecoder<R>
206where
207 R: BufRead,
208{
209 pub fn new(mut r: R) -> Result<SgiDecoder<R>, ImageError> {
211 let mut header = [0u8; HEADER_FULL_LENGTH];
212 r.read_exact(&mut header)?;
213 let (header_info, _name) = parse_header(&header)?;
214
215 Ok(SgiDecoder {
216 info: header_info,
217 reader: r,
218 })
219 }
220}
221
222#[derive(Clone, Copy)]
224struct SgiRgbScanlineState {
225 offset: u32,
227 length: u32,
229 row_id: u16,
231 position: u16,
233 prec_active_line: u32,
237 plane: u8,
239 counter: u8,
242 data_char_hi: u8,
244 at_counter: bool,
246 high_byte: bool,
248}
249
250struct SgiRgbDecodeState {
252 pos: u64,
255 max_active_row: Option<u32>,
257 cursor: usize,
259}
260
261#[inline]
265fn apply_rle16_byte(
266 row: &mut SgiRgbScanlineState,
267 buf_row: &mut [u8],
268 byte: u8,
269 xsize: u16,
270 channels: u8,
271) -> Result<bool, bool> {
272 let mut eor = false;
273 if row.high_byte {
274 row.data_char_hi = byte;
275 row.high_byte = false;
276 } else {
277 if row.at_counter {
278 row.counter = byte;
279 row.at_counter = false;
280 let count = row.counter & (!0x80);
281 if count == 0 {
282 if row.position != xsize {
284 return Err(false);
285 } else {
286 eor = true;
287 }
288 }
289 } else {
290 let data = u16::from_be_bytes([row.data_char_hi, byte]);
291 let mut count = row.counter & (!0x80);
292 if count == 0 {
294 unreachable!();
295 } else if row.counter & 0x80 == 0 {
296 let overflows_row = (count as u16) > xsize || row.position > xsize - (count as u16);
298 if overflows_row {
299 return Err(true);
300 }
301 let write_start: usize = (row.position as usize) * (channels as usize);
303 for i in 0..count {
304 let o = write_start + (row.plane as usize) + (channels as usize) * (i as usize);
305 buf_row[2 * o..2 * o + 2].copy_from_slice(&u16::to_ne_bytes(data));
306 }
307 row.position += count as u16;
308 row.at_counter = true;
309 } else {
310 let overflows_row = row.position > xsize - 1;
312 if overflows_row {
313 return Err(true);
314 }
315
316 let write_start: usize =
317 (row.position as usize) * (channels as usize) + (row.plane as usize);
318 buf_row[2 * write_start..2 * write_start + 2]
319 .copy_from_slice(&u16::to_ne_bytes(data));
320
321 count -= 1;
322 row.position += 1;
323 row.counter = 0x80 | count;
324 row.at_counter = count == 0;
325 }
326 }
327
328 row.high_byte = !row.high_byte;
329 }
330 Ok(eor)
331}
332
333#[inline]
337fn apply_rle8_byte(
338 row: &mut SgiRgbScanlineState,
339 buf_row: &mut [u8],
340 byte: u8,
341 xsize: u16,
342 channels: u8,
343) -> Result<bool, bool> {
344 let mut eor = false;
345 if row.at_counter {
346 row.counter = byte;
347 row.at_counter = false;
348 let count = row.counter & (!0x80);
349 if count == 0 {
350 if row.position != xsize {
352 return Err(false);
353 } else {
354 eor = true;
355 }
356 }
357 } else {
358 let mut count = row.counter & (!0x80);
359 if count == 0 {
361 unreachable!();
362 } else if row.counter & 0x80 == 0 {
363 let overflows_row = (count as u16) > xsize || row.position > xsize - (count as u16);
365 if overflows_row {
366 return Err(true);
367 }
368 let write_start: usize = (row.position as usize) * (channels as usize);
370 for i in 0..count {
371 let o = write_start + (row.plane as usize) + (channels as usize) * (i as usize);
372 buf_row[o] = byte;
373 }
374 row.position += count as u16;
375 row.at_counter = true;
376 } else {
377 let overflows_row = row.position > xsize - 1;
379 if overflows_row {
380 return Err(true);
381 }
382
383 let write_start: usize =
384 (row.position as usize) * (channels as usize) + (row.plane as usize);
385 buf_row[write_start] = byte;
386
387 count -= 1;
388 row.position += 1;
389 row.counter = 0x80 | count;
390 row.at_counter = count == 0;
391 }
392 }
393 Ok(eor)
394}
395
396fn process_rle_segment<const DEEP: bool>(
399 buf_row: &mut [u8],
400 row: &mut SgiRgbScanlineState,
401 xsize: u16,
402 channels: u8,
403 segment: &[u8],
404 ending: bool,
405) -> Result<(), SgiRgbDecodeError> {
406 for (i, byte) in segment.iter().enumerate() {
407 let res = if DEEP {
408 apply_rle16_byte(row, buf_row, *byte, xsize, channels)
409 } else {
410 apply_rle8_byte(row, buf_row, *byte, xsize, channels)
411 };
412 let eor = res.map_err(|overflow| {
413 if overflow {
414 SgiRgbDecodeError::ScanlineOverflow
415 } else {
416 SgiRgbDecodeError::ScanlineUnderflow
417 }
418 })?;
419
420 let expecting_end = i + 1 == segment.len() && ending;
421 if eor != expecting_end {
422 if eor {
423 return Err(SgiRgbDecodeError::ScanlineTooShort);
425 } else {
426 return Err(SgiRgbDecodeError::ScanlineTooLong);
428 }
429 }
430 }
431
432 Ok(())
433}
434
435fn process_data_segment<const DEEP: bool>(
437 buf: &mut [u8],
438 info: SgiRgbHeaderInfo,
439 mut state: SgiRgbDecodeState,
440 rle_table: &mut [SgiRgbScanlineState],
441 segment: &[u8],
442) -> Result<(SgiRgbDecodeState, bool), SgiRgbDecodeError> {
443 let channels = info.color_type.channel_count();
444 let start_pos = state.pos;
445 let end_pos = state.pos + segment.len() as u64;
446
447 while state.cursor < rle_table.len() && rle_table[state.cursor].offset as u64 <= end_pos {
449 rle_table[state.cursor].prec_active_line = state.max_active_row.unwrap_or(u32::MAX);
450 state.max_active_row = Some(state.cursor as u32);
451 state.cursor += 1;
452 }
453
454 let mut prev_row: Option<u32> = None;
455 let Some(mut row_index) = state.max_active_row else {
456 state.pos = end_pos;
458 return Ok((state, false));
459 };
460
461 loop {
462 let row = &mut rle_table[row_index as usize];
463 let row_end = row.offset as u64 + row.length as u64;
464 debug_assert!(row.offset as u64 <= end_pos && row_end > start_pos);
465
466 let prev_active_row = row.prec_active_line;
467
468 let rs_start: usize = if row.offset as u64 > start_pos {
471 ((row.offset as u64) - start_pos) as usize
472 } else {
473 0
474 };
475 let rs_end: usize = if row_end <= end_pos {
476 (row_end - start_pos) as usize
477 } else {
478 segment.len()
479 };
480 let has_ending = row_end <= end_pos;
481 let row_input = &segment[rs_start..rs_end];
482
483 let bpp = if DEEP { 2 } else { 1 };
484 let stride = (info.xsize as usize) * (channels as usize) * bpp;
485 let buf_row = &mut buf[((info.ysize - 1 - row.row_id) as usize) * stride
486 ..((info.ysize - 1 - row.row_id) as usize) * stride + stride];
487
488 process_rle_segment::<DEEP>(buf_row, row, info.xsize, channels, row_input, has_ending)?;
489
490 if has_ending {
491 row.prec_active_line = u32::MAX;
494 if let Some(r) = prev_row {
495 rle_table[r as usize].prec_active_line = prev_active_row;
496 } else if prev_active_row == u32::MAX {
497 state.max_active_row = None;
498 } else {
499 state.max_active_row = Some(prev_active_row);
500 }
501 } else {
502 prev_row = Some(row_index);
503 }
504
505 if prev_active_row != u32::MAX {
506 row_index = prev_active_row;
507 } else {
508 break;
509 }
510 }
511
512 state.pos = end_pos;
513 let done = state.max_active_row.is_none() && state.cursor == rle_table.len();
514 Ok((state, done))
515}
516
517impl<R: BufRead> ImageDecoder for SgiDecoder<R> {
518 fn dimensions(&self) -> (u32, u32) {
519 (self.info.xsize as u32, self.info.ysize as u32)
520 }
521
522 fn color_type(&self) -> ColorType {
523 self.info.color_type
524 }
525
526 fn original_color_type(&self) -> ExtendedColorType {
527 self.info.color_type.into()
528 }
529
530 fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
531 assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
532
533 let channels = self.info.color_type.channel_count();
534 let deep = self.info.color_type.bytes_per_pixel() > channels;
535 if self.info.is_rle {
536 let rle_offset_entries = (channels as u32) * (self.info.ysize as u32);
553
554 let mut rle_table: Vec<SgiRgbScanlineState> = Vec::new();
555 rle_table
558 .try_reserve_exact(rle_offset_entries as usize)
559 .map_err(|_| ImageError::Limits(LimitErrorKind::InsufficientMemory.into()))?;
560 rle_table.resize(
561 rle_offset_entries as usize,
562 SgiRgbScanlineState {
563 offset: 0,
564 length: 0,
565 row_id: 0,
566 position: 0,
567 plane: 0,
568 counter: 0,
569 data_char_hi: 0,
570 prec_active_line: 0,
571 high_byte: deep,
572 at_counter: true,
573 },
574 );
575 for plane in 0..channels {
577 for y in 0..self.info.ysize {
578 let idx = (plane as usize) * (self.info.ysize as usize) + (y as usize);
579 let mut tmp = [0u8; 4];
580 self.reader.read_exact(&mut tmp)?;
581 rle_table[idx].offset = u32::from_be_bytes(tmp);
582 rle_table[idx].row_id = y;
583 rle_table[idx].plane = plane;
584 }
585 }
586 for plane in 0..channels {
588 for y in 0..self.info.ysize {
589 let idx = (plane as usize) * (self.info.ysize as usize) + (y as usize);
590 let mut tmp = [0u8; 4];
591 self.reader.read_exact(&mut tmp)?;
592 rle_table[idx].length = u32::from_be_bytes(tmp);
593
594 let scanline_too_early = rle_table[idx].offset
598 < (HEADER_FULL_LENGTH as u32) + rle_offset_entries * 8;
599 let zero_length = rle_table[idx].length == 0;
600
601 if scanline_too_early || zero_length {
602 return Err(SgiRgbDecodeError::RLERowInvalid(
603 rle_table[idx].offset,
604 rle_table[idx].length,
605 )
606 .into());
607 }
608 }
609 }
610
611 rle_table.sort_unstable_by_key(|f| {
614 (f.offset as u64) << 32 | (f.row_id as u64) << 16 | f.plane as u64
615 });
616
617 let mut rle_state = SgiRgbDecodeState {
619 cursor: 0,
620 max_active_row: None,
621 pos: (HEADER_FULL_LENGTH as u64) + (rle_table.len() as u64) * 8,
622 };
623
624 loop {
625 let buffer = self.reader.fill_buf()?;
626 if buffer.is_empty() {
627 return Err(SgiRgbDecodeError::EarlyEOF.into());
629 }
630
631 let (new_state, done) = if deep {
632 process_data_segment::<true>(buf, self.info, rle_state, &mut rle_table, buffer)?
633 } else {
634 process_data_segment::<false>(
635 buf,
636 self.info,
637 rle_state,
638 &mut rle_table,
639 buffer,
640 )?
641 };
642 if done {
643 return Ok(());
644 }
645 rle_state = new_state;
646
647 let buffer_length = buffer.len();
648 self.reader.consume(buffer_length);
649 }
650 } else {
651 if deep {
653 let bpp = 2 * channels;
654 let width = (bpp as u32) * (self.info.xsize as u32);
656 for plane in 0..channels as usize {
657 for row in buf.chunks_exact_mut(width as usize).rev() {
658 for px in row.chunks_exact_mut(bpp as usize) {
659 let mut tmp = [0_u8; 2];
660 self.reader.read_exact(&mut tmp)?;
661 px[2 * plane..2 * plane + 2]
662 .copy_from_slice(&u16::to_ne_bytes(u16::from_be_bytes(tmp)));
663 }
664 }
665 }
666 } else {
667 let width = (channels as u32) * (self.info.xsize as u32);
668 for plane in 0..channels as usize {
669 for row in buf.chunks_exact_mut(width as usize).rev() {
670 for px in row.chunks_exact_mut(channels as usize) {
671 self.reader.read_exact(&mut px[plane..plane + 1])?;
672 }
673 }
674 }
675 }
676 Ok(())
677 }
678 }
679
680 fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
681 (*self).read_image(buf)
682 }
683
684 fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
685 limits.check_support(&LimitSupport::default())?;
686 let (width, height) = self.dimensions();
687 limits.check_dimensions(width, height)?;
688
689 let max_image_bytes = 8 * (self.info.xsize as u64) * (self.info.ysize as u64);
691 let max_table_bytes =
693 (self.info.ysize as u64) * (std::mem::size_of::<SgiRgbScanlineState>() as u64);
694 let max_bytes = max_image_bytes + max_table_bytes;
696
697 let max_alloc = limits.max_alloc.unwrap_or(u64::MAX);
698 if max_alloc < max_bytes {
699 return Err(ImageError::Limits(LimitError::from_kind(
700 LimitErrorKind::InsufficientMemory,
701 )));
702 }
703 Ok(())
704 }
705}