diff --git a/CHANGELOG.md b/CHANGELOG.md index 769d739e..dbbbeca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) diff --git a/src/deflate/deflate.rs b/src/deflate/deflate.rs index a146418b..14ddf550 100644 --- a/src/deflate/deflate.rs +++ b/src/deflate/deflate.rs @@ -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, 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, 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, String> { let mut input = data.to_owned(); let mut stream = super::stream::Stream::new_compress(zc as c_int, diff --git a/src/lib.rs b/src/lib.rs index aa04b22d..32d6184a 100644 --- a/src/lib.rs +++ b/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, + /// 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, + /// Which filters to try on the file (0-5) pub filter: HashSet, + /// 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, + /// Which zlib compression levels to try on the file (1-9) pub compression: HashSet, + /// Which zlib memory levels to try on the file (1-9) pub memory: HashSet, + /// Which zlib compression strategies to try on the file (0-3) pub strategies: HashSet, + /// 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() { diff --git a/src/png.rs b/src/png.rs index f2ccebdc..19addd4c 100644 --- a/src/png.rs +++ b/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), + /// 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, } #[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, + /// The headers stored in the IHDR chunk pub ihdr_data: IhdrData, + /// The uncompressed, optionally filtered data from the IDAT chunk pub raw_data: Vec, + /// The palette containing colors used in an Indexed image + /// Contains 3 bytes per color (R+G+B), up to 768 pub palette: Option>, + /// The pixel value that should be rendered as transparent pub transparency_pixel: Option>, + /// A map of how transparent each color in the palette should be pub transparency_palette: Option>, + /// All non-critical headers from the PNG are stored here pub aux_headers: HashMap>, } #[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 { 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 { // 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 { 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 { 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 {