zfc is a C++20 library and command-line tool that reads, writes, and converts image formats from scratch. No libpng, no stb_image, no third-party dependencies at all. Every format is parsed and produced byte-by-byte directly from its specification, using only the C++ standard library. The project doubles as a ground-up implementation of the compression primitives behind modern image formats, including LZ77, Huffman coding, DEFLATE, zlib, CRC-32, and Adler-32.
- Zero Dependencies: Only the C++ standard library is linked. No libpng, no libz, no stb_image. Fewer dependencies means a smaller supply chain, a smaller attack surface, and no CVE triage for third-party image parsers
- Byte-Level Format I/O: Reads and writes BMP, PPM, TGA, QOI, and PNG directly from and to raw bytes
- RFC 1951 DEFLATE: Full encoder and decoder with dynamic and fixed Huffman blocks, stored blocks, LZ77 back-references, and length-limited canonical codes
- Zlib Container: 2-byte header and Adler-32 checksum compatible with libpng, zlib, and Pillow
- PNG Chunk Framing: CRC-32 validation and proper IHDR, PLTE, IDAT, and IEND emission with color-type-aware rules
- Adaptive Filtering: All five PNG filter types (None, Sub, Up, Average, Paeth) with a per-row MSAD heuristic for choosing the best filter
- Library or CLI: Invoke through the
zfcexecutable, or link as a CMake sub-project to use the same primitives from your own C++ code
git clone https://github.com/zraisan/zfc.git
cd zfc
cmake -B build
cmake --build build
./build/zfc- A C++20 compiler (GCC 11 or later, Clang 13 or later)
- CMake 3.23 or later
zfc image <input> <output>Input and output formats are inferred from the file extension. Examples:
zfc image photo.bmp photo.png
zfc image shot.ppm shot.bmp
zfc image render.bmp render.ppm- Reader and writer for uncompressed BI_RGB bitmaps
- Handles 4-byte row padding and both bottom-up and top-down orientations
- Binary (P6) reader and writer
- 8-bit-per-channel RGB pixel data
- Uncompressed truecolor reader and writer
- Handles both top-down and bottom-up orientations
- Full Quite OK Image reader and writer
- Chunk-based encoding with index, diff, luma, run, and RGB/RGBA tags
- Encoder for 8-bit grayscale, grayscale + alpha, truecolor, and truecolor + alpha
- Decoder for zlib-compressed IDAT data using the in-repo DEFLATE implementation
- All five adaptive filter types with MSAD-based per-row selection
- Dynamic Huffman coding with length-limited canonical codes
- Correct CRC-32 on every chunk and Adler-32 on the zlib stream
Drop zfc into another CMake project:
add_subdirectory(path/to/zfc)
target_link_libraries(my_app PRIVATE zfc::zfc)Skip the CLI build when embedding:
set(ZFC_BUILD_EXECUTABLE OFF)
add_subdirectory(path/to/zfc)Encode from C++:
#include "image/png.hpp"
png::FileHeader header{width, height, 3, 24, 0};
png::encode(header, image_data, "out.png");C++ only. No C bindings are provided.
zfc is organized around per-format encoder and decoder pairs backed by shared compression primitives:
src/
image/ Format-specific readers and writers (bmp, png, ppm, tga, qoi)
utils/ Reusable primitives (deflate, huffman, png_filter)
main.cpp CLI entry point
The DEFLATE implementation in src/utils/deflate.cpp powers the PNG encoder and can be reused by future formats that need Huffman coding or LZ77.
Contributions are welcome.
-
Create a pair of files in
src/image/named with the three-letter format extension (for examplegif.cppandgif.hpp). -
Wrap all format code in a namespace with the same three-letter name:
namespace gif { struct FileHeader { /* width, height, channels, etc. */ }; FileHeader read_header(const std::vector<unsigned char> &binary); std::vector<unsigned char> decode(std::vector<unsigned char> &binary); template <typename FH> void encode(FH &header, std::vector<unsigned char> &data, std::string output_path); }
-
Wire the new format into
process_imageinsrc/image/image.cppso the CLI dispatches on the file extension. -
Add the new
.cppto thezfc_libsource list inCMakeLists.txt.
Reusable infrastructure (compression, filtering, bit I/O, checksums) lives in src/utils/. Follow the existing deflate and png_filter style: a paired header and source file with a namespace matching the filename.
Keep the code C++20, free of third-party image libraries, and self-contained. Prefer standard-library containers and algorithms over platform-specific APIs.
GNU Affero General Public License v3.0. See LICENSE for the full text.