use bit_vec::BitVec;
use byteorder::{BigEndian, WriteBytesExt};
use colors::{BitDepth, ColorType};
use crc::crc32;
use error::PngError;
use filters::*;
use headers::*;
use interlace::{interlace_image, deinterlace_image};
use itertools::Itertools;
use reduction::*;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Read;
use std::iter::Iterator;
use std::path::Path;
#[derive(Debug,Clone)]
pub struct ScanLines<'a> {
pub png: &'a PngData,
start: usize,
end: usize,
pass: Option<(u8, u32)>,
}
impl<'a> Iterator for ScanLines<'a> {
type Item = ScanLine;
fn next(&mut self) -> Option<Self::Item> {
if self.end == self.png.raw_data.len() {
None
} else if self.png.ihdr_data.interlaced == 1 {
if self.pass.is_none() {
self.pass = Some((1, 0));
}
if self.png.ihdr_data.width < 5 && self.pass.unwrap().0 == 2 {
if let Some(pass) = self.pass.as_mut() {
pass.0 = 3;
pass.1 = 4;
}
}
if self.png.ihdr_data.height < 5 && self.pass.unwrap().0 == 3 {
if let Some(pass) = self.pass.as_mut() {
pass.0 = 4;
pass.1 = 0;
}
}
let bits_per_pixel = self.png.ihdr_data.bit_depth.as_u8() as u32 *
self.png.channels_per_pixel() as u32;
let y_steps;
let pixels_factor;
match self.pass {
Some((1, _)) | Some((2, _)) => {
pixels_factor = 8;
y_steps = 8;
}
Some((3, _)) => {
pixels_factor = 4;
y_steps = 8;
}
Some((4, _)) => {
pixels_factor = 4;
y_steps = 4;
}
Some((5, _)) => {
pixels_factor = 2;
y_steps = 4;
}
Some((6, _)) => {
pixels_factor = 2;
y_steps = 2;
}
Some((7, _)) => {
pixels_factor = 1;
y_steps = 2;
}
_ => unreachable!(),
}
let mut pixels_per_line = self.png.ihdr_data.width / pixels_factor as u32;
let gap = self.png.ihdr_data.width % pixels_factor;
if gap > 0 {
match self.pass.unwrap().0 {
1 | 3 | 5 => {
pixels_per_line += 1;
}
2 => {
if gap >= 5 {
pixels_per_line += 1;
}
}
4 => {
if gap >= 3 {
pixels_per_line += 1;
}
}
6 => {
if gap >= 2 {
pixels_per_line += 1;
}
}
_ => (),
};
}
let current_pass = if let Some(pass) = self.pass {
Some(pass.0)
} else {
None
};
let bytes_per_line = ((pixels_per_line * bits_per_pixel) as f32 / 8f32).ceil() as usize;
self.start = self.end;
self.end = self.start + bytes_per_line + 1;
if let Some(pass) = self.pass.as_mut() {
if pass.1 + y_steps >= self.png.ihdr_data.height {
pass.0 += 1;
pass.1 = match pass.0 {
3 => 4,
5 => 2,
7 => 1,
_ => 0,
};
} else {
pass.1 += y_steps;
}
}
Some(ScanLine {
filter: self.png.raw_data[self.start],
data: self.png.raw_data[(self.start + 1)..self.end].to_owned(),
pass: current_pass,
})
} else {
let bits_per_line = self.png.ihdr_data.width as usize *
self.png.ihdr_data.bit_depth.as_u8() as usize *
self.png.channels_per_pixel() as usize;
let bytes_per_line = (bits_per_line as f32 / 8f32).ceil() as usize;
self.start = self.end;
self.end = self.start + bytes_per_line + 1;
Some(ScanLine {
filter: self.png.raw_data[self.start],
data: self.png.raw_data[(self.start + 1)..self.end].to_owned(),
pass: None,
})
}
}
}
#[derive(Debug,Clone)]
pub struct ScanLine {
pub filter: u8,
pub data: Vec<u8>,
pub pass: Option<u8>,
}
#[derive(Debug,Clone)]
pub struct PngData {
pub idat_data: Vec<u8>,
pub ihdr_data: IhdrData,
pub raw_data: Vec<u8>,
pub palette: Option<Vec<u8>>,
pub transparency_pixel: Option<Vec<u8>>,
pub transparency_palette: Option<Vec<u8>>,
pub aux_headers: HashMap<String, Vec<u8>>,
}
impl PngData {
pub fn new(filepath: &Path, fix_errors: bool) -> Result<PngData, PngError> {
let mut file = match File::open(filepath) {
Ok(f) => f,
Err(_) => return Err(PngError::new("Failed to open file for reading")),
};
let mut byte_data: Vec<u8> = Vec::new();
match file.read_to_end(&mut byte_data) {
Ok(_) => (),
Err(_) => return Err(PngError::new("Failed to read from file")),
}
PngData::from_slice(&byte_data, fix_errors)
}
pub fn from_slice(byte_data: &[u8], fix_errors: bool) -> Result<PngData, PngError> {
let mut byte_offset: usize = 0;
let header: Vec<u8> = byte_data.iter().take(8).cloned().collect();
if !file_header_is_valid(header.as_ref()) {
return Err(PngError::new("Invalid PNG header detected"));
}
byte_offset += 8;
let mut aux_headers: HashMap<String, Vec<u8>> = HashMap::new();
let mut idat_headers: Vec<u8> = Vec::new();
loop {
let header = parse_next_header(byte_data.as_ref(), &mut byte_offset, fix_errors);
let header = match header {
Ok(x) => x,
Err(x) => return Err(x),
};
let header = match header {
Some(x) => x,
None => break,
};
if header.0 == "IDAT" {
idat_headers.extend(header.1);
} else {
aux_headers.insert(header.0, header.1);
}
}
if idat_headers.is_empty() {
return Err(PngError::new("Image data was empty, skipping"));
}
if aux_headers.get("IHDR").is_none() {
return Err(PngError::new("Image header data was missing, skipping"));
}
let ihdr_header = match parse_ihdr_header(aux_headers.remove("IHDR").unwrap().as_ref()) {
Ok(x) => x,
Err(x) => return Err(x),
};
let raw_data = match super::deflate::deflate::inflate(idat_headers.as_ref()) {
Ok(x) => x,
Err(x) => return Err(x),
};
let mut has_transparency_pixel = false;
let mut has_transparency_palette = false;
if aux_headers.contains_key("tRNS") {
if ihdr_header.color_type == ColorType::Indexed {
has_transparency_palette = true;
} else {
has_transparency_pixel = true;
}
}
let mut png_data = PngData {
idat_data: idat_headers,
ihdr_data: ihdr_header,
raw_data: raw_data,
palette: aux_headers.remove("PLTE"),
transparency_pixel: if has_transparency_pixel {
aux_headers.remove("tRNS")
} else {
None
},
transparency_palette: if has_transparency_palette {
aux_headers.remove("tRNS")
} else {
None
},
aux_headers: aux_headers,
};
png_data.raw_data = png_data.unfilter_image();
Ok(png_data)
}
#[inline]
pub fn channels_per_pixel(&self) -> u8 {
match self.ihdr_data.color_type {
ColorType::Grayscale | ColorType::Indexed => 1,
ColorType::GrayscaleAlpha => 2,
ColorType::RGB => 3,
ColorType::RGBA => 4,
}
}
pub fn output(&self) -> Vec<u8> {
let mut output = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
let mut ihdr_data = Vec::with_capacity(13);
ihdr_data.write_u32::<BigEndian>(self.ihdr_data.width).ok();
ihdr_data.write_u32::<BigEndian>(self.ihdr_data.height).ok();
ihdr_data.write_u8(self.ihdr_data.bit_depth.as_u8()).ok();
ihdr_data.write_u8(self.ihdr_data.color_type.png_header_code()).ok();
ihdr_data.write_u8(0).ok(); ihdr_data.write_u8(0).ok(); ihdr_data.write_u8(self.ihdr_data.interlaced).ok();
write_png_block(b"IHDR", &ihdr_data, &mut output);
for (key, header) in self.aux_headers
.iter()
.filter(|&(key, _)| !(*key == "bKGD" || *key == "hIST" || *key == "tRNS")) {
write_png_block(key.as_bytes(), header, &mut output);
}
if let Some(ref palette) = self.palette {
write_png_block(b"PLTE", palette, &mut output);
if let Some(ref transparency_palette) = self.transparency_palette {
write_png_block(b"tRNS", transparency_palette, &mut output);
}
} else if let Some(ref transparency_pixel) = self.transparency_pixel {
write_png_block(b"tRNS", transparency_pixel, &mut output);
}
for (key, header) in self.aux_headers
.iter()
.filter(|&(key, _)| *key == "bKGD" || *key == "hIST" || *key == "tRNS") {
write_png_block(key.as_bytes(), header, &mut output);
}
write_png_block(b"IDAT", &self.idat_data, &mut output);
write_png_block(b"IEND", &[], &mut output);
output
}
pub fn scan_lines(&self) -> ScanLines {
ScanLines {
png: self,
start: 0,
end: 0,
pass: None,
}
}
pub fn unfilter_image(&self) -> Vec<u8> {
let mut unfiltered = Vec::with_capacity(self.raw_data.len());
let bpp = (((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel()) as f32) /
8f32)
.ceil() as usize;
let mut last_line: Vec<u8> = Vec::new();
for line in self.scan_lines() {
let unfiltered_line = unfilter_line(line.filter, bpp, &line.data, &last_line);
unfiltered.push(0);
unfiltered.extend_from_slice(&unfiltered_line);
last_line = unfiltered_line;
}
unfiltered
}
pub fn filter_image(&self, filter: u8) -> Vec<u8> {
let mut filtered = Vec::with_capacity(self.raw_data.len());
let bpp = (((self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel()) as f32) /
8f32)
.ceil() as usize;
let mut last_line: Vec<u8> = Vec::new();
let mut last_pass: Option<u8> = None;
for line in self.scan_lines() {
match filter {
0 | 1 | 2 | 3 | 4 => {
if last_pass == line.pass || filter <= 1 {
filtered.push(filter);
filtered.extend_from_slice(&filter_line(filter,
bpp,
&line.data,
&last_line));
} else {
filtered.push(0);
filtered.extend_from_slice(&filter_line(0, bpp, &line.data, &last_line));
}
}
5 => {
let mut trials: HashMap<u8, Vec<u8>> = HashMap::with_capacity(5);
for filter in if last_pass == line.pass { 0..5 } else { 0..2 } {
trials.insert(filter, filter_line(filter, bpp, &line.data, &last_line));
}
let (best_filter, best_line) = trials.iter()
.min_by_key(|x| {
x.1.iter().fold(0u64, |acc, &x| {
let signed = x as i8;
acc + (signed as i16).abs() as u64
})
})
.unwrap();
filtered.push(*best_filter);
filtered.extend_from_slice(best_line);
}
_ => unreachable!(),
}
last_line = line.data;
last_pass = line.pass;
}
filtered
}
pub fn reduce_bit_depth(&mut self) -> bool {
if self.ihdr_data.bit_depth != BitDepth::Sixteen {
if self.ihdr_data.color_type == ColorType::Indexed ||
self.ihdr_data.color_type == ColorType::Grayscale {
return match reduce_bit_depth_8_or_less(self) {
Some((data, depth)) => {
self.raw_data = data;
self.ihdr_data.bit_depth = BitDepth::from_u8(depth);
true
}
None => false,
};
}
return false;
}
let mut reduced = Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
self.channels_per_pixel() as u32 +
self.ihdr_data
.height) as usize);
let mut high_byte = 0;
for line in self.scan_lines() {
reduced.push(line.filter);
for (i, byte) in line.data.iter().enumerate() {
if i % 2 == 0 {
high_byte = *byte;
} else {
if high_byte != *byte {
return false;
}
reduced.push(*byte);
}
}
}
self.ihdr_data.bit_depth = BitDepth::Eight;
self.raw_data = reduced;
true
}
pub fn reduce_palette(&mut self) -> bool {
if self.ihdr_data.color_type != ColorType::Indexed {
return false;
}
if self.ihdr_data.bit_depth == BitDepth::One {
return false;
}
let palette = self.palette.clone().unwrap();
let mut indexed_palette: Vec<&[u8]> = palette.chunks(3).collect();
let mut index_map: HashMap<u8, u8> = HashMap::new();
let mut duplicates: Vec<u8> = Vec::new();
{
let mut seen: HashMap<&[u8], u8> = HashMap::with_capacity(indexed_palette.len());
for (i, color) in indexed_palette.iter().enumerate() {
if seen.contains_key(color) {
let index = seen.get(color).unwrap();
duplicates.push(i as u8);
index_map.insert(i as u8, *index);
} else {
seen.insert(*color, i as u8);
}
}
}
if !duplicates.is_empty() {
self.do_palette_reduction(&duplicates, &mut index_map, &mut indexed_palette);
}
let mut seen = HashSet::with_capacity(indexed_palette.len());
for line in self.scan_lines() {
match self.ihdr_data.bit_depth {
BitDepth::Eight => {
for byte in &line.data {
seen.insert(*byte);
}
}
BitDepth::Four => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 4;
if bit {
current += 2u8.pow(3u32 - mod_i as u32);
}
if mod_i == 3 {
seen.insert(current);
current = 0;
}
}
}
BitDepth::Two => {
let bitvec = BitVec::from_bytes(&line.data);
let mut current = 0u8;
for (i, bit) in bitvec.iter().enumerate() {
let mod_i = i % 2;
if bit {
current += 2u8.pow(1u32 - mod_i as u32);
}
if mod_i == 1 {
seen.insert(current);
current = 0;
}
}
}
_ => unreachable!(),
}
if seen.len() == indexed_palette.len() {
return !duplicates.is_empty();
}
}
let unused: Vec<u8> =
(0..indexed_palette.len() as u8).filter(|i| !seen.contains(i)).collect();
self.do_palette_reduction(&unused, &mut index_map, &mut indexed_palette);
true
}
fn do_palette_reduction(&mut self,
indices: &[u8],
index_map: &mut HashMap<u8, u8>,
indexed_palette: &mut Vec<&[u8]>) {
let mut new_data = Vec::with_capacity(self.raw_data.len());
let mut alpha_palette = self.aux_headers.get("tRNS").cloned();
let original_len = indexed_palette.len();
for idx in indices.iter().sorted_by(|a, b| b.cmp(a)) {
for i in (*idx as usize + 1)..original_len {
let existing = index_map.entry(i as u8).or_insert(i as u8);
if *existing >= *idx {
*existing -= 1;
}
}
indexed_palette.remove(*idx as usize);
if let Some(ref mut alpha) = alpha_palette {
alpha.remove(*idx as usize);
}
}
if alpha_palette.is_some() {
let alpha_header = self.aux_headers.get_mut("tRNS");
if let Some(alpha_hdr) = alpha_header {
*alpha_hdr = alpha_palette.unwrap();
}
}
for line in self.scan_lines() {
new_data.push(line.filter);
match self.ihdr_data.bit_depth {
BitDepth::Eight => {
for byte in &line.data {
if let Some(new_idx) = index_map.get(byte) {
new_data.push(*new_idx);
} else {
new_data.push(*byte);
}
}
}
BitDepth::Four => {
for byte in &line.data {
let upper = *byte >> 4;
let lower = *byte & 0b00001111;
let mut new_byte = 0u8;
if let Some(new_idx) = index_map.get(&upper) {
new_byte &= *new_idx << 4;
} else {
new_byte &= upper << 4;
}
if let Some(new_idx) = index_map.get(&lower) {
new_byte &= *new_idx;
} else {
new_byte &= lower;
}
new_data.push(new_byte);
}
}
BitDepth::Two => {
for byte in &line.data {
let one = *byte >> 6;
let two = (*byte >> 4) & 0b00000011;
let three = (*byte >> 2) & 0b00000011;
let four = *byte & 0b00000011;
let mut new_byte = 0u8;
if let Some(new_idx) = index_map.get(&one) {
new_byte &= *new_idx << 6;
} else {
new_byte &= one << 6;
}
if let Some(new_idx) = index_map.get(&two) {
new_byte &= *new_idx << 4;
} else {
new_byte &= two << 4;
}
if let Some(new_idx) = index_map.get(&three) {
new_byte &= *new_idx << 2;
} else {
new_byte &= three << 2;
}
if let Some(new_idx) = index_map.get(&four) {
new_byte &= *new_idx;
} else {
new_byte &= four;
}
new_data.push(new_byte);
}
}
_ => unreachable!(),
}
}
index_map.clear();
self.raw_data = new_data;
let new_palette = indexed_palette.iter().cloned().flatten().cloned().collect::<Vec<u8>>();
self.palette = Some(new_palette);
}
pub fn reduce_color_type(&mut self) -> bool {
let mut changed = false;
let mut should_reduce_bit_depth = false;
if self.ihdr_data.color_type == ColorType::RGBA {
if let Some(data) = reduce_rgba_to_grayscale_alpha(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::GrayscaleAlpha;
changed = true;
} else if let Some(data) = reduce_rgba_to_rgb(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::RGB;
changed = true;
} else if let Some((data, palette, trans)) = reduce_rgba_to_palette(self) {
self.raw_data = data;
self.palette = Some(palette);
if trans.iter().any(|x| *x != 255) {
self.transparency_palette = Some(trans);
} else {
self.transparency_palette = None;
}
self.ihdr_data.color_type = ColorType::Indexed;
changed = true;
should_reduce_bit_depth = true;
}
}
if self.ihdr_data.color_type == ColorType::GrayscaleAlpha {
if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::Grayscale;
changed = true;
should_reduce_bit_depth = true;
}
}
if self.ihdr_data.color_type == ColorType::RGB {
if let Some(data) = reduce_rgb_to_grayscale(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::Grayscale;
changed = true;
should_reduce_bit_depth = true;
} else if let Some((data, palette)) = reduce_rgb_to_palette(self) {
self.raw_data = data;
self.palette = Some(palette);
self.ihdr_data.color_type = ColorType::Indexed;
changed = true;
should_reduce_bit_depth = true;
}
}
if self.ihdr_data.color_type == ColorType::Grayscale {
if let Some((data, palette)) = reduce_grayscale_to_palette(self) {
self.raw_data = data;
self.palette = Some(palette);
self.ihdr_data.color_type = ColorType::Indexed;
changed = true;
should_reduce_bit_depth = true;
}
}
if should_reduce_bit_depth {
if let Some((data, depth)) = reduce_bit_depth_8_or_less(self) {
self.raw_data = data;
self.ihdr_data.bit_depth = BitDepth::from_u8(depth);
}
}
changed
}
#[inline]
pub fn change_interlacing(&mut self, interlace: u8) -> bool {
if interlace == self.ihdr_data.interlaced {
return false;
}
if interlace == 1 {
interlace_image(self);
} else {
deinterlace_image(self);
}
true
}
}
#[inline]
fn write_png_block(key: &[u8], header: &[u8], output: &mut Vec<u8>) {
let mut header_data = Vec::with_capacity(header.len() + 4);
header_data.extend_from_slice(key);
header_data.extend_from_slice(header);
output.reserve(header_data.len() + 8);
output.write_u32::<BigEndian>(header_data.len() as u32 - 4).ok();
let crc = crc32::checksum_ieee(&header_data);
output.append(&mut header_data);
output.write_u32::<BigEndian>(crc).ok();
}