PNG initial code
10
LICENSE
|
@ -1,6 +1,10 @@
|
|||
Some of the data contained in tests/data and benches/data is
|
||||
Copyright © Bungie Software. I do not own them, but am permitted to
|
||||
redistribute them. Everything else is:
|
||||
Some of the data contained in tests/data is Copyright © Bungie Software. I do
|
||||
not own them, but am permitted to redistribute them. PngSuite is included in
|
||||
the test suite as well, its license is under tests/data/png/LICENSE. Everything
|
||||
else, including all source code is licensed as Public Domain as stated below.
|
||||
All program sources are licensed as such, are fully original, and may be used
|
||||
freely. If you wish to contribute to the project you MUST explicitly accept the
|
||||
terms and conditions and put your code as well under this license.
|
||||
|
||||
To the extent possible under law, I, Alison Sanderson, have waived all
|
||||
copyright and related or neighboring rights to this Document as described by
|
||||
|
|
|
@ -38,7 +38,7 @@ macro_rules! c_enum
|
|||
$(#[$outer:meta])*
|
||||
$vi:vis enum $t:ident: $ti:ty
|
||||
{
|
||||
$($(#[$inner:meta])* $va:expr => $en:ident,)+
|
||||
$($va:expr => $en:ident,)+
|
||||
}
|
||||
) => {
|
||||
$(#[$outer])*
|
||||
|
@ -48,16 +48,25 @@ macro_rules! c_enum
|
|||
$($en,)+
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl $t
|
||||
{
|
||||
/// Returns, if representable, the variant of `Self` from `n`.
|
||||
$vi fn from_repr(n: $ti) -> Result<Self, ReprError>
|
||||
{
|
||||
match n {
|
||||
$($(#[$inner])* $va => Ok($t::$en),)+
|
||||
$($va => Ok($t::$en),)+
|
||||
n => Err(ReprError::new(n))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the representation of this type.
|
||||
$vi fn to_repr(self) -> $ti
|
||||
{
|
||||
match self {
|
||||
$($t::$en => $va,)+
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod machdr;
|
|||
pub mod map;
|
||||
pub mod phy;
|
||||
pub mod pict;
|
||||
pub mod png;
|
||||
pub mod ppm;
|
||||
pub mod shp;
|
||||
pub mod snd;
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
//! Portable Network Graphics loader.
|
||||
|
||||
use crate::{durandal::{bin::*, cksum, err::*, image},
|
||||
marathon::defl};
|
||||
|
||||
pub fn load_png(b: &[u8]) -> ResultS<image::Image16>
|
||||
{
|
||||
read_data! {
|
||||
8, BE in b =>
|
||||
magic = u8[0..8] slice;
|
||||
}
|
||||
|
||||
if magic != b"\x89PNG\r\n\x1A\n" {
|
||||
bail!("invalid magic number");
|
||||
}
|
||||
|
||||
let mut p = 8;
|
||||
|
||||
// header chunk always comes first
|
||||
let (len, cnk) = load_chunk(&b[p..])?;
|
||||
p += len;
|
||||
|
||||
if cnk.typ != b"IHDR" {
|
||||
bail!("IHDR not first chunk");
|
||||
}
|
||||
|
||||
let hdr = load_cnk_ihdr(cnk)?;
|
||||
|
||||
let mut pal = None;
|
||||
let mut bmp = Vec::new();
|
||||
|
||||
// loop over all the chunks, mainly grabbing IDAT chunks
|
||||
loop {
|
||||
let (len, cnk) = load_chunk(&b[p..])?;
|
||||
p += len;
|
||||
|
||||
match &cnk.typ.0 {
|
||||
b"PLTE" => pal = Some(load_cnk_plte(cnk)?),
|
||||
b"IDAT" => bmp.extend_from_slice(&cnk.dat),
|
||||
b"IEND" => break,
|
||||
_ => {
|
||||
if !cnk.aux {
|
||||
bail!("no handler for vital chunk");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// inflate the bitmap
|
||||
let bmp = &bmp[defl::load_zlib_header(&bmp)?..];
|
||||
let bmp = defl::load_deflate(bmp)?.1;
|
||||
|
||||
let im = interlaced_adam7(&hdr, &bmp)?;
|
||||
|
||||
dbg!(im);
|
||||
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn interlaced_adam7(hdr: &Header, i_bmp: &[u8]) -> ResultS<image::Image16>
|
||||
{
|
||||
const INCR_X: [usize; 7] = [8, 8, 4, 4, 2, 2, 1];
|
||||
const INCR_Y: [usize; 7] = [8, 8, 8, 4, 4, 2, 2];
|
||||
const BEGN_X: [usize; 7] = [0, 4, 0, 2, 0, 1, 0];
|
||||
const BEGN_Y: [usize; 7] = [0, 0, 4, 0, 2, 0, 1];
|
||||
|
||||
let mut im = image::Image16::new_empty(hdr.width, hdr.heigh);
|
||||
let mut p = 0;
|
||||
|
||||
for pass in 0..7 {
|
||||
let w = hdr.width / INCR_X[pass];
|
||||
let h = hdr.heigh / INCR_Y[pass];
|
||||
|
||||
if w == 0 || h == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scanl = std::cmp::max(w * usize::from(hdr.pbits), 8) / 8 + 1;
|
||||
|
||||
let pp = p;
|
||||
p += scanl * h;
|
||||
|
||||
let mut last = Vec::new();
|
||||
let block = ok!(i_bmp.get(pp..p), "not enough data")?;
|
||||
|
||||
for y in 0..h {
|
||||
let beg = y * scanl;
|
||||
let end = beg + scanl;
|
||||
|
||||
let llin = if last.is_empty() {None} else {Some(last.as_slice())};
|
||||
|
||||
let line = &block[beg..end];
|
||||
let line = unfilter_line(hdr, line, llin)?;
|
||||
|
||||
last = line;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(im)
|
||||
}
|
||||
|
||||
pub fn unfilter_line(hdr: &Header, line: &[u8], last: Option<&[u8]>)
|
||||
-> ResultS<Vec<u8>>
|
||||
{
|
||||
let filt = FilterType::from_repr(line[0])?;
|
||||
let line = &line[1..];
|
||||
|
||||
let mut out = line.to_vec();
|
||||
|
||||
let pbytes = usize::from(std::cmp::max(hdr.pbits / 8, 1));
|
||||
|
||||
match filt {
|
||||
FilterType::None => {}
|
||||
FilterType::Sub => {
|
||||
for x in 0..line.len() {
|
||||
if x > pbytes {
|
||||
out[x] = out[x].wrapping_add(out[x - pbytes]);
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterType::Up => {
|
||||
if let Some(last) = last {
|
||||
for x in 0..line.len() {
|
||||
out[x] = out[x].wrapping_add(last[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterType::Average => {
|
||||
for x in 0..line.len() {
|
||||
let mut n = 0;
|
||||
if x > pbytes {
|
||||
n = out[x - pbytes];
|
||||
}
|
||||
if let Some(last) = last {
|
||||
n = n.wrapping_add(last[x]);
|
||||
}
|
||||
out[x] = out[x].wrapping_add(n / 2);
|
||||
}
|
||||
}
|
||||
FilterType::Paeth => {
|
||||
for x in 0..line.len() {
|
||||
let u = if x > pbytes {out[x - pbytes]} else {0};
|
||||
let v = if let Some(last) = last {last[x]} else {0};
|
||||
let s = if let Some(last) = last {
|
||||
if x > pbytes {last[x - pbytes]} else {0}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
out[x] = out[x].wrapping_add(paeth_predictor(u, v, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// The Paeth prediction function.
|
||||
pub fn paeth_predictor(a: u8, b: u8, c: u8) -> u8
|
||||
{
|
||||
let ia = i16::from(a);
|
||||
let ib = i16::from(b);
|
||||
let ic = i16::from(c);
|
||||
|
||||
let p = ia + ib - ic;
|
||||
|
||||
let pa = (p - ia).abs();
|
||||
let pb = (p - ib).abs();
|
||||
let pc = (p - ic).abs();
|
||||
|
||||
if pa <= pb && pa <= pc {
|
||||
a
|
||||
} else if pb <= pc {
|
||||
b
|
||||
} else {
|
||||
c
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_chunk(b: &[u8]) -> ResultS<(usize, Chunk)>
|
||||
{
|
||||
read_data! {
|
||||
4, BE in b =>
|
||||
len = u32[0] usize;
|
||||
}
|
||||
|
||||
read_data! {
|
||||
len + 12, BE in b =>
|
||||
typ = Ident[4];
|
||||
dat = u8[8..8 + len] slice;
|
||||
crc = u32[8 + len];
|
||||
}
|
||||
|
||||
let computed_crc = cksum::crc32(&typ.0, !0);
|
||||
let computed_crc = cksum::crc32(dat, !computed_crc);
|
||||
|
||||
if crc != computed_crc {
|
||||
bail!("bad CRC in chunk");
|
||||
}
|
||||
|
||||
let dat = dat.to_vec();
|
||||
let aux = typ.0[0] & 0x20 != 0;
|
||||
let cpy = typ.0[3] & 0x20 != 0;
|
||||
|
||||
Ok((len + 12, Chunk{typ, dat, aux, cpy}))
|
||||
}
|
||||
|
||||
pub fn load_cnk_ihdr(cnk: Chunk) -> ResultS<Header>
|
||||
{
|
||||
read_data! {
|
||||
13, BE in cnk.dat =>
|
||||
width = u32[0] usize;
|
||||
heigh = u32[4] usize;
|
||||
depth = u8[8];
|
||||
ctype = u8[9];
|
||||
compr = u8[10];
|
||||
filtr = u8[11];
|
||||
xlace = u8[12];
|
||||
}
|
||||
|
||||
if compr != 0 {bail!("unrecognized compression method");}
|
||||
if filtr != 0 {bail!("unrecognized filter method");}
|
||||
if width == 0 {bail!("zero width");}
|
||||
if heigh == 0 {bail!("zero height");}
|
||||
|
||||
let depth = BitDepth::from_repr(depth)?;
|
||||
let ctype = ColorType::from_repr(ctype)?;
|
||||
let xlace = XlaceMode::from_repr(xlace)?;
|
||||
let pbits = depth.to_repr();
|
||||
let pbits = match ctype {
|
||||
ColorType::Grayscale => pbits,
|
||||
ColorType::Truecolor => pbits * 3,
|
||||
ColorType::Indexed => pbits,
|
||||
ColorType::GrayscaleAlpha => pbits * 2,
|
||||
ColorType::TruecolorAlpha => pbits * 4,
|
||||
};
|
||||
|
||||
Ok(Header{width, heigh, pbits, depth, ctype, xlace})
|
||||
}
|
||||
|
||||
pub fn load_cnk_plte(cnk: Chunk) -> ResultS<ColorMap>
|
||||
{
|
||||
let mut colors = [image::Color8::new(0, 0, 0); 256];
|
||||
let mut p = 0;
|
||||
|
||||
for col in colors.iter_mut() {
|
||||
read_data! {
|
||||
p + 3, BE in cnk.dat =>
|
||||
r = u8[p];
|
||||
g = u8[p + 1];
|
||||
b = u8[p + 2];
|
||||
}
|
||||
|
||||
p += 3;
|
||||
|
||||
*col = image::Color8::new(r, g, b);
|
||||
|
||||
if p == cnk.dat.len() {
|
||||
break;
|
||||
} else if p > cnk.dat.len() {
|
||||
bail!("bad PLTE length");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(colors)
|
||||
}
|
||||
|
||||
pub type ColorMap = [image::Color8; 256];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Header
|
||||
{
|
||||
width: usize,
|
||||
heigh: usize,
|
||||
pbits: u8,
|
||||
depth: BitDepth,
|
||||
ctype: ColorType,
|
||||
xlace: XlaceMode,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Chunk
|
||||
{
|
||||
typ: Ident,
|
||||
dat: Vec<u8>,
|
||||
aux: bool,
|
||||
cpy: bool,
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
enum BitDepth: u8
|
||||
{
|
||||
1 => Bits1,
|
||||
2 => Bits2,
|
||||
4 => Bits4,
|
||||
8 => Bits8,
|
||||
16 => Bits16,
|
||||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
enum ColorType: u8
|
||||
{
|
||||
0 => Grayscale,
|
||||
2 => Truecolor,
|
||||
3 => Indexed,
|
||||
4 => GrayscaleAlpha,
|
||||
6 => TruecolorAlpha,
|
||||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
enum FilterType: u8
|
||||
{
|
||||
0 => None,
|
||||
1 => Sub,
|
||||
2 => Up,
|
||||
3 => Average,
|
||||
4 => Paeth,
|
||||
}
|
||||
}
|
||||
|
||||
c_enum! {
|
||||
#[derive(Debug)]
|
||||
enum XlaceMode: u8
|
||||
{
|
||||
0 => None,
|
||||
1 => Adam7,
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,9 @@
|
|||
PngSuite
|
||||
--------
|
||||
|
||||
Permission to use, copy, modify and distribute these images for any
|
||||
purpose and without fee is hereby granted.
|
||||
|
||||
|
||||
(c) Willem van Schaik, 1996, 2011
|
||||
|
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 154 B |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 299 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 595 B |
After Width: | Height: | Size: 132 B |
After Width: | Height: | Size: 193 B |
After Width: | Height: | Size: 327 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 361 B |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 164 B |
After Width: | Height: | Size: 104 B |
After Width: | Height: | Size: 145 B |
After Width: | Height: | Size: 138 B |
After Width: | Height: | Size: 167 B |
After Width: | Height: | Size: 145 B |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 112 B |
After Width: | Height: | Size: 146 B |
After Width: | Height: | Size: 216 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 184 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 184 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 140 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 202 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 404 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 232 B |
After Width: | Height: | Size: 724 B |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 259 B |
After Width: | Height: | Size: 186 B |
After Width: | Height: | Size: 271 B |
After Width: | Height: | Size: 149 B |
After Width: | Height: | Size: 256 B |
After Width: | Height: | Size: 273 B |
After Width: | Height: | Size: 792 B |
After Width: | Height: | Size: 742 B |
After Width: | Height: | Size: 716 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 941 B |
After Width: | Height: | Size: 753 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 355 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 389 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 269 B |
After Width: | Height: | Size: 985 B |
After Width: | Height: | Size: 426 B |
After Width: | Height: | Size: 345 B |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 363 B |
After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 219 B |
After Width: | Height: | Size: 339 B |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 206 B |
After Width: | Height: | Size: 321 B |
After Width: | Height: | Size: 340 B |
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 262 B |
After Width: | Height: | Size: 285 B |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 383 B |
After Width: | Height: | Size: 405 B |
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 167 B |
After Width: | Height: | Size: 302 B |