|
| 1 | +use tiff::decoder::{BufferLayoutPreference, Decoder, DecodingBuffer, DecodingResult}; |
| 2 | + |
| 3 | +fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 4 | + let Some(image) = std::env::args_os().nth(1) else { |
| 5 | + eprintln!("Usage: decode FILE"); |
| 6 | + return Ok(()); |
| 7 | + }; |
| 8 | + |
| 9 | + let file = std::fs::File::open(image)?; |
| 10 | + let io = std::io::BufReader::new(file); |
| 11 | + let mut reader = Decoder::new(io)?; |
| 12 | + |
| 13 | + let mut data = DecodingResult::I8(vec![]); |
| 14 | + |
| 15 | + for i in 0u32.. { |
| 16 | + let colortype = reader.colortype()?; |
| 17 | + let dimensions = reader.dimensions()?; |
| 18 | + let layout = reader.read_image_to_buffer(&mut data)?; |
| 19 | + |
| 20 | + debug_planes(i, &mut data, &layout, &colortype, dimensions); |
| 21 | + |
| 22 | + if !reader.more_images() { |
| 23 | + break; |
| 24 | + } |
| 25 | + |
| 26 | + reader.next_image()? |
| 27 | + } |
| 28 | + |
| 29 | + Ok(()) |
| 30 | +} |
| 31 | + |
| 32 | +fn debug_planes( |
| 33 | + index: u32, |
| 34 | + data: &mut DecodingResult, |
| 35 | + layout: &BufferLayoutPreference, |
| 36 | + colortype: &tiff::ColorType, |
| 37 | + (width, height): (u32, u32), |
| 38 | +) { |
| 39 | + let (depth, mut tupltype) = match colortype { |
| 40 | + tiff::ColorType::Gray(_) => (1, "GRAYSCALE"), |
| 41 | + tiff::ColorType::RGB(_) => (3, "RGB"), |
| 42 | + tiff::ColorType::RGBA(_) => (4, "RGB_ALPHA"), |
| 43 | + tiff::ColorType::Palette(_) => (1, "PALETTE"), |
| 44 | + tiff::ColorType::CMYK(_) => (4, "CMYK"), |
| 45 | + tiff::ColorType::Multiband { num_samples: 2, .. } => (2, "GRAYSCALE_ALPHA"), |
| 46 | + _ => { |
| 47 | + eprintln!("Unsupported color type for PAM output: {:?}", colortype); |
| 48 | + return; |
| 49 | + } |
| 50 | + }; |
| 51 | + |
| 52 | + let maxval = match data.as_buffer(0) { |
| 53 | + DecodingBuffer::U8(_) => u64::from(u8::MAX), |
| 54 | + DecodingBuffer::U16(_) => u64::from(u16::MAX), |
| 55 | + DecodingBuffer::U32(_) => u64::from(u32::MAX), |
| 56 | + DecodingBuffer::U64(_) => u64::MAX, |
| 57 | + // Eh, this is a good effort I guess. |
| 58 | + DecodingBuffer::I8(items) => { |
| 59 | + items.iter_mut().for_each(|v| *v = 0.max(*v)); |
| 60 | + u64::from(i8::MAX as u8) |
| 61 | + } |
| 62 | + DecodingBuffer::I16(items) => { |
| 63 | + items.iter_mut().for_each(|v| *v = 0.max(*v)); |
| 64 | + u64::from(i16::MAX as u16) |
| 65 | + } |
| 66 | + DecodingBuffer::I32(items) => { |
| 67 | + items.iter_mut().for_each(|v| *v = 0.max(*v)); |
| 68 | + u64::from(i32::MAX as u32) |
| 69 | + } |
| 70 | + DecodingBuffer::I64(items) => { |
| 71 | + items.iter_mut().for_each(|v| *v = 0.max(*v)); |
| 72 | + i64::MAX as u64 |
| 73 | + } |
| 74 | + // Not supported by PAM. |
| 75 | + DecodingBuffer::F16(_) | DecodingBuffer::F32(_) | DecodingBuffer::F64(_) => return, |
| 76 | + }; |
| 77 | + |
| 78 | + let byte_len = if layout.planes > 1 { |
| 79 | + tupltype = "GRAYSCALE"; |
| 80 | + layout.plane_stride.unwrap().get() |
| 81 | + } else { |
| 82 | + data.as_buffer(0).byte_len() |
| 83 | + }; |
| 84 | + |
| 85 | + // Write out this image's planes to PAM files. |
| 86 | + let header = format!("P7\nWIDTH {width}\nHEIGHT {height}\nDEPTH {depth}\nMAXVAL {maxval}\nTUPLTYPE {tupltype}\nENDHDR\n"); |
| 87 | + |
| 88 | + for (plane, plane_data) in data |
| 89 | + .as_buffer(0) |
| 90 | + .as_bytes_mut() |
| 91 | + .chunks_exact(byte_len) |
| 92 | + .enumerate() |
| 93 | + { |
| 94 | + let filename = format!("image_{index:03}_plane_{plane:03}.pam"); |
| 95 | + let mut file = std::fs::File::create(filename).expect("Failed to create output file"); |
| 96 | + |
| 97 | + use std::io::Write; |
| 98 | + file.write_all(header.as_bytes()) |
| 99 | + .expect("Failed to write PAM header"); |
| 100 | + file.write_all(plane_data) |
| 101 | + .expect("Failed to write PAM data"); |
| 102 | + } |
| 103 | +} |
0 commit comments