• Stars
    star
    206
  • Rank 190,504 (Top 4 %)
  • Language
    Rust
  • License
    Other
  • Created about 6 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A parallelized PNG encoder in Rust

mtpng

A parallelized PNG encoder in Rust

by Brion Vibber [email protected]

Background

Compressing PNG files is a relatively slow operation at large image sizes, and can take from half a second to over a second for 4K resolution and beyond. See my blog post series on the subject for more details.

The biggest CPU costs in traditional libpng seem to be the filtering, which is easy to parallelize, and the deflate compression, which can be parallelized in chunks at a slight loss of compression between block boundaries.

pigz is a well-known C implementation of parallelized deflate/gzip compression, and was a strong inspiration for the chunking scheme used here.

I was also inspired by an experimental C++/OpenMP project called png-parallel by Pascal Beyeler, which didn't implement filtering but confirmed the basic theory.

State

Creates correct files in all color formats (input must be pre-packed). Performs well on large files, but needs work for small files and ancillary chunks. Planning API stability soon, but not yet there -- things will change before 1.0.

Goals

Performance:

  • ☑️ MUST be faster than libpng when multi-threaded
  • ☑️ SHOULD be as fast as or faster than libpng when single-threaded

Functionality:

  • ☑️ MUST support all standard color types and depths
  • ☑️ MUST support all standard filter modes
  • ☑️ MUST compress within a few percent as well as libpng
  • MAY achieve better compression than libpng, but MUST NOT do so at the cost of performance
  • ☑️ SHOULD support streaming output
  • MAY support interlacing

Compatibility:

  • MUST have a good Rust API (in progress)
  • MUST have a good C API (in progress)
  • ☑️ MUST work on Linux x86, x86_64
  • ☑️ MUST work on Linux arm, arm64
  • ☑️ SHOULD work on macOS x86_64
  • ☑️ SHOULD work on iOS arm64
  • ☑️ SHOULD work on Windows x86, x86_64
  • ☑️️ SHOULD work on Windows arm64

Compression

Compression ratio is a tiny fraction worse than libpng with the dual-4K screenshot and the arch photo at the current default 256 KiB chunk size, getting closer the larger you increase it.

Using a smaller chunk size, or enabling streaming mode, will increase the file size slightly more in exchange for greater parallelism (small chunks) and lower latency to bytes hitting the wire (streaming).

In 0.3.5 a correction was made to the filter heuristic algorithm to match libpng in some circumstances where it differs; this should provide very similar results to libpng when used as a drop-in replacement now. Later research may involve changing the heuristic, as it fails to correctly predict good performance of the "none" filter on many screenshot-style true color images.

Performance

Note that unoptimized debug builds are about 50x slower than optimized release builds. Always run with --release!

As of September 26, 2018 with Rust 1.29.0, single-threaded performance on Linux x86_64 is ~30-40% faster than libpng saving the same dual-4K screenshot sample image on Linux and macOS x86_64. Using multiple threads consistently beats libpng by a lot, and scales reasonably well at least to 8 physical cores.

See docs/perf.md for informal benchmarks on various devices.

At the default settings, files whose uncompressed data is less than 128 KiB will not see any multi-threading gains, but may still run faster than libpng due to faster filtering.

Todos

See the projects list on GitHub for active details.

Usage

Note: the Rust and C APIs are not yet stable, and will change before 1.0.

Rust usage

See the crate API docs for details.

The mtpng CLI tool can be used as an example of writing files.

In short, something like this:

let mut writer = Vec::<u8>::new();

let mut header = Header::new();
header.set_size(640, 480)?;
header.set_color(ColorType::TruecolorAlpha, 8)?;

let mut options = Options::new();

let mut encoder = Encoder::new(writer, &options);

encoder.write_header(&header)?;
encoder.write_image_rows(&data)?;
encoder.finish()?;

C usage

See c/mtpng.h for a C header file which connects to unsafe-Rust wrapper functions in the mtpng::capi module.

To build the C sample on Linux or macOS, run make. On Windows, run build-win.bat x64 for an x86-64 native build, or pass x86 or arm64 to build for those platforms.

These will build a sample executable from sample.c as well as a libmtpng.so, libmtpng.dylib, or mtpng.dll for it to link. It produces an output file in out/csample.png.

Data flow

Encoding can be broken into many parallel blocks:

Encoder data flow diagram

Decoding cannot; it must be run as a stream, but can pipeline (not yet implemented):

Decoder data flow diagram

Dependencies

Rayon is used for its ThreadPool implementation. You can create an encoder using either the default Rayon global pool or a custom ThreadPool instance.

crc is used for calculating PNG chunk checksums.

libz-sys is used to wrap libz for the deflate compression. I briefly looked at pure-Rust implementations but couldn't find any supporting raw stream output, dictionary setting, and flushing to byte boundaries without closing the stream.

itertools is used to manage iteration in the filters.

png is used by the CLI tool to load input files to recompress for testing.

clap is used by the CLI tool to handle option parsing and help display.

time is used by the CLI tool to time compression.

License

You may use this software under the following MIT-style license:

Copyright (c) 2018-2022 Brion Vibber

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

More Repositories

1

ogv.js

JavaScript media player using Ogg/Vorbis/Theora/Opus/WebM libs compiled with Emscripten
JavaScript
1,197
star
2

yuv-canvas

JS class to draw YUV image frame buffers to an HTML5 canvas
JavaScript
271
star
3

OGVKit

Ogg and WebM media playback for iOS
Objective-C
216
star
4

hdrfix

tool for converting HDR screenshots to SDR with suitable tone-mapping
Rust
114
star
5

audio-feeder

Small JS library to abstract Web Audio raw PCM output
JavaScript
53
star
6

yuv-buffer

YUV image frame buffer helper utilities for JavaScript
JavaScript
38
star
7

wasm2swf

Experimental WebAssembly to ActionScript bytecode translator
JavaScript
19
star
8

gimp-icns

Gimp image editor plugin to load and save Mac OS X .icns icon resource files
C
18
star
9

stream-file

Small JS library to abstract streaming of large files via XHR w/ Range-based chunking
JavaScript
18
star
10

jsdec-openh264

Experimental OpenH264 emscripten build for Gecko experimental JavaScript video decoding API
C++
15
star
11

StatusNet

alternate copy of StatusNet repo from our Gitorious master
PHP
14
star
12

librsvg

Test fork of librsvg from git.gnome.org
C
12
star
13

aotjs

Experimental ahead-of-time JavaScript compiler targetting WebAssembly and native
C++
12
star
14

min-wasm-fail

Minimal test case for iOS 11.2.2/11.2.5 wasm failure
JavaScript
10
star
15

BugTender

mobile web app to manage Bugzilla from your phone
JavaScript
10
star
16

av1-demo

av1 encoding demo using svt-av1
JavaScript
9
star
17

chromium-win-arm64

Experimental dev builds of Chromium for Windows 10 ARM64
8
star
18

raw-to-mse

Experimental library for producing MSE-compatible buffers with uncompressed audio/video data
JavaScript
7
star
19

wasmosis

capability-passing "microkernel" for safely isolating WebAssembly modules
C
6
star
20

MediaWiki

PHP
6
star
21

svg-edit-test

SVG-Edit test tool comparing round-tripping of data files
JavaScript
6
star
22

emsdk-npm

emscripten SDK helper module for npm projects
JavaScript
5
star
23

statusnet-client

StatusNet Desktop and Mobile clients (mirror from Gitorious)
JavaScript
5
star
24

jpegxr

Rust wrapper for Microsoft's C JPEG XR codec library
C
4
star
25

mw-js-plugin

Experimental repo for MediaWiki JavaScript iframe-based plugin system
JavaScript
4
star
26

aomedia

work fork of aomedia - see http://aomedia.org/contributor-guide/
C
3
star
27

brot

Mandelbrot WebWorkers test
JavaScript
3
star
28

MediaWiki-MobileSidebar

mobile sidebar gadget
JavaScript
3
star
29

OEmbedConsumer

PHP
3
star
30

mediawiki-svn

A mirror of MediaWiki's Subversion repository produced with git-svn(1)
PHP
3
star
31

WLMTest

Development moved to https://github.com/wikimedia/WLMMobile
JavaScript
2
star
32

MediaWiki-ExtensionFetcher

Extension for MediaWiki to manage fetching extensions from git
PHP
2
star
33

WebM-iOS

iOS builds of libwebm
Ruby
2
star
34

density-bookmarklet

Experimental bookmarklet to swap PNG and SVG images to high-res on wikipedia
JavaScript
2
star
35

FlatRoller

HTML canvas game demo/tutorial/exploration
JavaScript
2
star
36

WebViewTest

quick Android webview test to see if text selection works
Java
2
star
37

ohgeevee

VR experiments with ogv.js and A-Frame
HTML
2
star
38

VPX-iOS

Local builds of libvpx for iOS
Makefile
2
star
39

CommonsMetro

Wikimedia Commons app for Windows 8/Windows RT
JavaScript
2
star
40

OEmbedProvider

PHP
2
star
41

cortado

mirror of old xiph cortado, for fun
Java
2
star
42

OgvRT

Experimental Ogg Theora/Vorbis player for WinRT (Windows 8.1 including RT and Phone)
C++
2
star
43

OGVKit-Specs

temp location of dependent CocoaPods specs for OGVKit
Ruby
2
star
44

EmbedScript

MediaWiki extension for safe embedding of locally-hosted scripts
PHP
2
star
45

WikipediaMetroTest

testbed for windows 8 metro integration features
JavaScript
1
star
46

orwel

OpenRaster Web Editing Library
JavaScript
1
star
47

EmbedTurtle

turtle graphics embedding test demo
JavaScript
1
star
48

turtle-world

Logo interpreter and turtle graphics environment for the web
JavaScript
1
star
49

MobileFrontend

MobileFrontend extension copy
PHP
1
star
50

gnome-system-monitor

Forked from master repo at http://git.gnome.org/browse/gnome-system-monitor/
C++
1
star
51

svg-test

SVG rendering test framework
JavaScript
1
star
52

EmbedPiechart

Experimental EmbedSandbox provider for drawing & editing pie charts
JavaScript
1
star
53

EmbedScriptSandbox

Sandbox page for MediaWiki EmbedScript extension
1
star
54

OLD-apps-win8-wikipedia

Wikipedia reader app for Windows 8/RT
JavaScript
1
star
55

CentralAuthAPI

MediaWiki API proxy for CentralAuth-connected wikis
PHP
1
star
56

test-emscripten-lib

C
1
star
57

WikipediaFrameTest

test of phonegap iframe issue on iOS
JavaScript
1
star
58

gnome-emscripten

Experiments at building GNOME low-level libraries for emscripten
C
1
star
59

EmbedSandbox

experimental MediaWiki extension for embedding remote scriptable objects safely
PHP
1
star
60

mozilla-SN---Plugin

Plugin for mozilla SN
PHP
1
star
61

operations-debs-ffmpeg2theorawmf

C
1
star
62

mandelbrot-shootout

Fractal programming language test demos
C
1
star
63

ffmpeg2theora

work fork of ffmpeg2theora
C
1
star
64

iframe-android-test

JavaScript
1
star
65

drive256

Drive256 - a VGA loadable driver experiment from 1992
Pascal
1
star
66

Vaders

HTML5 version of old DOS game
JavaScript
1
star
67

browser-transcode-test

Notes and experiments for browser-based transcoding of audio and video
HTML
1
star