Skip to content

Commit

Permalink
it works.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lokathor committed Dec 5, 2021
1 parent 1a7beb5 commit 2322b76
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 39 deletions.
145 changes: 133 additions & 12 deletions examples/demo.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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),
};
Expand Down Expand Up @@ -75,37 +73,160 @@ fn main() -> Result<(), Error> {
}

fn parse_me_a_png_yo(png: &[u8]) -> Result<(Vec<RGBA8>, 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 }
}
Binary file removed examples/exp2_0.png
Binary file not shown.
Binary file removed examples/the_mandrill.png
Binary file not shown.
Binary file removed examples/tiles-sheet.png
Binary file not shown.
2 changes: 1 addition & 1 deletion src/pixel_formats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
46 changes: 20 additions & 26 deletions src/png/unfilter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
//
Expand All @@ -239,46 +235,44 @@ 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);
}
}
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;
}
}
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()
Expand All @@ -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);
}
Expand Down

0 comments on commit 2322b76

Please sign in to comment.