Skip to content

Library

YadeWira edited this page Jun 13, 2026 · 2 revisions

libpackPNG

packPNG ships as a library — static (.a) and shared (.so / .dll) — with a small C API, so other programs (archivers, asset pipelines) can embed the codec. Multithreading is ON by default. Verified byte-exact on Linux and Windows.

Build

make lib       # Linux:   libpackpng.a + libpackpng.so + packpng.h
make lib-win   # Windows: libpackpng-win.a + packpng.dll + libpackpng.dll.a + packpng.def + packpng.h

Both static archives are self-contained fat archives (packPNG + packJPG + kanzi + preflate merged); the shared libraries bundle the same. The consumer links only system libraries. Prebuilt SDK bundles are attached to each release (*-lib.tar.gz / *-lib.zip).

API (packpng.h)

typedef enum {
    PACKPNG_TCIP = 0, /* default: preflate + WebP-lossless */
    PACKPNG_TVCP = 1, /* fast:    kanzi + zstd            */
    PACKPNG_TMCP = 2, /* archival: preflate + kanzi-TPAQX */
    PACKPNG_TPCL = 3  /* legacy:  preflate + LZMA2        */
} packpng_backend;

/* File → file */
int  packpng_compress_file(const char* in_path, const char* out_path, packpng_backend backend);
int  packpng_decompress_file(const char* in_path, const char* out_dir); /* auto-detects magic */

/* In-memory (buffer → buffer) — for archivers / embedding.
 * On success returns 0 and sets *out to a heap buffer of *out_len bytes
 * (free with packpng_free). On error returns nonzero, leaves *out NULL. */
int  packpng_compress_mem(const unsigned char* in, size_t in_len, const char* name_hint,
                          unsigned char** out, size_t* out_len, packpng_backend backend);
int  packpng_decompress_mem(const unsigned char* in, size_t in_len,
                            unsigned char** out, size_t* out_len);
void packpng_free(unsigned char* p);

void packpng_set_threads(int n);          /* 0 = auto = default (hardware threads) */
const char* packpng_last_error(void);     /* thread-local, valid until next call   */
const char* packpng_version(void);        /* e.g. "2.0a"                            */

The *_file and *_mem calls return 0 on success, nonzero on error (message via packpng_last_error). The image type is detected from the input bytes, not the name — name_hint (may be NULL) is just the original filename stored in the container. JNG/MNG inputs ignore backend and use their format codec.

Example — file API

#include "packpng.h"
#include <stdio.h>
int main(void) {
    if (packpng_compress_file("image.png", "image.ppg", PACKPNG_TCIP))
        { printf("error: %s\n", packpng_last_error()); return 1; }
    packpng_decompress_file("image.ppg", "out/");   /* → out/image.png, byte-exact */
    return 0;
}

Example — in-memory API (archiver embedding)

#include "packpng.h"
#include <stdlib.h>

/* png/png_len already hold a PNG read into memory. */
unsigned char* ppg; size_t ppg_len;
if (packpng_compress_mem(png, png_len, "image.png", &ppg, &ppg_len, PACKPNG_TCIP) == 0) {
    /* ... store the ppg/ppg_len blob in your archive ... */
    packpng_free(ppg);
}

/* Later, restore the byte-identical original: */
unsigned char* orig; size_t orig_len;
if (packpng_decompress_mem(ppg, ppg_len, &orig, &orig_len) == 0) {
    /* orig/orig_len == the original PNG, same SHA-256 */
    packpng_free(orig);
}

packpng_set_threads(0) (the default) auto-sizes the worker pool to the hardware; pass a positive n to cap it.

Link lines

Static

# Linux
gcc app.c libpackpng.a -lzstd -llzma -lz -lpthread -ldl -lm -lstdc++ -o app

# Windows (mingw)
x86_64-w64-mingw32-gcc app.c libpackpng-win.a -L vendor/mingw-deps/lib \
    -lzstd -llzma -lz -lws2_32 -luserenv -lbcrypt -lntdll -static -lstdc++ -o app.exe

Shared

# Linux — run with libpackpng.so on the loader path (LD_LIBRARY_PATH / rpath)
gcc app.c -L. -lpackpng -o app

# Windows — link the import lib, ship packpng.dll alongside app.exe
x86_64-w64-mingw32-gcc app.c -L. -lpackpng -o app.exe

For MSVC, build an import lib from the provided packpng.def (lib /def:packpng.def /machine:x64) and link against packpng.dll.

Clone this wiki locally