ColorType Refactor (#500)

* Refactor ColorType
Move transparency and palette data into the ColorType

* Fixup tests

* Make more use of helper functions

* Change BitDepth to u8 representation with TryFrom

* Fix clippy lints

* Don't use unstable language features

* Restore documentation on transparency/palette
This commit is contained in:
andrews05 2023-04-28 11:49:54 +12:00 committed by GitHub
parent 36af4198ed
commit c97572be3e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 866 additions and 836 deletions

View file

@ -1,14 +1,26 @@
use rgb::{RGB16, RGBA8};
use std::fmt;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
use crate::PngError;
#[derive(Debug, PartialEq, Eq, Clone)]
/// The color type used to represent this image
pub enum ColorType {
/// Grayscale, with one color channel
Grayscale,
Grayscale {
/// Optional shade of gray that should be rendered as transparent
transparent_shade: Option<u16>,
},
/// RGB, with three color channels
RGB,
/// Indexed, with one byte per pixel representing one of up to 256 colors in the image
Indexed,
RGB {
/// Optional color value that should be rendered as transparent
transparent_color: Option<RGB16>,
},
/// Indexed, with one byte per pixel representing a color from the palette
Indexed {
/// The palette containing the colors used, up to 256 entries
palette: Vec<RGBA8>,
},
/// Grayscale + Alpha, with two color channels
GrayscaleAlpha,
/// RGBA, with four color channels
@ -22,9 +34,9 @@ impl fmt::Display for ColorType {
f,
"{}",
match *self {
ColorType::Grayscale => "Grayscale",
ColorType::RGB => "RGB",
ColorType::Indexed => "Indexed",
ColorType::Grayscale { .. } => "Grayscale",
ColorType::RGB { .. } => "RGB",
ColorType::Indexed { .. } => "Indexed",
ColorType::GrayscaleAlpha => "Grayscale + Alpha",
ColorType::RGBA => "RGB + Alpha",
}
@ -35,85 +47,71 @@ impl fmt::Display for ColorType {
impl ColorType {
/// Get the code used by the PNG specification to denote this color type
#[inline]
pub fn png_header_code(self) -> u8 {
pub fn png_header_code(&self) -> u8 {
match self {
ColorType::Grayscale => 0,
ColorType::RGB => 2,
ColorType::Indexed => 3,
ColorType::Grayscale { .. } => 0,
ColorType::RGB { .. } => 2,
ColorType::Indexed { .. } => 3,
ColorType::GrayscaleAlpha => 4,
ColorType::RGBA => 6,
}
}
#[inline]
pub fn channels_per_pixel(self) -> u8 {
pub fn channels_per_pixel(&self) -> u8 {
match self {
ColorType::Grayscale | ColorType::Indexed => 1,
ColorType::Grayscale { .. } | ColorType::Indexed { .. } => 1,
ColorType::GrayscaleAlpha => 2,
ColorType::RGB => 3,
ColorType::RGB { .. } => 3,
ColorType::RGBA => 4,
}
}
#[inline]
pub fn is_rgb(&self) -> bool {
matches!(self, ColorType::RGB { .. } | ColorType::RGBA)
}
#[inline]
pub fn has_alpha(&self) -> bool {
matches!(self, ColorType::GrayscaleAlpha | ColorType::RGBA)
}
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
/// The number of bits to be used per channel per pixel
pub enum BitDepth {
/// One bit per channel per pixel
One,
One = 1,
/// Two bits per channel per pixel
Two,
Two = 2,
/// Four bits per channel per pixel
Four,
Four = 4,
/// Eight bits per channel per pixel
Eight,
Eight = 8,
/// Sixteen bits per channel per pixel
Sixteen,
Sixteen = 16,
}
impl TryFrom<u8> for BitDepth {
type Error = PngError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::One),
2 => Ok(Self::Two),
4 => Ok(Self::Four),
8 => Ok(Self::Eight),
16 => Ok(Self::Sixteen),
_ => Err(PngError::new("Unexpected bit depth")),
}
}
}
impl fmt::Display for BitDepth {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match *self {
BitDepth::One => "1",
BitDepth::Two => "2",
BitDepth::Four => "4",
BitDepth::Eight => "8",
BitDepth::Sixteen => "16",
}
)
}
}
impl BitDepth {
/// Retrieve the number of bits per channel per pixel as a `u8`
#[inline]
pub fn as_u8(self) -> u8 {
match self {
BitDepth::One => 1,
BitDepth::Two => 2,
BitDepth::Four => 4,
BitDepth::Eight => 8,
BitDepth::Sixteen => 16,
}
}
/// Parse a number of bits per channel per pixel into a `BitDepth`
///
/// # Panics
///
/// If depth is unsupported
#[inline]
pub fn from_u8(depth: u8) -> BitDepth {
match depth {
1 => BitDepth::One,
2 => BitDepth::Two,
4 => BitDepth::Four,
8 => BitDepth::Eight,
16 => BitDepth::Sixteen,
_ => panic!("Unsupported bit depth"),
}
write!(f, "{}", *self as u8)
}
}

View file

@ -4,10 +4,11 @@ use crate::error::PngError;
use crate::interlace::Interlacing;
use crate::PngResult;
use indexmap::IndexSet;
use rgb::{RGB16, RGBA8};
use std::io;
use std::io::{Cursor, Read};
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
/// Headers from the IHDR chunk of the image
pub struct IhdrData {
/// The width of the image in pixels
@ -30,8 +31,8 @@ impl IhdrData {
/// Bits per pixel
#[must_use]
#[inline]
pub fn bpp(&self) -> u8 {
self.bit_depth.as_u8() * self.color_type.channels_per_pixel()
pub fn bpp(&self) -> usize {
self.bit_depth as usize * self.color_type.channels_per_pixel() as usize
}
/// Byte length of IDAT that is correct for this IHDR
@ -41,8 +42,8 @@ impl IhdrData {
let h = self.height as usize;
let bpp = self.bpp();
fn bitmap_size(bpp: u8, w: usize, h: usize) -> usize {
(((w / 8) * bpp as usize) + ((w & 7) * bpp as usize + 7) / 8) * h
fn bitmap_size(bpp: usize, w: usize, h: usize) -> usize {
((w * bpp + 7) / 8) * h
}
if self.interlaced == Interlacing::None {
@ -143,27 +144,36 @@ pub fn parse_next_header<'a>(
Ok(Some(RawHeader { name, data }))
}
pub fn parse_ihdr_header(byte_data: &[u8]) -> PngResult<IhdrData> {
pub fn parse_ihdr_header(
byte_data: &[u8],
palette_data: Option<Vec<u8>>,
trns_data: Option<Vec<u8>>,
) -> PngResult<IhdrData> {
// This eliminates bounds checks for the rest of the function
let interlaced = byte_data.get(12).copied().ok_or(PngError::TruncatedData)?;
let mut rdr = Cursor::new(&byte_data[0..8]);
Ok(IhdrData {
color_type: match byte_data[9] {
0 => ColorType::Grayscale,
2 => ColorType::RGB,
3 => ColorType::Indexed,
0 => ColorType::Grayscale {
transparent_shade: trns_data
.filter(|t| t.len() >= 2)
.map(|t| u16::from_be_bytes([t[0], t[1]])),
},
2 => ColorType::RGB {
transparent_color: trns_data.filter(|t| t.len() >= 6).map(|t| RGB16 {
r: u16::from_be_bytes([t[0], t[1]]),
g: u16::from_be_bytes([t[2], t[3]]),
b: u16::from_be_bytes([t[4], t[5]]),
}),
},
3 => ColorType::Indexed {
palette: palette_to_rgba(palette_data, trns_data).unwrap_or_default(),
},
4 => ColorType::GrayscaleAlpha,
6 => ColorType::RGBA,
_ => return Err(PngError::new("Unexpected color type in header")),
},
bit_depth: match byte_data[8] {
1 => BitDepth::One,
2 => BitDepth::Two,
4 => BitDepth::Four,
8 => BitDepth::Eight,
16 => BitDepth::Sixteen,
_ => return Err(PngError::new("Unexpected bit depth in header")),
},
bit_depth: byte_data[8].try_into()?,
width: read_be_u32(&mut rdr).map_err(|_| PngError::TruncatedData)?,
height: read_be_u32(&mut rdr).map_err(|_| PngError::TruncatedData)?,
compression: byte_data[10],
@ -172,6 +182,25 @@ pub fn parse_ihdr_header(byte_data: &[u8]) -> PngResult<IhdrData> {
})
}
/// Construct an RGBA palette from the raw palette and transparency data
fn palette_to_rgba(
palette_data: Option<Vec<u8>>,
trns_data: Option<Vec<u8>>,
) -> Result<Vec<RGBA8>, PngError> {
let palette_data = palette_data.ok_or_else(|| PngError::new("no palette in indexed image"))?;
let mut palette: Vec<_> = palette_data
.chunks(3)
.map(|color| RGBA8::new(color[0], color[1], color[2], 255))
.collect();
if let Some(trns_data) = trns_data {
for (color, trns) in palette.iter_mut().zip(trns_data) {
color.a = trns;
}
}
Ok(palette)
}
#[inline]
fn read_be_u32<T: AsRef<[u8]>>(rdr: &mut Cursor<T>) -> Result<u32, io::Error> {
let mut int_buf = [0; 4];

View file

@ -45,11 +45,11 @@ pub fn interlace_image(png: &PngImage) -> PngImage {
let bit_vec = line.data.view_bits::<Msb0>();
for (i, bit) in bit_vec.iter().by_vals().enumerate() {
// Avoid moving padded 0's into new image
if i >= (png.ihdr.width * u32::from(bits_per_pixel)) as usize {
if i >= (png.ihdr.width as usize * bits_per_pixel) {
break;
}
// Copy pixels into interlaced passes
let pix_modulo = (i / bits_per_pixel as usize) % 8;
let pix_modulo = (i / bits_per_pixel) % 8;
match index % 8 {
0 => match pix_modulo {
0 => passes[0].push(bit),
@ -87,12 +87,11 @@ pub fn interlace_image(png: &PngImage) -> PngImage {
PngImage {
data: output,
ihdr: IhdrData {
color_type: png.ihdr.color_type.clone(),
interlaced: Interlacing::Adam7,
..png.ihdr
},
aux_headers: png.aux_headers.clone(),
palette: png.palette.clone(),
transparency_pixel: png.transparency_pixel.clone(),
}
}
@ -103,19 +102,18 @@ pub fn deinterlace_image(png: &PngImage) -> PngImage {
_ => deinterlace_bits(png),
},
ihdr: IhdrData {
color_type: png.ihdr.color_type.clone(),
interlaced: Interlacing::None,
..png.ihdr
},
aux_headers: png.aux_headers.clone(),
palette: png.palette.clone(),
transparency_pixel: png.transparency_pixel.clone(),
}
}
/// Deinterlace by bits, for images with less than 8bpp
fn deinterlace_bits(png: &PngImage) -> Vec<u8> {
let bits_per_pixel = png.ihdr.bpp();
let bits_per_line = bits_per_pixel as usize * png.ihdr.width as usize;
let bits_per_line = bits_per_pixel * png.ihdr.width as usize;
// Initialize each output line with blank data
let mut lines: Vec<BitVec<u8, Msb0>> =
vec![bitvec![u8, Msb0; 0; bits_per_line]; png.ihdr.height as usize];
@ -128,22 +126,22 @@ fn deinterlace_bits(png: &PngImage) -> Vec<u8> {
+ u32::from(pass_constants.x_step)
- 1)
/ u32::from(pass_constants.x_step)) as usize
* bits_per_pixel as usize;
* bits_per_pixel;
for (i, bit) in bit_vec.iter().by_vals().enumerate() {
// Avoid moving padded 0's into new image
if i >= bits_in_line {
break;
}
let current_x: usize = pass_constants.x_shift as usize
+ (i / bits_per_pixel as usize) * pass_constants.x_step as usize;
+ (i / bits_per_pixel) * pass_constants.x_step as usize;
// Copy this bit into the output line
let index = (i % bits_per_pixel as usize) + current_x * bits_per_pixel as usize;
let index = (i % bits_per_pixel) + current_x * bits_per_pixel;
lines[current_y].set(index, bit);
}
// Calculate the next line and move to next pass if necessary
current_y += pass_constants.y_step as usize;
if current_y >= png.ihdr.height as usize {
if !increment_pass(&mut current_pass, png.ihdr) {
if !increment_pass(&mut current_pass, &png.ihdr) {
break;
}
pass_constants = interlaced_constants(current_pass);
@ -163,7 +161,7 @@ fn deinterlace_bits(png: &PngImage) -> Vec<u8> {
/// Deinterlace by bytes, for images with at least 8bpp
fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
let bytes_per_pixel = png.ihdr.bpp() / 8;
let bytes_per_line = bytes_per_pixel as usize * png.ihdr.width as usize;
let bytes_per_line = bytes_per_pixel * png.ihdr.width as usize;
// Initialize each output line with some blank data
let mut lines: Vec<Vec<u8>> = vec![vec![0; bytes_per_line]; png.ihdr.height as usize];
let mut current_pass = 1;
@ -172,15 +170,15 @@ fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
for line in png.scan_lines(false) {
for (i, byte) in line.data.iter().enumerate() {
let current_x: usize = pass_constants.x_shift as usize
+ (i / bytes_per_pixel as usize) * pass_constants.x_step as usize;
+ (i / bytes_per_pixel) * pass_constants.x_step as usize;
// Copy this byte into the output line
let index = (i % bytes_per_pixel as usize) + current_x * bytes_per_pixel as usize;
let index = (i % bytes_per_pixel) + current_x * bytes_per_pixel;
lines[current_y][index] = *byte;
}
// Calculate the next line and move to next pass if necessary
current_y += pass_constants.y_step as usize;
if current_y >= png.ihdr.height as usize {
if !increment_pass(&mut current_pass, png.ihdr) {
if !increment_pass(&mut current_pass, &png.ihdr) {
break;
}
pass_constants = interlaced_constants(current_pass);
@ -190,7 +188,7 @@ fn deinterlace_bytes(png: &PngImage) -> Vec<u8> {
lines.concat()
}
fn increment_pass(current_pass: &mut u8, ihdr: IhdrData) -> bool {
fn increment_pass(current_pass: &mut u8, ihdr: &IhdrData) -> bool {
if *current_pass == 7 {
return false;
}

View file

@ -25,7 +25,7 @@ extern crate rayon;
mod rayon;
use crate::atomicmin::AtomicMin;
use crate::colors::BitDepth;
use crate::colors::{BitDepth, ColorType};
use crate::deflate::{crc32, inflate};
use crate::evaluate::Evaluator;
use crate::png::PngData;
@ -569,7 +569,7 @@ fn optimize_png(
if filters.is_empty() {
// Pick a filter automatically
if png.raw.ihdr.bit_depth.as_u8() >= 8 {
if png.raw.ihdr.bit_depth as u8 >= 8 {
// Bigrams is the best all-rounder when there's at least one byte per pixel
filters.insert(RowFilter::Bigrams);
} else {
@ -814,7 +814,7 @@ impl Deadline {
/// Display the format of the image data
fn report_format(prefix: &str, png: &PngImage) {
if let Some(ref palette) = png.palette {
if let ColorType::Indexed { palette } = &png.ihdr.color_type {
debug!(
"{}{} bits/pixel, {} colors in palette ({})",
prefix,

View file

@ -1,4 +1,4 @@
use crate::colors::ColorType;
use crate::colors::{BitDepth, ColorType};
use crate::deflate;
use crate::error::PngError;
use crate::filters::*;
@ -8,7 +8,6 @@ use bitvec::bitarr;
use indexmap::IndexMap;
use libdeflater::{CompressionLvl, Compressor};
use rgb::ComponentSlice;
use rgb::RGBA8;
use rustc_hash::FxHashMap;
use std::fs::File;
use std::io::{BufReader, Read, Write};
@ -31,11 +30,6 @@ pub struct PngImage {
pub ihdr: IhdrData,
/// The uncompressed, unfiltered data from the IDAT chunk
pub data: Vec<u8>,
/// The palette containing colors used in an Indexed image
/// Contains 3 bytes per color (R+G+B), up to 768
pub palette: Option<Vec<RGBA8>>,
/// The pixel value that should be rendered as transparent
pub transparency_pixel: Option<Vec<u8>>,
/// All non-critical headers from the PNG are stored here
pub aux_headers: IndexMap<[u8; 4], Vec<u8>>,
}
@ -51,8 +45,6 @@ pub struct PngData {
pub filtered: Vec<u8>,
}
type PaletteWithTrns = (Option<Vec<RGBA8>>, Option<Vec<u8>>);
impl PngData {
/// Create a new `PngData` struct by opening a file
#[inline]
@ -116,7 +108,11 @@ impl PngData {
Some(ihdr) => ihdr,
None => return Err(PngError::ChunkMissing("IHDR")),
};
let ihdr_header = parse_ihdr_header(&ihdr)?;
let ihdr_header = parse_ihdr_header(
&ihdr,
aux_headers.remove(b"PLTE"),
aux_headers.remove(b"tRNS"),
)?;
let raw_data = deflate::inflate(idat_headers.as_ref(), ihdr_header.raw_data_size())?;
// Reject files with incorrect width/height or truncated data
@ -124,17 +120,9 @@ impl PngData {
return Err(PngError::TruncatedData);
}
let (palette, transparency_pixel) = Self::palette_to_rgba(
ihdr_header.color_type,
aux_headers.remove(b"PLTE"),
aux_headers.remove(b"tRNS"),
)?;
let mut raw = PngImage {
ihdr: ihdr_header,
data: raw_data,
palette,
transparency_pixel,
aux_headers,
};
let unfiltered = raw.unfilter_image()?;
@ -146,31 +134,6 @@ impl PngData {
})
}
/// Handle transparency header
fn palette_to_rgba(
color_type: ColorType,
palette_data: Option<Vec<u8>>,
trns_data: Option<Vec<u8>>,
) -> Result<PaletteWithTrns, PngError> {
if color_type == ColorType::Indexed {
let palette_data =
palette_data.ok_or_else(|| PngError::new("no palette in indexed image"))?;
let mut palette: Vec<_> = palette_data
.chunks(3)
.map(|color| RGBA8::new(color[0], color[1], color[2], 255))
.collect();
if let Some(trns_data) = trns_data {
for (color, trns) in palette.iter_mut().zip(trns_data) {
color.a = trns;
}
}
Ok((Some(palette), None))
} else {
Ok((None, trns_data))
}
}
/// Format the `PngData` struct into a valid PNG bytestream
pub fn output(&self) -> Vec<u8> {
// PNG header
@ -181,7 +144,7 @@ impl PngData {
ihdr_data
.write_all(&self.raw.ihdr.height.to_be_bytes())
.ok();
ihdr_data.write_all(&[self.raw.ihdr.bit_depth.as_u8()]).ok();
ihdr_data.write_all(&[self.raw.ihdr.bit_depth as u8]).ok();
ihdr_data
.write_all(&[self.raw.ihdr.color_type.png_header_code()])
.ok();
@ -198,40 +161,49 @@ impl PngData {
{
write_png_block(key, header, &mut output);
}
// Palette
if let Some(ref palette) = self.raw.palette {
let mut palette_data = Vec::with_capacity(palette.len() * 3);
let mut max_palette_size = 1 << (self.raw.ihdr.bit_depth.as_u8() as usize);
// Ensure bKGD color doesn't get truncated from palette
if let Some(&idx) = self.raw.aux_headers.get(b"bKGD").and_then(|b| b.first()) {
max_palette_size = max_palette_size.max(idx as usize + 1);
// Palette and transparency
match &self.raw.ihdr.color_type {
ColorType::Indexed { palette } => {
let mut palette_data = Vec::with_capacity(palette.len() * 3);
let mut max_palette_size = 1 << (self.raw.ihdr.bit_depth as u8);
// Ensure bKGD color doesn't get truncated from palette
if let Some(&idx) = self.raw.aux_headers.get(b"bKGD").and_then(|b| b.first()) {
max_palette_size = max_palette_size.max(idx as usize + 1);
}
for px in palette.iter().take(max_palette_size) {
palette_data.extend_from_slice(px.rgb().as_slice());
}
write_png_block(b"PLTE", &palette_data, &mut output);
let num_transparent = palette.iter().take(max_palette_size).enumerate().fold(
0,
|prev, (index, px)| {
if px.a == 255 {
prev
} else {
index + 1
}
},
);
if num_transparent > 0 {
let trns_data: Vec<_> =
palette[0..num_transparent].iter().map(|px| px.a).collect();
write_png_block(b"tRNS", &trns_data, &mut output);
}
}
for px in palette.iter().take(max_palette_size) {
palette_data.extend_from_slice(px.rgb().as_slice());
ColorType::Grayscale {
transparent_shade: Some(trns),
} => {
// Transparency pixel - 2 byte u16
write_png_block(b"tRNS", &trns.to_be_bytes(), &mut output);
}
write_png_block(b"PLTE", &palette_data, &mut output);
let num_transparent =
palette
.iter()
.take(max_palette_size)
.enumerate()
.fold(
0,
|prev, (index, px)| {
if px.a == 255 {
prev
} else {
index + 1
}
},
);
if num_transparent > 0 {
let trns_data: Vec<_> = palette[0..num_transparent].iter().map(|px| px.a).collect();
ColorType::RGB {
transparent_color: Some(trns),
} => {
// Transparency pixel - 6 byte RGB16
let trns_data: Vec<_> = trns.iter().flat_map(|c| c.to_be_bytes()).collect();
write_png_block(b"tRNS", &trns_data, &mut output);
}
} else if let Some(ref transparency_pixel) = self.raw.transparency_pixel {
// Transparency pixel
write_png_block(b"tRNS", transparency_pixel, &mut output);
_ => {}
}
// Special ancillary headers that need to come after PLTE but before IDAT
for (key, header) in self
@ -274,8 +246,18 @@ impl PngImage {
/// Return the number of channels in the image, based on color type
#[inline]
pub fn channels_per_pixel(&self) -> u8 {
self.ihdr.color_type.channels_per_pixel()
pub fn channels_per_pixel(&self) -> usize {
self.ihdr.color_type.channels_per_pixel() as usize
}
/// Return the number of bytes per channel in the image
#[inline]
pub fn bytes_per_channel(&self) -> usize {
match self.ihdr.bit_depth {
BitDepth::Sixteen => 2,
// Depths lower than 8 will round up to 1 byte
_ => 1,
}
}
/// Return an iterator over the scanlines of the image
@ -287,7 +269,7 @@ impl PngImage {
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
fn unfilter_image(&self) -> Result<Vec<u8>, PngError> {
let mut unfiltered = Vec::with_capacity(self.data.len());
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
let bpp = self.bytes_per_channel() * self.channels_per_pixel();
let mut last_line: Vec<u8> = Vec::new();
let mut last_pass = None;
let mut unfiltered_buf = Vec::new();
@ -309,13 +291,12 @@ impl PngImage {
/// Apply the specified filter type to all rows in the image
pub fn filter_image(&self, filter: RowFilter, optimize_alpha: bool) -> Vec<u8> {
let mut filtered = Vec::with_capacity(self.data.len());
let bpp = ((self.ihdr.bit_depth.as_u8() * self.channels_per_pixel() + 7) / 8) as usize;
let bpp = self.bytes_per_channel() * self.channels_per_pixel();
// If alpha optimization is enabled, determine how many bytes of alpha there are per pixel
let alpha_bytes = match self.ihdr.color_type {
ColorType::RGBA | ColorType::GrayscaleAlpha if optimize_alpha => {
(self.ihdr.bit_depth.as_u8() / 8) as usize
}
_ => 0,
let alpha_bytes = if optimize_alpha && self.ihdr.color_type.has_alpha() {
self.bytes_per_channel()
} else {
0
};
let mut prev_line = Vec::new();

View file

@ -43,7 +43,7 @@ impl<'a> Iterator for ScanLines<'a> {
struct ScanLineRanges {
/// Current pass number, and 0-indexed row within the pass
pass: Option<(u8, u32)>,
bits_per_pixel: u8,
bits_per_pixel: usize,
width: u32,
height: u32,
left: usize,
@ -53,7 +53,7 @@ struct ScanLineRanges {
impl ScanLineRanges {
pub fn new(png: &PngImage, has_filter: bool) -> Self {
Self {
bits_per_pixel: png.ihdr.bit_depth.as_u8() * png.channels_per_pixel(),
bits_per_pixel: png.ihdr.bpp(),
width: png.ihdr.width,
height: png.ihdr.height,
left: png.data.len(),
@ -143,8 +143,8 @@ impl Iterator for ScanLineRanges {
// Standard, non-interlaced PNG scanlines
(self.width, None)
};
let bits_per_line = pixels_per_line * u32::from(self.bits_per_pixel);
let mut len = ((bits_per_line + 7) / 8) as usize;
let bits_per_line = pixels_per_line as usize * self.bits_per_pixel;
let mut len = (bits_per_line + 7) / 8;
if self.has_filter {
len += 1;
}

View file

@ -1,23 +1,21 @@
use rgb::RGB16;
use crate::colors::{BitDepth, ColorType};
use crate::headers::IhdrData;
use crate::png::PngImage;
/// Clean the alpha channel by setting the color of all fully transparent pixels to black
pub fn cleaned_alpha_channel(png: &PngImage) -> Option<PngImage> {
let (bpc, bpp) = match png.ihdr.color_type {
ColorType::RGBA | ColorType::GrayscaleAlpha => {
let cpp = png.channels_per_pixel();
let bpc = png.ihdr.bit_depth.as_u8() / 8;
(bpc as usize, (bpc * cpp) as usize)
}
_ => {
return None;
}
};
if !png.ihdr.color_type.has_alpha() {
return None;
}
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
let colored_bytes = bpp - byte_depth;
let mut reduced = Vec::with_capacity(png.data.len());
for pixel in png.data.chunks(bpp) {
if pixel.iter().skip(bpp - bpc).all(|b| *b == 0) {
if pixel.iter().skip(colored_bytes).all(|b| *b == 0) {
reduced.resize(reduced.len() + bpp, 0);
} else {
reduced.extend_from_slice(pixel);
@ -26,23 +24,18 @@ pub fn cleaned_alpha_channel(png: &PngImage) -> Option<PngImage> {
Some(PngImage {
data: reduced,
ihdr: png.ihdr,
palette: png.palette.clone(),
transparency_pixel: png.transparency_pixel.clone(),
ihdr: png.ihdr.clone(),
aux_headers: png.aux_headers.clone(),
})
}
#[must_use]
pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<PngImage> {
let target_color_type = match png.ihdr.color_type {
ColorType::GrayscaleAlpha => ColorType::Grayscale,
ColorType::RGBA => ColorType::RGB,
_ => return None,
};
let byte_depth = (png.ihdr.bit_depth.as_u8() >> 3) as usize;
let channels = png.channels_per_pixel() as usize;
let bpp = channels * byte_depth;
if !png.ihdr.color_type.has_alpha() {
return None;
}
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
let colored_bytes = bpp - byte_depth;
// If alpha optimisation is enabled, see if the image contains only fully opaque and fully transparent pixels.
@ -66,13 +59,7 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<Png
let transparency_pixel = if has_transparency {
// If no unused color was found we will have to fail here
// Otherwise, proceed to construct the tRNS chunk
let unused_color = used_colors.iter().position(|b| !*b)? as u8;
Some(match png.ihdr.bit_depth {
BitDepth::Sixteen => vec![unused_color; colored_bytes],
// 8-bit is still stored as 16-bit, with the high byte set to 0
_ => [0, unused_color].repeat(colored_bytes),
})
Some(used_colors.iter().position(|b| !*b)? as u8)
} else {
None
};
@ -80,13 +67,27 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<Png
let mut raw_data = Vec::with_capacity(png.data.len());
for pixel in png.data.chunks(bpp) {
match transparency_pixel {
Some(ref trns) if pixel.iter().skip(colored_bytes).all(|b| *b == 0) => {
raw_data.resize(raw_data.len() + colored_bytes, trns[1]);
Some(trns) if pixel.iter().skip(colored_bytes).all(|b| *b == 0) => {
raw_data.resize(raw_data.len() + colored_bytes, trns);
}
_ => raw_data.extend_from_slice(&pixel[0..colored_bytes]),
};
}
// Construct the color type with appropriate transparency data
let transparent = transparency_pixel.map(|trns| match png.ihdr.bit_depth {
BitDepth::Sixteen => (trns as u16) << 8 | trns as u16,
_ => trns as u16,
});
let target_color_type = match png.ihdr.color_type {
ColorType::GrayscaleAlpha => ColorType::Grayscale {
transparent_shade: transparent,
},
_ => ColorType::RGB {
transparent_color: transparent.map(|t| RGB16::new(t, t, t)),
},
};
let mut aux_headers = png.aux_headers.clone();
// sBIT contains information about alpha channel's original depth,
// and alpha has just been removed
@ -102,7 +103,5 @@ pub fn reduced_alpha_channel(png: &PngImage, optimize_alpha: bool) -> Option<Png
..png.ihdr
},
aux_headers,
transparency_pixel,
palette: None,
})
}

View file

@ -2,13 +2,11 @@ use crate::colors::{BitDepth, ColorType};
use crate::headers::IhdrData;
use crate::png::PngImage;
/// Attempt to reduce the bit depth of the image
/// Returns true if the bit depth was reduced, false otherwise
/// Attempt to reduce the bit depth of the image, returning the reduced image if successful
#[must_use]
pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option<PngImage> {
if png.ihdr.bit_depth != BitDepth::Sixteen {
if png.ihdr.color_type == ColorType::Indexed || png.ihdr.color_type == ColorType::Grayscale
{
if png.channels_per_pixel() == 1 {
return reduce_bit_depth_8_or_less(png, minimum_bits);
}
return None;
@ -23,11 +21,10 @@ pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option<PngImage>
Some(PngImage {
data: png.data.iter().step_by(2).cloned().collect(),
ihdr: IhdrData {
color_type: png.ihdr.color_type.clone(),
bit_depth: BitDepth::Eight,
..png.ihdr
},
palette: None,
transparency_pixel: png.transparency_pixel.clone(),
aux_headers: png.aux_headers.clone(),
})
}
@ -35,14 +32,14 @@ pub fn reduce_bit_depth(png: &PngImage, minimum_bits: usize) -> Option<PngImage>
#[must_use]
pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Option<PngImage> {
assert!((1..8).contains(&minimum_bits));
let bit_depth: usize = png.ihdr.bit_depth.as_u8() as usize;
let bit_depth = png.ihdr.bit_depth as usize;
if minimum_bits >= bit_depth || bit_depth > 8 {
return None;
}
// Calculate the current number of pixels per byte
let ppb = 8 / bit_depth;
if png.ihdr.color_type == ColorType::Indexed {
if let ColorType::Indexed { .. } = png.ihdr.color_type {
for line in png.scan_lines(false) {
let line_max = line
.data
@ -129,12 +126,11 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op
}
// If the image is grayscale we also need to reduce the transparency pixel
let mut transparency_pixel = png
.transparency_pixel
.clone()
.filter(|t| png.ihdr.color_type == ColorType::Grayscale && t.len() >= 2);
if let Some(trans) = transparency_pixel {
let reduced_trans = trans[1] >> (bit_depth - minimum_bits);
let color_type = if let ColorType::Grayscale {
transparent_shade: Some(trans),
} = png.ihdr.color_type
{
let reduced_trans = (trans & 0xFF) >> (bit_depth - minimum_bits);
// Verify the reduction is valid by restoring back to original bit depth
let mut check = reduced_trans;
let mut bits = minimum_bits;
@ -142,22 +138,25 @@ pub fn reduce_bit_depth_8_or_less(png: &PngImage, mut minimum_bits: usize) -> Op
check = check << bits | check;
bits <<= 1;
}
if trans[0] == 0 && trans[1] == check {
transparency_pixel = Some(vec![0, reduced_trans]);
} else {
// The transparency doesn't fit the new bit depth and is therefore unused - set it to None
transparency_pixel = None;
// If the transparency doesn't fit the new bit depth it is therefore unused - set it to None
ColorType::Grayscale {
transparent_shade: if trans == check {
Some(reduced_trans)
} else {
None
},
}
}
} else {
png.ihdr.color_type.clone()
};
Some(PngImage {
data: reduced,
ihdr: IhdrData {
bit_depth: BitDepth::from_u8(minimum_bits as u8),
color_type,
bit_depth: (minimum_bits as u8).try_into().unwrap(),
..png.ihdr
},
aux_headers: png.aux_headers.clone(),
palette: png.palette.clone(),
transparency_pixel,
})
}

View file

@ -2,7 +2,7 @@ use crate::colors::{BitDepth, ColorType};
use crate::headers::IhdrData;
use crate::png::PngImage;
use indexmap::IndexMap;
use rgb::{FromSlice, RGB8, RGBA, RGBA8};
use rgb::{ComponentMap, FromSlice, RGBA, RGBA8};
use rustc_hash::FxHasher;
use std::hash::{BuildHasherDefault, Hash};
@ -41,12 +41,9 @@ pub fn reduce_to_palette(png: &PngImage) -> Option<PngImage> {
let mut raw_data = Vec::with_capacity(png.data.len());
let mut palette = FxIndexMap::default();
palette.reserve(257);
let transparency_pixel = png
.transparency_pixel
.as_ref()
.filter(|t| png.ihdr.color_type == ColorType::RGB && t.len() >= 6)
.map(|t| RGB8::new(t[1], t[3], t[5]));
let ok = if png.ihdr.color_type == ColorType::RGB {
let ok = if let ColorType::RGB { transparent_color } = png.ihdr.color_type {
// Convert the RGB16 transparency to RGB8
let transparency_pixel = transparent_color.map(|t| t.map(|c| c as u8));
reduce_scanline_to_palette(
png.data.as_rgb().iter().cloned().map(|px| {
px.alpha(if Some(px) != transparency_pixel {
@ -132,20 +129,20 @@ pub fn reduce_to_palette(png: &PngImage) -> Option<PngImage> {
Some(PngImage {
data: raw_data,
ihdr: IhdrData {
color_type: ColorType::Indexed,
color_type: ColorType::Indexed {
palette: palette_vec,
},
..png.ihdr
},
aux_headers,
transparency_pixel: None,
palette: Some(palette_vec),
})
}
#[must_use]
pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
let mut reduced = Vec::with_capacity(png.data.len());
let byte_depth = png.ihdr.bit_depth.as_u8() as usize >> 3;
let bpp = png.channels_per_pixel() as usize * byte_depth;
let byte_depth = png.bytes_per_channel();
let bpp = png.channels_per_pixel() * byte_depth;
let last_color = 2 * byte_depth;
for pixel in png.data.chunks(bpp) {
if byte_depth == 1 {
@ -158,16 +155,6 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
reduced.extend_from_slice(&pixel[last_color..]);
}
let transparency_pixel = if let Some(ref trns) = png.transparency_pixel {
if trns.len() != 6 || trns[0..2] != trns[2..4] || trns[2..4] != trns[4..6] {
None
} else {
Some(trns[0..2].to_owned())
}
} else {
png.transparency_pixel.clone()
};
let mut aux_headers = png.aux_headers.clone();
if let Some(sbit_header) = png.aux_headers.get(b"sBIT") {
if let Some(&byte) = sbit_header.first() {
@ -180,17 +167,22 @@ pub fn reduce_rgb_to_grayscale(png: &PngImage) -> Option<PngImage> {
}
}
let color_type = match png.ihdr.color_type {
ColorType::RGB { transparent_color } => ColorType::Grayscale {
// Copy the transparent component if it is also gray
transparent_shade: transparent_color
.filter(|t| t.r == t.g && t.g == t.b)
.map(|t| t.r),
},
_ => ColorType::GrayscaleAlpha,
};
Some(PngImage {
data: reduced,
ihdr: IhdrData {
color_type: match png.ihdr.color_type {
ColorType::RGBA => ColorType::GrayscaleAlpha,
_ => ColorType::Grayscale,
},
color_type,
..png.ihdr
},
aux_headers,
palette: None,
transparency_pixel,
})
}

View file

@ -18,10 +18,11 @@ pub(crate) use crate::bit_depth::reduce_bit_depth;
/// Attempt to reduce the number of colors in the palette
/// Returns `None` if palette hasn't changed
pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage> {
if png.ihdr.color_type != ColorType::Indexed {
let palette = match &png.ihdr.color_type {
ColorType::Indexed { palette } => palette,
// Can't reduce if there is no palette
return None;
}
_ => return None,
};
if png.ihdr.bit_depth == BitDepth::One {
// Gains from 1-bit images will be at most 1 byte
// Not worth the CPU time
@ -31,8 +32,6 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage>
let mut palette_map = [None; 256];
let mut used = [false; 256];
{
let palette = png.palette.as_ref()?;
// Find palette entries that are never used
match png.ihdr.bit_depth {
BitDepth::Eight => {
@ -109,11 +108,15 @@ pub fn reduced_palette(png: &PngImage, optimize_alpha: bool) -> Option<PngImage>
}
}
do_palette_reduction(png, &palette_map)
do_palette_reduction(png, palette, &palette_map)
}
#[must_use]
fn do_palette_reduction(png: &PngImage, palette_map: &[Option<u8>; 256]) -> Option<PngImage> {
fn do_palette_reduction(
png: &PngImage,
palette: &[RGBA8],
palette_map: &[Option<u8>; 256],
) -> Option<PngImage> {
let byte_map = palette_map_to_byte_map(png, palette_map)?;
// Reassign data bytes to new indices
@ -131,12 +134,12 @@ fn do_palette_reduction(png: &PngImage, palette_map: &[Option<u8>; 256]) -> Opti
Some(PngImage {
ihdr: IhdrData {
color_type: ColorType::Indexed,
color_type: ColorType::Indexed {
palette: reordered_palette(palette, palette_map),
},
..png.ihdr
},
data: raw_data,
transparency_pixel: None,
palette: Some(reordered_palette(png.palette.as_ref()?, palette_map)),
aux_headers,
})
}
@ -187,22 +190,20 @@ fn reordered_palette(palette: &[RGBA8], palette_map: &[Option<u8>; 256]) -> Vec<
new_palette
}
/// Attempt to reduce the color type of the image
/// Returns true if the color type was reduced, false otherwise
/// Attempt to reduce the color type of the image, returning the reduced image if successful
pub fn reduce_color_type(
png: &PngImage,
grayscale_reduction: bool,
optimize_alpha: bool,
) -> Option<PngImage> {
let mut should_reduce_bit_depth = false;
let was_single_channel = png.channels_per_pixel() == 1;
let mut reduced = Cow::Borrowed(png);
// Go down one step at a time
// Maybe not the most efficient, but it's safe
if grayscale_reduction && matches!(reduced.ihdr.color_type, ColorType::RGBA | ColorType::RGB) {
// Go down one step at a time - maybe not the most efficient, but it's safe
// Attempt to reduce RGB to grayscale
if grayscale_reduction && reduced.ihdr.color_type.is_rgb() {
if let Some(r) = reduce_rgb_to_grayscale(&reduced) {
reduced = Cow::Owned(r);
should_reduce_bit_depth = reduced.ihdr.color_type == ColorType::Grayscale;
}
}
@ -210,17 +211,13 @@ pub fn reduce_color_type(
if reduced.ihdr.color_type == ColorType::GrayscaleAlpha {
if let Some(r) = reduced_alpha_channel(&reduced, optimize_alpha) {
reduced = Cow::Owned(r);
should_reduce_bit_depth = true;
}
}
if matches!(
reduced.ihdr.color_type,
ColorType::RGBA | ColorType::RGB | ColorType::GrayscaleAlpha
) {
// Attempt to reduce to palette, if not already a single channel
if reduced.channels_per_pixel() != 1 {
if let Some(r) = reduce_to_palette(&reduced) {
reduced = Cow::Owned(r);
should_reduce_bit_depth = true;
// Make sure that palette gets sorted. Ideally, this should be done within reduce_to_palette.
if let Some(r) = reduced_palette(&reduced, optimize_alpha) {
@ -236,9 +233,8 @@ pub fn reduce_color_type(
}
}
if should_reduce_bit_depth {
// Some conversions will allow us to perform bit depth reduction that
// wasn't possible before
// Some conversions will allow us to perform bit depth reduction that wasn't possible before
if !was_single_channel && reduced.channels_per_pixel() == 1 {
if let Some(r) = reduce_bit_depth_8_or_less(&reduced, 1) {
reduced = Cow::Owned(r);
}

View file

@ -5,6 +5,12 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const GRAYSCALE_ALPHA: u8 = 4;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -23,9 +29,9 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
filter: RowFilter,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
@ -34,7 +40,7 @@ fn test_it_converts(
let png = PngData::new(&input, opts.fix_errors).unwrap();
opts.filter = IndexSet::new();
opts.filter.insert(filter);
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -52,12 +58,10 @@ fn test_it_converts(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
if let Some(palette) = png.raw.palette.as_ref() {
assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth.as_u8() as usize));
} else {
assert_ne!(png.raw.ihdr.color_type, ColorType::Indexed);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth as u8));
}
remove_file(output).ok();
@ -68,9 +72,9 @@ fn filter_0_for_rgba_16() {
test_it_converts(
"tests/files/filter_0_for_rgba_16.png",
RowFilter::None,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -80,9 +84,9 @@ fn filter_1_for_rgba_16() {
test_it_converts(
"tests/files/filter_1_for_rgba_16.png",
RowFilter::Sub,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -92,9 +96,9 @@ fn filter_2_for_rgba_16() {
test_it_converts(
"tests/files/filter_2_for_rgba_16.png",
RowFilter::Up,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -104,9 +108,9 @@ fn filter_3_for_rgba_16() {
test_it_converts(
"tests/files/filter_3_for_rgba_16.png",
RowFilter::Average,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -116,9 +120,9 @@ fn filter_4_for_rgba_16() {
test_it_converts(
"tests/files/filter_4_for_rgba_16.png",
RowFilter::Paeth,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -128,9 +132,9 @@ fn filter_5_for_rgba_16() {
test_it_converts(
"tests/files/filter_5_for_rgba_16.png",
RowFilter::MinSum,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -140,9 +144,9 @@ fn filter_0_for_rgba_8() {
test_it_converts(
"tests/files/filter_0_for_rgba_8.png",
RowFilter::None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -152,9 +156,9 @@ fn filter_1_for_rgba_8() {
test_it_converts(
"tests/files/filter_1_for_rgba_8.png",
RowFilter::Sub,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -164,9 +168,9 @@ fn filter_2_for_rgba_8() {
test_it_converts(
"tests/files/filter_2_for_rgba_8.png",
RowFilter::Up,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -176,9 +180,9 @@ fn filter_3_for_rgba_8() {
test_it_converts(
"tests/files/filter_3_for_rgba_8.png",
RowFilter::Average,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -188,9 +192,9 @@ fn filter_4_for_rgba_8() {
test_it_converts(
"tests/files/filter_4_for_rgba_8.png",
RowFilter::Paeth,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -200,9 +204,9 @@ fn filter_5_for_rgba_8() {
test_it_converts(
"tests/files/filter_5_for_rgba_8.png",
RowFilter::MinSum,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -212,9 +216,9 @@ fn filter_0_for_rgb_16() {
test_it_converts(
"tests/files/filter_0_for_rgb_16.png",
RowFilter::None,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -224,9 +228,9 @@ fn filter_1_for_rgb_16() {
test_it_converts(
"tests/files/filter_1_for_rgb_16.png",
RowFilter::Sub,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -236,9 +240,9 @@ fn filter_2_for_rgb_16() {
test_it_converts(
"tests/files/filter_2_for_rgb_16.png",
RowFilter::Up,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -248,9 +252,9 @@ fn filter_3_for_rgb_16() {
test_it_converts(
"tests/files/filter_3_for_rgb_16.png",
RowFilter::Average,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -260,9 +264,9 @@ fn filter_4_for_rgb_16() {
test_it_converts(
"tests/files/filter_4_for_rgb_16.png",
RowFilter::Paeth,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -272,9 +276,9 @@ fn filter_5_for_rgb_16() {
test_it_converts(
"tests/files/filter_5_for_rgb_16.png",
RowFilter::MinSum,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -284,9 +288,9 @@ fn filter_0_for_rgb_8() {
test_it_converts(
"tests/files/filter_0_for_rgb_8.png",
RowFilter::None,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -296,9 +300,9 @@ fn filter_1_for_rgb_8() {
test_it_converts(
"tests/files/filter_1_for_rgb_8.png",
RowFilter::Sub,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -308,9 +312,9 @@ fn filter_2_for_rgb_8() {
test_it_converts(
"tests/files/filter_2_for_rgb_8.png",
RowFilter::Up,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -320,9 +324,9 @@ fn filter_3_for_rgb_8() {
test_it_converts(
"tests/files/filter_3_for_rgb_8.png",
RowFilter::Average,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -332,9 +336,9 @@ fn filter_4_for_rgb_8() {
test_it_converts(
"tests/files/filter_4_for_rgb_8.png",
RowFilter::Paeth,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -344,9 +348,9 @@ fn filter_5_for_rgb_8() {
test_it_converts(
"tests/files/filter_5_for_rgb_8.png",
RowFilter::MinSum,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -356,9 +360,9 @@ fn filter_0_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_0_for_grayscale_alpha_16.png",
RowFilter::None,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -368,9 +372,9 @@ fn filter_1_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_1_for_grayscale_alpha_16.png",
RowFilter::Sub,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -380,9 +384,9 @@ fn filter_2_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_2_for_grayscale_alpha_16.png",
RowFilter::Up,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -392,9 +396,9 @@ fn filter_3_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_3_for_grayscale_alpha_16.png",
RowFilter::Average,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -404,9 +408,9 @@ fn filter_4_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_4_for_grayscale_alpha_16.png",
RowFilter::Paeth,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -416,9 +420,9 @@ fn filter_5_for_grayscale_alpha_16() {
test_it_converts(
"tests/files/filter_5_for_grayscale_alpha_16.png",
RowFilter::MinSum,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -428,9 +432,9 @@ fn filter_0_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_0_for_grayscale_alpha_8.png",
RowFilter::None,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -440,9 +444,9 @@ fn filter_1_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_1_for_grayscale_alpha_8.png",
RowFilter::Sub,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -452,9 +456,9 @@ fn filter_2_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_2_for_grayscale_alpha_8.png",
RowFilter::Up,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -464,9 +468,9 @@ fn filter_3_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_3_for_grayscale_alpha_8.png",
RowFilter::Average,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -476,9 +480,9 @@ fn filter_4_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_4_for_grayscale_alpha_8.png",
RowFilter::Paeth,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -488,9 +492,9 @@ fn filter_5_for_grayscale_alpha_8() {
test_it_converts(
"tests/files/filter_5_for_grayscale_alpha_8.png",
RowFilter::MinSum,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -500,9 +504,9 @@ fn filter_0_for_grayscale_16() {
test_it_converts(
"tests/files/filter_0_for_grayscale_16.png",
RowFilter::None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -512,9 +516,9 @@ fn filter_1_for_grayscale_16() {
test_it_converts(
"tests/files/filter_1_for_grayscale_16.png",
RowFilter::Sub,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -524,9 +528,9 @@ fn filter_2_for_grayscale_16() {
test_it_converts(
"tests/files/filter_2_for_grayscale_16.png",
RowFilter::Up,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -536,9 +540,9 @@ fn filter_3_for_grayscale_16() {
test_it_converts(
"tests/files/filter_3_for_grayscale_16.png",
RowFilter::Average,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -548,9 +552,9 @@ fn filter_4_for_grayscale_16() {
test_it_converts(
"tests/files/filter_4_for_grayscale_16.png",
RowFilter::Paeth,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -560,9 +564,9 @@ fn filter_5_for_grayscale_16() {
test_it_converts(
"tests/files/filter_5_for_grayscale_16.png",
RowFilter::MinSum,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -572,9 +576,9 @@ fn filter_0_for_grayscale_8() {
test_it_converts(
"tests/files/filter_0_for_grayscale_8.png",
RowFilter::None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -584,9 +588,9 @@ fn filter_1_for_grayscale_8() {
test_it_converts(
"tests/files/filter_1_for_grayscale_8.png",
RowFilter::Sub,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -596,9 +600,9 @@ fn filter_2_for_grayscale_8() {
test_it_converts(
"tests/files/filter_2_for_grayscale_8.png",
RowFilter::Up,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -608,9 +612,9 @@ fn filter_3_for_grayscale_8() {
test_it_converts(
"tests/files/filter_3_for_grayscale_8.png",
RowFilter::Average,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -620,9 +624,9 @@ fn filter_4_for_grayscale_8() {
test_it_converts(
"tests/files/filter_4_for_grayscale_8.png",
RowFilter::Paeth,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -632,9 +636,9 @@ fn filter_5_for_grayscale_8() {
test_it_converts(
"tests/files/filter_5_for_grayscale_8.png",
RowFilter::MinSum,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -644,9 +648,9 @@ fn filter_0_for_palette_4() {
test_it_converts(
"tests/files/filter_0_for_palette_4.png",
RowFilter::None,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -656,9 +660,9 @@ fn filter_1_for_palette_4() {
test_it_converts(
"tests/files/filter_1_for_palette_4.png",
RowFilter::Sub,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -668,9 +672,9 @@ fn filter_2_for_palette_4() {
test_it_converts(
"tests/files/filter_2_for_palette_4.png",
RowFilter::Up,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -680,9 +684,9 @@ fn filter_3_for_palette_4() {
test_it_converts(
"tests/files/filter_3_for_palette_4.png",
RowFilter::Average,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -692,9 +696,9 @@ fn filter_4_for_palette_4() {
test_it_converts(
"tests/files/filter_4_for_palette_4.png",
RowFilter::Paeth,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -704,9 +708,9 @@ fn filter_5_for_palette_4() {
test_it_converts(
"tests/files/filter_5_for_palette_4.png",
RowFilter::MinSum,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -716,9 +720,9 @@ fn filter_0_for_palette_2() {
test_it_converts(
"tests/files/filter_0_for_palette_2.png",
RowFilter::None,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -728,9 +732,9 @@ fn filter_1_for_palette_2() {
test_it_converts(
"tests/files/filter_1_for_palette_2.png",
RowFilter::Sub,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -740,9 +744,9 @@ fn filter_2_for_palette_2() {
test_it_converts(
"tests/files/filter_2_for_palette_2.png",
RowFilter::Up,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -752,9 +756,9 @@ fn filter_3_for_palette_2() {
test_it_converts(
"tests/files/filter_3_for_palette_2.png",
RowFilter::Average,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -764,9 +768,9 @@ fn filter_4_for_palette_2() {
test_it_converts(
"tests/files/filter_4_for_palette_2.png",
RowFilter::Paeth,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -776,9 +780,9 @@ fn filter_5_for_palette_2() {
test_it_converts(
"tests/files/filter_5_for_palette_2.png",
RowFilter::MinSum,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -788,9 +792,9 @@ fn filter_0_for_palette_1() {
test_it_converts(
"tests/files/filter_0_for_palette_1.png",
RowFilter::None,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -800,9 +804,9 @@ fn filter_1_for_palette_1() {
test_it_converts(
"tests/files/filter_1_for_palette_1.png",
RowFilter::Sub,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -812,9 +816,9 @@ fn filter_2_for_palette_1() {
test_it_converts(
"tests/files/filter_2_for_palette_1.png",
RowFilter::Up,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -824,9 +828,9 @@ fn filter_3_for_palette_1() {
test_it_converts(
"tests/files/filter_3_for_palette_1.png",
RowFilter::Average,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -836,9 +840,9 @@ fn filter_4_for_palette_1() {
test_it_converts(
"tests/files/filter_4_for_palette_1.png",
RowFilter::Paeth,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -848,9 +852,9 @@ fn filter_5_for_palette_1() {
test_it_converts(
"tests/files/filter_5_for_palette_1.png",
RowFilter::MinSum,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}

View file

@ -11,6 +11,11 @@ use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -32,9 +37,9 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
input: PathBuf,
output: &OutFile,
opts: &oxipng::Options,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
mut callback_pre: CBPRE,
mut callback_post: CBPOST,
@ -44,7 +49,7 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
{
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
callback_pre(&input);
@ -66,7 +71,7 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
remove_file(output).ok();
@ -77,9 +82,9 @@ fn test_it_converts(
input: PathBuf,
output: &OutFile,
opts: &oxipng::Options,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
test_it_converts_callbacks(
@ -153,9 +158,9 @@ fn verbose_mode() {
input,
&output,
&opts,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
};
@ -411,7 +416,7 @@ fn interlacing_0_to_1_small_files() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -430,7 +435,7 @@ fn interlacing_0_to_1_small_files() {
};
assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::One);
remove_file(output).ok();
@ -445,7 +450,7 @@ fn interlacing_1_to_0_small_files() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -464,7 +469,7 @@ fn interlacing_1_to_0_small_files() {
};
assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
// the depth can't be asserted reliably, because on such small file different zlib implementations pick different depth as the best
remove_file(output).ok();
@ -556,9 +561,9 @@ fn preserve_attrs() {
input,
&output,
&opts,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
callback_pre,
callback_post,
@ -575,7 +580,7 @@ fn fix_errors() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, ColorType::RGBA);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), RGBA);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -593,7 +598,7 @@ fn fix_errors() {
}
};
assert_eq!(png.raw.ihdr.color_type, ColorType::Grayscale);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), GRAYSCALE);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
// Cannot check if pixels are equal because image crate cannot read corrupt (input) PNGs
@ -613,9 +618,9 @@ fn zopfli_mode() {
input,
&output,
&opts,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}

View file

@ -5,6 +5,12 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const GRAYSCALE_ALPHA: u8 = 4;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -22,16 +28,16 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
let (output, opts) = get_opts(&input);
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
@ -50,7 +56,7 @@ fn test_it_converts(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
remove_file(output).ok();
@ -60,9 +66,9 @@ fn test_it_converts(
fn interlaced_rgba_16_should_be_rgba_16() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_rgba_16.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -71,9 +77,9 @@ fn interlaced_rgba_16_should_be_rgba_16() {
fn interlaced_rgba_16_should_be_rgba_8() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_rgba_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -82,9 +88,9 @@ fn interlaced_rgba_16_should_be_rgba_8() {
fn interlaced_rgba_8_should_be_rgba_8() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_rgba_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -93,9 +99,9 @@ fn interlaced_rgba_8_should_be_rgba_8() {
fn interlaced_rgba_16_should_be_rgb_16() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_rgb_16.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -104,9 +110,9 @@ fn interlaced_rgba_16_should_be_rgb_16() {
fn interlaced_rgba_16_should_be_rgb_8() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_rgb_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -115,9 +121,9 @@ fn interlaced_rgba_16_should_be_rgb_8() {
fn interlaced_rgba_8_should_be_rgb_8() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_rgb_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -126,9 +132,9 @@ fn interlaced_rgba_8_should_be_rgb_8() {
fn interlaced_rgba_16_should_be_palette_8() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_palette_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -137,9 +143,9 @@ fn interlaced_rgba_16_should_be_palette_8() {
fn interlaced_rgba_8_should_be_palette_8() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_palette_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -148,9 +154,9 @@ fn interlaced_rgba_8_should_be_palette_8() {
fn interlaced_rgba_16_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_palette_4.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -159,9 +165,9 @@ fn interlaced_rgba_16_should_be_palette_4() {
fn interlaced_rgba_8_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_palette_4.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -170,9 +176,9 @@ fn interlaced_rgba_8_should_be_palette_4() {
fn interlaced_rgba_16_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_palette_2.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -181,9 +187,9 @@ fn interlaced_rgba_16_should_be_palette_2() {
fn interlaced_rgba_8_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_palette_2.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -192,9 +198,9 @@ fn interlaced_rgba_8_should_be_palette_2() {
fn interlaced_rgba_16_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_palette_1.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -203,9 +209,9 @@ fn interlaced_rgba_16_should_be_palette_1() {
fn interlaced_rgba_8_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_palette_1.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -214,9 +220,9 @@ fn interlaced_rgba_8_should_be_palette_1() {
fn interlaced_rgba_16_should_be_grayscale_alpha_16() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -225,9 +231,9 @@ fn interlaced_rgba_16_should_be_grayscale_alpha_16() {
fn interlaced_rgba_16_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -236,9 +242,9 @@ fn interlaced_rgba_16_should_be_grayscale_alpha_8() {
fn interlaced_rgba_8_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_grayscale_alpha_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -247,9 +253,9 @@ fn interlaced_rgba_8_should_be_grayscale_alpha_8() {
fn interlaced_rgba_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_grayscale_16.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -258,9 +264,9 @@ fn interlaced_rgba_16_should_be_grayscale_16() {
fn interlaced_rgba_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_rgba_16_should_be_grayscale_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -269,9 +275,9 @@ fn interlaced_rgba_16_should_be_grayscale_8() {
fn interlaced_rgba_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_rgba_8_should_be_grayscale_8.png",
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -280,9 +286,9 @@ fn interlaced_rgba_8_should_be_grayscale_8() {
fn interlaced_rgb_16_should_be_rgb_16() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_rgb_16.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -291,9 +297,9 @@ fn interlaced_rgb_16_should_be_rgb_16() {
fn interlaced_rgb_16_should_be_rgb_8() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_rgb_8.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -302,9 +308,9 @@ fn interlaced_rgb_16_should_be_rgb_8() {
fn interlaced_rgb_8_should_be_rgb_8() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_rgb_8.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -313,9 +319,9 @@ fn interlaced_rgb_8_should_be_rgb_8() {
fn interlaced_rgb_16_should_be_palette_8() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_palette_8.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -324,9 +330,9 @@ fn interlaced_rgb_16_should_be_palette_8() {
fn interlaced_rgb_8_should_be_palette_8() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_palette_8.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -335,9 +341,9 @@ fn interlaced_rgb_8_should_be_palette_8() {
fn interlaced_rgb_16_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_palette_4.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -346,9 +352,9 @@ fn interlaced_rgb_16_should_be_palette_4() {
fn interlaced_rgb_8_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_palette_4.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -357,9 +363,9 @@ fn interlaced_rgb_8_should_be_palette_4() {
fn interlaced_rgb_16_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_palette_2.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -368,9 +374,9 @@ fn interlaced_rgb_16_should_be_palette_2() {
fn interlaced_rgb_8_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_palette_2.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -379,9 +385,9 @@ fn interlaced_rgb_8_should_be_palette_2() {
fn interlaced_rgb_16_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_palette_1.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -390,9 +396,9 @@ fn interlaced_rgb_16_should_be_palette_1() {
fn interlaced_rgb_8_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_palette_1.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -401,9 +407,9 @@ fn interlaced_rgb_8_should_be_palette_1() {
fn interlaced_rgb_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_grayscale_16.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -412,9 +418,9 @@ fn interlaced_rgb_16_should_be_grayscale_16() {
fn interlaced_rgb_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_grayscale_8.png",
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -423,9 +429,9 @@ fn interlaced_rgb_16_should_be_grayscale_8() {
fn interlaced_rgb_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_grayscale_8.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -434,9 +440,9 @@ fn interlaced_rgb_8_should_be_grayscale_8() {
fn interlaced_palette_8_should_be_palette_8() {
test_it_converts(
"tests/files/interlaced_palette_8_should_be_palette_8.png",
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -445,9 +451,9 @@ fn interlaced_palette_8_should_be_palette_8() {
fn interlaced_palette_8_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_palette_8_should_be_palette_4.png",
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -456,9 +462,9 @@ fn interlaced_palette_8_should_be_palette_4() {
fn interlaced_palette_4_should_be_palette_4() {
test_it_converts(
"tests/files/interlaced_palette_4_should_be_palette_4.png",
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -467,9 +473,9 @@ fn interlaced_palette_4_should_be_palette_4() {
fn interlaced_palette_8_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_palette_8_should_be_palette_2.png",
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -478,9 +484,9 @@ fn interlaced_palette_8_should_be_palette_2() {
fn interlaced_palette_4_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_palette_4_should_be_palette_2.png",
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -489,9 +495,9 @@ fn interlaced_palette_4_should_be_palette_2() {
fn interlaced_palette_2_should_be_palette_2() {
test_it_converts(
"tests/files/interlaced_palette_2_should_be_palette_2.png",
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -500,9 +506,9 @@ fn interlaced_palette_2_should_be_palette_2() {
fn interlaced_palette_8_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_palette_8_should_be_palette_1.png",
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -511,9 +517,9 @@ fn interlaced_palette_8_should_be_palette_1() {
fn interlaced_palette_4_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_palette_4_should_be_palette_1.png",
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -522,9 +528,9 @@ fn interlaced_palette_4_should_be_palette_1() {
fn interlaced_palette_2_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_palette_2_should_be_palette_1.png",
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -533,9 +539,9 @@ fn interlaced_palette_2_should_be_palette_1() {
fn interlaced_palette_1_should_be_palette_1() {
test_it_converts(
"tests/files/interlaced_palette_1_should_be_palette_1.png",
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -544,9 +550,9 @@ fn interlaced_palette_1_should_be_palette_1() {
fn interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -555,9 +561,9 @@ fn interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16() {
fn interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -566,9 +572,9 @@ fn interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8() {
fn interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -577,9 +583,9 @@ fn interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8() {
fn interlaced_grayscale_alpha_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_16.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -588,9 +594,9 @@ fn interlaced_grayscale_alpha_16_should_be_grayscale_16() {
fn interlaced_grayscale_alpha_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_8.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -599,9 +605,9 @@ fn interlaced_grayscale_alpha_16_should_be_grayscale_8() {
fn interlaced_grayscale_alpha_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_8.png",
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -610,9 +616,9 @@ fn interlaced_grayscale_alpha_8_should_be_grayscale_8() {
fn interlaced_grayscale_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/interlaced_grayscale_16_should_be_grayscale_16.png",
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -621,9 +627,9 @@ fn interlaced_grayscale_16_should_be_grayscale_16() {
fn interlaced_grayscale_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_grayscale_16_should_be_grayscale_8.png",
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -632,9 +638,9 @@ fn interlaced_grayscale_16_should_be_grayscale_8() {
fn interlaced_grayscale_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/interlaced_grayscale_8_should_be_grayscale_8.png",
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -643,9 +649,9 @@ fn interlaced_grayscale_8_should_be_grayscale_8() {
fn interlaced_small_files() {
test_it_converts(
"tests/files/interlaced_small_files.png",
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -654,9 +660,9 @@ fn interlaced_small_files() {
fn interlaced_odd_width() {
test_it_converts(
"tests/files/interlaced_odd_width.png",
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}

View file

@ -5,6 +5,9 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -23,16 +26,16 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
interlace: Interlacing,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
let (output, mut opts) = get_opts(&input);
let png = PngData::new(&input, opts.fix_errors).unwrap();
opts.interlace = Some(interlace);
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
assert_eq!(
png.raw.ihdr.interlaced,
@ -58,7 +61,7 @@ fn test_it_converts(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
remove_file(output).ok();
@ -69,9 +72,9 @@ fn deinterlace_rgb_16() {
test_it_converts(
"tests/files/interlaced_rgb_16_should_be_rgb_16.png",
Interlacing::None,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -81,9 +84,9 @@ fn deinterlace_rgb_8() {
test_it_converts(
"tests/files/interlaced_rgb_8_should_be_rgb_8.png",
Interlacing::None,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -93,9 +96,9 @@ fn deinterlace_palette_8() {
test_it_converts(
"tests/files/interlaced_palette_8_should_be_palette_8.png",
Interlacing::None,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -105,9 +108,9 @@ fn deinterlace_palette_4() {
test_it_converts(
"tests/files/interlaced_palette_4_should_be_palette_4.png",
Interlacing::None,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -117,9 +120,9 @@ fn deinterlace_palette_2() {
test_it_converts(
"tests/files/interlaced_palette_2_should_be_palette_2.png",
Interlacing::None,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -129,9 +132,9 @@ fn deinterlace_palette_1() {
test_it_converts(
"tests/files/interlaced_palette_1_should_be_palette_1.png",
Interlacing::None,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -141,9 +144,9 @@ fn interlace_rgb_16() {
test_it_converts(
"tests/files/rgb_16_should_be_rgb_16.png",
Interlacing::Adam7,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -153,9 +156,9 @@ fn interlace_rgb_8() {
test_it_converts(
"tests/files/rgb_8_should_be_rgb_8.png",
Interlacing::Adam7,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -165,9 +168,9 @@ fn interlace_palette_8() {
test_it_converts(
"tests/files/palette_8_should_be_palette_8.png",
Interlacing::Adam7,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -177,9 +180,9 @@ fn interlace_palette_4() {
test_it_converts(
"tests/files/palette_4_should_be_palette_4.png",
Interlacing::Adam7,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -189,9 +192,9 @@ fn interlace_palette_2() {
test_it_converts(
"tests/files/palette_2_should_be_palette_2.png",
Interlacing::Adam7,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -201,9 +204,9 @@ fn interlace_palette_1() {
test_it_converts(
"tests/files/palette_1_should_be_palette_1.png",
Interlacing::Adam7,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}

View file

@ -5,6 +5,12 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const GRAYSCALE_ALPHA: u8 = 4;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -23,9 +29,9 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
optimize_alpha: bool,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
@ -33,7 +39,7 @@ fn test_it_converts(
opts.optimize_alpha = optimize_alpha;
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in, "test file is broken");
assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
@ -52,7 +58,7 @@ fn test_it_converts(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
remove_file(output).ok();
@ -63,9 +69,9 @@ fn rgba_16_should_be_rgba_16() {
test_it_converts(
"tests/files/rgba_16_should_be_rgba_16.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
);
}
@ -75,9 +81,9 @@ fn rgba_16_should_be_rgba_8() {
test_it_converts(
"tests/files/rgba_16_should_be_rgba_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -87,9 +93,9 @@ fn rgba_8_should_be_rgba_8() {
test_it_converts(
"tests/files/rgba_8_should_be_rgba_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -99,9 +105,9 @@ fn rgba_16_should_be_rgb_16() {
test_it_converts(
"tests/files/rgba_16_should_be_rgb_16.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -111,9 +117,9 @@ fn rgba_16_should_be_rgb_8() {
test_it_converts(
"tests/files/rgba_16_should_be_rgb_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -123,9 +129,9 @@ fn rgba_8_should_be_rgb_8() {
test_it_converts(
"tests/files/rgba_8_should_be_rgb_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -135,9 +141,9 @@ fn rgba_16_should_be_rgb_trns_16() {
test_it_converts(
"tests/files/rgba_16_should_be_rgb_trns_16.png",
true,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -147,9 +153,9 @@ fn rgba_8_should_be_rgb_trns_8() {
test_it_converts(
"tests/files/rgba_8_should_be_rgb_trns_8.png",
true,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -159,9 +165,9 @@ fn rgba_16_should_be_palette_8() {
test_it_converts(
"tests/files/rgba_16_should_be_palette_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -171,9 +177,9 @@ fn rgba_8_should_be_palette_8() {
test_it_converts(
"tests/files/rgba_8_should_be_palette_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -183,9 +189,9 @@ fn rgba_16_should_be_palette_4() {
test_it_converts(
"tests/files/rgba_16_should_be_palette_4.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -195,9 +201,9 @@ fn rgba_8_should_be_palette_4() {
test_it_converts(
"tests/files/rgba_8_should_be_palette_4.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -207,9 +213,9 @@ fn rgba_16_should_be_palette_2() {
test_it_converts(
"tests/files/rgba_16_should_be_palette_2.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -219,9 +225,9 @@ fn rgba_8_should_be_palette_2() {
test_it_converts(
"tests/files/rgba_8_should_be_palette_2.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -231,9 +237,9 @@ fn rgba_16_should_be_palette_1() {
test_it_converts(
"tests/files/rgba_16_should_be_palette_1.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -243,9 +249,9 @@ fn rgba_8_should_be_palette_1() {
test_it_converts(
"tests/files/rgba_8_should_be_palette_1.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -255,9 +261,9 @@ fn rgba_16_should_be_grayscale_alpha_16() {
test_it_converts(
"tests/files/rgba_16_should_be_grayscale_alpha_16.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -267,9 +273,9 @@ fn rgba_16_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/rgba_16_should_be_grayscale_alpha_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -279,9 +285,9 @@ fn rgba_8_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/rgba_8_should_be_grayscale_alpha_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -291,9 +297,9 @@ fn rgba_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/rgba_16_should_be_grayscale_16.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -303,9 +309,9 @@ fn rgba_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/rgba_16_should_be_grayscale_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -315,9 +321,9 @@ fn rgba_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/rgba_8_should_be_grayscale_8.png",
false,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -327,9 +333,9 @@ fn rgb_16_should_be_rgb_16() {
test_it_converts(
"tests/files/rgb_16_should_be_rgb_16.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -339,9 +345,9 @@ fn rgb_16_should_be_rgb_8() {
test_it_converts(
"tests/files/rgb_16_should_be_rgb_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -351,9 +357,9 @@ fn rgb_8_should_be_rgb_8() {
test_it_converts(
"tests/files/rgb_8_should_be_rgb_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -363,9 +369,9 @@ fn rgb_16_should_be_palette_8() {
test_it_converts(
"tests/files/rgb_16_should_be_palette_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -375,9 +381,9 @@ fn rgb_8_should_be_palette_8() {
test_it_converts(
"tests/files/rgb_8_should_be_palette_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -387,9 +393,9 @@ fn rgb_16_should_be_palette_4() {
test_it_converts(
"tests/files/rgb_16_should_be_palette_4.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -399,9 +405,9 @@ fn rgb_8_should_be_palette_4() {
test_it_converts(
"tests/files/rgb_8_should_be_palette_4.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -411,9 +417,9 @@ fn rgb_16_should_be_palette_2() {
test_it_converts(
"tests/files/rgb_16_should_be_palette_2.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -423,9 +429,9 @@ fn rgb_8_should_be_palette_2() {
test_it_converts(
"tests/files/rgb_8_should_be_palette_2.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -435,9 +441,9 @@ fn rgb_16_should_be_palette_1() {
test_it_converts(
"tests/files/rgb_16_should_be_palette_1.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -447,9 +453,9 @@ fn rgb_8_should_be_palette_1() {
test_it_converts(
"tests/files/rgb_8_should_be_palette_1.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -459,9 +465,9 @@ fn rgb_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/rgb_16_should_be_grayscale_16.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -471,9 +477,9 @@ fn rgb_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/rgb_16_should_be_grayscale_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -483,9 +489,9 @@ fn rgb_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/rgb_8_should_be_grayscale_8.png",
false,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -495,9 +501,9 @@ fn palette_8_should_be_palette_8() {
test_it_converts(
"tests/files/palette_8_should_be_palette_8.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -507,9 +513,9 @@ fn palette_8_should_be_palette_4() {
test_it_converts(
"tests/files/palette_8_should_be_palette_4.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -519,9 +525,9 @@ fn palette_4_should_be_palette_4() {
test_it_converts(
"tests/files/palette_4_should_be_palette_4.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -531,9 +537,9 @@ fn palette_8_should_be_palette_2() {
test_it_converts(
"tests/files/palette_8_should_be_palette_2.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -543,9 +549,9 @@ fn palette_4_should_be_palette_2() {
test_it_converts(
"tests/files/palette_4_should_be_palette_2.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -555,9 +561,9 @@ fn palette_2_should_be_palette_2() {
test_it_converts(
"tests/files/palette_2_should_be_palette_2.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -567,9 +573,9 @@ fn palette_8_should_be_palette_1() {
test_it_converts(
"tests/files/palette_8_should_be_palette_1.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -579,9 +585,9 @@ fn palette_4_should_be_palette_1() {
test_it_converts(
"tests/files/palette_4_should_be_palette_1.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -591,9 +597,9 @@ fn palette_2_should_be_palette_1() {
test_it_converts(
"tests/files/palette_2_should_be_palette_1.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -603,9 +609,9 @@ fn palette_1_should_be_palette_1() {
test_it_converts(
"tests/files/palette_1_should_be_palette_1.png",
false,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -615,9 +621,9 @@ fn grayscale_alpha_16_should_be_grayscale_alpha_16() {
test_it_converts(
"tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
);
}
@ -627,9 +633,9 @@ fn grayscale_alpha_16_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -639,9 +645,9 @@ fn grayscale_alpha_8_should_be_grayscale_alpha_8() {
test_it_converts(
"tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -651,9 +657,9 @@ fn grayscale_alpha_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/grayscale_alpha_16_should_be_grayscale_16.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -663,9 +669,9 @@ fn grayscale_alpha_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/grayscale_alpha_16_should_be_grayscale_8.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -675,9 +681,9 @@ fn grayscale_alpha_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/grayscale_alpha_8_should_be_grayscale_8.png",
false,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -687,9 +693,9 @@ fn grayscale_16_should_be_grayscale_16() {
test_it_converts(
"tests/files/grayscale_16_should_be_grayscale_16.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -699,9 +705,9 @@ fn grayscale_16_should_be_grayscale_8() {
test_it_converts(
"tests/files/grayscale_16_should_be_grayscale_8.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -711,9 +717,9 @@ fn grayscale_8_should_be_grayscale_8() {
test_it_converts(
"tests/files/grayscale_8_should_be_grayscale_8.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -723,9 +729,9 @@ fn grayscale_8_should_be_grayscale_4() {
test_it_converts(
"tests/files/grayscale_8_should_be_grayscale_4.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Four,
);
}
@ -735,9 +741,9 @@ fn grayscale_8_should_be_grayscale_2() {
test_it_converts(
"tests/files/grayscale_8_should_be_grayscale_2.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Two,
);
}
@ -747,9 +753,9 @@ fn grayscale_4_should_be_grayscale_2() {
test_it_converts(
"tests/files/grayscale_4_should_be_grayscale_2.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Four,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Two,
);
}
@ -759,9 +765,9 @@ fn grayscale_8_should_be_grayscale_1() {
test_it_converts(
"tests/files/grayscale_8_should_be_grayscale_1.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -771,9 +777,9 @@ fn grayscale_4_should_be_grayscale_1() {
test_it_converts(
"tests/files/grayscale_4_should_be_grayscale_1.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Four,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -783,9 +789,9 @@ fn grayscale_2_should_be_grayscale_1() {
test_it_converts(
"tests/files/grayscale_2_should_be_grayscale_1.png",
false,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Two,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -795,9 +801,9 @@ fn grayscale_alpha_16_should_be_grayscale_trns_16() {
test_it_converts(
"tests/files/grayscale_alpha_16_should_be_grayscale_trns_16.png",
true,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Sixteen,
);
}
@ -807,9 +813,9 @@ fn grayscale_alpha_8_should_be_grayscale_trns_8() {
test_it_converts(
"tests/files/grayscale_alpha_8_should_be_grayscale_trns_8.png",
true,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -821,7 +827,7 @@ fn small_files() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -839,7 +845,7 @@ fn small_files() {
}
};
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
// depth varies depending on zlib implementation used
remove_file(output).ok();
@ -852,9 +858,11 @@ fn palette_should_be_reduced_with_dupes() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 43);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 43);
}
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
@ -871,9 +879,11 @@ fn palette_should_be_reduced_with_dupes() {
}
};
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 35);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 35);
}
remove_file(output).ok();
}
@ -885,9 +895,11 @@ fn palette_should_be_reduced_with_unused() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 35);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 35);
}
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
@ -904,9 +916,11 @@ fn palette_should_be_reduced_with_unused() {
}
};
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 33);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 33);
}
remove_file(output).ok();
}
@ -918,9 +932,11 @@ fn palette_should_be_reduced_with_both() {
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 43);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 43);
}
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
Ok(_) => (),
@ -937,9 +953,11 @@ fn palette_should_be_reduced_with_both() {
}
};
assert_eq!(png.raw.ihdr.color_type, ColorType::Indexed);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
assert_eq!(png.raw.palette.as_ref().unwrap().len(), 33);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert_eq!(palette.len(), 33);
}
remove_file(output).ok();
}
@ -949,9 +967,9 @@ fn rgba_16_reduce_alpha() {
test_it_converts(
"tests/files/rgba_16_reduce_alpha.png",
true,
ColorType::RGBA,
RGBA,
BitDepth::Sixteen,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -961,9 +979,9 @@ fn rgba_8_reduce_alpha() {
test_it_converts(
"tests/files/rgba_8_reduce_alpha.png",
true,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -973,9 +991,9 @@ fn grayscale_alpha_16_reduce_alpha() {
test_it_converts(
"tests/files/grayscale_alpha_16_reduce_alpha.png",
true,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Sixteen,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -985,9 +1003,9 @@ fn grayscale_alpha_8_reduce_alpha() {
test_it_converts(
"tests/files/grayscale_alpha_8_reduce_alpha.png",
true,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}

View file

@ -5,6 +5,12 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const GRAYSCALE_ALPHA: u8 = 4;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -23,9 +29,9 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
custom: Option<(OutFile, oxipng::Options)>,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
@ -33,7 +39,8 @@ fn test_it_converts(
let png = PngData::new(&input, opts.fix_errors).unwrap();
assert_eq!(
png.raw.ihdr.color_type, color_type_in,
png.raw.ihdr.color_type.png_header_code(),
color_type_in,
"test file is broken"
);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in, "test file is broken");
@ -54,23 +61,22 @@ fn test_it_converts(
};
assert_eq!(
png.raw.ihdr.color_type, color_type_out,
png.raw.ihdr.color_type.png_header_code(),
color_type_out,
"optimized to wrong color type"
);
assert_eq!(
png.raw.ihdr.bit_depth, bit_depth_out,
"optimized to wrong bit depth"
);
if let Some(palette) = png.raw.palette.as_ref() {
let mut max_palette_size = 1 << (png.raw.ihdr.bit_depth.as_u8() as usize);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
let mut max_palette_size = 1 << (png.raw.ihdr.bit_depth as u8);
// Ensure bKGD color is valid
if let Some(&idx) = png.raw.aux_headers.get(b"bKGD").and_then(|b| b.first()) {
assert!(palette.len() > idx as usize);
max_palette_size = max_palette_size.max(idx as usize + 1);
}
assert!(palette.len() <= max_palette_size);
} else {
assert_ne!(png.raw.ihdr.color_type, ColorType::Indexed);
}
remove_file(output).ok();
@ -81,9 +87,9 @@ fn issue_29() {
test_it_converts(
"tests/files/issue-29.png",
None,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -127,9 +133,9 @@ fn issue_52_01() {
test_it_converts(
"tests/files/issue-52-01.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -139,9 +145,9 @@ fn issue_52_02() {
test_it_converts(
"tests/files/issue-52-02.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -151,9 +157,9 @@ fn issue_52_03() {
test_it_converts(
"tests/files/issue-52-03.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -163,9 +169,9 @@ fn issue_52_04() {
test_it_converts(
"tests/files/issue-52-04.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -175,9 +181,9 @@ fn issue_52_05() {
test_it_converts(
"tests/files/issue-52-05.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -187,9 +193,9 @@ fn issue_52_06() {
test_it_converts(
"tests/files/issue-52-06.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
);
}
@ -199,9 +205,9 @@ fn issue_56() {
test_it_converts(
"tests/files/issue-56.png",
None,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -211,9 +217,9 @@ fn issue_58() {
test_it_converts(
"tests/files/issue-58.png",
None,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -223,9 +229,9 @@ fn issue_59() {
test_it_converts(
"tests/files/issue-59.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -235,9 +241,9 @@ fn issue_60() {
test_it_converts(
"tests/files/issue-60.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -247,9 +253,9 @@ fn issue_80() {
test_it_converts(
"tests/files/issue-80.png",
None,
ColorType::Indexed,
INDEXED,
BitDepth::Two,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -259,9 +265,9 @@ fn issue_82() {
test_it_converts(
"tests/files/issue-82.png",
None,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
ColorType::Indexed,
INDEXED,
BitDepth::Four,
);
}
@ -271,9 +277,9 @@ fn issue_89() {
test_it_converts(
"tests/files/issue-89.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -283,9 +289,9 @@ fn issue_92_filter_0() {
test_it_converts(
"tests/files/issue-92.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -300,9 +306,9 @@ fn issue_92_filter_5() {
test_it_converts(
input,
Some((output, opts)),
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -316,9 +322,9 @@ fn issue_113() {
test_it_converts(
input,
Some((output, opts)),
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::GrayscaleAlpha,
GRAYSCALE_ALPHA,
BitDepth::Eight,
);
}
@ -326,14 +332,7 @@ fn issue_113() {
#[test]
fn issue_129() {
let input = "tests/files/issue-129.png";
test_it_converts(
input,
None,
ColorType::RGB,
BitDepth::Eight,
ColorType::Indexed,
BitDepth::Eight,
);
test_it_converts(input, None, RGB, BitDepth::Eight, INDEXED, BitDepth::Eight);
}
#[test]
@ -344,9 +343,9 @@ fn issue_133() {
test_it_converts(
input,
Some((output, opts)),
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -356,9 +355,9 @@ fn issue_140() {
test_it_converts(
"tests/files/issue-140.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Two,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Two,
);
}
@ -368,9 +367,9 @@ fn issue_141() {
test_it_converts(
"tests/files/issue-141.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -380,9 +379,9 @@ fn issue_153() {
test_it_converts(
"tests/files/issue-153.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -392,9 +391,9 @@ fn issue_159() {
test_it_converts(
"tests/files/issue-159.png",
None,
ColorType::Indexed,
INDEXED,
BitDepth::One,
ColorType::Indexed,
INDEXED,
BitDepth::One,
);
}
@ -404,9 +403,9 @@ fn issue_171() {
test_it_converts(
"tests/files/issue-171.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -416,9 +415,9 @@ fn issue_175() {
test_it_converts(
"tests/files/issue-175.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -432,9 +431,9 @@ fn issue_182() {
test_it_converts(
input,
Some((output, opts)),
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -444,9 +443,9 @@ fn issue_195() {
test_it_converts(
"tests/files/issue-195.png",
None,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}
@ -456,9 +455,9 @@ fn issue_426_01() {
test_it_converts(
"tests/files/issue-426-01.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}
@ -468,9 +467,9 @@ fn issue_426_02() {
test_it_converts(
"tests/files/issue-426-02.png",
None,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::One,
);
}

View file

@ -5,6 +5,11 @@ use std::fs::remove_file;
use std::path::Path;
use std::path::PathBuf;
const GRAYSCALE: u8 = 0;
const RGB: u8 = 2;
const INDEXED: u8 = 3;
const RGBA: u8 = 6;
fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
let mut options = oxipng::Options {
force: true,
@ -23,9 +28,9 @@ fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
fn test_it_converts(
input: &str,
filter: RowFilter,
color_type_in: ColorType,
color_type_in: u8,
bit_depth_in: BitDepth,
color_type_out: ColorType,
color_type_out: u8,
bit_depth_out: BitDepth,
) {
let input = PathBuf::from(input);
@ -34,7 +39,7 @@ fn test_it_converts(
let png = PngData::new(&input, opts.fix_errors).unwrap();
opts.filter = IndexSet::new();
opts.filter.insert(filter);
assert_eq!(png.raw.ihdr.color_type, color_type_in);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
match oxipng::optimize(&InFile::Path(input), &output, &opts) {
@ -52,12 +57,10 @@ fn test_it_converts(
}
};
assert_eq!(png.raw.ihdr.color_type, color_type_out);
assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
if let Some(palette) = png.raw.palette.as_ref() {
assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth.as_u8() as usize));
} else {
assert_ne!(png.raw.ihdr.color_type, ColorType::Indexed);
if let ColorType::Indexed { palette } = &png.raw.ihdr.color_type {
assert!(palette.len() <= 1 << (png.raw.ihdr.bit_depth as u8));
}
remove_file(output).ok();
@ -68,9 +71,9 @@ fn filter_minsum() {
test_it_converts(
"tests/files/rgb_16_should_be_rgb_16.png",
RowFilter::MinSum,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
ColorType::RGB,
RGB,
BitDepth::Sixteen,
);
}
@ -80,9 +83,9 @@ fn filter_entropy() {
test_it_converts(
"tests/files/rgb_8_should_be_rgb_8.png",
RowFilter::Entropy,
ColorType::RGB,
RGB,
BitDepth::Eight,
ColorType::RGB,
RGB,
BitDepth::Eight,
);
}
@ -92,9 +95,9 @@ fn filter_bigrams() {
test_it_converts(
"tests/files/rgba_8_should_be_rgba_8.png",
RowFilter::Bigrams,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
ColorType::RGBA,
RGBA,
BitDepth::Eight,
);
}
@ -104,9 +107,9 @@ fn filter_bigent() {
test_it_converts(
"tests/files/grayscale_8_should_be_grayscale_8.png",
RowFilter::BigEnt,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
ColorType::Grayscale,
GRAYSCALE,
BitDepth::Eight,
);
}
@ -116,9 +119,9 @@ fn filter_brute() {
test_it_converts(
"tests/files/palette_8_should_be_palette_8.png",
RowFilter::Brute,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
ColorType::Indexed,
INDEXED,
BitDepth::Eight,
);
}