parent
911f51faa9
commit
eba8062ba5
4 changed files with 99 additions and 0 deletions
|
|
@ -1,3 +1,6 @@
|
|||
**Version 0.2.1**
|
||||
- Add rustdoc for public methods and structs
|
||||
|
||||
**Version 0.2.0**
|
||||
- Fix program version that is displayed when running `oxipng -V`
|
||||
- Ensure `--quiet` mode is actually quiet (@SethDusek [#20](https://github.com/shssoichiro/oxipng/pull/20))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use libz_sys;
|
||||
use libc::c_int;
|
||||
|
||||
/// Decompress a data stream using the DEFLATE algorithm
|
||||
pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
|
||||
let mut input = data.to_owned();
|
||||
let mut stream = super::stream::Stream::new_decompress();
|
||||
|
|
@ -17,6 +18,7 @@ pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
|
|||
Ok(output)
|
||||
}
|
||||
|
||||
/// Compress a data stream using the zlib implementation of the DEFLATE algorithm
|
||||
pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result<Vec<u8>, String> {
|
||||
let mut input = data.to_owned();
|
||||
let mut stream = super::stream::Stream::new_compress(zc as c_int,
|
||||
|
|
|
|||
33
src/lib.rs
33
src/lib.rs
|
|
@ -17,33 +17,66 @@ pub mod deflate {
|
|||
pub mod png;
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
/// Options controlling the output of the `optimize` function
|
||||
pub struct Options {
|
||||
/// Whether the input file should be backed up before writing the output
|
||||
pub backup: bool,
|
||||
/// Path to write the output file to
|
||||
pub out_file: PathBuf,
|
||||
/// Used only in CLI interface
|
||||
pub out_dir: Option<PathBuf>,
|
||||
/// Write to stdout instead of a file
|
||||
pub stdout: bool,
|
||||
/// Attempt to fix errors when decoding the input file
|
||||
pub fix_errors: bool,
|
||||
/// Don't actually write any output, just calculate the best results
|
||||
pub pretend: bool,
|
||||
/// Used only in CLI interface
|
||||
pub recursive: bool,
|
||||
/// Overwrite existing output files
|
||||
pub clobber: bool,
|
||||
/// Create new output files if they don't exist
|
||||
pub create: bool,
|
||||
/// Write to output even if there was no improvement in compression
|
||||
pub force: bool,
|
||||
/// Ensure the output file has the same permissions as the input file
|
||||
pub preserve_attrs: bool,
|
||||
/// How verbose the console logging should be (`None` for quiet, `Some(0)` for normal, `Some(1)` for verbose)
|
||||
pub verbosity: Option<u8>,
|
||||
/// Which filters to try on the file (0-5)
|
||||
pub filter: HashSet<u8>,
|
||||
/// Whether to change the interlacing type of the file
|
||||
/// `None` will not change the current interlacing type
|
||||
/// `Some(x)` will change the file to interlacing mode `x`
|
||||
pub interlace: Option<u8>,
|
||||
/// Which zlib compression levels to try on the file (1-9)
|
||||
pub compression: HashSet<u8>,
|
||||
/// Which zlib memory levels to try on the file (1-9)
|
||||
pub memory: HashSet<u8>,
|
||||
/// Which zlib compression strategies to try on the file (0-3)
|
||||
pub strategies: HashSet<u8>,
|
||||
/// Window size to use when compressing the file, as `2^window` bytes
|
||||
/// Doesn't affect compression but may affect speed and memory usage
|
||||
/// 15 is recommended default, 8-15 are valid values
|
||||
pub window: u8,
|
||||
/// Whether to attempt bit depth reduction
|
||||
pub bit_depth_reduction: bool,
|
||||
/// Whether to attempt color type reduction
|
||||
pub color_type_reduction: bool,
|
||||
/// Whether to attempt palette reduction
|
||||
pub palette_reduction: bool,
|
||||
/// Whether to perform IDAT recoding
|
||||
/// If any type of reduction is performed, IDAT recoding will be performed
|
||||
/// regardless of this setting
|
||||
pub idat_recoding: bool,
|
||||
/// Which headers to strip from the PNG file, if any
|
||||
pub strip: png::Headers,
|
||||
/// Whether to use heuristics to pick the best filter and compression
|
||||
/// Intended for use with `-o 1` from the CLI interface
|
||||
pub use_heuristics: bool,
|
||||
}
|
||||
|
||||
/// Perform optimization on the input file using the options provided
|
||||
pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
|
||||
// Decode PNG from file
|
||||
if opts.verbosity.is_some() {
|
||||
|
|
|
|||
61
src/png.rs
61
src/png.rs
|
|
@ -10,11 +10,17 @@ use std::iter::Iterator;
|
|||
use std::path::Path;
|
||||
|
||||
#[derive(Debug,PartialEq,Clone,Copy)]
|
||||
/// The color type used to represent this image
|
||||
pub enum ColorType {
|
||||
/// Grayscale, with one color channel
|
||||
Grayscale,
|
||||
/// RGB, with three color channels
|
||||
RGB,
|
||||
/// Indexed, with one byte per pixel representing one of up to 256 colors in the image
|
||||
Indexed,
|
||||
/// Grayscale + Alpha, with two color channels
|
||||
GrayscaleAlpha,
|
||||
/// RGBA, with four color channels
|
||||
RGBA,
|
||||
}
|
||||
|
||||
|
|
@ -45,11 +51,17 @@ impl ColorType {
|
|||
}
|
||||
|
||||
#[derive(Debug,PartialEq,Clone,Copy)]
|
||||
/// The number of bits to be used per channel per pixel
|
||||
pub enum BitDepth {
|
||||
/// One bit per channel per pixel
|
||||
One,
|
||||
/// Two bits per channel per pixel
|
||||
Two,
|
||||
/// Four bits per channel per pixel
|
||||
Four,
|
||||
/// Eight bits per channel per pixel
|
||||
Eight,
|
||||
/// Sixteen bits per channel per pixel
|
||||
Sixteen,
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +80,7 @@ impl fmt::Display for BitDepth {
|
|||
}
|
||||
|
||||
impl BitDepth {
|
||||
/// Retrieve the number of bits per channel per pixel as a `u8`
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match *self {
|
||||
BitDepth::One => 1,
|
||||
|
|
@ -77,6 +90,7 @@ impl BitDepth {
|
|||
BitDepth::Sixteen => 16,
|
||||
}
|
||||
}
|
||||
/// Parse a number of bits per channel per pixel into a `BitDepth`
|
||||
pub fn from_u8(depth: u8) -> BitDepth {
|
||||
match depth {
|
||||
1 => BitDepth::One,
|
||||
|
|
@ -90,15 +104,22 @@ impl BitDepth {
|
|||
}
|
||||
|
||||
#[derive(Debug,PartialEq,Clone)]
|
||||
/// Options to use for performing operations on headers (such as stripping)
|
||||
pub enum Headers {
|
||||
/// None
|
||||
None,
|
||||
/// Some, with a list of 4-character chunk codes
|
||||
Some(Vec<String>),
|
||||
/// Headers that won't affect rendering (all but cHRM, gAMA, iCCP, sBIT, sRGB, bKGD, hIST, pHYs, sPLT)
|
||||
Safe,
|
||||
/// All non-critical headers
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
/// An iterator over the scan lines of a PNG image
|
||||
pub struct ScanLines<'a> {
|
||||
/// A reference to the PNG image being iterated upon
|
||||
pub png: &'a PngData,
|
||||
start: usize,
|
||||
end: usize,
|
||||
|
|
@ -126,34 +147,55 @@ impl<'a> Iterator for ScanLines<'a> {
|
|||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
/// A scan line in a PNG image
|
||||
pub struct ScanLine {
|
||||
/// The filter type used to encode the current scan line (0-4)
|
||||
pub filter: u8,
|
||||
/// The byte data for the current scan line, encoded with the filter specified in the `filter` field
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
/// Contains all data relevant to a PNG image
|
||||
pub struct PngData {
|
||||
/// The filtered and compressed data of the IDAT chunk
|
||||
pub idat_data: Vec<u8>,
|
||||
/// The headers stored in the IHDR chunk
|
||||
pub ihdr_data: IhdrData,
|
||||
/// The uncompressed, optionally filtered data from the IDAT chunk
|
||||
pub raw_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<u8>>,
|
||||
/// The pixel value that should be rendered as transparent
|
||||
pub transparency_pixel: Option<Vec<u8>>,
|
||||
/// A map of how transparent each color in the palette should be
|
||||
pub transparency_palette: Option<Vec<u8>>,
|
||||
/// All non-critical headers from the PNG are stored here
|
||||
pub aux_headers: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
/// Headers from the IHDR chunk of the image
|
||||
pub struct IhdrData {
|
||||
/// The width of the image in pixels
|
||||
pub width: u32,
|
||||
/// The height of the image in pixels
|
||||
pub height: u32,
|
||||
/// The color type of the image
|
||||
pub color_type: ColorType,
|
||||
/// The bit depth of the image
|
||||
pub bit_depth: BitDepth,
|
||||
/// The compression method used for this image (0 for DEFLATE)
|
||||
pub compression: u8,
|
||||
/// The filter mode used for this image (currently only 0 is valid)
|
||||
pub filter: u8,
|
||||
/// The interlacing mode of the image (0 = None, 1 = Adam7)
|
||||
pub interlaced: u8,
|
||||
}
|
||||
|
||||
impl PngData {
|
||||
/// Create a new `PngData` struct by opening a file
|
||||
pub fn new(filepath: &Path) -> Result<PngData, String> {
|
||||
let mut file = match File::open(filepath) {
|
||||
Ok(f) => f,
|
||||
|
|
@ -237,6 +279,7 @@ impl PngData {
|
|||
// Return the PngData
|
||||
Ok(png_data)
|
||||
}
|
||||
/// Return the number of channels in the image, based on color type
|
||||
pub fn channels_per_pixel(&self) -> u8 {
|
||||
match self.ihdr_data.color_type {
|
||||
ColorType::Grayscale | ColorType::Indexed => 1,
|
||||
|
|
@ -245,6 +288,7 @@ impl PngData {
|
|||
ColorType::RGBA => 4,
|
||||
}
|
||||
}
|
||||
/// Format the `PngData` struct into a valid PNG bytestream
|
||||
pub fn output(&self) -> Vec<u8> {
|
||||
// FIXME: This code can all be refactored
|
||||
// PNG header
|
||||
|
|
@ -341,6 +385,7 @@ impl PngData {
|
|||
|
||||
output
|
||||
}
|
||||
/// Return an iterator over the scanlines of the image
|
||||
pub fn scan_lines(&self) -> ScanLines {
|
||||
ScanLines {
|
||||
png: &self,
|
||||
|
|
@ -348,6 +393,7 @@ impl PngData {
|
|||
end: 0,
|
||||
}
|
||||
}
|
||||
/// Reverse all filters applied on the image, returning an unfiltered IDAT bytestream
|
||||
pub fn unfilter_image(&self) -> Vec<u8> {
|
||||
let mut unfiltered = Vec::with_capacity(self.raw_data.len());
|
||||
let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel();
|
||||
|
|
@ -442,6 +488,13 @@ impl PngData {
|
|||
}
|
||||
unfiltered
|
||||
}
|
||||
/// Apply the specified filter type to all rows in the image
|
||||
/// 0: None
|
||||
/// 1: Sub
|
||||
/// 2: Up
|
||||
/// 3: Average
|
||||
/// 4: Paeth
|
||||
/// 5: All (heuristically pick the best filter for each line)
|
||||
pub fn filter_image(&self, filter: u8) -> Vec<u8> {
|
||||
let mut filtered = Vec::with_capacity(self.raw_data.len());
|
||||
let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel();
|
||||
|
|
@ -601,6 +654,8 @@ impl PngData {
|
|||
}
|
||||
filtered
|
||||
}
|
||||
/// Attempt to reduce the bit depth of the image
|
||||
/// Returns true if the bit depth was reduced, false otherwise
|
||||
pub fn reduce_bit_depth(&mut self) -> bool {
|
||||
if self.ihdr_data.bit_depth != BitDepth::Sixteen {
|
||||
if self.ihdr_data.color_type == ColorType::Indexed ||
|
||||
|
|
@ -645,10 +700,14 @@ impl PngData {
|
|||
self.raw_data = reduced;
|
||||
true
|
||||
}
|
||||
/// Attempt to reduce the number of colors in the palette
|
||||
/// Returns true if the palette was reduced, false otherwise
|
||||
pub fn reduce_palette(&mut self) -> bool {
|
||||
// TODO: Implement
|
||||
false
|
||||
}
|
||||
/// Attempt to reduce the color type of the image
|
||||
/// Returns true if the color type was reduced, false otherwise
|
||||
pub fn reduce_color_type(&mut self) -> bool {
|
||||
let mut changed = false;
|
||||
let mut should_reduce_bit_depth = false;
|
||||
|
|
@ -732,6 +791,8 @@ impl PngData {
|
|||
|
||||
changed
|
||||
}
|
||||
/// Convert the image to the specified interlacing type
|
||||
/// Returns true if the interlacing was changed, false otherwise
|
||||
pub fn change_interlacing(&mut self, interlace: u8) -> bool {
|
||||
// TODO: Implement
|
||||
if interlace != self.ihdr_data.interlaced {
|
||||
|
|
|
|||
Loading…
Reference in a new issue