Get zlib compression to work
This commit is contained in:
parent
59609983a9
commit
052b047222
7 changed files with 304 additions and 82 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
target
|
||||
Cargo.lock
|
||||
*.bk
|
||||
|
|
|
|||
|
|
@ -4,5 +4,7 @@ version = "2.0.0"
|
|||
authors = ["Joshua Holmer <jholmer.in@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "0.4."
|
||||
byteorder = "~0.4.0"
|
||||
crc = "^1.0.0"
|
||||
libz-sys = "^1.0.0"
|
||||
libc = "~0.2.4"
|
||||
|
|
|
|||
37
src/deflate/deflate.rs
Normal file
37
src/deflate/deflate.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use libz_sys;
|
||||
use libc;
|
||||
|
||||
pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
let mut input = data.to_owned();
|
||||
let mut stream = super::stream::Stream::new_decompress();
|
||||
let mut output = Vec::with_capacity(data.len());
|
||||
loop {
|
||||
match stream.decompress_vec(input.as_mut(), output.as_mut()) {
|
||||
libz_sys::Z_OK => output.reserve(data.len()),
|
||||
libz_sys::Z_STREAM_END => break,
|
||||
c => return Err(format!("Error code on decompress: {}", c)),
|
||||
}
|
||||
}
|
||||
output.shrink_to_fit();
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u16) -> Result<Vec<u8>, String> {
|
||||
let mut input = data.to_owned();
|
||||
let mut stream = super::stream::Stream::new_compress(zc as libc::c_int,
|
||||
zw as libc::c_int,
|
||||
zm as libc::c_int,
|
||||
zs as libc::c_int);
|
||||
let mut output = Vec::with_capacity(data.len() / 20);
|
||||
loop {
|
||||
match stream.compress_vec(input.as_mut(), output.as_mut()) {
|
||||
libz_sys::Z_OK => output.reserve(data.len() / 20),
|
||||
libz_sys::Z_STREAM_END => break,
|
||||
c => return Err(format!("Error code on compress: {}", c)),
|
||||
}
|
||||
}
|
||||
output.shrink_to_fit();
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
130
src/deflate/stream.rs
Normal file
130
src/deflate/stream.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
// Raw un-exported bindings to libz for encoding/decoding
|
||||
// Copyright (c) 2014 Alex Crichton, MIT & Apache licenses
|
||||
// Originally from flate2 crate for miniz
|
||||
// Modified for use in Optipng
|
||||
|
||||
use std::marker;
|
||||
use std::mem;
|
||||
use libc::{c_int, c_uint};
|
||||
use libz_sys;
|
||||
|
||||
pub struct Stream<D: Direction> {
|
||||
raw: libz_sys::z_stream,
|
||||
_marker: marker::PhantomData<D>,
|
||||
}
|
||||
|
||||
pub enum Compress {}
|
||||
pub enum Decompress {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Direction {
|
||||
unsafe fn destroy(stream: *mut libz_sys::z_stream) -> c_int;
|
||||
}
|
||||
|
||||
impl Stream<Compress> {
|
||||
pub fn new_compress(lvl: c_int,
|
||||
window_bits: c_int,
|
||||
mem_size: c_int,
|
||||
strategy: c_int)
|
||||
-> Stream<Compress> {
|
||||
unsafe {
|
||||
let mut state: libz_sys::z_stream = mem::zeroed();
|
||||
let ret = libz_sys::deflateInit2_(&mut state,
|
||||
lvl,
|
||||
libz_sys::Z_DEFLATED,
|
||||
window_bits,
|
||||
mem_size,
|
||||
strategy,
|
||||
libz_sys::zlibVersion(),
|
||||
mem::size_of::<libz_sys::z_stream>() as i32);
|
||||
debug_assert_eq!(ret, 0);
|
||||
Stream {
|
||||
raw: state,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_decompress() -> Stream<Decompress> {
|
||||
unsafe {
|
||||
let mut state: libz_sys::z_stream = mem::zeroed();
|
||||
let ret = libz_sys::inflateInit2_(&mut state,
|
||||
15,
|
||||
libz_sys::zlibVersion(),
|
||||
mem::size_of::<libz_sys::z_stream>() as i32);
|
||||
debug_assert_eq!(ret, 0);
|
||||
Stream {
|
||||
raw: state,
|
||||
_marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Direction> Stream<T> {
|
||||
pub fn total_in(&self) -> u64 {
|
||||
self.raw.total_in as u64
|
||||
}
|
||||
|
||||
pub fn total_out(&self) -> u64 {
|
||||
self.raw.total_out as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream<Decompress> {
|
||||
pub fn decompress_vec(&mut self, input: &mut [u8], output: &mut Vec<u8>) -> c_int {
|
||||
self.raw.avail_in = (input.len() - self.total_in() as usize) as c_uint;
|
||||
self.raw.avail_out = (output.capacity() - self.total_out() as usize) as c_uint;
|
||||
|
||||
unsafe {
|
||||
self.raw.next_in = input.as_mut_ptr().offset(self.total_in() as isize);
|
||||
self.raw.next_out = output.as_mut_ptr().offset(self.total_out() as isize);
|
||||
let rc = libz_sys::inflate(&mut self.raw, libz_sys::Z_NO_FLUSH);
|
||||
output.set_len(self.total_out() as usize);
|
||||
rc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream<Compress> {
|
||||
pub fn compress_vec(&mut self, input: &mut [u8], output: &mut Vec<u8>) -> c_int {
|
||||
self.raw.avail_in = (input.len() - self.total_in() as usize) as c_uint;
|
||||
self.raw.avail_out = (output.capacity() - self.total_out() as usize) as c_uint;
|
||||
|
||||
unsafe {
|
||||
self.raw.next_in = input.as_mut_ptr().offset(self.total_in() as isize);
|
||||
self.raw.next_out = output.as_mut_ptr().offset(self.total_out() as isize);
|
||||
let rc = libz_sys::deflate(&mut self.raw,
|
||||
if self.raw.avail_in > 0 {
|
||||
libz_sys::Z_NO_FLUSH
|
||||
} else {
|
||||
libz_sys::Z_FINISH
|
||||
});
|
||||
output.set_len(self.total_out() as usize);
|
||||
rc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) -> c_int {
|
||||
unsafe { libz_sys::deflateReset(&mut self.raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Direction for Compress {
|
||||
unsafe fn destroy(stream: *mut libz_sys::z_stream) -> c_int {
|
||||
libz_sys::deflateEnd(stream)
|
||||
}
|
||||
}
|
||||
impl Direction for Decompress {
|
||||
unsafe fn destroy(stream: *mut libz_sys::z_stream) -> c_int {
|
||||
libz_sys::inflateEnd(stream)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Direction> Drop for Stream<D> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = <D as Direction>::destroy(&mut self.raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
110
src/lib.rs
110
src/lib.rs
|
|
@ -1,10 +1,16 @@
|
|||
extern crate byteorder;
|
||||
extern crate crc;
|
||||
extern crate libz_sys;
|
||||
extern crate libc;
|
||||
|
||||
use std::path::Path;
|
||||
use std::collections::HashSet;
|
||||
|
||||
mod png;
|
||||
pub mod png;
|
||||
pub mod deflate {
|
||||
pub mod deflate;
|
||||
pub mod stream;
|
||||
}
|
||||
|
||||
pub struct Options<'a> {
|
||||
pub backup: bool,
|
||||
|
|
@ -20,7 +26,7 @@ pub struct Options<'a> {
|
|||
pub compression: HashSet<u8>,
|
||||
pub zm: HashSet<u8>,
|
||||
pub zs: HashSet<u8>,
|
||||
pub zw: u32,
|
||||
pub zw: u16,
|
||||
pub bit_depth_reduction: bool,
|
||||
pub color_type_reduction: bool,
|
||||
pub palette_reduction: bool,
|
||||
|
|
@ -28,61 +34,89 @@ pub struct Options<'a> {
|
|||
pub idat_paranoia: bool,
|
||||
}
|
||||
|
||||
// Processing: /Users/holmerj/Downloads/renpy-6.99.7-sdk/doc/_images/frame_example.png
|
||||
// 579x354 pixels, PNG format
|
||||
// 3x8 bits/pixel, RGB
|
||||
// IDAT size = 45711 bytes
|
||||
// file size = 45881 bytes
|
||||
// Trying: 8 combinations
|
||||
// Output:
|
||||
// IDAT size = 45711 bytes (no change)
|
||||
// file size = 45821 bytes (60 bytes = 0.13% decrease)
|
||||
pub fn optimize(filepath: &Path, opts: Options) -> Result<(), String> {
|
||||
// Decode PNG from file
|
||||
println!("Processing: {}", filepath.to_str().unwrap());
|
||||
let in_file = Path::new(filepath);
|
||||
let png = match png::PngData::new(&in_file) {
|
||||
let mut png = match png::PngData::new(&in_file) {
|
||||
Ok(x) => x,
|
||||
Err(x) => return Err(x)
|
||||
Err(x) => return Err(x),
|
||||
};
|
||||
|
||||
// Print png info
|
||||
let idat_current_size = png.idat_data.len();
|
||||
let file_current_size = filepath.metadata().unwrap().len();
|
||||
if opts.verbosity.is_some() {
|
||||
println!(" {}x{} pixels, PNG format", png.ihdr_data.width, png.ihdr_data.height);
|
||||
println!(" {}x{} pixels, PNG format",
|
||||
png.ihdr_data.width,
|
||||
png.ihdr_data.height);
|
||||
if let Some(palette) = png.palette.clone() {
|
||||
println!(" {} bits/pixel, {} colors in palette", png.ihdr_data.bit_depth, palette.len() / 3);
|
||||
println!(" {} bits/pixel, {} colors in palette",
|
||||
png.ihdr_data.bit_depth,
|
||||
palette.len() / 3);
|
||||
} else {
|
||||
println!(" {}x{} bits/pixel, {:?}", png.bits_per_pixel(), png.ihdr_data.bit_depth, png.ihdr_data.color_type);
|
||||
println!(" {}x{} bits/pixel, {:?}",
|
||||
png.bits_per_pixel(),
|
||||
png.ihdr_data.bit_depth,
|
||||
png.ihdr_data.color_type);
|
||||
}
|
||||
println!(" IDAT size = {} bytes", idat_current_size);
|
||||
println!(" File size = {} bytes", file_current_size);
|
||||
}
|
||||
// TODO: Bit depth/palette reduction
|
||||
//
|
||||
// // TODO: Bit depth/palette reduction
|
||||
// TODO: Apply interlacing changes
|
||||
// TODO: Force reencoding if interlacing was changed
|
||||
//
|
||||
// // Go through selected permutations and determine the best
|
||||
// let mut best: (Option<(u8, u8, u8, u8)>, usize) = (None, idat_current_size.clone());
|
||||
// if opts.idat_recoding {
|
||||
// let combinations = opts.f.len() * opts.zc.len() * opts.zm.len() * opts.zs.len();
|
||||
// println!("Trying: {} combinations", combinations);
|
||||
// // TODO: Multithreading
|
||||
// for f in &opts.f {
|
||||
// for zc in &opts.zc {
|
||||
// for zm in &opts.zm {
|
||||
// for zs in &opts.zs {
|
||||
// // TODO: Test compressions
|
||||
// let new_idat = compress::compress(img_buf.clone(), *f, opts.i, *zc, *zm, *zs, opts.zw);
|
||||
// // TODO: Force reencoding if interlacing was changed
|
||||
// if new_idat.len() < best.1 {
|
||||
// best = (Some((*f, *zc, *zm, *zs)), new_idat.len());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Go through selected permutations and determine the best
|
||||
let mut best: Option<(u8, u8, u8, u8)> = None;
|
||||
if opts.idat_recoding {
|
||||
let combinations = opts.filter.len() * opts.compression.len() * opts.zm.len() *
|
||||
opts.zs.len();
|
||||
println!("Trying: {} combinations", combinations);
|
||||
// TODO: Multithreading
|
||||
for f in &opts.filter {
|
||||
for zc in &opts.compression {
|
||||
for zm in &opts.zm {
|
||||
for zs in &opts.zs {
|
||||
let new_idat = match deflate::deflate::deflate(png.raw_data.as_ref(),
|
||||
*zc,
|
||||
*zm,
|
||||
*zs,
|
||||
opts.zw) {
|
||||
Ok(x) => x,
|
||||
Err(x) => return Err(x),
|
||||
};
|
||||
// TODO: Apply filtering
|
||||
if new_idat.len() < png.idat_data.len() {
|
||||
best = Some((*f, *zc, *zm, *zs));
|
||||
png.idat_data = new_idat.clone();
|
||||
}
|
||||
if opts.verbosity == Some(1) {
|
||||
println!(" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||
*zc,
|
||||
*zm,
|
||||
*zs,
|
||||
*f,
|
||||
new_idat.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(better) = best {
|
||||
println!("Found better combination:");
|
||||
println!(" zc = {} zm = {} zs = {} f = {} {} bytes",
|
||||
better.1,
|
||||
better.2,
|
||||
better.3,
|
||||
better.0,
|
||||
png.idat_data.len());
|
||||
} else {
|
||||
println!("IDAT already optimized");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Backup before writing?
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ fn main() {
|
|||
compression: compression,
|
||||
zm: zm,
|
||||
zs: zs,
|
||||
zw: 4096,
|
||||
zw: 15,
|
||||
bit_depth_reduction: true,
|
||||
color_type_reduction: true,
|
||||
palette_reduction: true,
|
||||
|
|
@ -43,5 +43,8 @@ fn main() {
|
|||
};
|
||||
// TODO: Handle command line args
|
||||
// TODO: Handle optimization presets
|
||||
optipng::optimize(infile, default_opts);
|
||||
match optipng::optimize(infile, default_opts) {
|
||||
Ok(_) => (),
|
||||
Err(x) => panic!(x),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
97
src/png.rs
97
src/png.rs
|
|
@ -18,13 +18,15 @@ pub enum ColorType {
|
|||
|
||||
impl fmt::Display for ColorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
ColorType::Grayscale => "Grayscale",
|
||||
ColorType::RGB => "RGB",
|
||||
ColorType::Indexed => "Indexed",
|
||||
ColorType::GrayscaleAlpha => "Grayscale + Alpha",
|
||||
ColorType::RGBA => "RGB + Alpha",
|
||||
})
|
||||
write!(f,
|
||||
"{}",
|
||||
match *self {
|
||||
ColorType::Grayscale => "Grayscale",
|
||||
ColorType::RGB => "RGB",
|
||||
ColorType::Indexed => "Indexed",
|
||||
ColorType::GrayscaleAlpha => "Grayscale + Alpha",
|
||||
ColorType::RGBA => "RGB + Alpha",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,13 +41,15 @@ pub enum BitDepth {
|
|||
|
||||
impl fmt::Display for BitDepth {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", match *self {
|
||||
BitDepth::One => "1",
|
||||
BitDepth::Two => "2",
|
||||
BitDepth::Four => "4",
|
||||
BitDepth::Eight => "8",
|
||||
BitDepth::Sixteen => "16",
|
||||
})
|
||||
write!(f,
|
||||
"{}",
|
||||
match *self {
|
||||
BitDepth::One => "1",
|
||||
BitDepth::Two => "2",
|
||||
BitDepth::Four => "4",
|
||||
BitDepth::Eight => "8",
|
||||
BitDepth::Sixteen => "16",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +57,7 @@ impl fmt::Display for BitDepth {
|
|||
pub struct PngData {
|
||||
pub idat_data: Vec<u8>,
|
||||
pub ihdr_data: IhdrData,
|
||||
pub raw_data: Vec<u8>,
|
||||
pub palette: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
|
|
@ -71,13 +76,13 @@ impl PngData {
|
|||
pub fn new(filepath: &Path) -> Result<PngData, String> {
|
||||
let mut file = match File::open(filepath) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Err("Failed to open file for reading".to_owned())
|
||||
Err(_) => return Err("Failed to open file for reading".to_owned()),
|
||||
};
|
||||
let mut byte_data: Vec<u8> = Vec::new();
|
||||
// Read raw png data into memory
|
||||
match file.read_to_end(&mut byte_data) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return Err("Failed to read from file".to_owned())
|
||||
Err(_) => return Err("Failed to read from file".to_owned()),
|
||||
}
|
||||
let mut byte_offset: usize = 0;
|
||||
// Test that png header is valid
|
||||
|
|
@ -93,11 +98,11 @@ impl PngData {
|
|||
let header = parse_next_header(byte_data.as_ref(), &mut byte_offset);
|
||||
let header = match header {
|
||||
Ok(x) => x,
|
||||
Err(x) => return Err(x)
|
||||
Err(x) => return Err(x),
|
||||
};
|
||||
let header = match header {
|
||||
Some(x) => x,
|
||||
None => break
|
||||
None => break,
|
||||
};
|
||||
if header.0 == "IDAT" {
|
||||
idat_headers.extend(header.1);
|
||||
|
|
@ -106,33 +111,29 @@ impl PngData {
|
|||
}
|
||||
}
|
||||
// Parse the headers into our PngData
|
||||
if idat_headers.len() == 0 {
|
||||
if idat_headers.is_empty() {
|
||||
return Err("Image data was empty, skipping".to_owned());
|
||||
}
|
||||
if aux_headers.get("IHDR").is_none() {
|
||||
return Err("Image header data was missing, skipping".to_owned());
|
||||
}
|
||||
let ihdr_header = parse_ihdr_header(aux_headers.get("IHDR").unwrap().as_ref());
|
||||
let raw_data = match super::deflate::deflate::inflate(idat_headers.as_ref()) {
|
||||
Ok(x) => x,
|
||||
Err(x) => return Err(x),
|
||||
};
|
||||
// TODO: Reverse filtering?
|
||||
// Return the PngData
|
||||
Ok(PngData {
|
||||
idat_data: idat_headers,
|
||||
idat_data: idat_headers.clone(),
|
||||
ihdr_data: match ihdr_header {
|
||||
Ok(x) => x,
|
||||
Err(x) => return Err(x)
|
||||
Err(x) => return Err(x),
|
||||
},
|
||||
raw_data: raw_data,
|
||||
palette: aux_headers.get("PLTE").cloned(),
|
||||
})
|
||||
}
|
||||
pub fn buffer_size(&self) -> usize {
|
||||
// Return the size needed to hold a decoded frame
|
||||
self.ihdr_data.width * self.ihdr_data.height * match self.ihdr_data.bit_depth {
|
||||
BitDepth::One => 1,
|
||||
BitDepth::Two => 2,
|
||||
BitDepth::Four => 4,
|
||||
BitDepth::Eight => 8,
|
||||
BitDepth::Sixteen => 16
|
||||
} as usize
|
||||
}
|
||||
pub fn bits_per_pixel(&self) -> u8 {
|
||||
match self.ihdr_data.color_type {
|
||||
ColorType::Grayscale => 1,
|
||||
|
|
@ -150,31 +151,45 @@ fn file_header_is_valid(bytes: &[u8]) -> bool {
|
|||
bytes.iter().zip(expected_header.iter()).all(|x| x.0 == x.1)
|
||||
}
|
||||
|
||||
fn parse_next_header(byte_data: &[u8], byte_offset: &mut usize) -> Result<Option<(String, Vec<u8>)>, String> {
|
||||
let mut rdr = Cursor::new(byte_data.iter().skip(*byte_offset).take(4).cloned().collect::<Vec<u8>>());
|
||||
fn parse_next_header(byte_data: &[u8],
|
||||
byte_offset: &mut usize)
|
||||
-> Result<Option<(String, Vec<u8>)>, String> {
|
||||
let mut rdr = Cursor::new(byte_data.iter()
|
||||
.skip(*byte_offset)
|
||||
.take(4)
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>());
|
||||
let length: u32 = match rdr.read_u32::<BigEndian>() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned())
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned()),
|
||||
};
|
||||
*byte_offset += 4;
|
||||
|
||||
let mut header_bytes: Vec<u8> = byte_data.iter().skip(*byte_offset).take(4).cloned().collect();
|
||||
let header = match String::from_utf8(header_bytes.clone()) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned())
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned()),
|
||||
};
|
||||
if header == "IEND".to_owned() {
|
||||
if header == "IEND" {
|
||||
// End of data
|
||||
return Ok(None);
|
||||
}
|
||||
*byte_offset += 4;
|
||||
|
||||
let data: Vec<u8> = byte_data.iter().skip(*byte_offset).take(length as usize).cloned().collect();
|
||||
let data: Vec<u8> = byte_data.iter()
|
||||
.skip(*byte_offset)
|
||||
.take(length as usize)
|
||||
.cloned()
|
||||
.collect();
|
||||
*byte_offset += length as usize;
|
||||
let mut rdr = Cursor::new(byte_data.iter().skip(*byte_offset).take(4).cloned().collect::<Vec<u8>>());
|
||||
let mut rdr = Cursor::new(byte_data.iter()
|
||||
.skip(*byte_offset)
|
||||
.take(4)
|
||||
.cloned()
|
||||
.collect::<Vec<u8>>());
|
||||
let crc: u32 = match rdr.read_u32::<BigEndian>() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned())
|
||||
Err(_) => return Err("Invalid data found--unable to read PNG file".to_owned()),
|
||||
};
|
||||
*byte_offset += 4;
|
||||
header_bytes.extend(data.clone());
|
||||
|
|
@ -194,7 +209,7 @@ fn parse_ihdr_header(byte_data: &[u8]) -> Result<IhdrData, String> {
|
|||
3 => ColorType::Indexed,
|
||||
4 => ColorType::GrayscaleAlpha,
|
||||
6 => ColorType::RGBA,
|
||||
_ => return Err("Unexpected color type in header".to_owned())
|
||||
_ => return Err("Unexpected color type in header".to_owned()),
|
||||
},
|
||||
bit_depth: match byte_data[8] {
|
||||
1 => BitDepth::One,
|
||||
|
|
@ -202,7 +217,7 @@ fn parse_ihdr_header(byte_data: &[u8]) -> Result<IhdrData, String> {
|
|||
4 => BitDepth::Four,
|
||||
8 => BitDepth::Eight,
|
||||
16 => BitDepth::Sixteen,
|
||||
_ => return Err("Unexpected bit depth in header".to_owned())
|
||||
_ => return Err("Unexpected bit depth in header".to_owned()),
|
||||
},
|
||||
width: rdr.read_u32::<BigEndian>().unwrap(),
|
||||
height: rdr.read_u32::<BigEndian>().unwrap(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue