All formats

libpng

LanguageC (usable from C++)
LicensePNG Reference Library License v2 (permissive)
Repositorygithub.com/pnggroup/libpng
Documentationlibpng manual
Depends onzlib
Read / writeyes / yes, all color types and bit depths, APNG in recent versions

libpng is the reference implementation of PNG, maintained by the PNG Development Group. It’s used by most image programs, browsers’ older code paths, ImageMagick, GIMP and many others. It supports every chunk and every pixel format.

libpng has two APIs:

Installation#

Shell
# Debian / Ubuntu
sudo apt install libpng-dev
# Fedora
sudo dnf install libpng-devel
# macOS
brew install libpng
# Windows (vcpkg)
vcpkg install libpng

With CMake:

cmake
find_package(PNG REQUIRED)
target_link_libraries(myapp PRIVATE PNG::PNG)

Basic usage: read and write a PNG#

The simplified API converts any PNG to the format you request (here 8-bit RGBA) and writes it back:

C
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    png_image image;
    memset(&image, 0, sizeof(image));
    image.version = PNG_IMAGE_VERSION;

    if (!png_image_begin_read_from_file(&image, "in.png")) {
        fprintf(stderr, "read error: %s\n", image.message);
        return 1;
    }
    image.format = PNG_FORMAT_RGBA;  // 4 bytes per pixel

    png_bytep buffer = malloc(PNG_IMAGE_SIZE(image));
    if (!buffer) {
        png_image_free(&image);
        return 1;
    }
    if (!png_image_finish_read(&image, NULL /*background*/, buffer, 0 /*row_stride*/, NULL /*colormap*/)) {
        fprintf(stderr, "decode error: %s\n", image.message);
        free(buffer);
        return 1;
    }
    printf("%u x %u\n", image.width, image.height);

    // buffer holds width*height RGBA pixels, top row first
    if (!png_image_write_to_file(&image, "out.png", 0 /*convert_to_8bit*/, buffer, 0, NULL)) {
        fprintf(stderr, "write error: %s\n", image.message);
    }
    free(buffer);
    return 0;
}

How to get PNG width and height without decoding#

png_image_begin_read_from_file reads only the header, so the size is known before any pixels are decoded:

C
png_image image;
memset(&image, 0, sizeof(image));
image.version = PNG_IMAGE_VERSION;
if (png_image_begin_read_from_file(&image, "in.png")) {
    printf("%u x %u, %s\n", image.width, image.height,
           (image.format & PNG_FORMAT_FLAG_ALPHA) ? "has alpha" : "no alpha");
    png_image_free(&image);
}

How to write a PNG from a pixel buffer#

C
#include <png.h>
#include <string.h>

int save_rgba(const char *path, const unsigned char *pixels, int width, int height) {
    png_image image;
    memset(&image, 0, sizeof(image));
    image.version = PNG_IMAGE_VERSION;
    image.width = width;
    image.height = height;
    image.format = PNG_FORMAT_RGBA;  // or PNG_FORMAT_RGB, PNG_FORMAT_GRAY
    return png_image_write_to_file(&image, path, 0, pixels, 0 /*stride: width*4*/, NULL);
}

How to read text chunks (tEXt, zTXt, iTXt)#

Text metadata needs the classic API. Text chunks that appear after the image data are only available after png_read_end:

C
#include <png.h>
#include <stdio.h>

int print_text(const char *path) {
    FILE *fp = fopen(path, "rb");
    if (!fp) return 1;
    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop info = png_create_info_struct(png);
    if (setjmp(png_jmpbuf(png))) {  // libpng jumps here on error
        png_destroy_read_struct(&png, &info, NULL);
        fclose(fp);
        return 1;
    }
    png_init_io(png, fp);
    png_read_info(png, info);

    png_textp text = NULL;
    int num_text = 0;
    png_get_text(png, info, &text, &num_text);
    for (int i = 0; i < num_text; i++) {
        printf("%s: %s\n", text[i].key, text[i].text);
    }
    png_destroy_read_struct(&png, &info, NULL);
    fclose(fp);
    return 0;
}

How to write a PNG with a compression level and text#

C
#include <png.h>
#include <stdio.h>

int write_png(const char *path, png_bytep *rows, int width, int height) {
    FILE *fp = fopen(path, "wb");
    if (!fp) return 1;
    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop info = png_create_info_struct(png);
    if (setjmp(png_jmpbuf(png))) {
        png_destroy_write_struct(&png, &info);
        fclose(fp);
        return 1;
    }
    png_init_io(png, fp);
    png_set_compression_level(png, 9);  // 0 (none) .. 9 (smallest)
    png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

    png_text text;
    text.compression = PNG_TEXT_COMPRESSION_NONE;
    text.key = "Software";
    text.text = "my app";
    png_set_text(png, info, &text, 1);

    png_write_info(png, info);
    png_write_image(png, rows);  // rows[y] points to width*4 bytes
    png_write_end(png, NULL);
    png_destroy_write_struct(&png, &info);
    fclose(fp);
    return 0;
}

Tips#