#![cfg_attr(feature = "cargo-clippy", allow(identity_op))]
#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
#![cfg_attr(feature = "cargo-clippy", allow(new_without_default))]
#![cfg_attr(feature = "cargo-clippy", allow(verbose_bit_mask))]
#![cfg_attr(feature = "cargo-clippy", allow(many_single_char_names))]
#![cfg_attr(feature = "cargo-clippy", allow(trivially_copy_pass_by_ref))]
#![cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
#![cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
use super::*;
use crate::ffi::ColorProfile;
use crate::ffi::State;
use crate::huffman::HuffmanTree;
use crate::ChunkPosition;
pub use rgb::RGBA8 as RGBA;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs;
use std::io::prelude::*;
use std::mem;
use std::num::Wrapping;
use std::os::raw::*;
use std::path::*;
use std::ptr;
use std::slice;
pub(crate) unsafe fn vec_from_raw(data: *mut u8, len: usize) -> Vec<u8> {
slice::from_raw_parts_mut(data, len).to_owned()
}
pub(crate) fn vec_into_raw(v: Vec<u8>) -> Result<(*mut u8, usize), Error> {
unsafe {
let len = v.len();
let data = lodepng_malloc(len) as *mut u8;
if data.is_null() {
Err(Error(83))
} else {
slice::from_raw_parts_mut(data, len).clone_from_slice(&v);
Ok((data, len))
}
}
}
pub(crate) fn lodepng_malloc(size: usize) -> *mut c_void {
unsafe {
libc::malloc(size) as *mut _
}
}
pub(crate) unsafe fn lodepng_realloc(ptr: *mut c_void, size: usize) -> *mut c_void {
libc::realloc(ptr as *mut _, size) as *mut _
}
pub(crate) unsafe fn lodepng_free(ptr: *mut c_void) {
libc::free(ptr as *mut _)
}
fn write_signature(out: &mut Vec<u8>) {
out.push(137u8);
out.push(80u8);
out.push(78u8);
out.push(71u8);
out.push(13u8);
out.push(10u8);
out.push(26u8);
out.push(10u8);
}
#[derive(Eq, PartialEq)]
enum PaletteTranslucency {
Opaque,
Key,
Semi,
}
fn get_palette_translucency(palette: &[RGBA]) -> PaletteTranslucency {
let mut key = PaletteTranslucency::Opaque;
let mut r = 0;
let mut g = 0;
let mut b = 0;
let mut i = 0;
while i < palette.len() {
if key == PaletteTranslucency::Opaque && palette[i].a == 0 {
r = palette[i].r;
g = palette[i].g;
b = palette[i].b;
key = PaletteTranslucency::Key;
i = 0;
continue;
} else if palette[i].a != 255 {
return PaletteTranslucency::Semi;
} else if key == PaletteTranslucency::Key && r == palette[i].r && g == palette[i].g && b == palette[i].b {
return PaletteTranslucency::Semi;
}
i += 1;
}
key
}
fn add_padding_bits(out: &mut [u8], inp: &[u8], olinebits: usize, ilinebits: usize, h: usize) {
let diff = olinebits - ilinebits;
let mut obp = 0;
let mut ibp = 0;
for _ in 0..h {
for _ in 0..ilinebits {
let bit = read_bit_from_reversed_stream(&mut ibp, inp);
set_bit_of_reversed_stream(&mut obp, out, bit);
}
for _ in 0..diff {
set_bit_of_reversed_stream(&mut obp, out, 0u8);
}
}
}
fn pre_process_scanlines(inp: &[u8], w: usize, h: usize, info_png: &Info, settings: &EncoderSettings) -> Result<Vec<u8>, Error> {
let h = h as usize;
let w = w as usize;
let bpp = info_png.color.bpp() as usize;
if info_png.interlace_method == 0 {
let outsize = h + (h * ((w * bpp + 7) / 8));
let mut out = vec![0u8; outsize];
if bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8 {
let mut padded = vec![0u8; h * ((w * bpp + 7) / 8)];
add_padding_bits(&mut padded, inp, ((w * bpp + 7) / 8) * 8, w * bpp, h);
filter(&mut out, &padded, w, h, &info_png.color, settings)?;
} else {
filter(&mut out, inp, w, h, &info_png.color, settings)?;
}
Ok(out)
} else {
let (passw, passh, filter_passstart, padded_passstart, passstart) = adam7_get_pass_values(w, h, bpp);
let outsize = filter_passstart[7];
let mut out = vec![0u8; outsize];
let mut adam7 = vec![0u8; passstart[7] + 1];
adam7_interlace(&mut adam7, inp, w, h, bpp);
for i in 0..7 {
if bpp < 8 {
let mut padded = vec![0u8; padded_passstart[i + 1] - padded_passstart[i]];
add_padding_bits(
&mut padded,
&adam7[passstart[i]..],
((passw[i] as usize * bpp + 7) / 8) * 8,
passw[i] as usize * bpp,
passh[i] as usize,
);
filter(&mut out[filter_passstart[i]..], &padded, passw[i] as usize, passh[i] as usize, &info_png.color, settings)?;
} else {
filter(
&mut out[filter_passstart[i]..],
&adam7[padded_passstart[i]..],
passw[i] as usize,
passh[i] as usize,
&info_png.color,
settings,
)?;
}
}
Ok(out)
}
}
fn filter(out: &mut [u8], inp: &[u8], w: usize, h: usize, info: &ColorMode, settings: &EncoderSettings) -> Result<(), Error> {
let bpp = info.bpp() as usize;
let linebytes = ((w * bpp + 7) / 8) as usize;
let bytewidth = (bpp + 7) / 8;
let mut prevline = None;
let strategy = if settings.filter_palette_zero != 0 && (info.colortype == ColorType::PALETTE || info.bitdepth() < 8) {
FilterStrategy::ZERO
} else {
settings.filter_strategy
};
if bpp == 0 {
return Err(Error(31));
}
match strategy {
FilterStrategy::ZERO => for y in 0..h {
let outindex = (1 + linebytes) * y;
let inindex = linebytes * y;
out[outindex] = 0u8;
filter_scanline(&mut out[(outindex + 1)..], &inp[inindex..], prevline, linebytes, bytewidth, 0u8);
prevline = Some(&inp[inindex..]);
},
FilterStrategy::MINSUM => {
let mut sum: [usize; 5] = [0, 0, 0, 0, 0];
let mut attempt = [
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
];
let mut smallest = 0;
let mut best_type = 0;
for y in 0..h {
for type_ in 0..5 {
filter_scanline(&mut attempt[type_], &inp[(y * linebytes)..], prevline, linebytes, bytewidth, type_ as u8);
sum[type_] = if type_ == 0 {
attempt[type_][0..linebytes].iter().map(|&s| s as usize).sum()
} else {
attempt[type_][0..linebytes].iter().map(|&s| if s < 128 { s } else { 255 - s } as usize).sum()
};
if type_ == 0 || sum[type_] < smallest {
best_type = type_;
smallest = sum[type_];
};
}
prevline = Some(&inp[(y * linebytes)..]);
out[y * (linebytes + 1)] = best_type as u8;
for x in 0..linebytes {
out[y * (linebytes + 1) + 1 + x] = attempt[best_type][x];
}
}
},
FilterStrategy::ENTROPY => {
let mut sum: [f32; 5] = [0., 0., 0., 0., 0.];
let mut smallest = 0.;
let mut best_type = 0;
let mut attempt = [
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
];
for y in 0..h {
for type_ in 0..5 {
filter_scanline(&mut attempt[type_], &inp[(y * linebytes)..], prevline, linebytes, bytewidth, type_ as u8);
let mut count: [u32; 256] = [0; 256];
for x in 0..linebytes {
count[attempt[type_][x] as usize] += 1;
}
count[type_] += 1;
sum[type_] = 0.;
for &c in count.iter() {
let p = c as f32 / ((linebytes + 1) as f32);
sum[type_] += if c == 0 { 0. } else { (1. / p).log2() * p };
}
if type_ == 0 || sum[type_] < smallest {
best_type = type_;
smallest = sum[type_];
};
}
prevline = Some(&inp[(y * linebytes)..]);
out[y * (linebytes + 1)] = best_type as u8;
for x in 0..linebytes {
out[y * (linebytes + 1) + 1 + x] = attempt[best_type][x];
}
}
},
FilterStrategy::PREDEFINED => for y in 0..h {
let outindex = (1 + linebytes) * y;
let inindex = linebytes * y;
let filters = unsafe { settings.predefined_filters(h)? };
let type_ = filters[y];
out[outindex] = type_;
filter_scanline(&mut out[(outindex + 1)..], &inp[inindex..], prevline, linebytes, bytewidth, type_);
prevline = Some(&inp[inindex..]);
},
FilterStrategy::BRUTE_FORCE => {
let mut size: [usize; 5] = [0, 0, 0, 0, 0];
let mut smallest = 0;
let mut best_type = 0;
let mut zlibsettings = settings.zlibsettings.clone();
zlibsettings.btype = 1;
zlibsettings.custom_zlib = None;
zlibsettings.custom_deflate = None;
let mut attempt = [
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
vec![0u8; linebytes],
];
for y in 0..h {
for type_ in 0..5 {
filter_scanline(&mut attempt[type_], &inp[(y * linebytes)..], prevline, linebytes, bytewidth, type_ as u8);
size[type_] = 0;
let _ = zlib_compress(&attempt[type_], &zlibsettings)?;
if type_ == 0 || size[type_] < smallest {
best_type = type_;
smallest = size[type_];
}
}
prevline = Some(&inp[(y * linebytes)..]);
out[y * (linebytes + 1)] = best_type as u8;
for x in 0..linebytes {
out[y * (linebytes + 1) + 1 + x] = attempt[best_type][x];
}
}
},
};
Ok(())
}
#[test]
fn test_filter() {
let mut line1 = Vec::with_capacity(1<<16);
let mut line2 = Vec::with_capacity(1<<16);
for p in 0..256 {
for q in 0..256 {
line1.push(q as u8);
line2.push(p as u8);
}
}
let mut filtered = vec![99u8; 1<<16];
let mut unfiltered = vec![66u8; 1<<16];
for filter_type in 0..5 {
let len = filtered.len();
filter_scanline(&mut filtered, &line1, Some(&line2), len, 1, filter_type);
unfilter_scanline(&mut unfiltered, &filtered, Some(&line2), 1, filter_type, len).unwrap();
assert_eq!(unfiltered, line1, "prev+filter={}", filter_type);
}
for filter_type in 0..5 {
let len = filtered.len();
filter_scanline(&mut filtered, &line1, None, len, 1, filter_type);
unfilter_scanline(&mut unfiltered, &filtered, None, 1, filter_type, len).unwrap();
assert_eq!(unfiltered, line1, "none+filter={}", filter_type);
}
}
fn filter_scanline(out: &mut [u8], scanline: &[u8], prevline: Option<&[u8]>, length: usize, bytewidth: usize, filter_type: u8) {
match filter_type {
0 => {
out[..length].clone_from_slice(&scanline[..length]);
},
1 => {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(scanline[i - bytewidth]);
}
},
2 => if let Some(prevline) = prevline {
for i in 0..length {
out[i] = scanline[i].wrapping_sub(prevline[i]);
}
} else {
out[..length].clone_from_slice(&scanline[..length]);
},
3 => if let Some(prevline) = prevline {
for i in 0..bytewidth {
out[i] = scanline[i].wrapping_sub(prevline[i] >> 1);
}
for i in bytewidth..length {
let s = scanline[i - bytewidth] as u16 + prevline[i] as u16;
out[i] = scanline[i].wrapping_sub((s >> 1) as u8);
}
} else {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(scanline[i - bytewidth] >> 1);
}
},
4 => if let Some(prevline) = prevline {
for i in 0..bytewidth {
out[i] = scanline[i].wrapping_sub(prevline[i]);
}
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(paeth_predictor(scanline[i - bytewidth].into(), prevline[i].into(), prevline[i - bytewidth].into()));
}
} else {
out[..bytewidth].clone_from_slice(&scanline[..bytewidth]);
for i in bytewidth..length {
out[i] = scanline[i].wrapping_sub(scanline[i - bytewidth]);
}
},
_ => return,
};
}
fn paeth_predictor(a: i16, b: i16, c: i16) -> u8 {
let pa = (b - c).abs();
let pb = (a - c).abs();
let pc = (a + b - c - c).abs();
if pc < pa && pc < pb {
c as u8
} else if pb < pa {
b as u8
} else {
a as u8
}
}
pub fn lodepng_encode_file(filename: &Path, image: &[u8], w: u32, h: u32, colortype: ColorType, bitdepth: u32) -> Result<(), Error> {
let v = lodepng_encode_memory(image, w, h, colortype, bitdepth)?;
lodepng_save_file(&v, filename)
}
pub(crate) fn lodepng_get_bpp_lct(colortype: ColorType, bitdepth: u32) -> u32 {
assert!(bitdepth >= 1 && bitdepth <= 16);
let ch = colortype.channels() as u32;
ch * if ch > 1 {
if bitdepth == 8 {
8
} else {
16
}
} else {
bitdepth
}
}
pub fn lodepng_get_raw_size_lct(w: u32, h: u32, colortype: ColorType, bitdepth: u32) -> usize {
let bpp = lodepng_get_bpp_lct(colortype, bitdepth) as usize;
let n = w as usize * h as usize;
((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8
}
pub const LENGTHBASE: [u32; 29] = [
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, ];
pub const LENGTHEXTRA: [u32; 29] = [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, ];
pub const DISTANCEBASE: [u32; 30] = [
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, ];
pub const DISTANCEEXTRA: [u32; 30] = [
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, ];
pub(crate) unsafe fn string_cleanup(out: &mut *mut c_char) {
lodepng_free((*out) as *mut _);
*out = ptr::null_mut();
}
pub(crate) fn string_copy(inp: &[u8]) -> Result<*mut c_char, Error> {
let insize = inp.len();
unsafe {
let out = lodepng_malloc(insize + 1) as *mut c_char;
if out.is_null() {
return Err(Error(83));
}
for i in 0..insize {
*out.offset(i as isize) = inp[i] as c_char;
}
*out.offset(insize as isize) = 0;
Ok(out)
}
}
#[inline]
pub(crate) fn lodepng_read32bit_int(buffer: &[u8]) -> u32 {
((buffer[0] as u32) << 24) | ((buffer[1] as u32) << 16) | ((buffer[2] as u32) << 8) | buffer[3] as u32
}
#[inline(always)]
fn lodepng_set32bit_int(buffer: &mut [u8], value: u32) {
buffer[0] = ((value >> 24) & 255) as u8;
buffer[1] = ((value >> 16) & 255) as u8;
buffer[2] = ((value >> 8) & 255) as u8;
buffer[3] = ((value) & 255) as u8;
}
#[inline(always)]
fn add32bit_int(buffer: &mut Vec<u8>, value: u32) {
buffer.push(((value >> 24) & 255) as u8);
buffer.push(((value >> 16) & 255) as u8);
buffer.push(((value >> 8) & 255) as u8);
buffer.push(((value) & 255) as u8);
}
#[inline]
fn lodepng_add32bit_int(buffer: &mut Vec<u8>, value: u32) {
add32bit_int(buffer, value);
}
pub(crate) fn text_copy(dest: &mut Info, source: &Info) -> Result<(), Error> {
dest.text_keys = ptr::null_mut();
dest.text_strings = ptr::null_mut();
dest.text_num = 0;
for (k, v) in source.text_keys_cstr() {
dest.push_text(k.to_bytes(), v.to_bytes())?;
}
Ok(())
}
unsafe fn realloc_extend(array: &mut *mut *mut c_char, len: usize) -> Result<(), Error> {
*array = lodepng_realloc((*array) as *mut _, (len + 1) * mem::size_of::<*mut c_char>()) as *mut _;
if array.is_null() {
return Err(Error(83));
}
*array.offset(len as isize) = ptr::null_mut();
Ok(())
}
impl Info {
pub(crate) fn push_itext(&mut self, key: &[u8], langtag: &[u8], transkey: &[u8], str: &[u8]) -> Result<(), Error> {
unsafe {
realloc_extend(&mut self.itext_keys, self.itext_num)?;
realloc_extend(&mut self.itext_langtags, self.itext_num)?;
realloc_extend(&mut self.itext_transkeys, self.itext_num)?;
realloc_extend(&mut self.itext_strings, self.itext_num)?;
let n = self.itext_num as isize;
self.itext_num += 1;
*self.itext_keys.offset(n) = string_copy(key)?;
*self.itext_langtags.offset(n) = string_copy(langtag)?;
*self.itext_transkeys.offset(n) = string_copy(transkey)?;
*self.itext_strings.offset(n) = string_copy(str)?;
}
Ok(())
}
pub(crate) fn push_text(&mut self, k: &[u8], v: &[u8]) -> Result<(), Error> {
unsafe {
realloc_extend(&mut self.text_keys, self.text_num)?;
realloc_extend(&mut self.text_strings, self.text_num)?;
let n = self.text_num as isize;
self.text_num += 1;
*self.text_keys.offset(n) = string_copy(k)?;
*self.text_strings.offset(n) = string_copy(v)?;
}
Ok(())
}
fn push_unknown_chunk(&mut self, critical_pos: ChunkPosition, chunk: &[u8]) -> Result<(), Error> {
let set = critical_pos as usize;
unsafe {
let mut tmp = vec_from_raw(self.unknown_chunks_data[set], self.unknown_chunks_size[set]);
chunk_append(&mut tmp, chunk);
let (data, size) = vec_into_raw(tmp)?;
self.unknown_chunks_data[set] = data;
self.unknown_chunks_size[set] = size;
}
Ok(())
}
#[inline]
fn unknown_chunks_data(&self, critical_pos: ChunkPosition) -> Option<&[u8]> {
let set = critical_pos as usize;
unsafe {
if self.unknown_chunks_data[set].is_null() {
return None;
}
Some(slice::from_raw_parts(self.unknown_chunks_data[set], self.unknown_chunks_size[set]))
}
}
}
pub(crate) fn itext_copy(dest: &mut Info, source: &Info) -> Result<(), Error> {
dest.itext_keys = ptr::null_mut();
dest.itext_langtags = ptr::null_mut();
dest.itext_transkeys = ptr::null_mut();
dest.itext_strings = ptr::null_mut();
dest.itext_num = 0;
for (k,l,t,s) in source.itext_keys() {
dest.push_itext(k.as_bytes(),
l.as_bytes(),
t.as_bytes(),
s.as_bytes())?;
}
Ok(())
}
fn add_color_bits(out: &mut [u8], index: usize, bits: u32, mut inp: u32) {
let m = match bits {
1 => 7,
2 => 3,
_ => 1,
};
let p = index & m;
inp &= (1 << bits) - 1;
inp <<= bits * (m - p) as u32;
if p == 0 {
out[index * bits as usize / 8] = inp as u8;
} else {
out[index * bits as usize / 8] |= inp as u8;
}
}
pub type ColorTree = HashMap<(u8,u8,u8,u8), u16>;
#[inline(always)]
fn rgba8_to_pixel(out: &mut [u8], i: usize, mode: &ColorMode, tree: &mut ColorTree, r: u8, g: u8, b: u8, a: u8) -> Result<(), Error> {
match mode.colortype {
ColorType::GREY => {
let grey = r;
if mode.bitdepth() == 8 {
out[i] = grey;
} else if mode.bitdepth() == 16 {
out[i * 2 + 0] = {
out[i * 2 + 1] = grey;
out[i * 2 + 1]
};
} else {
let grey = (grey >> (8 - mode.bitdepth())) & ((1 << mode.bitdepth()) - 1);
add_color_bits(out, i, mode.bitdepth(), grey.into());
};
},
ColorType::RGB => if mode.bitdepth() == 8 {
out[i * 3 + 0] = r;
out[i * 3 + 1] = g;
out[i * 3 + 2] = b;
} else {
out[i * 6 + 0] = r;
out[i * 6 + 1] = r;
out[i * 6 + 2] = g;
out[i * 6 + 3] = g;
out[i * 6 + 4] = b;
out[i * 6 + 5] = b;
},
ColorType::PALETTE => {
let index = *tree.get(&(r, g, b, a)).ok_or(Error(82))?;
if mode.bitdepth() == 8 {
out[i] = index as u8;
} else {
add_color_bits(out, i, mode.bitdepth(), u32::from(index));
};
},
ColorType::GREY_ALPHA => {
let grey = r;
if mode.bitdepth() == 8 {
out[i * 2 + 0] = grey;
out[i * 2 + 1] = a;
} else if mode.bitdepth() == 16 {
out[i * 4 + 0] = grey;
out[i * 4 + 1] = grey;
out[i * 4 + 2] = a;
out[i * 4 + 3] = a;
}
},
ColorType::RGBA => if mode.bitdepth() == 8 {
out[i * 4 + 0] = r;
out[i * 4 + 1] = g;
out[i * 4 + 2] = b;
out[i * 4 + 3] = a;
} else {
out[i * 8 + 0] = r;
out[i * 8 + 1] = r;
out[i * 8 + 2] = g;
out[i * 8 + 3] = g;
out[i * 8 + 4] = b;
out[i * 8 + 5] = b;
out[i * 8 + 6] = a;
out[i * 8 + 7] = a;
},
ColorType::BGRA |
ColorType::BGR |
ColorType::BGRX => {
return Err(Error(31));
},
};
Ok(())
}
#[inline(always)]
fn rgba16_to_pixel(out: &mut [u8], i: usize, mode: &ColorMode, r: u16, g: u16, b: u16, a: u16) {
match mode.colortype {
ColorType::GREY => {
let grey = r;
out[i * 2 + 0] = (grey >> 8) as u8;
out[i * 2 + 1] = grey as u8;
},
ColorType::RGB => {
out[i * 6 + 0] = (r >> 8) as u8;
out[i * 6 + 1] = r as u8;
out[i * 6 + 2] = (g >> 8) as u8;
out[i * 6 + 3] = g as u8;
out[i * 6 + 4] = (b >> 8) as u8;
out[i * 6 + 5] = b as u8;
},
ColorType::GREY_ALPHA => {
let grey = r;
out[i * 4 + 0] = (grey >> 8) as u8;
out[i * 4 + 1] = grey as u8;
out[i * 4 + 2] = (a >> 8) as u8;
out[i * 4 + 3] = a as u8;
},
ColorType::RGBA => {
out[i * 8 + 0] = (r >> 8) as u8;
out[i * 8 + 1] = r as u8;
out[i * 8 + 2] = (g >> 8) as u8;
out[i * 8 + 3] = g as u8;
out[i * 8 + 4] = (b >> 8) as u8;
out[i * 8 + 5] = b as u8;
out[i * 8 + 6] = (a >> 8) as u8;
out[i * 8 + 7] = a as u8;
},
ColorType::BGR |
ColorType::BGRA |
ColorType::BGRX |
ColorType::PALETTE => unreachable!(),
};
}
fn get_pixel_color_rgba8(inp: &[u8], i: usize, mode: &ColorMode) -> (u8,u8,u8,u8) {
match mode.colortype {
ColorType::GREY => {
if mode.bitdepth() == 8 {
let t = inp[i];
let a = if mode.key() == Some((u16::from(t), u16::from(t), u16::from(t))) {
0
} else {
255
};
(t, t, t, a)
} else if mode.bitdepth() == 16 {
let t = inp[i * 2 + 0];
let g = 256 * inp[i * 2 + 0] as u16 + inp[i * 2 + 1] as u16;
let a = if mode.key() == Some((g, g, g)) {
0
} else {
255
};
(t, t, t, a)
} else {
let highest = (1 << mode.bitdepth()) - 1;
let mut j = i as usize * mode.bitdepth() as usize;
let value = read_bits_from_reversed_stream(&mut j, inp, mode.bitdepth() as usize);
let t = ((value * 255) / highest) as u8;
let a = if mode.key() == Some((t as u16, t as u16, t as u16)) {
0
} else {
255
};
(t, t, t, a)
}
},
ColorType::RGB => if mode.bitdepth() == 8 {
let r = inp[i * 3 + 0];
let g = inp[i * 3 + 1];
let b = inp[i * 3 + 2];
let a = if mode.key() == Some((u16::from(r), u16::from(g), u16::from(b))) {
0
} else {
255
};
(r, g, b, a)
} else {
(
inp[i * 6 + 0],
inp[i * 6 + 2],
inp[i * 6 + 4],
if mode.key()
== Some((
256 * inp[i * 6 + 0] as u16 + inp[i * 6 + 1] as u16,
256 * inp[i * 6 + 2] as u16 + inp[i * 6 + 3] as u16,
256 * inp[i * 6 + 4] as u16 + inp[i * 6 + 5] as u16,
)) {
0
} else {
255
},
)
},
ColorType::PALETTE => {
let index = if mode.bitdepth() == 8 {
inp[i] as usize
} else {
let mut j = i as usize * mode.bitdepth() as usize;
read_bits_from_reversed_stream(&mut j, inp, mode.bitdepth() as usize) as usize
};
let pal = mode.palette();
if index >= pal.len() {
(0, 0, 0, 255)
} else {
let p = pal[index];
(p.r, p.g, p.b, p.a)
}
},
ColorType::GREY_ALPHA => if mode.bitdepth() == 8 {
let t = inp[i * 2 + 0];
(t, t, t, inp[i * 2 + 1])
} else {
let t = inp[i * 4 + 0];
(t, t, t, inp[i * 4 + 2])
},
ColorType::RGBA => if mode.bitdepth() == 8 {
(inp[i * 4 + 0], inp[i * 4 + 1], inp[i * 4 + 2], inp[i * 4 + 3])
} else {
(inp[i * 8 + 0], inp[i * 8 + 2], inp[i * 8 + 4], inp[i * 8 + 6])
},
ColorType::BGRA => {
(inp[i * 4 + 2], inp[i * 4 + 1], inp[i * 4 + 0], inp[i * 4 + 3])
},
ColorType::BGR => {
let b = inp[i * 3 + 0];
let g = inp[i * 3 + 1];
let r = inp[i * 3 + 2];
let a = if mode.key() == Some((u16::from(r), u16::from(g), u16::from(b))) {
0
} else {
255
};
(r, g, b, a)
},
ColorType::BGRX => {
let b = inp[i * 4 + 0];
let g = inp[i * 4 + 1];
let r = inp[i * 4 + 2];
let a = if mode.key() == Some((u16::from(r), u16::from(g), u16::from(b))) {
0
} else {
255
};
(r, g, b, a)
}
}
}
fn get_pixel_colors_rgba8(buffer: &mut [u8], numpixels: usize, has_alpha: bool, inp: &[u8], mode: &ColorMode) {
let num_channels = if has_alpha { 4 } else { 3 };
match mode.colortype {
ColorType::GREY => {
if mode.bitdepth() == 8 {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i];
buffer[1] = inp[i];
buffer[2] = inp[i];
if has_alpha {
let a = inp[i] as u16;
buffer[3] = if mode.key() == Some((a, a, a)) {
0
} else {
255
};
}
}
} else if mode.bitdepth() == 16 {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 2];
buffer[1] = inp[i * 2];
buffer[2] = inp[i * 2];
if has_alpha {
let a = 256 * inp[i * 2 + 0] as u16 + inp[i * 2 + 1] as u16;
buffer[3] = if mode.key() == Some((a, a, a)) {
0
} else {
255
};
};
}
} else {
let highest = (1 << mode.bitdepth()) - 1;
let mut j = 0;
for buffer in buffer.chunks_mut(num_channels).take(numpixels) {
let value = read_bits_from_reversed_stream(&mut j, inp, mode.bitdepth() as usize);
buffer[0] = ((value * 255) / highest) as u8;
buffer[1] = ((value * 255) / highest) as u8;
buffer[2] = ((value * 255) / highest) as u8;
if has_alpha {
let a = value as u16;
buffer[3] = if mode.key() == Some((a, a, a)) {
0
} else {
255
};
};
}
};
},
ColorType::RGB => {
if mode.bitdepth() == 8 {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 3 + 0];
buffer[1] = inp[i * 3 + 1];
buffer[2] = inp[i * 3 + 2];
if has_alpha {
buffer[3] = if mode.key() == Some((buffer[0] as u16, buffer[1] as u16, buffer[2] as u16)) {
0
} else {
255
};
};
}
} else {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 6 + 0];
buffer[1] = inp[i * 6 + 2];
buffer[2] = inp[i * 6 + 4];
if has_alpha {
let r = 256 * inp[i * 6 + 0] as u16 + inp[i * 6 + 1] as u16;
let g = 256 * inp[i * 6 + 2] as u16 + inp[i * 6 + 3] as u16;
let b = 256 * inp[i * 6 + 4] as u16 + inp[i * 6 + 5] as u16;
buffer[3] = if mode.key() == Some((r, g, b)) {
0
} else {
255
};
};
}
};
},
ColorType::PALETTE => {
let mut j = 0;
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
let index = if mode.bitdepth() == 8 {
inp[i] as usize
} else {
read_bits_from_reversed_stream(&mut j, inp, mode.bitdepth() as usize) as usize
};
let pal = mode.palette();
if index >= pal.len() {
buffer[0] = 0;
buffer[1] = 0;
buffer[2] = 0;
if has_alpha {
buffer[3] = 255u8;
}
} else {
let p = pal[index as usize];
buffer[0] = p.r;
buffer[1] = p.g;
buffer[2] = p.b;
if has_alpha {
buffer[3] = p.a;
}
};
}
},
ColorType::GREY_ALPHA => if mode.bitdepth() == 8 {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 2 + 0];
buffer[1] = inp[i * 2 + 0];
buffer[2] = inp[i * 2 + 0];
if has_alpha {
buffer[3] = inp[i * 2 + 1];
};
}
} else {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 4 + 0];
buffer[1] = inp[i * 4 + 0];
buffer[2] = inp[i * 4 + 0];
if has_alpha {
buffer[3] = inp[i * 4 + 2];
};
}
},
ColorType::RGBA => if mode.bitdepth() == 8 {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 4 + 0];
buffer[1] = inp[i * 4 + 1];
buffer[2] = inp[i * 4 + 2];
if has_alpha {
buffer[3] = inp[i * 4 + 3];
}
}
} else {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 8 + 0];
buffer[1] = inp[i * 8 + 2];
buffer[2] = inp[i * 8 + 4];
if has_alpha {
buffer[3] = inp[i * 8 + 6];
}
}
},
ColorType::BGR => {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 3 + 2];
buffer[1] = inp[i * 3 + 1];
buffer[2] = inp[i * 3 + 0];
if has_alpha {
buffer[3] = if mode.key() == Some((buffer[0] as u16, buffer[1] as u16, buffer[2] as u16)) {
0
} else {
255
};
};
}
},
ColorType::BGRX => {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 4 + 2];
buffer[1] = inp[i * 4 + 1];
buffer[2] = inp[i * 4 + 0];
if has_alpha {
buffer[3] = if mode.key() == Some((buffer[0] as u16, buffer[1] as u16, buffer[2] as u16)) {
0
} else {
255
};
};
}
},
ColorType::BGRA => {
for (i, buffer) in buffer.chunks_mut(num_channels).take(numpixels).enumerate() {
buffer[0] = inp[i * 4 + 2];
buffer[1] = inp[i * 4 + 1];
buffer[2] = inp[i * 4 + 0];
if has_alpha {
buffer[3] = inp[i * 4 + 3];
}
}
},
};
}
#[inline(always)]
fn get_pixel_color_rgba16(inp: &[u8], i: usize, mode: &ColorMode) -> (u16,u16,u16,u16) {
match mode.colortype {
ColorType::GREY => {
let t = 256 * inp[i * 2 + 0] as u16 + inp[i * 2 + 1] as u16;
(t,t,t,
if mode.key() == Some((t,t,t)) {
0
} else {
0xffff
})
},
ColorType::RGB => {
let r = 256 * inp[i * 6 + 0] as u16 + inp[i * 6 + 1] as u16;
let g = 256 * inp[i * 6 + 2] as u16 + inp[i * 6 + 3] as u16;
let b = 256 * inp[i * 6 + 4] as u16 + inp[i * 6 + 5] as u16;
let a = if mode.key() == Some((r, g, b)) {
0
} else {
0xffff
};
(r, g, b, a)
},
ColorType::GREY_ALPHA => {
let t = 256 * inp[i * 4 + 0] as u16 + inp[i * 4 + 1] as u16;
let a = 256 * inp[i * 4 + 2] as u16 + inp[i * 4 + 3] as u16;
(t, t, t, a)
},
ColorType::RGBA => (
256 * inp[i * 8 + 0] as u16 + inp[i * 8 + 1] as u16,
256 * inp[i * 8 + 2] as u16 + inp[i * 8 + 3] as u16,
256 * inp[i * 8 + 4] as u16 + inp[i * 8 + 5] as u16,
256 * inp[i * 8 + 6] as u16 + inp[i * 8 + 7] as u16,
),
ColorType::BGR |
ColorType::BGRA |
ColorType::BGRX |
ColorType::PALETTE => unreachable!(),
}
}
#[inline(always)]
fn read_bits_from_reversed_stream(bitpointer: &mut usize, bitstream: &[u8], nbits: usize) -> u32 {
let mut result = 0;
for _ in 0..nbits {
result <<= 1;
result |= read_bit_from_reversed_stream(bitpointer, bitstream) as u32;
}
result
}
fn read_chunk_plte(color: &mut ColorMode, data: &[u8]) -> Result<(), Error> {
color.palette_clear();
for c in data.chunks(3).take(data.len() / 3) {
color.palette_add(RGBA {
r: c[0],
g: c[1],
b: c[2],
a: 255,
})?;
}
Ok(())
}
fn read_chunk_trns(color: &mut ColorMode, data: &[u8]) -> Result<(), Error> {
if color.colortype == ColorType::PALETTE {
let pal = color.palette_mut();
if data.len() > pal.len() {
return Err(Error(38));
}
for (i, &d) in data.iter().enumerate() {
pal[i].a = d;
}
} else if color.colortype == ColorType::GREY {
if data.len() != 2 {
return Err(Error(30));
}
let t = 256 * data[0] as u16 + data[1] as u16;
color.set_key(t, t, t);
} else if color.colortype == ColorType::RGB {
if data.len() != 6 {
return Err(Error(41));
}
color.set_key(
256 * data[0] as u16 + data[1] as u16,
256 * data[2] as u16 + data[3] as u16,
256 * data[4] as u16 + data[5] as u16,
);
} else {
return Err(Error(42));
}
Ok(())
}
fn read_chunk_bkgd(info: &mut Info, data: &[u8]) -> Result<(), Error> {
let chunk_length = data.len();
if info.color.colortype == ColorType::PALETTE {
if chunk_length != 1 {
return Err(Error(43));
}
info.background_defined = 1;
info.background_r = {
info.background_g = {
info.background_b = data[0] as u32;
info.background_b
};
info.background_g
};
} else if info.color.colortype == ColorType::GREY || info.color.colortype == ColorType::GREY_ALPHA {
if chunk_length != 2 {
return Err(Error(44));
}
info.background_defined = 1;
info.background_r = {
info.background_g = {
info.background_b = 256 * data[0] as u32 + data[1] as u32;
info.background_b
};
info.background_g
};
} else if info.color.colortype == ColorType::RGB || info.color.colortype == ColorType::RGBA {
if chunk_length != 6 {
return Err(Error(45));
}
info.background_defined = 1;
info.background_r = 256 * data[0] as u32 + data[1] as u32;
info.background_g = 256 * data[2] as u32 + data[3] as u32;
info.background_b = 256 * data[4] as u32 + data[5] as u32;
}
Ok(())
}
fn read_chunk_text(info: &mut Info, data: &[u8]) -> Result<(), Error> {
let (keyword, str) = split_at_nul(data);
if keyword.is_empty() || keyword.len() > 79 {
return Err(Error(89));
}
info.push_text(keyword, str)
}
fn read_chunk_ztxt(info: &mut Info, zlibsettings: &DecompressSettings, data: &[u8]) -> Result<(), Error> {
let mut length = 0;
while length < data.len() && data[length] != 0 {
length += 1
}
if length + 2 >= data.len() {
return Err(Error(75));
}
if length < 1 || length > 79 {
return Err(Error(89));
}
let key = &data[0..length];
if data[length + 1] != 0 {
return Err(Error(72));
}
let string2_begin = length + 2;
if string2_begin > data.len() {
return Err(Error(75));
}
let inl = &data[string2_begin..];
let decoded = zlib_decompress(inl, zlibsettings)?;
info.push_text(key, &decoded)?;
Ok(())
}
fn split_at_nul(data: &[u8]) -> (&[u8], &[u8]) {
let mut part = data.splitn(2, |&b| b == 0);
(part.next().unwrap(), part.next().unwrap_or(&data[0..0]))
}
fn read_chunk_itxt(info: &mut Info, zlibsettings: &DecompressSettings, data: &[u8]) -> Result<(), Error> {
if data.len() < 5 {
return Err(Error(30));
}
let (key, data) = split_at_nul(data);
if key.is_empty() || key.len() > 79 {
return Err(Error(89));
}
if data.len() < 2 {
return Err(Error(75));
}
let compressed_flag = data[0];
if data[1] != 0 {
return Err(Error(72));
}
let (langtag, data) = split_at_nul(&data[2..]);
let (transkey, data) = split_at_nul(data);
let decoded;
let rest = if compressed_flag != 0 {
decoded = zlib_decompress(data, zlibsettings)?;
&decoded[..]
} else {
data
};
info.push_itext(key, langtag, transkey, rest)?;
Ok(())
}
fn read_chunk_time(info: &mut Info, data: &[u8]) -> Result<(), Error> {
let chunk_length = data.len();
if chunk_length != 7 {
return Err(Error(73));
}
info.time_defined = 1;
info.time.year = 256 * data[0] as u32 + data[1] as u32;
info.time.month = data[2] as u32;
info.time.day = data[3] as u32;
info.time.hour = data[4] as u32;
info.time.minute = data[5] as u32;
info.time.second = data[6] as u32;
Ok(())
}
fn read_chunk_phys(info: &mut Info, data: &[u8]) -> Result<(), Error> {
let chunk_length = data.len();
if chunk_length != 9 {
return Err(Error(74));
}
info.phys_defined = 1;
info.phys_x = 16777216 * data[0] as u32 + 65536 * data[1] as u32 + 256 * data[2] as u32 + data[3] as u32;
info.phys_y = 16777216 * data[4] as u32 + 65536 * data[5] as u32 + 256 * data[6] as u32 + data[7] as u32;
info.phys_unit = data[8] as u32;
Ok(())
}
fn add_chunk_idat(out: &mut Vec<u8>, data: &[u8], zlibsettings: &CompressSettings) -> Result<(), Error> {
let zlib = zlib_compress(data, zlibsettings)?;
add_chunk(out, b"IDAT", &zlib)?;
Ok(())
}
fn add_chunk_iend(out: &mut Vec<u8>) -> Result<(), Error> {
add_chunk(out, b"IEND", &[])
}
fn add_chunk_text(out: &mut Vec<u8>, keyword: &CStr, textstring: &CStr) -> Result<(), Error> {
if keyword.to_bytes().is_empty() || keyword.to_bytes().len() > 79 {
return Err(Error(89));
}
let mut text = Vec::from(keyword.to_bytes_with_nul());
text.extend_from_slice(textstring.to_bytes());
add_chunk(out, b"tEXt", &text)
}
fn add_chunk_ztxt(out: &mut Vec<u8>, keyword: &CStr, textstring: &CStr, zlibsettings: &CompressSettings) -> Result<(), Error> {
if keyword.to_bytes().is_empty() || keyword.to_bytes().len() > 79 {
return Err(Error(89));
}
let mut data = Vec::from(keyword.to_bytes_with_nul());
data.push(0u8);
let textstring = textstring.to_bytes();
let v = zlib_compress(textstring, zlibsettings)?;
data.extend_from_slice(&v);
add_chunk(out, b"zTXt", &data)?;
Ok(())
}
fn add_chunk_itxt(
out: &mut Vec<u8>, compressed: bool, keyword: &str, langtag: &str, transkey: &str, textstring: &str, zlibsettings: &CompressSettings,
) -> Result<(), Error> {
let k_len = keyword.len();
if k_len < 1 || k_len > 79 {
return Err(Error(89));
}
let mut data = Vec::with_capacity(2048);
data.extend_from_slice(keyword.as_bytes()); data.push(0);
data.push(compressed as u8);
data.push(0);
data.extend_from_slice(langtag.as_bytes()); data.push(0);
data.extend_from_slice(transkey.as_bytes()); data.push(0);
if compressed {
let compressed_data = zlib_compress(textstring.as_bytes(), zlibsettings)?;
data.extend_from_slice(&compressed_data);
} else {
data.extend_from_slice(textstring.as_bytes());
}
add_chunk(out, b"iTXt", &data)
}
fn add_chunk_bkgd(out: &mut Vec<u8>, info: &Info) -> Result<(), Error> {
let mut bkgd = Vec::with_capacity(16);
if info.color.colortype == ColorType::GREY || info.color.colortype == ColorType::GREY_ALPHA {
bkgd.push((info.background_r >> 8) as u8);
bkgd.push((info.background_r & 255) as u8);
} else if info.color.colortype == ColorType::RGB || info.color.colortype == ColorType::RGBA {
bkgd.push((info.background_r >> 8) as u8);
bkgd.push((info.background_r & 255) as u8);
bkgd.push((info.background_g >> 8) as u8);
bkgd.push((info.background_g & 255) as u8);
bkgd.push((info.background_b >> 8) as u8);
bkgd.push((info.background_b & 255) as u8);
} else if info.color.colortype == ColorType::PALETTE {
bkgd.push((info.background_r & 255) as u8);
}
add_chunk(out, b"bKGD", &bkgd)
}
fn add_chunk_ihdr(out: &mut Vec<u8>, w: usize, h: usize, colortype: ColorType, bitdepth: usize, interlace_method: u8) -> Result<(), Error> {
let mut header = Vec::with_capacity(16);
add32bit_int(&mut header, w as u32);
add32bit_int(&mut header, h as u32);
header.push(bitdepth as u8);
header.push(colortype as u8);
header.push(0u8);
header.push(0u8);
header.push(interlace_method);
add_chunk(out, b"IHDR", &header)
}
fn add_chunk_trns(out: &mut Vec<u8>, info: &ColorMode) -> Result<(), Error> {
let mut trns = Vec::with_capacity(32);
if info.colortype == ColorType::PALETTE {
let palette = info.palette();
let mut amount = palette.len();
let mut i = palette.len();
while i != 0 {
if palette[i - 1].a == 255 {
amount -= 1;
} else {
break;
};
i -= 1;
}
for p in &palette[0..amount] {
trns.push(p.a);
}
} else if info.colortype == ColorType::GREY {
if let Some((r, _, _)) = info.key() {
trns.push((r >> 8) as u8);
trns.push((r & 255) as u8);
};
} else if info.colortype == ColorType::RGB {
if let Some((r, g, b)) = info.key() {
trns.push((r >> 8) as u8);
trns.push((r & 255) as u8);
trns.push((g >> 8) as u8);
trns.push((g & 255) as u8);
trns.push((b >> 8) as u8);
trns.push((b & 255) as u8);
};
}
add_chunk(out, b"tRNS", &trns)
}
fn add_chunk_plte(out: &mut Vec<u8>, info: &ColorMode) -> Result<(), Error> {
let mut plte = Vec::with_capacity(1024);
for p in info.palette() {
plte.push(p.r);
plte.push(p.g);
plte.push(p.b);
}
add_chunk(out, b"PLTE", &plte)
}
fn add_chunk_time(out: &mut Vec<u8>, time: &Time) -> Result<(), Error> {
let data = [
(time.year >> 8) as u8,
(time.year & 255) as u8,
time.month as u8,
time.day as u8,
time.hour as u8,
time.minute as u8,
time.second as u8,
];
add_chunk(out, b"tIME", &data)
}
fn add_chunk_phys(out: &mut Vec<u8>, info: &Info) -> Result<(), Error> {
let mut data = Vec::with_capacity(16);
add32bit_int(&mut data, info.phys_x);
add32bit_int(&mut data, info.phys_y);
data.push(info.phys_unit as u8);
add_chunk(out, b"pHYs", &data)
}
pub(crate) fn add_chunk(out: &mut Vec<u8>, type_: &[u8; 4], data: &[u8]) -> Result<(), Error> {
let length = data.len() as usize;
if length > (1 << 31) {
return Err(Error(77));
}
let previous_length = out.len();
out.reserve(length + 12);
lodepng_add32bit_int(out, length as u32);
out.extend_from_slice(&type_[..]);
out.extend_from_slice(data);
lodepng_add32bit_int(out, 0);
lodepng_chunk_generate_crc(&mut out[previous_length..]);
Ok(())
}
pub const ADAM7_IX: [u32; 7] = [0, 4, 0, 2, 0, 1, 0];
pub const ADAM7_IY: [u32; 7] = [0, 0, 4, 0, 2, 0, 1];
pub const ADAM7_DX: [u32; 7] = [8, 8, 4, 4, 2, 2, 1];
pub const ADAM7_DY: [u32; 7] = [8, 8, 8, 4, 4, 2, 2];
fn adam7_get_pass_values(w: usize, h: usize, bpp: usize) -> ([u32; 7], [u32; 7], [usize; 8], [usize; 8], [usize; 8]) {
let mut passw: [u32; 7] = [0; 7];
let mut passh: [u32; 7] = [0; 7];
let mut filter_passstart: [usize; 8] = [0; 8];
let mut padded_passstart: [usize; 8] = [0; 8];
let mut passstart: [usize; 8] = [0; 8];
for i in 0..7 {
passw[i] = (w as u32 + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
passh[i] = (h as u32 + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
if passw[i] == 0 {
passh[i] = 0;
}
if passh[i] == 0 {
passw[i] = 0;
};
}
filter_passstart[0] = 0;
padded_passstart[0] = 0;
passstart[0] = 0;
for i in 0..7 {
filter_passstart[i + 1] = filter_passstart[i] + if passw[i] != 0 && passh[i] != 0 {
passh[i] as usize * (1 + (passw[i] as usize * bpp + 7) / 8)
} else {
0
};
padded_passstart[i + 1] = padded_passstart[i] + passh[i] as usize * ((passw[i] as usize * bpp + 7) / 8) as usize;
passstart[i + 1] = passstart[i] + (passh[i] as usize * passw[i] as usize * bpp + 7) / 8;
}
(passw, passh, filter_passstart, padded_passstart, passstart)
}
fn adam7_deinterlace(out: &mut [u8], inp: &[u8], w: usize, h: usize, bpp: usize) {
let (passw, passh, _, _, passstart) = adam7_get_pass_values(w, h, bpp);
if bpp >= 8 {
for i in 0..7 {
let bytewidth = bpp / 8;
for y in 0..passh[i] {
for x in 0..passw[i] {
let pixelinstart = passstart[i] + (y * passw[i] + x) as usize * bytewidth;
let pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) as usize * w + ADAM7_IX[i] as usize + x as usize * ADAM7_DX[i] as usize) * bytewidth;
out[pixeloutstart..(bytewidth + pixeloutstart)]
.clone_from_slice(&inp[pixelinstart..(bytewidth + pixelinstart)])
}
}
}
} else {
for i in 0..7 {
let ilinebits = bpp * passw[i] as usize;
let olinebits = bpp * w;
for y in 0..passh[i] as usize {
for x in 0..passw[i] as usize {
let mut ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp) as usize;
let mut obp = ((ADAM7_IY[i] as usize + y * ADAM7_DY[i] as usize) * olinebits + (ADAM7_IX[i] as usize + x * ADAM7_DX[i] as usize) * bpp) as usize;
for _ in 0..bpp {
let bit = read_bit_from_reversed_stream(&mut ibp, inp);
set_bit_of_reversed_stream0(&mut obp, out, bit);
}
}
}
}
};
}
#[inline(always)]
fn read_bit_from_reversed_stream(bitpointer: &mut usize, bitstream: &[u8]) -> u8 {
let result = ((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 7))) & 1) as u8;
*bitpointer += 1;
result
}
fn set_bit_of_reversed_stream0(bitpointer: &mut usize, bitstream: &mut [u8], bit: u8) {
if bit != 0 {
bitstream[(*bitpointer) >> 3] |= bit << (7 - ((*bitpointer) & 7));
}
*bitpointer += 1;
}
fn set_bit_of_reversed_stream(bitpointer: &mut usize, bitstream: &mut [u8], bit: u8) {
if bit == 0 {
bitstream[(*bitpointer) >> 3] &= (!(1 << (7 - ((*bitpointer) & 7)))) as u8;
} else {
bitstream[(*bitpointer) >> 3] |= 1 << (7 - ((*bitpointer) & 7));
}
*bitpointer += 1;
}
#[inline]
pub fn lodepng_chunk_length(chunk: &[u8]) -> usize {
lodepng_read32bit_int(chunk) as usize
}
pub fn lodepng_chunk_generate_crc(chunk: &mut [u8]) {
let ch = ChunkRef::new(chunk).unwrap();
let length = ch.len();
let crc = ch.crc();
lodepng_set32bit_int(&mut chunk[8 + length..], crc);
}
pub(crate) fn chunk_append(out: &mut Vec<u8>, chunk: &[u8]) {
let total_chunk_length = lodepng_chunk_length(chunk) as usize + 12;
out.extend_from_slice(&chunk[0..total_chunk_length]);
}
fn check_png_color_validity(colortype: ColorType, bd: u32) -> Result<(), Error> {
match colortype {
ColorType::GREY => if !(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16) {
return Err(Error(37));
},
ColorType::PALETTE => if !(bd == 1 || bd == 2 || bd == 4 || bd == 8) {
return Err(Error(37));
},
ColorType::RGB | ColorType::GREY_ALPHA | ColorType::RGBA => if !(bd == 8 || bd == 16) {
return Err(Error(37));
},
_ => {
return Err(Error(31))
},
}
Ok(())
}
fn check_lode_color_validity(colortype: ColorType, bd: u32) -> Result<(), Error> {
match colortype {
ColorType::BGRA | ColorType::BGRX | ColorType::BGR if bd == 8 => {
Ok(())
},
ct => check_png_color_validity(ct, bd),
}
}
pub fn lodepng_color_mode_equal(a: &ColorMode, b: &ColorMode) -> bool {
a.colortype == b.colortype &&
a.bitdepth() == b.bitdepth() &&
a.key() == b.key() &&
a.palette() == b.palette()
}
const MAX_SUPPORTED_DEFLATE_LENGTH: usize = 258;
fn add_huffman_symbol(bp: &mut usize, compressed: &mut Vec<u8>, code: u32, bitlen: u32) {
add_bits_to_stream_reversed(bp, compressed, code, bitlen as usize);
}
fn add_bits_to_stream_reversed(bitpointer: &mut usize, bitstream: &mut Vec<u8>, value: u32, nbits: usize) {
for i in 0..nbits {
if ((*bitpointer) & 7) == 0 {
bitstream.push(0u8);
}
let end = bitstream.len() - 1;
bitstream[end] |= (((value >> (nbits - 1 - i)) & 1) as u8) << ((*bitpointer) & 7);
*bitpointer += 1;
}
}
fn add_bits_to_stream(bitpointer: &mut usize, bitstream: &mut Vec<u8>, value: u32, nbits: usize) {
for i in 0..nbits {
if ((*bitpointer) & 7) == 0 {
bitstream.push(0u8);
}
let end = bitstream.len() - 1;
bitstream[end] |= (((value >> i) & 1) as u8) << ((*bitpointer) & 7);
*bitpointer += 1;
}
}
fn read_bit_from_stream(bitpointer: &mut usize, bitstream: &[u8]) -> u8 {
let result = ((bitstream[*bitpointer >> 3] >> (*bitpointer & 7)) & 1u8) as u8;
*bitpointer += 1;
result
}
fn read_bits_from_stream(bitpointer: &mut usize, bitstream: &[u8], nbits: usize) -> u32 {
let mut result = 0;
for i in 0..nbits {
result += (((bitstream[*bitpointer >> 3] >> (*bitpointer & 7)) & 1u8) as u32) << i;
*bitpointer += 1;
}
result
}
const NUM_DISTANCE_SYMBOLS: usize = 32;
fn generate_fixed_distance_tree() -> Result<HuffmanTree, Error> {
let bitlen = vec![5; NUM_DISTANCE_SYMBOLS];
HuffmanTree::from_lengths(&bitlen, 15)
}
fn get_tree_inflate_fixed() -> Result<(HuffmanTree, HuffmanTree), Error> {
Ok((generate_fixed_lit_len_tree()?, generate_fixed_distance_tree()?))
}
pub const NUM_CODE_LENGTH_CODES: usize = 19;
pub const CLCL_ORDER: [u32; 19] = [
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
];
pub const NUM_DEFLATE_CODE_SYMBOLS: usize = 288;
pub const FIRST_LENGTH_CODE_INDEX: u32 = 257;
pub const LAST_LENGTH_CODE_INDEX: u32 = 285;
fn inflate_huffman_block(out: &mut Vec<u8>, inp: &[u8], bp: &mut usize, pos: &mut usize, btype: u32) -> Result<(), Error> {
let (ref mut tree_ll, ref mut tree_d) = if btype == 1 {
get_tree_inflate_fixed()?
} else {
assert_eq!(2, btype);
get_tree_inflate_dynamic(inp, bp)?
};
loop {
match tree_ll.decode_symbol(inp, bp) {
Some(code_ll @ 0..=255) => {
out.push(code_ll as u8);
(*pos) += 1;
},
Some(code_ll @ FIRST_LENGTH_CODE_INDEX..=LAST_LENGTH_CODE_INDEX) => {
let numextrabits_d;
let mut length: usize = LENGTHBASE[(code_ll - FIRST_LENGTH_CODE_INDEX) as usize] as usize;
let numextrabits_l = LENGTHEXTRA[(code_ll - FIRST_LENGTH_CODE_INDEX) as usize] as usize;
let inbitlength = inp.len() * 8;
if (*bp + numextrabits_l) > inbitlength {
return Err(Error(51));
}
length += read_bits_from_stream(bp, inp, numextrabits_l) as usize;
let code_d = tree_d.decode_symbol(inp, bp).ok_or_else(|| Error(if (*bp) > inp.len() * 8 { 10 } else { 11 }))?;
if code_d > 29 {
return Err(Error(18));
}
let mut distance = DISTANCEBASE[code_d as usize] as usize;
numextrabits_d = DISTANCEEXTRA[code_d as usize] as usize;
let inbitlength = inp.len() * 8;
if (*bp + numextrabits_d) > inbitlength {
return Err(Error(51));
}
distance += read_bits_from_stream(bp, inp, numextrabits_d) as usize;
let start = *pos;
if distance > start {
return Err(Error(52));
}
let mut backward = start - distance;
unsafe {
out.reserve(length);
out.set_len((*pos) + length);
}
let out_data = &mut out[..];
if distance < length {
for _ in 0..length {
out_data[*pos] = out_data[backward];
backward += 1;
(*pos) += 1;
}
} else {
let (src, dest) = out_data.split_at_mut(*pos);
let dest = &mut dest[0..length];
let src = &src[backward..backward + length];
dest.clone_from_slice(src);
*pos += length;
}
},
Some(256) => {
break;
},
_ => {
return Err(Error(if (*bp) > inp.len() * 8 { 10 } else { 11 }));
},
}
}
Ok(())
}
fn inflate_no_compression(out: &mut Vec<u8>, inp: &[u8], bp: &mut usize, pos: &mut usize) -> Result<(), Error> {
while ((*bp) & 7) != 0 {
(*bp) += 1;
}
let mut p = (*bp) / 8;
if p + 4 >= inp.len() {
return Err(Error(52));
}
let len = inp[p] as usize + 256 * inp[p + 1] as usize;
p += 2;
let nlen = inp[p] as usize + 256 * inp[p + 1] as usize;
p += 2;
if len + nlen != 65535 {
return Err(Error(21));
}
if p + len > inp.len() {
return Err(Error(23));
}
out.extend_from_slice(&inp[p..p + len]);
p += len;
(*pos) += len;
(*bp) = p * 8;
Ok(())
}
fn get_tree_inflate_dynamic(inp: &[u8], bp: &mut usize) -> Result<(HuffmanTree, HuffmanTree), Error> {
if (*bp) + 14 > (inp.len() << 3) {
return Err(Error(49));
}
let hlit = (read_bits_from_stream(bp, inp, 5) + 257) as usize;
let hdist = (read_bits_from_stream(bp, inp, 5) + 1) as usize;
let hclen = (read_bits_from_stream(bp, inp, 4) + 4) as usize;
if (*bp) + hclen as usize * 3 > (inp.len() << 3) {
return Err(Error(50));
}
let mut bitlen_cl = vec![0; NUM_CODE_LENGTH_CODES];
for &clcl in CLCL_ORDER.iter().take(hclen) {
bitlen_cl[clcl as usize] = read_bits_from_stream(bp, inp, 3);
}
let tree_cl = HuffmanTree::from_lengths(&bitlen_cl, 7)?;
let mut bitlen_ll = vec![0; NUM_DEFLATE_CODE_SYMBOLS];
let mut bitlen_d = vec![0; NUM_DISTANCE_SYMBOLS];
let mut i = 0;
while i < hlit + hdist {
match tree_cl.decode_symbol(inp, bp) {
Some(code @ 0..=15) => {
if i < hlit {
bitlen_ll[i] = code;
} else {
bitlen_d[i - hlit] = code;
}
i += 1;
},
Some(16) => {
let mut replength = 3;
let value;
if i == 0 {
return Err(Error(54));
}
let inbitlength = inp.len() * 8;
if (*bp + 2) > inbitlength {
return Err(Error(50));
}
replength += read_bits_from_stream(bp, inp, 2);
if i < hlit + 1 {
value = bitlen_ll[i - 1];
} else {
value = bitlen_d[i - hlit - 1];
}
let mut n = 0;
while n < replength {
if i >= hlit + hdist {
return Err(Error(13));
}
if i < hlit {
bitlen_ll[i] = value;
} else {
bitlen_d[i - hlit] = value;
}
i += 1;
n += 1
}
},
Some(17) => {
let mut replength = 3;
let inbitlength = inp.len() * 8;
if (*bp + 3) > inbitlength {
return Err(Error(50));
}
replength += read_bits_from_stream(bp, inp, 3);
let mut n = 0;
while n < replength {
if i >= hlit + hdist {
return Err(Error(14));
}
if i < hlit {
bitlen_ll[i] = 0;
} else {
bitlen_d[i - hlit] = 0;
}
i += 1;
n += 1
}
},
Some(18) => {
let mut replength = 11;
let inbitlength = inp.len() * 8;
if (*bp + 7) > inbitlength {
return Err(Error(50));
}
replength += read_bits_from_stream(bp, inp, 7);
let mut n = 0;
while n < replength {
if i >= hlit + hdist {
return Err(Error(15));
}
if i < hlit {
bitlen_ll[i] = 0;
} else {
bitlen_d[i - hlit] = 0;
}
i += 1;
n += 1;
}
},
Some(_) => {
return Err(Error({
let inbitlength = inp.len() * 8;
if (*bp) > inbitlength {
10
} else {
11
}
}));
},
_ => {
return Err(Error(16));
},
};
}
if bitlen_ll[256] == 0 {
return Err(Error(64));
}
let tree_ll = HuffmanTree::from_lengths(&bitlen_ll, 15)?;
let tree_d = HuffmanTree::from_lengths(&bitlen_d, 15)?;
Ok((tree_ll, tree_d))
}
fn generate_fixed_lit_len_tree() -> Result<HuffmanTree, Error> {
let mut bitlen = vec![8; NUM_DEFLATE_CODE_SYMBOLS];
for b in &mut bitlen[144..256] {
*b = 9;
}
for b in &mut bitlen[256..280] {
*b = 7;
}
HuffmanTree::from_lengths(&bitlen, 15)
}
pub(crate) fn lodepng_inflatev(inp: &[u8], _settings: &DecompressSettings) -> Result<Vec<u8>, Error> {
let mut out = Vec::with_capacity(inp.len() * 3/2);
let mut bp = 0;
let mut bfinal = 0;
let mut pos = 0;
while bfinal == 0 {
let mut btype: u32;
if bp + 2 >= inp.len() * 8 {
return Err(Error(52));
}
bfinal = read_bit_from_stream(&mut bp, inp);
btype = 1 * read_bit_from_stream(&mut bp, inp) as u32;
btype += 2 * read_bit_from_stream(&mut bp, inp) as u32;
if btype == 3 {
return Err(Error(20));
} else if btype == 0 {
inflate_no_compression(&mut out, inp, &mut bp, &mut pos)?;
} else {
inflate_huffman_block(&mut out, inp, &mut bp, &mut pos, btype)?;
}
}
Ok(out)
}
fn inflate(inp: &[u8], settings: &DecompressSettings) -> Result<Vec<u8>, Error> {
if let Some(cb) = settings.custom_inflate {
unsafe {
let mut outdata = ptr::null_mut();
let mut outsize = 0;
Error((cb)(&mut outdata, &mut outsize, inp.as_ptr(), inp.len(), settings)).to_result()?;
Ok(vec_from_raw(outdata, outsize))
}
} else {
lodepng_inflatev(inp, settings)
}
}
fn search_code_index(array: &[u32], value: u32) -> u32 {
let idx = match array.binary_search(&value) {Ok(x) | Err(x) => x};
if idx > 0 && array[idx] > value {
idx as u32 - 1
} else {
idx as u32
}
}
#[inline]
fn add_length_distance(values: &mut Vec<u32>, length: u32, distance: u32) {
let length_code = search_code_index(&LENGTHBASE, length);
let extra_length = length - LENGTHBASE[length_code as usize];
let dist_code = search_code_index(&DISTANCEBASE, distance);
let extra_distance = distance - DISTANCEBASE[dist_code as usize];
values.push(length_code + FIRST_LENGTH_CODE_INDEX);
values.push(extra_length);
values.push(dist_code);
values.push(extra_distance);
}
pub const HASH_NUM_VALUES: usize = 65536;
struct Hash {
pub head: Vec<i32>,
pub chain: Vec<u16>,
pub val: Vec<i32>,
pub headz: Vec<i32>,
pub chainz: Vec<u16>,
pub zeros: Vec<u16>,
}
impl Hash {
pub fn new(windowsize: usize) -> Result<Self, Error> {
let mut hash = Hash {
head: vec![-1; HASH_NUM_VALUES],
val: vec![-1; windowsize],
chain: vec![0; windowsize],
chainz: vec![0; windowsize],
zeros: vec![0; windowsize],
headz: vec![-1; MAX_SUPPORTED_DEFLATE_LENGTH + 1],
};
for (i, c) in hash.chain.iter_mut().enumerate() {
*c = i as u16;
}
for (i, c) in hash.chainz.iter_mut().enumerate() {
*c = i as u16;
}
Ok(hash)
}
}
#[inline(always)]
fn get_hash(data: &[u8], pos: usize) -> u16 {
let mut result = 0;
if pos + 2 < data.len() {
result ^= (data[pos + 0] as u32) << 0;
result ^= (data[pos + 1] as u32) << 4;
result ^= (data[pos + 2] as u32) << 8;
} else {
if pos >= data.len() {
return 0;
}
for i in 0..data.len() - pos {
result ^= (Wrapping(data[pos + i]) << (i * 8)).0 as u32
}
}
result as u16
}
#[inline(always)]
fn count_zeros(data: &[u8], pos: usize) -> u32 {
data[pos..].iter()
.take(MAX_SUPPORTED_DEFLATE_LENGTH)
.take_while(|&d| *d == 0)
.count() as u32
}
#[inline]
fn update_hash_chain(hash: &mut Hash, wpos: u32, hashval: u32, numzeros: u16) {
hash.val[wpos as usize] = hashval as i32;
if hash.head[hashval as usize] != -1 {
hash.chain[wpos as usize] = hash.head[hashval as usize] as u16;
}
hash.head[hashval as usize] = wpos as i32;
hash.zeros[wpos as usize] = numzeros;
if hash.headz[numzeros as usize] != -1 {
hash.chainz[wpos as usize] = hash.headz[numzeros as usize] as u16;
}
hash.headz[numzeros as usize] = wpos as i32;
}
fn deflate_no_compression(data: &[u8]) -> Result<Vec<u8>, Error> {
let numdeflateblocks = (data.len() + 65534) / 65535;
let mut datapos = 0;
let mut out = Vec::with_capacity(data.len()*5/4);
for i in 0..numdeflateblocks {
let bfinal = (i == numdeflateblocks - 1) as usize;
let btype = 0;
let firstbyte = (bfinal + ((btype & 1) << 1) + ((btype & 2) << 1)) as u8;
out.push(firstbyte);
let len = (data.len() - datapos).min(65535);
let nlen = 65535 - len;
out.push((len & 255) as u8);
out.push((len >> 8) as u8);
out.push((nlen & 255) as u8);
out.push((nlen >> 8) as u8);
let mut j = 0;
while j < 65535 && datapos < data.len() {
out.push(data[datapos]);
datapos += 1;
j += 1
}
}
Ok(out)
}
fn write_lz77_data(bp: &mut usize, out: &mut Vec<u8>, lz77_encoded: &[u32], tree_ll: &HuffmanTree, tree_d: &HuffmanTree) {
let mut i = 0;
while i != lz77_encoded.len() {
let val = lz77_encoded[i as usize];
add_huffman_symbol(bp, out, tree_ll.code(val), tree_ll.length(val));
if val > 256 {
let length_index = (val - FIRST_LENGTH_CODE_INDEX as u32) as usize;
let n_length_extra_bits = LENGTHEXTRA[length_index] as usize;
i += 1;
let length_extra_bits = lz77_encoded[i as usize];
i += 1;
let distance_code = lz77_encoded[i as usize];
let distance_index = distance_code as usize;
let n_distance_extra_bits = DISTANCEEXTRA[distance_index];
i += 1;
let distance_extra_bits = lz77_encoded[i as usize];
add_bits_to_stream(bp, out, length_extra_bits, n_length_extra_bits);
add_huffman_symbol(bp, out, tree_d.code(distance_code), tree_d.length(distance_code));
add_bits_to_stream(bp, out, distance_extra_bits, n_distance_extra_bits as usize);
};
i += 1
}
}
fn deflate_dynamic(
out: &mut Vec<u8>,
bp: &mut usize,
hash: &mut Hash,
data: &[u8],
datapos: usize,
dataend: usize,
settings: &CompressSettings,
bfinal: u32,
) -> Result<(), Error> {
let datasize = dataend - datapos;
let data = &data[0..dataend];
let lz77_encoded = if settings.use_lz77 != 0 {
encode_lz77(hash, data, datapos, settings.windowsize, settings.minmatch, settings.nicematch, settings.lazymatching != 0)?
} else {
let mut t = Vec::with_capacity(datasize);
for &d in &data[datapos..dataend] {
t.push(d as u32);
}
t
};
let mut frequencies_ll = [0; 286];
let mut frequencies_d = [0; 30];
let mut l = &lz77_encoded[..];
while !l.is_empty() {
let symbol = l[0];
frequencies_ll[symbol as usize] += 1;
let skip = if symbol > 256 {
let dist = l[2];
frequencies_d[dist as usize] += 1;
4
} else {
1
};
l = &l[skip..];
}
frequencies_ll[256] = 1;
let tree_ll = HuffmanTree::from_frequencies(&frequencies_ll, 257, 15)?;
let tree_d = HuffmanTree::from_frequencies(&frequencies_d, 2, 15)?;
let numcodes_ll = tree_ll.numcodes.min(286);
let numcodes_d = tree_d.numcodes.min(30);
let mut bitlen_lld = Vec::with_capacity(256);
for i in 0..numcodes_ll {
bitlen_lld.push(tree_ll.length(i as u32));
}
for i in 0..numcodes_d {
bitlen_lld.push(tree_d.length(i as u32));
}
let mut bitlen_lld_e = Vec::with_capacity(256);
let mut i = 0;
while i < bitlen_lld.len() {
let mut j = 0;
while i + j + 1 < (bitlen_lld.len()) && bitlen_lld[i + j + 1] == bitlen_lld[i] {
j += 1;
}
if bitlen_lld[i] == 0 && j >= 2 {
j += 1;
if j <= 10 {
bitlen_lld_e.push(17);
bitlen_lld_e.push(j as u32 - 3);
} else {
if j > 138 {
j = 138;
}
bitlen_lld_e.push(18);
bitlen_lld_e.push(j as u32 - 11);
}
i += j - 1;
} else if j >= 3 {
let num = j / 6;
let rest = j % 6;
bitlen_lld_e.push(bitlen_lld[i]);
for _ in 0..num {
bitlen_lld_e.push(16);
bitlen_lld_e.push(6 - 3);
}
if rest >= 3 {
bitlen_lld_e.push(16);
bitlen_lld_e.push(rest as u32 - 3);
} else {
j -= rest;
}
i += j;
} else {
bitlen_lld_e.push(bitlen_lld[i]);
}
i += 1
}
let mut frequencies_cl = [0; NUM_CODE_LENGTH_CODES];
let mut i = 0;
while i != bitlen_lld_e.len() {
frequencies_cl[bitlen_lld_e[i] as usize] += 1;
if bitlen_lld_e[i] >= 16 {
i += 1;
}
i += 1
}
let tree_cl = HuffmanTree::from_frequencies(&frequencies_cl, frequencies_cl.len(), 7)?;
let mut bitlen_cl = vec![0; tree_cl.numcodes];
for i in 0..tree_cl.numcodes {
bitlen_cl[i] = tree_cl.length(CLCL_ORDER[i]);
}
while bitlen_cl[bitlen_cl.len() - 1] == 0 && bitlen_cl.len() > 4 {
let s = bitlen_cl.len() - 1;
bitlen_cl.resize(s, 0);
}
if ((*bp) & 7) == 0 {
out.push(0);
}
*out.last_mut().unwrap() |= ((bfinal as u32) << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
if ((*bp) & 7) == 0 {
out.push(0);
}
*out.last_mut().unwrap() |= (0 << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
if ((*bp) & 7) == 0 {
out.push(0);
}
*out.last_mut().unwrap() |= (1 << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
let hlit = (numcodes_ll - 257) as u32;
let hdist = (numcodes_d - 1) as u32;
let mut hclen = (bitlen_cl.len() as u32) - 4;
while bitlen_cl[hclen as usize + 4 - 1] == 0 && hclen > 0 {
hclen -= 1;
}
add_bits_to_stream(bp, out, hlit, 5);
add_bits_to_stream(bp, out, hdist, 5);
add_bits_to_stream(bp, out, hclen, 4);
for &b in &bitlen_cl[0..hclen as usize + 4] {
add_bits_to_stream(bp, out, b, 3);
}
let mut i = 0;
while i != bitlen_lld_e.len() {
add_huffman_symbol(bp, out, tree_cl.code(bitlen_lld_e[i]), tree_cl.length(bitlen_lld_e[i]));
if bitlen_lld_e[i] == 16 {
i += 1;
add_bits_to_stream(bp, out, bitlen_lld_e[i], 2);
} else if bitlen_lld_e[i] == 17 {
i += 1;
add_bits_to_stream(bp, out, bitlen_lld_e[i], 3);
} else if bitlen_lld_e[i] == 18 {
i += 1;
add_bits_to_stream(bp, out, bitlen_lld_e[i], 7);
};
i += 1;
}
write_lz77_data(bp, out, &lz77_encoded, &tree_ll, &tree_d);
if tree_ll.length(256) == 0 {
return Err(Error(64));
}
add_huffman_symbol(bp, out, tree_ll.code(256), tree_ll.length(256));
Ok(())
}
fn deflate_fixed(out: &mut Vec<u8>, bp: &mut usize, hash: &mut Hash, data: &[u8], datapos: usize, dataend: usize, settings: &CompressSettings, bfinal: u32) -> Result<(), Error> {
let tree_ll = generate_fixed_lit_len_tree()?;
let tree_d = generate_fixed_distance_tree()?;
if ((*bp) & 7) == 0 {
out.push(0);
}
let end = out.len() - 1;
out[end] |= (bfinal << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
if ((*bp) & 7) == 0 {
out.push(0);
}
let end = out.len() - 1;
out[end] |= (1 << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
if ((*bp) & 7) == 0 {
out.push(0);
}
let end = out.len() - 1;
out[end] |= (0 << ((*bp as u32) & 7)) as u8;
(*bp) += 1;
if settings.use_lz77 != 0 {
let lz77_encoded = encode_lz77(
hash,
data,
datapos,
settings.windowsize,
settings.minmatch,
settings.nicematch,
settings.lazymatching != 0,
)?;
write_lz77_data(bp, out, &lz77_encoded, &tree_ll, &tree_d);
} else {
for &d in &data[datapos..dataend] {
add_huffman_symbol(bp, out, tree_ll.code(d as u32), tree_ll.length(d as u32));
}
}
add_huffman_symbol(bp, out, tree_ll.code(256), tree_ll.length(256));
Ok(())
}
pub(crate) fn lodepng_deflatev(inp: &[u8], settings: &CompressSettings) -> Result<Vec<u8>, Error> {
let mut blocksize: usize;
let mut bp = 0;
if settings.btype > 2 {
return Err(Error(61));
} else if settings.btype == 0 {
return deflate_no_compression(inp);
} else if settings.btype == 1 {
blocksize = inp.len();
} else {
blocksize = inp.len() / 8 + 8;
if blocksize < 65536 {
blocksize = 65536;
}
if blocksize > 262144 {
blocksize = 262144;
};
}
let mut numdeflateblocks = (inp.len() + blocksize - 1) / blocksize;
if numdeflateblocks == 0 {
numdeflateblocks = 1;
}
let mut hash = Hash::new(settings.windowsize as usize)?;
let mut out = Vec::new();
for i in 0..numdeflateblocks {
let final_ = numdeflateblocks - 1 == i;
let start = i * blocksize;
let end = (start + blocksize).min(inp.len());
if settings.btype == 1 {
deflate_fixed(&mut out, &mut bp, &mut hash, inp, start, end, settings, final_ as u32)?;
} else {
debug_assert_eq!(2, settings.btype);
deflate_dynamic(&mut out, &mut bp, &mut hash, inp, start, end, settings, final_ as u32)?;
}
}
Ok(out)
}
fn deflate(inp: &[u8], settings: &CompressSettings) -> Result<Vec<u8>, Error> {
if let Some(cb) = settings.custom_deflate {
unsafe {
let mut outdata = ptr::null_mut();
let mut outsize = 0;
Error((cb)(&mut outdata, &mut outsize, inp.as_ptr(), inp.len(), settings)).to_result()?;
Ok(vec_from_raw(outdata, outsize))
}
} else {
lodepng_deflatev(inp, settings)
}
}
fn update_adler32(adler: u32, data: &[u8]) -> u32 {
let mut s1 = adler & 65535;
let mut s2 = (adler >> 16) & 65535;
for part in data.chunks(5550) {
for &v in part {
s1 += v as u32;
s2 += s1;
}
s1 %= 65521;
s2 %= 65521;
}
(s2 << 16) | s1
}
fn adler32(data: &[u8]) -> u32 {
update_adler32(1, data)
}
pub fn lodepng_zlib_decompress(inp: &[u8], settings: &DecompressSettings) -> Result<Vec<u8>, Error> {
if inp.len() < 2 {
return Err(Error(53));
}
if (inp[0] as u32 * 256 + inp[1] as u32) % 31 != 0 {
return Err(Error(24));
}
let cm = inp[0] as u32 & 15;
let cinfo = ((inp[0] as u32) >> 4) & 15;
let fdict = ((inp[1] as u32) >> 5) & 1;
if cm != 8 || cinfo > 7 {
return Err(Error(25));
}
if fdict != 0 {
return Err(Error(26));
}
let out = inflate(&inp[2..], settings)?;
if (! cfg!(fuzzing)) && settings.ignore_adler32 == 0 {
let adler32_val = lodepng_read32bit_int(&inp[(inp.len() - 4)..]);
let checksum = adler32(&out);
if checksum != adler32_val {
return Err(Error(58));
};
}
Ok(out)
}
pub fn zlib_decompress(inp: &[u8], settings: &DecompressSettings) -> Result<Vec<u8>, Error> {
if let Some(cb) = settings.custom_zlib {
unsafe {
let mut outdata = ptr::null_mut();
let mut outsize = 0;
Error((cb)(&mut outdata, &mut outsize, inp.as_ptr(), inp.len(), settings)).to_result()?;
Ok(vec_from_raw(outdata, outsize))
}
} else {
lodepng_zlib_decompress(inp, settings)
}
}
pub fn lodepng_zlib_compress(outv: &mut Vec<u8>, inp: &[u8], settings: &CompressSettings) -> Result<(), Error> {
let cmf = 120;
let flevel = 0;
let fdict = 0;
let mut cmfflg = 256 * cmf + fdict * 32 + flevel * 64;
let fcheck = 31 - cmfflg % 31;
cmfflg += fcheck;
outv.push((cmfflg >> 8) as u8);
outv.push((cmfflg & 255) as u8);
let deflated = deflate(inp, settings)?;
let adler32_val = adler32(inp);
outv.extend_from_slice(&deflated);
lodepng_add32bit_int(outv, adler32_val);
Ok(())
}
pub fn zlib_compress(inp: &[u8], settings: &CompressSettings) -> Result<Vec<u8>, Error> {
let mut out = Vec::with_capacity(inp.len()/2);
if let Some(cb) = settings.custom_zlib {
unsafe {
let mut outdata = ptr::null_mut();
let mut outsize = 0;
Error((cb)(&mut outdata, &mut outsize, inp.as_ptr(), inp.len(), settings)).to_result()?;
Ok(vec_from_raw(outdata, outsize))
}
} else {
lodepng_zlib_compress(&mut out, inp, settings)?;
Ok(out)
}
}
pub const DEFAULT_WINDOWSIZE: usize = 2048;
const LODEPNG_CRC32_TABLE: [u32; 256] = [
0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035,
249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049,
498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639,
325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317,
997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443,
901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665,
651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303,
671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565,
1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059,
2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297,
1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223,
1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405,
1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995,
1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649,
1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015,
1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989,
3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523,
3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377,
4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879,
4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637,
3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859,
3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161,
3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815,
3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221,
2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371,
2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881,
2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567,
2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701,
2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035,
2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897,
3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431,
3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117
];
pub fn lodepng_crc32(data: &[u8]) -> u32 {
let mut r = 4294967295u32;
for &d in data {
r = LODEPNG_CRC32_TABLE[((r ^ d as u32) & 255) as usize] ^ (r >> 8);
}
r ^ 4294967295
}
impl Drop for Info {
fn drop(&mut self) {
unsafe {
self.clear_text();
self.clear_itext();
for &i in &self.unknown_chunks_data {
lodepng_free(i as *mut _);
}
}
}
}
pub fn lodepng_convert(out: &mut [u8], inp: &[u8], mode_out: &ColorMode, mode_in: &ColorMode, w: u32, h: u32) -> Result<(), Error> {
let numpixels = w as usize * h as usize;
if lodepng_color_mode_equal(mode_out, mode_in) {
let numbytes = mode_in.raw_size(w, h);
out[..numbytes].clone_from_slice(&inp[..numbytes]);
return Ok(());
}
let mut tree = ColorTree::new();
if mode_out.colortype == ColorType::PALETTE {
let mut palette = mode_out.palette();
let palsize = 1 << mode_out.bitdepth();
if palette.is_empty() {
palette = mode_in.palette();
}
palette = &palette[0..palette.len().min(palsize)];
for (i, p) in palette.iter().enumerate() {
tree.insert((p.r, p.g, p.b, p.a), i as u16);
}
}
if mode_in.bitdepth() == 16 && mode_out.bitdepth() == 16 {
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba16(inp, i, mode_in);
rgba16_to_pixel(out, i, mode_out, r, g, b, a);
}
} else if mode_out.bitdepth() == 8 && mode_out.colortype == ColorType::RGBA {
get_pixel_colors_rgba8(out, numpixels as usize, true, inp, mode_in);
} else if mode_out.bitdepth() == 8 && mode_out.colortype == ColorType::RGB {
get_pixel_colors_rgba8(out, numpixels as usize, false, inp, mode_in);
} else {
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba8(inp, i, mode_in);
rgba8_to_pixel(out, i, mode_out, &mut tree, r, g, b, a)?;
}
}
Ok(())
}
fn postprocess_scanlines(out: &mut [u8], inp: &mut [u8], w: usize, h: usize, info_png: &Info) -> Result<(), Error> {
let bpp = info_png.color.bpp() as usize;
if bpp == 0 {
return Err(Error(31));
}
if info_png.interlace_method == 0 {
if bpp < 8 && w as usize * bpp != ((w as usize * bpp + 7) / 8) * 8 {
unfilter_aliased(inp, 0, 0, w, h, bpp)?;
remove_padding_bits(out, inp, w as usize * bpp, ((w as usize * bpp + 7) / 8) * 8, h);
} else {
unfilter(out, inp, w, h, bpp)?;
};
} else {
let (passw, passh, filter_passstart, padded_passstart, passstart) = adam7_get_pass_values(w, h, bpp);
for i in 0..7 {
unfilter_aliased(inp, padded_passstart[i], filter_passstart[i], passw[i] as usize, passh[i] as usize, bpp)?;
if bpp < 8 {
remove_padding_bits_aliased(
inp,
passstart[i],
padded_passstart[i],
passw[i] as usize * bpp,
((passw[i] as usize * bpp + 7) / 8) * 8,
passh[i] as usize,
);
};
}
adam7_deinterlace(out, inp, w, h, bpp);
}
Ok(())
}
fn unfilter(out: &mut [u8], inp: &[u8], w: usize, h: usize, bpp: usize) -> Result<(), Error> {
let mut prevline = None;
let bytewidth = (bpp + 7) / 8;
let linebytes = (w * bpp + 7) / 8;
let in_linebytes = 1 + linebytes;
for (out_line, in_line) in out.chunks_mut(linebytes).zip(inp.chunks(in_linebytes)).take(h) {
let filter_type = in_line[0];
unfilter_scanline(out_line, &in_line[1..], prevline, bytewidth, filter_type, linebytes)?;
prevline = Some(out_line);
}
Ok(())
}
fn unfilter_aliased(inout: &mut [u8], out_off: usize, in_off: usize, w: usize, h: usize, bpp: usize) -> Result<(), Error> {
let mut prevline = None;
let bytewidth = (bpp + 7) / 8;
let linebytes = (w * bpp + 7) / 8;
for y in 0..h as usize {
let outindex = linebytes * y;
let inindex = (1 + linebytes) * y;
let filter_type = inout[in_off + inindex];
unfilter_scanline_aliased(inout, out_off + outindex, in_off + inindex + 1, prevline, bytewidth, filter_type, linebytes)?;
prevline = Some(out_off + outindex);
}
Ok(())
}
fn unfilter_scanline(recon: &mut [u8], scanline: &[u8], precon: Option<&[u8]>, bytewidth: usize, filter_type: u8, length: usize) -> Result<(), Error> {
match filter_type {
0 => recon.clone_from_slice(scanline),
1 => {
recon[0..bytewidth].clone_from_slice(&scanline[0..bytewidth]);
for i in bytewidth..length {
recon[i] = scanline[i].wrapping_add(recon[i - bytewidth]);
}
},
2 => if let Some(precon) = precon {
for i in 0..length {
recon[i] = scanline[i].wrapping_add(precon[i]);
}
} else {
recon.clone_from_slice(scanline);
},
3 => if let Some(precon) = precon {
for i in 0..bytewidth {
recon[i] = scanline[i].wrapping_add(precon[i] >> 1);
}
for i in bytewidth..length {
let t = recon[i - bytewidth] as u16 + precon[i] as u16;
recon[i] = scanline[i].wrapping_add((t >> 1) as u8);
}
} else {
recon[0..bytewidth].clone_from_slice(&scanline[0..bytewidth]);
for i in bytewidth..length {
recon[i] = scanline[i].wrapping_add(recon[i - bytewidth] >> 1);
}
},
4 => if let Some(precon) = precon {
for i in 0..bytewidth {
recon[i] = scanline[i].wrapping_add(precon[i]);
}
for i in bytewidth..length {
recon[i] = scanline[i].wrapping_add(paeth_predictor(
recon[i - bytewidth] as i16,
precon[i] as i16,
precon[i - bytewidth] as i16,
));
}
} else {
recon[0..bytewidth].clone_from_slice(&scanline[0..bytewidth]);
for i in bytewidth..length {
recon[i] = scanline[i].wrapping_add(recon[i - bytewidth]);
}
},
_ => return Err(Error(36)),
}
Ok(())
}
fn unfilter_scanline_aliased(inout: &mut [u8], recon: usize, scanline: usize, precon: Option<usize>, bytewidth: usize, filter_type: u8, length: usize) -> Result<(), Error> {
match filter_type {
0 => for i in 0..length {
inout[recon + i] = inout[scanline + i];
},
1 => {
for i in 0..bytewidth {
inout[recon + i] = inout[scanline + i];
}
for i in bytewidth..length {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[recon + i - bytewidth]);
}
},
2 => if let Some(precon) = precon {
for i in 0..length {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[precon + i]);
}
} else {
for i in 0..length {
inout[recon + i] = inout[scanline + i];
}
},
3 => if let Some(precon) = precon {
for i in 0..bytewidth {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[precon + i] >> 1);
}
for i in bytewidth..length {
let t = inout[recon + i - bytewidth] as u16 + inout[precon + i] as u16;
inout[recon + i] = inout[scanline + i].wrapping_add((t >> 1) as u8);
}
} else {
for i in 0..bytewidth {
inout[recon + i] = inout[scanline + i];
}
for i in bytewidth..length {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[recon + i - bytewidth] >> 1);
}
},
4 => if let Some(precon) = precon {
for i in 0..bytewidth {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[precon + i]);
}
for i in bytewidth..length {
inout[recon + i] = inout[scanline + i].wrapping_add(paeth_predictor(
inout[recon + i - bytewidth] as i16,
inout[precon + i] as i16,
inout[precon + i - bytewidth] as i16,
));
}
} else {
for i in 0..bytewidth {
inout[recon + i] = inout[scanline + i];
}
for i in bytewidth..length {
inout[recon + i] = inout[scanline + i].wrapping_add(inout[recon + i - bytewidth]);
}
},
_ => return Err(Error(36)),
}
Ok(())
}
fn remove_padding_bits(out: &mut [u8], inp: &[u8], olinebits: usize, ilinebits: usize, h: usize) {
let diff = ilinebits - olinebits;
let mut ibp = 0;
let mut obp = 0;
for _ in 0..h {
for _ in 0..olinebits {
let bit = read_bit_from_reversed_stream(&mut ibp, inp);
set_bit_of_reversed_stream(&mut obp, out, bit);
}
ibp += diff;
}
}
fn remove_padding_bits_aliased(inout: &mut [u8], out_off: usize, in_off: usize, olinebits: usize, ilinebits: usize, h: usize) {
let diff = ilinebits - olinebits;
let mut ibp = 0;
let mut obp = 0;
for _ in 0..h {
for _ in 0..olinebits {
let bit = read_bit_from_reversed_stream(&mut ibp, &inout[in_off..]);
set_bit_of_reversed_stream(&mut obp, &mut inout[out_off..], bit);
}
ibp += diff;
}
}
fn adam7_interlace(out: &mut [u8], inp: &[u8], w: usize, h: usize, bpp: usize) {
let (passw, passh, _, _, passstart) = adam7_get_pass_values(w, h, bpp);
let bpp = bpp;
if bpp >= 8 {
for i in 0..7 {
let bytewidth = bpp / 8;
for y in 0..passh[i] as usize {
for x in 0..passw[i] as usize {
let pixelinstart = ((ADAM7_IY[i] as usize + y * ADAM7_DY[i] as usize) * w as usize + ADAM7_IX[i] as usize + x * ADAM7_DX[i] as usize) * bytewidth;
let pixeloutstart = passstart[i] + (y * passw[i] as usize + x) * bytewidth;
out[pixeloutstart..(bytewidth + pixeloutstart)]
.clone_from_slice(&inp[pixelinstart..(bytewidth + pixelinstart)]);
}
}
}
} else {
for i in 0..7 {
let ilinebits = bpp * passw[i] as usize;
let olinebits = bpp * w;
for y in 0..passh[i] as usize {
for x in 0..passw[i] as usize {
let mut ibp = (ADAM7_IY[i] as usize + y * ADAM7_DY[i] as usize) * olinebits + (ADAM7_IX[i] as usize + x * ADAM7_DX[i] as usize) * bpp;
let mut obp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
for _ in 0..bpp {
let bit = read_bit_from_reversed_stream(&mut ibp, inp);
set_bit_of_reversed_stream(&mut obp, out, bit);
}
}
}
}
};
}
pub fn lodepng_inspect(decoder: &DecoderSettings, inp: &[u8], read_chunks: bool) -> Result<(Info, usize, usize), Error> {
if inp.len() < 33 {
return Err(Error(27));
}
let mut info_png = Info::new();
if inp[0..8] != [137, 80, 78, 71, 13, 10, 26, 10] {
return Err(Error(28));
}
let mut chunks = ChunksIterFallible {data: &inp[8..]};
let ihdr = chunks.next().ok_or(Error(28))??;
if &ihdr.name() != b"IHDR" {
return Err(Error(29));
}
if ihdr.len() != 13 {
return Err(Error(94));
}
let w = lodepng_read32bit_int(&inp[16..]) as usize;
let h = lodepng_read32bit_int(&inp[20..]) as usize;
let bitdepth = inp[24];
if bitdepth == 0 || bitdepth > 16 {
return Err(Error(29));
}
info_png.color.set_bitdepth(inp[24] as u32);
info_png.color.colortype = match inp[25] {
0 => ColorType::GREY,
2 => ColorType::RGB,
3 => ColorType::PALETTE,
4 => ColorType::GREY_ALPHA,
6 => ColorType::RGBA,
_ => return Err(Error(31)),
};
info_png.compression_method = inp[26] as u32;
info_png.filter_method = inp[27] as u32;
info_png.interlace_method = inp[28] as u32;
if w == 0 || h == 0 {
return Err(Error(93));
}
if decoder.ignore_crc == 0 && !ihdr.check_crc() {
return Err(Error(57));
}
if info_png.compression_method != 0 {
return Err(Error(32));
}
if info_png.filter_method != 0 {
return Err(Error(33));
}
if info_png.interlace_method > 1 {
return Err(Error(34));
}
if read_chunks {
for ch in chunks {
let ch = ch?;
match &ch.name() {
b"IDAT" | b"IEND" => break,
b"PLTE" => {
read_chunk_plte(&mut info_png.color, ch.data())?;
},
b"tRNS" => {
read_chunk_trns(&mut info_png.color, ch.data())?;
},
b"bKGD" => {
read_chunk_bkgd(&mut info_png, ch.data())?;
},
_ => {},
}
}
}
check_png_color_validity(info_png.color.colortype, info_png.color.bitdepth())?;
Ok((info_png, w, h))
}
fn decode_generic(state: &mut State, inp: &[u8]) -> Result<(Vec<u8>, usize, usize), Error> {
let mut found_iend = false;
let mut unknown = false;
let mut critical_pos = ChunkPosition::IHDR;
let (info, w, h) = lodepng_inspect(&state.decoder, inp, false)?;
state.info_png = info;
let numpixels = match w.checked_mul(h) {
Some(n) => n,
None => {
return Err(Error(92));
},
};
if numpixels > 268435455 {
return Err(Error(92));
}
let mut idat = Vec::with_capacity(inp.len() - 33);
let chunks = ChunksIterFallible {
data: &inp[33..],
};
for ch in chunks {
let ch = ch?;
let data = ch.data();
match &ch.name() {
b"IDAT" => {
idat.extend_from_slice(data);
critical_pos = ChunkPosition::IDAT;
},
b"IEND" => {
found_iend = true;
},
b"PLTE" => {
read_chunk_plte(&mut state.info_png.color, data)?;
critical_pos = ChunkPosition::PLTE;
},
b"tRNS" => {
read_chunk_trns(&mut state.info_png.color, data)?;
},
b"bKGD" => {
read_chunk_bkgd(&mut state.info_png, data)?;
},
b"tEXt" => if state.decoder.read_text_chunks != 0 {
read_chunk_text(&mut state.info_png, data)?;
},
b"zTXt" => if state.decoder.read_text_chunks != 0 {
read_chunk_ztxt(&mut state.info_png, &state.decoder.zlibsettings, data)?;
},
b"iTXt" => if state.decoder.read_text_chunks != 0 {
read_chunk_itxt(&mut state.info_png, &state.decoder.zlibsettings, data)?;
},
b"tIME" => {
read_chunk_time(&mut state.info_png, data)?;
},
b"pHYs" => {
read_chunk_phys(&mut state.info_png, data)?;
},
_ => {
if !ch.is_ancillary() {
return Err(Error(69));
}
unknown = true;
if state.decoder.remember_unknown_chunks != 0 {
state.info_png.push_unknown_chunk(critical_pos, ch.whole_chunk_data())?;
}
},
};
if state.decoder.ignore_crc == 0 && !unknown && !ch.check_crc() {
return Err(Error(57));
}
if found_iend {
break;
}
}
let predict = if state.info_png.interlace_method == 0 {
state.info_png.color.raw_size_idat(w, h) + h
} else {
let color = &state.info_png.color;
let mut predict = color.raw_size_idat((w + 7) >> 3, (h + 7) >> 3) + ((h + 7) >> 3) as usize;
if w > 4 {
predict += color.raw_size_idat((w + 3) >> 3, (h + 7) >> 3) + ((h + 7) >> 3) as usize;
}
predict += color.raw_size_idat((w + 3) >> 2, (h + 3) >> 3) + ((h + 3) >> 3) as usize;
if w > 2 {
predict += color.raw_size_idat((w + 1) >> 2, (h + 3) >> 2) + ((h + 3) >> 2) as usize;
}
predict += color.raw_size_idat((w + 1) >> 1, (h + 1) >> 2) + ((h + 1) >> 2) as usize;
if w > 1 {
predict += color.raw_size_idat((w + 0) >> 1, (h + 1) >> 1) + ((h + 1) >> 1) as usize;
}
predict += color.raw_size_idat(w + 0, (h + 0) >> 1) + ((h + 0) >> 1) as usize;
predict
};
let mut scanlines = zlib_decompress(&idat, &state.decoder.zlibsettings)?;
if scanlines.len() != predict {
return Err(Error(91));
}
let mut out = vec![0; state.info_png.color.raw_size(w as u32, h as u32)];
postprocess_scanlines(&mut out, &mut scanlines, w, h, &state.info_png)?;
Ok((out, w, h))
}
pub fn lodepng_decode(state: &mut State, inp: &[u8]) -> Result<(Vec<u8>, usize, usize), Error> {
let (decoded, w, h) = decode_generic(state, inp)?;
if state.decoder.color_convert == 0 || lodepng_color_mode_equal(&state.info_raw, &state.info_png.color) {
if state.decoder.color_convert == 0 {
state.info_raw = state.info_png.color.clone();
}
Ok((decoded, w, h))
} else {
if !(state.info_raw.colortype == ColorType::RGB || state.info_raw.colortype == ColorType::RGBA) && (state.info_raw.bitdepth() != 8) {
return Err(Error(56));
}
let mut out = vec![0; state.info_raw.raw_size(w as u32, h as u32)];
lodepng_convert(&mut out, &decoded, &state.info_raw, &state.info_png.color, w as u32, h as u32)?;
Ok((out, w, h))
}
}
pub fn lodepng_decode_memory(inp: &[u8], colortype: ColorType, bitdepth: u32) -> Result<(Vec<u8>, usize, usize), Error> {
let mut state = Decoder::new();
state.info_raw_mut().colortype = colortype;
state.info_raw_mut().set_bitdepth(bitdepth);
lodepng_decode(&mut state.state, inp)
}
pub fn lodepng_decode_file(filename: &Path, colortype: ColorType, bitdepth: u32) -> Result<(Vec<u8>, usize, usize), Error> {
let buf = lodepng_load_file(filename)?;
lodepng_decode_memory(&buf, colortype, bitdepth)
}
pub fn lodepng_buffer_file(out: &mut [u8], filename: &Path) -> Result<(), Error> {
fs::File::open(filename)
.and_then(|mut f| f.read_exact(out))
.map_err(|_| Error(78))?;
Ok(())
}
pub fn lodepng_load_file(filename: &Path) -> Result<Vec<u8>, Error> {
fs::read(filename).map_err(|_| Error(78))
}
pub fn lodepng_save_file(buffer: &[u8], filename: &Path) -> Result<(), Error> {
fs::write(filename, buffer)
.map_err(|_| Error(79))
}
fn add_unknown_chunks(out: &mut Vec<u8>, data: &[u8]) -> Result<(), Error> {
let chunks = ChunksIterFallible {data};
for ch in chunks {
chunk_append(out, ch?.whole_chunk_data());
}
Ok(())
}
pub const LODEPNG_VERSION_STRING: &[u8] = b"20161127\0";
pub fn lodepng_encode(image: &[u8], w: u32, h: u32, state: &mut State) -> Result<Vec<u8>, Error> {
let w = w as usize;
let h = h as usize;
let mut info = state.info_png.clone();
if (info.color.colortype == ColorType::PALETTE || state.encoder.force_palette != 0) && (info.color.palette().is_empty() || info.color.palette().len() > 256) {
return Err(Error(68));
}
if state.encoder.auto_convert != 0 {
info.color = auto_choose_color(image, w, h, &state.info_raw)?;
}
if state.encoder.zlibsettings.btype > 2 {
return Err(Error(61));
}
if state.info_png.interlace_method > 1 {
return Err(Error(71));
}
check_png_color_validity(info.color.colortype, info.color.bitdepth())?;
check_lode_color_validity(state.info_raw.colortype, state.info_raw.bitdepth())?;
let data = if !lodepng_color_mode_equal(&state.info_raw, &info.color) {
let size = (w * h * (info.color.bpp() as usize) + 7) / 8;
let mut converted = vec![0u8; size];
lodepng_convert(&mut converted, image, &info.color, &state.info_raw, w as u32, h as u32)?;
pre_process_scanlines(&converted, w, h, &info, &state.encoder)?
} else {
pre_process_scanlines(image, w, h, &info, &state.encoder)?
};
let mut outv = Vec::new();
write_signature(&mut outv);
add_chunk_ihdr(&mut outv, w, h, info.color.colortype, info.color.bitdepth() as usize, info.interlace_method as u8)?;
if let Some(chunks) = info.unknown_chunks_data(ChunkPosition::IHDR) {
add_unknown_chunks(&mut outv, chunks)?;
}
if info.color.colortype == ColorType::PALETTE {
add_chunk_plte(&mut outv, &info.color)?;
}
if state.encoder.force_palette != 0 && (info.color.colortype == ColorType::RGB || info.color.colortype == ColorType::RGBA) {
add_chunk_plte(&mut outv, &info.color)?;
}
if info.color.colortype == ColorType::PALETTE && get_palette_translucency(info.color.palette()) != PaletteTranslucency::Opaque {
add_chunk_trns(&mut outv, &info.color)?;
}
if (info.color.colortype == ColorType::GREY || info.color.colortype == ColorType::RGB) && info.color.key().is_some() {
add_chunk_trns(&mut outv, &info.color)?;
}
if info.background_defined != 0 {
add_chunk_bkgd(&mut outv, &info)?;
}
if info.phys_defined != 0 {
add_chunk_phys(&mut outv, &info)?;
}
if let Some(chunks) = info.unknown_chunks_data(ChunkPosition::PLTE) {
add_unknown_chunks(&mut outv, chunks)?;
}
add_chunk_idat(&mut outv, &data, &state.encoder.zlibsettings)?;
if info.time_defined != 0 {
add_chunk_time(&mut outv, &info.time)?;
}
for (t, v) in info.text_keys_cstr() {
if t.to_bytes().len() > 79 {
return Err(Error(66));
}
if t.to_bytes().is_empty() {
return Err(Error(67));
}
if state.encoder.text_compression != 0 {
add_chunk_ztxt(&mut outv, t, v, &state.encoder.zlibsettings)?;
} else {
add_chunk_text(&mut outv, t, v)?;
}
}
if state.encoder.add_id != 0 {
let alread_added_id_text = info.text_keys_cstr()
.any(|(t, _)| t.to_str().unwrap_or("") == "LodePNG");
if !alread_added_id_text {
let l = CStr::from_bytes_with_nul(b"LodePNG\0").unwrap();
let v = CStr::from_bytes_with_nul(LODEPNG_VERSION_STRING).unwrap();
add_chunk_text(&mut outv, l, v)?;
}
}
for (k, l, t, s) in info.itext_keys() {
if k.as_bytes().len() > 79 {
return Err(Error(66));
}
if k.as_bytes().is_empty() {
return Err(Error(67));
}
add_chunk_itxt(&mut outv, state.encoder.text_compression != 0, k, l, t, s, &state.encoder.zlibsettings)?;
}
if let Some(chunks) = info.unknown_chunks_data(ChunkPosition::IDAT) {
add_unknown_chunks(&mut outv, chunks)?;
}
add_chunk_iend(&mut outv)?;
Ok(outv)
}
pub fn get_color_profile(inp: &[u8], w: u32, h: u32, mode: &ColorMode) -> Result<ColorProfile, Error> {
let mut profile = ColorProfile::new();
let numpixels: usize = w as usize * h as usize;
let mut colored_done = mode.is_greyscale_type();
let mut alpha_done = !mode.can_have_alpha();
let mut numcolors_done = false;
let bpp = mode.bpp() as usize;
let mut bits_done = bpp == 1;
let maxnumcolors = match bpp {
1 => 2,
2 => 4,
4 => 16,
5..=8 => 256,
_ => 257,
};
let mut sixteen = false;
if mode.bitdepth() == 16 {
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba16(inp, i, mode);
if (r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255) {
sixteen = true;
break;
};
}
}
if sixteen {
profile.bits = 16;
bits_done = true;
numcolors_done = true;
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba16(inp, i, mode);
if !colored_done && (r != g || r != b) {
profile.colored = 1;
colored_done = true;
}
if !alpha_done {
let matchkey = r == profile.key_r && g == profile.key_g && b == profile.key_b;
if a != 65535 && (a != 0 || (profile.key != 0 && !matchkey)) {
profile.alpha = 1;
profile.key = 0;
alpha_done = true;
} else if a == 0 && profile.alpha == 0 && profile.key == 0 {
profile.key = 1;
profile.key_r = r;
profile.key_g = g;
profile.key_b = b;
} else if a == 65535 && profile.key != 0 && matchkey {
profile.alpha = 1;
profile.key = 0;
alpha_done = true;
};
}
if alpha_done && numcolors_done && colored_done && bits_done {
break;
};
}
if profile.key != 0 && profile.alpha == 0 {
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba16(inp, i, mode);
if a != 0 && r == profile.key_r && g == profile.key_g && b == profile.key_b {
profile.alpha = 1;
profile.key = 0;
}
}
}
} else {
let mut tree = ColorTree::new();
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba8(inp, i, mode);
if !bits_done && profile.bits < 8 {
let bits = get_value_required_bits(r) as u32;
if bits > profile.bits {
profile.bits = bits;
};
}
bits_done = profile.bits as usize >= bpp;
if !colored_done && (r != g || r != b) {
profile.colored = 1;
colored_done = true;
if profile.bits < 8 {
profile.bits = 8;
};
}
if !alpha_done {
let matchkey = r as u16 == profile.key_r && g as u16 == profile.key_g && b as u16 == profile.key_b;
if a != 255 && (a != 0 || (profile.key != 0 && !matchkey)) {
profile.alpha = 1;
profile.key = 0;
alpha_done = true;
if profile.bits < 8 {
profile.bits = 8;
};
} else if a == 0 && profile.alpha == 0 && profile.key == 0 {
profile.key = 1;
profile.key_r = r as u16;
profile.key_g = g as u16;
profile.key_b = b as u16;
} else if a == 255 && profile.key != 0 && matchkey {
profile.alpha = 1;
profile.key = 0;
alpha_done = true;
if profile.bits < 8 {
profile.bits = 8;
};
};
}
if !numcolors_done && tree.get(&(r, g, b, a)).is_none() {
tree.insert((r, g, b, a), profile.numcolors as u16);
if profile.numcolors < 256 {
profile.palette[profile.numcolors as usize] = RGBA { r, g, b, a };
}
profile.numcolors += 1;
numcolors_done = profile.numcolors >= maxnumcolors;
}
if alpha_done && numcolors_done && colored_done && bits_done {
break;
};
}
if profile.key != 0 && profile.alpha == 0 {
for i in 0..numpixels {
let (r, g, b, a) = get_pixel_color_rgba8(inp, i, mode);
if a != 0 && r as u16 == profile.key_r && g as u16 == profile.key_g && b as u16 == profile.key_b {
profile.alpha = 1;
profile.key = 0;
if profile.bits < 8 {
profile.bits = 8;
};
};
}
}
profile.key_r += profile.key_r << 8;
profile.key_g += profile.key_g << 8;
profile.key_b += profile.key_b << 8;
}
Ok(profile)
}
pub fn auto_choose_color(image: &[u8], w: usize, h: usize, mode_in: &ColorMode) -> Result<ColorMode, Error> {
let mut mode_out = ColorMode::new();
let mut prof = get_color_profile(image, w as u32, h as u32, mode_in)?;
mode_out.clear_key();
if prof.key != 0 && w * h <= 16 {
prof.alpha = 1;
prof.key = 0;
if prof.bits < 8 {
prof.bits = 8;
};
}
let n = prof.numcolors;
let palettebits = if n <= 2 {
1
} else if n <= 4 {
2
} else if n <= 16 {
4
} else {
8
};
let palette_ok = (n <= 256 && prof.bits <= 8) &&
(w * h >= (n * 2) as usize) &&
(prof.colored != 0 || prof.bits > palettebits);
if palette_ok {
let pal = &prof.palette[0..prof.numcolors as usize];
mode_out.palette_clear();
for p in pal {
mode_out.palette_add(*p)?;
}
mode_out.colortype = ColorType::PALETTE;
mode_out.set_bitdepth(palettebits);
if mode_in.colortype == ColorType::PALETTE && mode_in.palette().len() >= mode_out.palette().len() && mode_in.bitdepth() == mode_out.bitdepth() {
mode_out = mode_in.clone();
};
} else {
mode_out.set_bitdepth(prof.bits);
mode_out.colortype = if prof.alpha != 0 {
if prof.colored != 0 {
ColorType::RGBA
} else {
ColorType::GREY_ALPHA
}
} else if prof.colored != 0 {
ColorType::RGB
} else {
ColorType::GREY
};
if prof.key != 0 {
let mask = ((1 << mode_out.bitdepth()) - 1) as u16;
mode_out.set_key(
prof.key_r as u16 & mask,
prof.key_g as u16 & mask,
prof.key_b as u16 & mask);
};
}
Ok(mode_out)
}
pub fn lodepng_filesize(filename: &Path) -> Option<u64> {
fs::metadata(filename).map(|m| m.len()).ok()
}
pub fn lodepng_encode_memory(image: &[u8], w: u32, h: u32, colortype: ColorType, bitdepth: u32) -> Result<Vec<u8>, Error> {
let mut state = Encoder::new();
state.info_raw_mut().colortype = colortype;
state.info_raw_mut().set_bitdepth(bitdepth);
state.info_png_mut().color.colortype = colortype;
state.info_png_mut().color.set_bitdepth(bitdepth);
lodepng_encode(image, w, h, &mut state.state)
}
impl EncoderSettings {
unsafe fn predefined_filters(&self, len: usize) -> Result<&[u8], Error> {
if self.predefined_filters.is_null() {
Err(Error(1))
} else {
Ok(slice::from_raw_parts(self.predefined_filters, len))
}
}
}
impl ColorProfile {
pub fn new() -> Self {
Self {
colored: 0,
key: 0,
key_r: 0,
key_g: 0,
key_b: 0,
alpha: 0,
numcolors: 0,
bits: 1,
palette: [RGBA{r:0,g:0,b:0,a:0}; 256],
}
}
}
fn get_value_required_bits(value: u8) -> u8 {
match value {
0 | 255 => 1,
x if x % 17 == 0 => {
if value % 85 == 0 { 2 } else { 4 }
},
_ => 8,
}
}
#[inline]
fn longest_match(a: &[u8], b: &[u8]) -> usize {
let len = a.len().min(b.len());
let a = &a[0..len];
let b = &b[0..len];
for i in 0..len {
if a[i] != b[i] {
return i;
}
}
len
}
fn encode_lz77(
hash: &mut Hash,
in_: &[u8],
inpos: usize,
windowsize: u32,
minmatch: u32,
nicematch: u32,
lazymatching: bool,
) -> Result<Vec<u32>, Error> {
let mut out = Vec::new();
let nicematch = (nicematch).min(MAX_SUPPORTED_DEFLATE_LENGTH as u32);
let maxchainlength = if windowsize >= 8192 {
windowsize
} else {
windowsize / 8
};
let maxlazymatch = if windowsize >= 8192 {
MAX_SUPPORTED_DEFLATE_LENGTH as u32
} else {
64
};
if windowsize == 0 || windowsize > 32768 {
Error(60).to_result()?;
}
if (windowsize & (windowsize - 1)) != 0 {
Error(90).to_result()?;
}
let mut numzeros = 0;
let mut lazylength = 0;
let mut lazyoffset = 0;
let mut lazy = false;
let mut pos = inpos;
while pos < in_.len() {
let mut wpos = pos as u32 & (windowsize - 1);
let hashval = get_hash(in_, pos) as u32;
let usezeros = true;
if usezeros && hashval == 0 {
if numzeros == 0 {
numzeros = count_zeros(in_, pos);
} else if pos + numzeros as usize > in_.len() || in_[pos + numzeros as usize - 1] != 0 {
numzeros -= 1;
};
} else {
numzeros = 0;
}
update_hash_chain(hash, wpos, hashval, numzeros as u16);
let mut length = 0;
let mut offset: u32 = 0;
let mut hashpos = u32::from(hash.chain[wpos as usize]);
let lastptr = in_.len().min(pos + MAX_SUPPORTED_DEFLATE_LENGTH) as u32;
let mut prev_offset = 0;
for _ in 0..maxchainlength {
let current_offset = if hashpos <= wpos {
wpos - hashpos
} else {
wpos + windowsize - hashpos
};
if current_offset < prev_offset {
break;
}
prev_offset = current_offset;
if current_offset > 0 {
let mut foreptr = pos as u32;
let mut backptr = pos as u32 - current_offset;
if numzeros >= 3 {
let mut skip = hash.zeros[hashpos as usize] as u32;
if skip > numzeros {
skip = numzeros;
}
backptr += skip;
foreptr += skip;
}
let current_length = longest_match(&in_[foreptr as usize..lastptr as usize], &in_[backptr as usize..]) as u32;
if current_length > length {
length = current_length;
offset = current_offset;
if current_length >= nicematch {
break;
};
}
}
if hashpos == hash.chain[hashpos as usize] as u32 {
break;
}
if numzeros >= 3 && length > numzeros {
hashpos = u32::from(hash.chainz[hashpos as usize]);
if hash.zeros[hashpos as usize] as u32 != numzeros {
break;
};
} else {
hashpos = u32::from(hash.chain[hashpos as usize]);
if hash.val[hashpos as usize] as u32 != hashval {
break;
};
};
}
if lazymatching {
if !lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH as u32 {
lazy = true;
lazylength = length;
lazyoffset = offset;
pos += 1;
continue;
}
if lazy {
lazy = false;
if pos == 0 {
return Err(Error(81));
}
if length > lazylength + 1 {
out.push(in_[pos - 1] as u32);
} else {
length = lazylength;
offset = lazyoffset;
hash.head[hashval as usize] = -1;
hash.headz[numzeros as usize] = -1;
pos -= 1;
};
};
}
if length >= 3 && offset > windowsize {
return Err(Error(86));
}
if length < 3 || length < minmatch || (length == 3 && offset > 4096) {
out.push(in_[pos] as u32);
} else {
add_length_distance(&mut out, length, offset);
for _ in 1..length {
pos += 1;
wpos = pos as u32 & (windowsize - 1);
let hashval = get_hash(in_, pos) as u32;
if usezeros && hashval == 0 {
if numzeros == 0 {
numzeros = count_zeros(in_, pos);
} else if pos + numzeros as usize > in_.len() || in_[pos + numzeros as usize - 1] != 0 {
numzeros -= 1;
}
} else {
numzeros = 0;
}
update_hash_chain(hash, wpos, hashval, numzeros as u16);
}
}
pos += 1;
}
Ok(out)
}
unsafe impl Sync for CompressSettings {}
unsafe impl Sync for DecompressSettings {}