diff --git a/examples/demo.rs b/examples/demo.rs index e1cb3b04..b5bf8598 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -1,4 +1,4 @@ -use imagine::{png::*, RGB8, RGBA8}; +use imagine::{png::*, RGB16_BE, RGB8, RGBA16_BE, RGBA8, YA16_BE, YA8}; use pixels::{Error, Pixels, SurfaceTexture}; use winit::{ dpi::LogicalSize, @@ -10,10 +10,8 @@ use winit::{ #[allow(dead_code)] fn main() -> Result<(), Error> { const GLIDER_BIG_RAINBOW: &[u8] = include_bytes!("glider-big-rainbow.png"); - const TILES_SHEET: &[u8] = include_bytes!("tiles-sheet.png"); - const EXP2: &[u8] = include_bytes!("exp2_0.png"); - let (mut rgba8, width, height) = match parse_me_a_png_yo(EXP2) { + let (mut rgba8, width, height) = match parse_me_a_png_yo(GLIDER_BIG_RAINBOW) { Ok((rgba8, width, height)) => (rgba8, width, height), Err(e) => panic!("Error: {:?}", e), }; @@ -75,37 +73,160 @@ fn main() -> Result<(), Error> { } fn parse_me_a_png_yo(png: &[u8]) -> Result<(Vec, u32, u32), PngError> { + println!("== Parsing A PNG..."); let mut it = RawPngChunkIter::new(png).map(PngChunk::try_from).filter(critical_errors_only); let ihdr = it.next().ok_or(PngError::NoChunksPresent)??.to_ihdr().ok_or(PngError::FirstChunkNotIHDR)?; println!("{:?}", ihdr); + let mut palette: Option<&[RGB8]> = None; + let idat_peek = it.peekable(); let idat_slice_it = idat_peek.filter_map(|r_chunk| match r_chunk { Ok(PngChunk::IDAT(IDAT { data })) => Some(data), + Ok(PngChunk::PLTE(PLTE { data })) => { + println!("Found a Palette!"); + palette = Some(data); + None + } + Ok(PngChunk::iCCP(_)) => { + println!("iCCP(iCCP {{ .. }})"); + None + } + Ok(other) => { + println!("{:?}", other); + None + } _ => None, }); let mut temp_memory_buffer = vec![0; ihdr.temp_memory_requirement()]; decompress_idat_to_temp_storage(&mut temp_memory_buffer, idat_slice_it)?; // - let mut vec = Vec::new(); - vec.resize((ihdr.width * ihdr.height) as usize, RGBA8::default()); + let mut final_storage = Vec::new(); + final_storage.resize((ihdr.width * ihdr.height) as usize, RGBA8::default()); // match ihdr.pixel_format { + // we already have all four channels PngPixelFormat::RGBA8 => { unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { - vec[(y * ihdr.width + x) as usize] = bytemuck::cast_slice(data)[0]; + let rgba8: RGBA8 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = rgba8; + })? + } + PngPixelFormat::RGBA16 => { + // TODO: some day we might want to display the full 16-bit channels, WGPU + // supports it, we think. + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let rgba16_be: RGBA16_BE = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = + RGBA8 { r: rgba16_be.r[0], g: rgba16_be.g[0], b: rgba16_be.b[0], a: rgba16_be.a[0] }; })? } + + // with rgb only, it adds alpha as fully opaque PngPixelFormat::RGB8 => { unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { - let rgb: RGB8 = bytemuck::cast_slice(data)[0]; - vec[(y * ihdr.width + x) as usize] = RGBA8 { r: rgb.r, g: rgb.g, b: rgb.b, a: 0xFF }; + let rgb8: RGB8 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = rgb8_to_rgba8(rgb8); + })? + } + PngPixelFormat::RGB16 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let rgb16_be: RGB16_BE = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = + RGBA8 { r: rgb16_be.r[0], g: rgb16_be.g[0], b: rgb16_be.b[0], a: 0xFF }; + })? + } + + // grayscale + PngPixelFormat::Y1 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let y1 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = y1_to_rgba8(y1); + })? + } + PngPixelFormat::Y2 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let y2 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = y2_to_rgba8(y2); + })? + } + PngPixelFormat::Y4 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let y4 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = y4_to_rgba8(y4); + })? + } + PngPixelFormat::Y8 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let y8 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = y8_to_rgba8(y8); + })? + } + PngPixelFormat::Y16 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let y8 = bytemuck::cast_slice(data)[0]; + final_storage[(y * ihdr.width + x) as usize] = y8_to_rgba8(y8); + })? + } + + // also grayscale, but now we already have an alpha value we keep + PngPixelFormat::YA8 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let ya8: YA8 = bytemuck::cast_slice(data)[0]; + let mut rgba8 = y8_to_rgba8(ya8.y); + rgba8.a = ya8.a; + final_storage[(y * ihdr.width + x) as usize] = rgba8; + })? + } + PngPixelFormat::YA16 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let ya16_be: YA16_BE = bytemuck::cast_slice(data)[0]; + let mut rgba8 = y8_to_rgba8(ya16_be.y[0]); + rgba8.a = ya16_be.a[0]; + final_storage[(y * ihdr.width + x) as usize] = rgba8; + })? + } + + // indexed color looks into the palette (or black) + PngPixelFormat::I1 | PngPixelFormat::I2 | PngPixelFormat::I4 | PngPixelFormat::I8 => { + unfilter_decompressed_data(ihdr, &mut temp_memory_buffer, |x, y, data| { + let index = data[0] as usize; + let rgb8 = palette + .map(|pal| match pal.get(index) { + Some(thing) => *thing, + None => RGB8::default(), + }) + .unwrap_or_default(); + final_storage[(y * ihdr.width + x) as usize] = + RGBA8 { r: rgb8.r, g: rgb8.g, b: rgb8.b, a: 0xFF }; })? } - _ => return Err(PngError::Illegal_IHDR), } - println!(); // - Ok((vec, ihdr.width, ihdr.height)) + Ok((final_storage, ihdr.width, ihdr.height)) +} + +fn y1_to_rgba8(y1: u8) -> RGBA8 { + let y2 = y1 | (y1 << 1); + y2_to_rgba8(y2) +} + +fn y2_to_rgba8(y2: u8) -> RGBA8 { + let y4 = y2 | (y2 << 2); + y4_to_rgba8(y4) +} + +fn y4_to_rgba8(y4: u8) -> RGBA8 { + let y8 = y4 | (y4 << 4); + y8_to_rgba8(y8) +} + +fn y8_to_rgba8(y8: u8) -> RGBA8 { + let y = y8 as f32; + RGBA8 { r: (0.299 * y) as u8, g: (0.587 * y) as u8, b: (0.114 * y) as u8, a: 0xFF } +} + +fn rgb8_to_rgba8(rgb8: RGB8) -> RGBA8 { + RGBA8 { r: rgb8.r, g: rgb8.g, b: rgb8.b, a: 0xFF } } diff --git a/examples/exp2_0.png b/examples/exp2_0.png deleted file mode 100644 index addc212a..00000000 Binary files a/examples/exp2_0.png and /dev/null differ diff --git a/examples/the_mandrill.png b/examples/the_mandrill.png deleted file mode 100644 index 593e3846..00000000 Binary files a/examples/the_mandrill.png and /dev/null differ diff --git a/examples/tiles-sheet.png b/examples/tiles-sheet.png deleted file mode 100644 index e202b1a7..00000000 Binary files a/examples/tiles-sheet.png and /dev/null differ diff --git a/src/pixel_formats.rs b/src/pixel_formats.rs index 0070bb0d..d76b65a9 100644 --- a/src/pixel_formats.rs +++ b/src/pixel_formats.rs @@ -37,7 +37,7 @@ //! bits, and to *increase* bit depth you should use the current bit pattern //! as the top X many bits, and then copy that bit pattern down however many //! times is required to fill in all newly added bits. -//! * Alternately, you can use floats: in this case, increaseing or decreasing +//! * Alternately, you can use floats: in this case, increasing or decreasing //! the bit depth uses the same system. Convert the integer value to a float //! and divide by the maximum value of the starting bit depth (giving a //! normalized value), then multiply by the maximum of the target bit depth, diff --git a/src/png/unfilter.rs b/src/png/unfilter.rs index 56e12ed6..9fc25103 100644 --- a/src/png/unfilter.rs +++ b/src/png/unfilter.rs @@ -150,15 +150,12 @@ where .map(|(r_y, (f, pixels))| (r_y as u32, f, pixels)) }; - extern crate std; - // The first line of each image has special handling because filters can // refer to the previous line, but for the first line the "previous line" is // an implied zero. let mut b_pixels = if let Some((reduced_y, f, pixels)) = row_iter.next() { let mut p_it = pixels.chunks_exact_mut(filter_chunk_size).enumerate().map(|(r_x, d)| (r_x as u32, d)); - std::print!("{}, ", f); match f { 1 => { // Sub @@ -220,17 +217,16 @@ where }; for (reduced_y, f, pixels) in row_iter { - let mut line_it = + let mut p_it = pixels.chunks_exact_mut(filter_chunk_size).enumerate().map(|(r_x, d)| (r_x as u32, d)); let mut b_it = b_pixels.chunks_exact(filter_chunk_size); - std::print!("{}, ", f); match f { 1 => { // Sub - let (reduced_x, mut pixel): (u32, &mut [u8]) = line_it.next().unwrap(); + let (reduced_x, mut pixel): (u32, &mut [u8]) = p_it.next().unwrap(); send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); let mut a_pixel = pixel; - while let Some((reduced_x, pixel)) = line_it.next() { + while let Some((reduced_x, pixel)) = p_it.next() { a_pixel.iter().copied().zip(pixel.iter_mut()).for_each(|(a, p)| *p = p.wrapping_add(a)); send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); // @@ -239,7 +235,7 @@ where } 2 => { // Up - for ((reduced_x, pixel), b_pixel) in line_it.zip(b_it) { + for ((reduced_x, pixel), b_pixel) in p_it.zip(b_it) { b_pixel.iter().copied().zip(pixel.iter_mut()).for_each(|(b, p)| *p = p.wrapping_add(b)); // send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); @@ -247,21 +243,20 @@ where } 3 => { // Average - let mut ab_it = line_it.zip(b_it); - let ((reduced_x, mut pixel), b_pixel) = ab_it.next().unwrap(); + let mut pb_it = p_it.zip(b_it).map(|((r_x, p), b)| (r_x, p, b)); + let (reduced_x, pixel, b_pixel) = pb_it.next().unwrap(); pixel .iter_mut() .zip(b_pixel.iter().copied()) .for_each(|(p, b)| *p = p.wrapping_add(b / 2)); send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); - let mut a_pixel = pixel; - while let Some(((reduced_x, pixel), b_pixel)) = ab_it.next() { - a_pixel - .iter() - .copied() - .zip(b_pixel.iter().copied()) - .zip(pixel.iter_mut()) - .for_each(|((a, b), p)| *p = p.wrapping_add(((a as usize + b as usize) / 2) as u8)); + let mut a_pixel: &[u8] = pixel; + while let Some((reduced_x, pixel, b_pixel)) = pb_it.next() { + a_pixel.iter().copied().zip(b_pixel.iter().copied()).zip(pixel.iter_mut()).for_each( + |((a, b), p)| { + *p = p.wrapping_add(((a as u32 + b as u32) / 2) as u8); + }, + ); send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); // a_pixel = pixel; @@ -269,16 +264,15 @@ where } 4 => { // Paeth - let mut ab_it = line_it.zip(b_it); - let ((reduced_x, mut pixel), b_pixel) = ab_it.next().unwrap(); - pixel - .iter_mut() - .zip(b_pixel.iter().copied()) - .for_each(|(p, b)| *p = p.wrapping_add(b / 2)); + let mut pb_it = p_it.zip(b_it).map(|((r_x, p), b)| (r_x, p, b)); + let (reduced_x, pixel, b_pixel) = pb_it.next().unwrap(); + pixel.iter_mut().zip(b_pixel.iter().copied()).for_each(|(p, b)| { + *p = p.wrapping_add(paeth_predict(0, b, 0)); + }); send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); let mut a_pixel = pixel; let mut c_pixel = b_pixel; - while let Some(((reduced_x, pixel), b_pixel)) = ab_it.next() { + while let Some((reduced_x, pixel, b_pixel)) = pb_it.next() { a_pixel .iter() .copied() @@ -295,7 +289,7 @@ where } } _ => { - for (reduced_x, pixel) in line_it { + for (reduced_x, pixel) in p_it { // None send_out_pixel(header, image_level, reduced_x, reduced_y, pixel, &mut op); }