This is a comparison between libspng and the reference implementation libpng.

Lines of code

libspng is ~3 kLOC while libpng is ~10 kLOC without the encoder or 18 kLOC in total.


libspng does not have a configuration header, you only have to include spng.c/spng.h in your project.


The API is a lot simpler, decoding a PNG file takes 6 function calls, there are no row pointers or callback functions involved.

Error handling

All functions return zero on success and non-zero on error. libpng uses setjmp() for error handling, this makes it difficult to use from other languages such as C++.


libspng has a clear way to request a specific output format, with libpng the output format depends on whether you set alpha filler bits, it also has weird defaults such as not converting 16-bit PNG's to host endianness unless png_set_swap() is called.

Decode example - libspng

void *read_image(void *buf, size_t bufsize, uint32_t *width, uint32_t *height)
    spng_ctx *ctx;
    struct spng_ihdr ihdr;
    void *image = NULL;
    size_t size;

    ctx = spng_ctx_new(0);
    if(ctx==NULL) return NULL;

    if(spng_set_png_buffer(ctx, buf, bufsize)) goto err;

    if(spng_get_ihdr(ctx, &ihdr)) goto err;

    if(spng_decoded_image_size(ctx, SPNG_FMT_RGBA8, &size)) goto err;

    image = malloc(size);
    if(image==NULL) goto err;

    if(spng_decode_image(ctx, image, size, SPNG_FMT_RGBA8, SPNG_DECODE_USE_TRNS)) goto err;

    *width = ihdr.width;
    *height = ihdr.height;

    return image;

    if(image != NULL) free(image);

    return NULL;

Decode example - libpng

struct buf_state
    unsigned char *data;
    size_t bytes_left;

void libpng_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
    struct buf_state *state = png_get_io_ptr(png_ptr);

    if(length > state->bytes_left)
        png_error(png_ptr, "read_fn error");

    memcpy(data, state->data, length);
    state->bytes_left -= length;
    state->data += length;

void *read_image(void *buf, size_t size, uint32_t *width, uint32_t *height)
    png_infop info_ptr;
    png_structp png_ptr;
    struct buf_state state;

    int bit_depth, colour_type, interlace_type, compression_type, filter_method;
    unsigned char *image = NULL;
    png_bytep *row_pointers = NULL;

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

    if(png_ptr == NULL) return NULL;

    info_ptr = png_create_info_struct(png_ptr);

        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return NULL;

        printf("libpng error\n");
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        if(image != NULL) free(image);
        if(row_pointers != NULL) free(row_pointers);
        return NULL;
    } = buf;
    state.bytes_left = size;

    png_set_read_fn(png_ptr, &state, libpng_read_fn);

    if(png_sig_cmp(buf, 0, 8))
        printf("libpng: invalid signature\n");
        return NULL;

    png_read_info(png_ptr, info_ptr);

    if(!png_get_IHDR(png_ptr, info_ptr, width, height, &bit_depth,
                        &colour_type, &interlace_type, &compression_type, &filter_method))
        printf("png_get_IHDR failed\n");
        return NULL;

    /* Decode to RGBA8 layout regardless of PNG format */
    png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);

    png_read_update_info(png_ptr, info_ptr);

    size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
    size_t image_size = *height * rowbytes;

    image = malloc(image_size);
    row_pointers = malloc(*height * sizeof(png_bytep));

    if(image == NULL || row_pointers == NULL)
        if(image != NULL) free(image);
        if(row_pointers != NULL) free(row_pointers);
        return NULL;

    int k;
    for(k=0; k < *height; k++)
        row_pointers[k] = image + k * rowbytes;

    png_read_image(png_ptr, row_pointers);
    png_read_end(png_ptr, info_ptr);


    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    return image;

results matching ""

    No results matching ""