Very fast C++ .PNG writer for 24/32bpp images.

Overview

fpng

Very fast C++ .PNG writer for 24/32bpp images. fpng.cpp was written to see just how fast you can write .PNG's without sacrificing too much compression, and to determine how slow the existing .PNG writers are. (Turns out they are quite slow.)

On average, fpng.cpp produces files roughly 1-15% smaller than QOI but is 1.4-1.5x slower on 24bpp images.

fpng.cpp compared to stb_image_write.h: fpng.cpp is over 10x faster with roughly 16-18% smaller files. (I was surprised by this. stb_image_write.h does a decent job choosing the PNG filter method and has a stronger byte-oriented parser than fpng's, but its Deflate compressor is crippled by being limited to only static Huffman tables.)

fpng.cpp compared to lodepng.cpp: fpng.cpp is 12-16x faster with roughly 15-18% larger files.

On 32bpp images with correlated alpha signals (a common case in video games) qoi outputs files 1.7x larger vs. fpng and 1.98x larger vs. lodepng.

Benchmark using the 184 QOI test images:

qoi:              4.69 secs   359.55 MB
fpng:             6.66 secs   353.50 MB
lodepng:          110.72 secs 300.14 MB
stb_image_write:  68.07 secs  425.64 MB

Benchmark using my 303 test images:

qoi:             2.25 secs  300.84 MB
fpng:            3.40 secs  253.26 MB
lodepng:         41.68 secs 220.40 MB
stb_image_write: 37.23 secs 311.41 MB

Benchmark using the 184 QOI test images, but with the green channel copied into alpha (to create a correlated alpha channel):

qoi:              2.75 secs   697.2 MB
fpng:             7.48 secs   404.0 MB
lodepng:          151.57 secs 352.1 MB
stbi:             81.58 secs  486.4 MB

182 of the qoi test images don't have alpha channels, so I'm guessing it's not being heavily tested with alpha images much yet.

Low-level description

fpng.cpp uses a custom image aware pixel-wise Deflate compressor which was optimized for simplicity over high ratios. It uses a simple LZRW1-style parser, all literals (except the PNG filter bytes) are output in groups of 3 or 4, all matches are multiples of 3/4 bytes, and it only utilizes a single dynamic Huffman block with one PNG IDAT block. It utilizes 64-bit registers and exploits unaligned little endian reads/writes. (On big endian CPU's it'll use 32/64bpp byteswaps.)

Passes over the input image and dynamic allocations are minimized, although it does use std::vector internally. The first scanline always uses filter #0, and the rest use filter #2 (previous scanline). It uses the fast CRC-32 code described by Brumme here. The original high-level PNG function (that code that writes the headers) was written by Alex Evans.

lodepng v20210627 fetched 12/18/2021

stb_image_write.h v1.16 fetched 12/18/2021

qoi.h fetched 12/18/2021

Building

To build, compile from the included .SLN with Visual Studio 2019/2022 or use cmake to generate a .SLN file. For Linux/OSX, use "cmake ." then "make". Tested with MSVC 2019/gcc/clang.

I have only tested fpng.cpp on little endian systems. The code is there for big endian, and it should work, but it needs testing.

Testing

From the "bin" directory, run "fpng_test.exe" or "./fpng_test" like this:

fpng_test.exe

To generate .CSV output only:

fpng_test.exe -c

There will be several output files written to the current directory: stbi.png, lodepng.png, qoi.qoi, and fpng.png. Statistics or .CSV data will be printed to stdout, and errors to stderr.

Using fpng

To use fpng.cpp in other programs, copy fpng.cpp/.h and Crc32.cpp/.h into your project. No other configuration or files are needed. Computing the CRC-32 of buffers is a substantial proportion of overall compression time in fpng, so if you have a faster CRC-32 function you can modify fpng_crc() in fpng.cpp to call that instead. The one included in Crc32.cpp doesn't utilize any special CPU instruction sets, so it could be faster.

#include "fpng.h" then call one of these C-style functions in the "fpng" namespace:

bool fpng_encode_image_to_memory(const void* pImage, int w, int h, int num_chans, bool flip, std::vector
   
    & out_buf);
bool fpng_encode_image_to_file(const char* pFilename, const void* pImage, int w, int h, int num_chans, bool flip);

   

num_chans must be 3 or 4. There must be w*3*h or w*4*h bytes pointed to by pImage. The image row pitch is always w*3 or w*4 bytes. There is no automatic determination if the image actually uses an alpha channel, so if you call it with 4 you will always get a 32bpp .PNG file.

Note the adler32 function in fpng.cpp could be vectorized for higher performance.

Fuzzing

I've started to fuzz fpng.cpp, but it will take several days to complete. So far it's survived several different random fuzz methods.

License

fpng.cpp/.h: Public Domain or zlib (your choice). See the end of fpng.cpp.

Crc32.cpp/.h: Copyright (c) 2011-2019 Stephan Brumme. zlib license:

https://github.com/stbrumme/crc32

Comments
  • Compilation fails on Apple M1

    Compilation fails on Apple M1

    I don't manage to compile fpng on my Apple MacBook Pro M1 (ARM). I get the following compilation error:

    fpng.cpp:304:22: error: invalid output constraint '+b' in asm
                    __asm__("cpuid;" : "+b"(ebx), "+a"(eax), "+c"(ecx), "=d"(edx));
    

    System:

    $ uname -a
    Darwin macbookpro 21.2.0 Darwin Kernel Version 21.2.0: Sun Nov 28 20:28:41 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T6000 arm64
    
    /Library/Developer/CommandLineTools/usr/bin/c++ --version
    Apple clang version 13.0.0 (clang-1300.0.29.30)
    Target: arm64-apple-darwin21.2.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin
    

    Any help appreciated! Thanks.

    bug 
    opened by rossant 7
  • Request a valid version number tag / release.

    Request a valid version number tag / release.

    fpng is great, I am adding it to Xmake's package management repository, it requires a valid version number to do the version of the package, and then download the specified tar.gz through the version number. like this, v1.0, v1.0.1, ...

    see https://github.com/xmake-io/xmake-repo/pull/812

    Although even if there is no version number, I can also implement it through git commit + fake version, but this is not recommended.

    I hope that fpng can have a good version tag list, thank you very much.

    enhancement 
    opened by waruqi 7
  • Cannot load my png-file...

    Cannot load my png-file...

    Hello!

    I'm trying to add FPNG to our image error metric called FLIP (https://github.com/NVlabs/flip/) because we need faster PNG loading. Unfortunately, I cannot load the first image I tried with: reference

    The code exits on line 3033 in fpng.cpp: if ((idat_ofs) || (!found_fdec_chunk)) return FPNG_DECODE_NOT_FPNG;

    The cause is that found_fdec_chunk==false. Any ideas on what can be wrong? The image can be loaded by stbi_image.h and PhotoShop.

    Thanks in advance! /Tomas

    help wanted 
    opened by inversepixel 5
  • Not an Issue - Just Need Help

    Not an Issue - Just Need Help

    Hey! So I am I'm new to all of this in terms of C. I am just a web dev and trying to figure out how to maybe compress PNG images to be usable (Size of File for Speed) on web. Was looking at this and QOI. But this one looks a lot more promising due to it being compatible with PNG decoders already. Anyway, I am doing the Testing section and trying to convert my PNG to FPNG but I am just getting a bigger file of my original PNG or the same size.

    Am I do doing it right?

    // CMD Line in ./fpng_main/bin
    
    ./fpng.exe -s test.png
    

    Is there something else I need to be doing? Sorry again, I am a complete newbie to this type of stuff lol

    opened by zayne-anthony 3
  • Possible (likely innocuous) typo in chunk name parsing

    Possible (likely innocuous) typo in chunk name parsing

    I was looking over the source code, and I noticed this oddity in the code for detecting chunk type

    https://github.com/richgel999/fpng/blob/b964f657bd3ace341df47fab3c249a24c6eac995/src/fpng.cpp#L2520-L2526

    for (uint32_t i = 0; i < 4; i++)
    {
    	const uint8_t c = pChunk->m_type[0];
    

    It appears that it's checking for non-alphabetical characters in the chunk type string, but it's only repeatedly checking that that's true for the first character in the type. I suspect m_type[0] was meant to be m_type[i]

    I don't believe that this would cause problems, since the other checks should still reject any not-recognized chunk types

    bug 
    opened by Benjins 2
  • QOI has a more comprehensive test suite

    QOI has a more comprehensive test suite

    fpng's top-level README.md says "the [emphasis added] 184 QOI test images (note 182 of the qoi test images don't have alpha channels, so this is almost entirely a 24bpp test)".

    QOI updated its test suite a month ago (https://github.com/phoboslab/qoi/issues/69). The new suite contains 2848 images.

    opened by nigeltao 1
  • Update README.md with Wuffs numbers

    Update README.md with Wuffs numbers

    There's lodepng and stb_image numbers, but Wuffs is faster than both.

    https://github.com/randy408/libspng is another "single file C library" contender, if you haven't already seen it.

    documentation 
    opened by nigeltao 0
  • Python bindings for fpng

    Python bindings for fpng

    Hi, thanks for the great library. I had a need to use this from python, so I wrote python bindings that seem to have a good speedup over opencv-python's imwrite/imencode.

    I published the interface here, with the same license as this project: https://github.com/qrmt/fpng-python

    Perhaps someone else will find find use for the bindings (and possibly review it), so I thought I'd post it here. Cheers!

    opened by qrmt 0
  • Adding resolution/icc info tags and 8 bits support ?

    Adding resolution/icc info tags and 8 bits support ?

    Hi Rich, (I sent an email about this issue, but I guess it didn't reach you)

    fpng is really efficient and interesting work. Congratulations !

    However, to cover most of png usual features (and according my need), I think it would be great to add the following support/features :

    • resolution information (easy to add)
    • ICC profile (or at least the sRGB tag) (somehow easy to add)
    • support for indexed pictures (more complicated)

    I think that I can add the support of resolution and icc profile, keeping the code fast and the API simple (just passing an extra pointer for optional tags, that can be extend to other tags in the future)...

    Do you think you may extend your code in the future to support indexed images ?

    Best regards

    Sébastien Léon

    enhancement 
    opened by sebastienleon 3
  • Simplify wuffs_decode implementation

    Simplify wuffs_decode implementation

    Wuffs has a low-level C API, necessary if e.g. decoding an animated image (or decoding incrementally over the network) while concurrently doing other work, and a high-level C++ API. fpng_test just wants to decode a still (non-animated) PNG image whose compressed form is entirely in memory, so it can use the simpler C++ API.

    opened by nigeltao 0
  • Ignore checksums more often

    Ignore checksums more often

    Adler-32 and CRC-32 computations are fast with SIMD, but if you just want the fastest possible PNG decoder (e.g. to compare to QOI's speed), ignoring the checksums can be even faster.

    For decode (not encode, obviously), IIUC fpng already skips computing/verifying Adler-32 always and CRC-32 sometimes (depending on FPNG_DISABLE_DECODE_CRC32_CHECKS).

    Some of the other libraries (lodepng and stb_image) also do this automatically. Wuffs needs to opt in, with a one-liner patch:

    diff --git a/src/fpng_test.cpp b/src/fpng_test.cpp
    index afc3f77..7302146 100644
    --- a/src/fpng_test.cpp
    +++ b/src/fpng_test.cpp
    @@ -683,6 +683,7 @@ static void* wuffs_decode(void* pData, size_t data_len, uint32_t &width, uint32_
            wuffs_png__decoder* pDec = wuffs_png__decoder__alloc();
            if (!pDec) 
                    return nullptr;
    +       wuffs_png__decoder__set_quirk_enabled(pDec, WUFFS_BASE__QUIRK_IGNORE_CHECKSUM, true);
     
            wuffs_base__image_config ic;
            wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader((uint8_t *)pData, data_len, true);
    
    enhancement 
    opened by nigeltao 6
Releases(vectorized_checksums)
Owner
Rich Geldreich
Rich Geldreich
Fast streaming PNG<->QOI converter with some compression-improving extensions

QOIG Fast streaming PNG<->QOI converter with some compression-improving extensions. Can achieve 1%-10% better compression than QOI without sacrificing

David Rutter 3 Oct 3, 2022
CGIF, A fast and lightweight GIF encoder that can create GIF animations and images

CGIF, a GIF encoder written in C A fast and lightweight GIF encoder that can create GIF animations and images. Summary of the main features: user-defi

Daniel Löbl 71 Oct 11, 2022
Arduino PNG image decoder library

An 'embedded-friendly' (aka Arduino) PNG image decoding library

Larry Bank 99 Nov 26, 2022
pngtostl is a program that converts a PNG image into STL 3D models

pngtostl is a program that converts a PNG image into a litophane, in STL format, suitable to be printed by entry level 3D printers.

Salvatore Sanfilippo 158 Sep 4, 2022
libspng is a C library for reading and writing PNG format files with a focus on security and ease of use.

libspng (simple png) is a C library for reading and writing Portable Network Graphics (PNG) format files with a focus on security and ease of use.

Randy 555 Nov 20, 2022
Simple, generally spec-compliant, hacky PNG Decoder written in C99.

Simple, generally spec-compliant, hacky PNG Decoder written in C99.

cristei 2 Nov 2, 2021
PNG encoder and decoder in C and C++.

PNG encoder and decoder in C and C++, without dependencies

Lode Vandevenne 1.7k Nov 23, 2022
An image and texture viewer for tga, png, apng, exr, dds, gif, hdr, jpg, tif, ico, webp, and bmp files

An image and texture viewer for tga, png, apng, exr, dds, gif, hdr, jpg, tif, ico, webp, and bmp files. Uses Dear ImGui, OpenGL, and Tacent. Useful for game devs as it displays information like the presence of an alpha channel and querying specific pixels for their colour.

Tristan Grimmer 156 Nov 19, 2022
Rate-Distortion Optimized Lossy PNG Encoding Tool

rdopng is a command line tool which uses LZ match optimization, Lagrangian multiplier rate distortion optimization (RDO), a simple perceptual error tolerance model, and Oklab-based colorspace error metrics to encode 24/32bpp PNG files which are 30-80% smaller relative to lodepng/libpng.

Rich Geldreich 39 Nov 10, 2022
An open source library for face detection in images. The face detection speed can reach 1000FPS.

libfacedetection This is an open source library for CNN-based face detection in images. The CNN model has been converted to static variables in C sour

Shiqi Yu 11.3k Nov 28, 2022
Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications.

README for OpenImageIO Introduction The primary target audience for OIIO is VFX studios and developers of tools such as renderers, compositors, viewer

OpenImageIO 1.6k Nov 22, 2022
HDRView is a simple research-oriented image viewer with an emphasis on examining and comparing high-dynamic range (HDR) images

HDRView is a simple research-oriented high-dynamic range image viewer with an emphasis on examining and comparing images, and including minimalistic tonemapping capabilities. HDRView currently supports reading EXR, PNG, TGA, BMP, HDR, JPG, GIF, PNM, PFM, and PSD images and writing EXR, HDR, PNG, TGA, PPM, PFM, and BMP images.

Wojciech Jarosz 122 Nov 23, 2022
A simple API wrapper that generates images & facts of any animal

animality.h A simple API wrapper that generates images & facts of any animal Required dependencies: libcurl for sending HTTPS requests. pthreads for t

animality 5 Nov 10, 2022
You can use this to compile the code and output images into a word doc for assignment purposes

Code_n_Ouput_to_docx You can use this to compile the code and output images into a word doc for assignment purposes Basic requirements: Python 3.7 or

Aaditya Prabu K 0 Aug 21, 2022
A crude untested example showing how to retrieve and display images from multiple cameras with OpenCV and wxWidgets.

About wxOpenCVCameras is a crude untested example of how to retrieve and display images from multiple cameras, using OpenCV to grab images from a came

PB 7 Nov 18, 2022
Convert images to ASCII art.

Image-to-ascii Convert images to ASCII art. This program using stb library to load images. Usage Usage: compile the program to get *.out file

Abdeljalil Bouchfar 13 Aug 20, 2022
Make It Pixel is a programming language to process images to look like pixel art.

Make images look like pixel art Make It Pixel is a programming language to process images to look like pixel art. Its interpreter is written in C++ an

null 20 Oct 14, 2022
Given a set of images, LeastAverageImage generates the least average -- the opposite of the average.

LeastAverageImage Given a set of images, LeastAverageImage generates the least average -- the opposite of the average. More information about the prog

Andrew Eckel 5 Apr 12, 2022
A (very) simple notification wrapper for Dear ImGui

imgui-notify Is a header-only wrapper made to create notifications with Dear ImGui. As I couldn't find any library for this I just decided to create m

Patrick 197 Nov 19, 2022