Skip to content

Commit

Permalink
Switch to parsenic for decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
AldaronLau committed Nov 6, 2024
1 parent c1e1440 commit 4c88919
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 174 deletions.
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "png_pong"
version = "0.9.2"
version = "0.9.3"
license = "Apache-2.0 OR Zlib"
description = "A pure Rust PNG/APNG encoder & decoder"
repository = "https://github.com/AldaronLau/png_pong"
Expand Down Expand Up @@ -30,6 +30,12 @@ version = "0.3"
version = "0.8"
features = ["simd"]

[dependencies.parsenic]
version = "0.2"

[dependencies.traitful]
version = "0.3"

[dev-dependencies.criterion]
version = "0.5"

Expand Down
33 changes: 26 additions & 7 deletions src/chunk/bkgd.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::io::{Read, Write};

use parsenic::{be::Read as _, Read as _, Reader};

use super::{Chunk, DecoderError, EncoderError};
use crate::{consts, decoder::Parser, encoder::Enc};

Expand All @@ -19,13 +21,30 @@ impl Background {
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
match parse.len() {
1 => Ok(Chunk::Background(Background::Palette(parse.u8()?))),
2 => Ok(Chunk::Background(Background::Gray(parse.u16()?))),
6 => Ok(Chunk::Background(Background::Rgb(
parse.u16()?,
parse.u16()?,
parse.u16()?,
))),
1 => {
let buffer: [u8; 1] = parse.bytes()?;
let mut reader = Reader::new(&buffer);
let index = reader.u8()?;

reader.end().unwrap();
Ok(Chunk::Background(Background::Palette(index)))
}
2 => {
let buffer: [u8; 2] = parse.bytes()?;
let mut reader = Reader::new(&buffer);
let value = reader.u16()?;

reader.end().unwrap();
Ok(Chunk::Background(Background::Gray(value)))
}
6 => {
let buffer: [u8; 6] = parse.bytes()?;
let mut reader = Reader::new(&buffer);
let [r, g, b] = [reader.u16()?, reader.u16()?, reader.u16()?];

reader.end().unwrap();
Ok(Chunk::Background(Background::Rgb(r, g, b)))
}
_ => Err(DecoderError::ChunkLength(consts::BACKGROUND)),
}
}
Expand Down
81 changes: 52 additions & 29 deletions src/chunk/ihdr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use std::io::{Read, Write};
use std::{
io::{Read, Write},
num::NonZeroU32,
};

use parsenic::{be::Read as _, Read as _, Reader};

use crate::{
chunk::Chunk, consts, decode::Error as DecoderError, decoder::Parser,
Expand Down Expand Up @@ -109,40 +114,58 @@ impl ImageHeader {
pub(crate) fn parse<R: Read>(
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
// Read file
let width = parse.u32()?;
let height = parse.u32()?;
if width == 0 || height == 0 {
return Err(DecoderError::ImageDimensions);
}
let bit_depth = parse.u8()?;
if bit_depth == 0 || bit_depth > 16 {
return Err(DecoderError::BitDepth(bit_depth));
}
let color_type = match parse.u8()? {
0 => ColorType::Grey,
2 => ColorType::Rgb,
3 => ColorType::Palette,
4 => ColorType::GreyAlpha,
6 => ColorType::Rgba,
c => return Err(DecoderError::ColorType(c)),
let buffer: [u8; 13] = parse.bytes()?;
let mut reader = Reader::new(&buffer);
let width = NonZeroU32::new(reader.u32()?)
.ok_or(DecoderError::ImageDimensions)?
.get();
let height = NonZeroU32::new(reader.u32()?)
.ok_or(DecoderError::ImageDimensions)?
.get();
let bit_depth = {
let bit_depth = reader.u8()?;

(1..=16)
.contains(&bit_depth)
.then_some(bit_depth)
.ok_or(DecoderError::BitDepth(bit_depth))?
};
color_type.check_png_color_validity(bit_depth)?;
if parse.u8()? != 0 {
/* error: only compression method 0 is allowed in the
* specification */
return Err(DecoderError::CompressionMethod);
}
if parse.u8()? != 0 {
/* error: only filter method 0 is allowed in the specification */
return Err(DecoderError::FilterMethod);
}
let interlace = match parse.u8()? {
let color_type = {
let color_type = match reader.u8()? {
0 => ColorType::Grey,
2 => ColorType::Rgb,
3 => ColorType::Palette,
4 => ColorType::GreyAlpha,
6 => ColorType::Rgba,
c => return Err(DecoderError::ColorType(c)),
};

color_type.check_png_color_validity(bit_depth)?;
color_type
};
let _compression_method = {
let compression_method = reader.u8()?;

// error: only compression method 0 is allowed in the specification
(compression_method == 0)
.then_some(compression_method)
.ok_or(DecoderError::CompressionMethod)?
};
let _filter_method = {
let filter_method = reader.u8()?;

// error: only filter method 0 is allowed in the specification
(filter_method == 0)
.then_some(filter_method)
.ok_or(DecoderError::FilterMethod)?
};
let interlace = match reader.u8()? {
0 => false,
1 => true,
_ => return Err(DecoderError::InterlaceMethod),
};

reader.end().unwrap();
Ok(Chunk::ImageHeader(Self {
width,
height,
Expand Down
49 changes: 35 additions & 14 deletions src/chunk/itxt.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::io::{Read, Write};

use parsenic::{Read as _, Reader};

use super::Chunk;
use crate::{
consts, decode::Error as DecoderError, decoder::Parser,
encode::Error as EncoderError, encoder::Enc, zlib,
encode::Error as EncoderError, encoder::Enc, parsing::Read as _, zlib,
};

/// International Text Chunk Data (iTXt)
Expand All @@ -29,25 +31,44 @@ impl InternationalText {
pub(crate) fn parse<R: Read>(
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
let key = parse.str()?;
if key.is_empty() || key.len() > 79 {
return Err(DecoderError::KeySize(key.len()));
}
let compressed = parse.u8()? != 0;
if parse.u8()? != 0 {
return Err(DecoderError::CompressionMethod);
}
let langtag = parse.str()?;
let transkey = parse.str()?;
let data = parse.vec(
parse.len() - (key.len() + langtag.len() + transkey.len() + 5),
)?;
let buffer = parse.raw()?;
let mut reader = Reader::new(&buffer);
let key = {
let key = reader.strz()?;
let key_len = key.len();

(1..=79)
.contains(&key_len)
.then_some(key)
.ok_or(DecoderError::KeySize(key_len))?
};
let compressed = match reader.u8()? {
0 => false,
1 => true,
// FIXME: More specific error
_ => return Err(DecoderError::CompressionMethod),
};
let _compression_method = {
let compression_method = reader.u8()?;

(compression_method == 0)
.then_some(compression_method)
.ok_or(DecoderError::CompressionMethod)?
};
let langtag = reader.strz()?;
let transkey = reader.strz()?;
let data = reader
.slice(
parse.len() - (key.len() + langtag.len() + transkey.len() + 5),
)?
.to_vec();
let val = if compressed {
String::from_utf8_lossy(&zlib::decompress(&data)?).to_string()
} else {
String::from_utf8_lossy(&data).to_string()
};

reader.end().unwrap();
Ok(Chunk::InternationalText(InternationalText {
key,
langtag,
Expand Down
12 changes: 8 additions & 4 deletions src/chunk/phys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::io::{Read, Write};

use parsenic::{be::Read as _, Read as _, Reader};

use super::{Chunk, DecoderError, EncoderError};
use crate::{consts, decoder::Parser, encoder::Enc};

Expand Down Expand Up @@ -29,15 +31,17 @@ impl Physical {
pub(crate) fn parse<R: Read>(
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
// 9 bytes
let ppu_x = parse.u32()?;
let ppu_y = parse.u32()?;
let is_meter = match parse.u8()? {
let buffer: [u8; 9] = parse.bytes()?;
let mut reader = Reader::new(&buffer);
let ppu_x = reader.u32()?;
let ppu_y = reader.u32()?;
let is_meter = match reader.u8()? {
0 => false,
1 => true,
_ => return Err(DecoderError::PhysUnits),
};

reader.end().unwrap();
Ok(Chunk::Physical(Physical {
ppu_x,
ppu_y,
Expand Down
20 changes: 13 additions & 7 deletions src/chunk/plte.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::io::{Read, Write};

use parsenic::{Read as _, Reader};
use pix::rgb::{Rgb, SRgb8};

use super::{Chunk, DecoderError, EncoderError};
Expand All @@ -18,13 +19,18 @@ impl Palette {
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
parse.set_palette();
let mut palette = Vec::new();
for _ in 0..(parse.len() / 3) {
let red = parse.u8()?;
let green = parse.u8()?;
let blue = parse.u8()?;
palette.push(SRgb8::new(red, green, blue));
}

let buffer = parse.raw()?;
let mut reader = Reader::new(&buffer);
let palette = (0..(parse.len() / 3))
.map(|_| -> Result<_, DecoderError> {
let [r, g, b] = [reader.u8()?, reader.u8()?, reader.u8()?];

Ok(SRgb8::new(r, g, b))
})
.collect::<Result<_, _>>()?;

reader.end().unwrap();
Ok(Chunk::Palette(Palette { palette }))
}

Expand Down
25 changes: 19 additions & 6 deletions src/chunk/text.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::io::{Read, Write};

use parsenic::{Read as _, Reader};

use super::{Chunk, DecoderError, EncoderError};
use crate::{consts, decoder::Parser, encoder::Enc};
use crate::{consts, decoder::Parser, encoder::Enc, parsing::Read as _};

/// Non-International Text Chunk Data (tEXt and zTXt)
#[derive(Clone, Debug)]
Expand All @@ -19,12 +21,23 @@ impl Text {
pub(crate) fn parse<R: Read>(
parse: &mut Parser<R>,
) -> Result<Chunk, DecoderError> {
let key = parse.str()?;
if key.is_empty() || key.len() > 79 {
return Err(DecoderError::KeySize(key.len()));
}
let val = parse.string(parse.len() - (key.len() + 1))?;
let buffer = parse.raw()?;
let mut reader = Reader::new(&buffer);
let key = {
let key = reader.strz()?;
let key_len = key.len();

(1..=79)
.contains(&key_len)
.then_some(key)
.ok_or(DecoderError::KeySize(key_len))?
};
let val = String::from_utf8_lossy(
reader.slice(parse.len() - (key.len() + 1))?,
)
.into_owned();

reader.end().unwrap();
Ok(Chunk::Text(Text { key, val }))
}

Expand Down
Loading

0 comments on commit 4c88919

Please sign in to comment.