# ![PNG Pong](https://raw.githubusercontent.com/AldaronLau/png_pong/master/res/icon.png)
#### A pure Rust PNG/APNG encoder & decoder
[![Build Status](https://api.travis-ci.org/AldaronLau/png_pong.svg?branch=master)](https://travis-ci.org/AldaronLau/png_pong)
[![Docs](https://docs.rs/png_pong/badge.svg)](https://docs.rs/png_pong)
[![crates.io](https://img.shields.io/crates/v/png_pong.svg)](https://crates.io/crates/png_pong)
This is a pure Rust PNG image decoder and encoder taking inspiration from
lodepng. This crate allows easy reading and writing of PNG files without any
system dependencies.
### Why another PNG crate?
These are the 4 Rust PNG encoder/decoder crates I know of:
- [png](https://crates.io/crates/png) - The one everyone uses (used to be able
to load less pngs than png_pong and slower, but has caught up).
- [lodepng](https://crates.io/crates/lodepng) - Loads all the PNGs, code
is ported from C, therefore code is hard read & maintain, also uses
slow implementation of deflate/inflate algorithm.
- [imagefmt](https://crates.io/crates/imagefmt) - Abandoned, and
limited in what files it can open, but with a lot less lines of code.
- [imagine](https://crates.io/crates/imagine) - PNG decoding only.
Originally I made the [aci_png](https://crates.io/crates/aci_png) based
on imagefmt, and intended to add more features. At the time, I didn't want to
write a PNG encoder/decoder from scratch so I decided to take `lodepng` which
has more features (and more low level features) and clean up the code, upgrade
to 2018 edition of Rust, depend on the miniz\_oxide crate (because it can
decompress faster than lodepng's INFLATE implementation) and get rid of the libc
dependency so it *actually* becomes pure Rust (lodepng claims to be, but calls
C's malloc and free). Then, I rewrote the entire library, based on
[gift](https://crates.io/crates/gift) and [pix](https://crates.io/crates/pix).
### Goals
- Forbid unsafe.
- APNG support as iterator.
- Fast.
- Compatible with pix / gift-style API.
- Load all PNG files crushed with pngcrush.
- Save crushed PNG files.
- Clean, well-documented, concise code.
- Implement all completed, non-deprecated chunks in the
[PNG 1.2 Specification](http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html),
including the
[PNG 1.2 Extensions](https://pmt.sourceforge.io/specs/pngext-1.2.0-pdg-h20.html)
and the
[APNG Specification](https://wiki.mozilla.org/APNG_Specification)
### TODO
- Implement APNG reading.
- Implement Chunk reading (with all the different chunk structs).
- StepDecoder should wrap StepDecoder & RasterEncoder should wrap ChunkEncoder
- Replace `ParseError` with Rust-style enum instead of having a C integer.
- More test cases to test against.
### Benchmarks And Comparisons
Using Rust 1.45.0, criterion and 4 different PNG sizes with PNGs from
"./tests/png/" (units are: us / microseconds). I stopped anything that was
predicted to take longer than a half hour with criterion with the message
"TIMEOUT".
- sRGB 1x1: Uses `tests/png/profile.png`
- sRGBA 1x1: Uses `tests/png/test.png`
- sRGB 64x64: Uses `test/png/4.png`
- sRGBA 64x64: Uses `tests/png/res.png`
- sRGB 256x256: `tests/png/PngSuite.png`
- sRGBA 256x256: Uses `tests/png/icon.png`
- sRGB 4096x4096: `tests/png/plopgrizzly.png`
- sRGBA 4096x4096: Uses `tests/png/noise.png`
#### Decoder
| png_pong | 7.7904 | 4.1947 | 82.783 | 101.75 | 864.58 | 905.51 | 174,040 | 542,570 |
| png | 10.656 | 6.5267 | 52.879 | 70.930 | 634.74 | 686.75 | 119,790 | 300,980 |
| lodepng | 9.0110 | 8.5484 | 193.79 | 200.67 | 856.78 | 1,280.4 | 196,740 | 1,722,800 |
| imagefmt | 4.0710 | 3.8689 | 63.258 | 69.192 | 491.58 | 637.12 | 67,663 | 464,730 |
| imagine | 2.8407 | 0.52495 | 3,135.8 | 1,938.7 | 1,655.4 | 9,473.0 | 404,520 | TIMEOUT |
| aci_png | 3.9223 | 4.1440 | 212.54 | 177.63 | 1,395.7 | 1,674.3 | 373,510 | 1,242,000 |
| libpng-sys | 3.2617 | 0.43611 | 1.8694 | 0.58886 | 24.782 | 4.1214 | 17,539 | 17,259 |
#### Encoder
| png_pong | 42.012 | 9.9705 | 1,038.2 | 721.43 | 2,575.2 | 5,105.4 | 579,200 | 3,201,900 |
| png | 25.448 | 9.1111 | 192.52 | 190.61 | 868.28 | 1,432.2 | 184,340 | 1,384,400 |
| lodepng | 12.241 | 11.915 | 2,005.2 | 4,361.0 | 24,595 | 162,510 | TIMEOUT | TIMEOUT |
| imagefmt | 8.1248 | 9.7751 | 151.89 | 140.72 | 819.41 | 1,483.4 | 214,010 | 770,080 |
| imagine | --- | --- | --- | --- | --- | --- | --- | --- |
| aci_png | FAILS | 28.228 | FAILS | 245.12 | FAILS | 2,167.0 | FAILS | 1,823,400 | | |
| libpng-sys | 3.0473 | 0.038876 | 3.0797 | 0.039217 | 2.7762 | 0.039250 | 3.7263 | 0.039266 |
## Table of Contents
- [API](#api)
- [Features](#features)
- [Upgrade](#upgrade)
- [License](#license)
- [Contribution](#contribution)
## API
API documentation can be found on [docs.rs](https://docs.rs/png_pong).
## Features
There are no optional features.
## Upgrade
You can use the
[changelog](https://github.com/AldaronLau/png_pong/blob/master/CHANGELOG.md)
to facilitate upgrading this crate as a dependency.
## License
Licensed under either of
- Apache License, Version 2.0,
([LICENSE-APACHE](https://github.com/AldaronLau/png_pong/blob/master/LICENSE-APACHE)
or https://www.apache.org/licenses/LICENSE-2.0)
- Zlib License,
([LICENSE-ZLIB](https://github.com/AldaronLau/png_pong/blob/master/LICENSE-ZLIB)
or https://opensource.org/licenses/Zlib)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
Before contributing, check out the
[contribution guidelines](https://github.com/AldaronLau/png_pong/blob/master/CONTRIBUTING.md),
and, as always, make sure to always follow the
[code of conduct](https://github.com/AldaronLau/png_pong/blob/master/CODE_OF_CONDUCT.md).