Skip to content

Comparison

This is a comparison between libspng v0.7.0 and the reference implementation libpng v1.6.37.

Lines of code

libspng is 5.2 KSLOC while libpng is 20 KSLOC.

libpng-1.6.37$ cloc png.c pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c pngrtran.c pngrutil.c pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c pngwutil.c

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C                               15           4627           5396          20831
-------------------------------------------------------------------------------
SUM:                            15           4627           5396          20831
-------------------------------------------------------------------------------
spng$ cloc spng.c

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C                                1           1510            258           5165
-------------------------------------------------------------------------------

Build

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

API

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++.

Decoding

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_TRNS)) goto err;

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

    return image;

err:
    if(image != NULL) free(image);
    spng_ctx_free(ctx);

    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 *volatile image = NULL;
    png_bytep *volatile 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);

    if(!info_ptr)
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
        return NULL;
    }

    if(setjmp(png_jmpbuf(png_ptr)))
    {
        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;
    }

    state.data = 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_gray_to_rgb(png_ptr);
    png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
    png_set_expand(png_ptr);
    png_set_strip_16(png_ptr);

    png_set_interlace_handling(png_ptr);
    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);

    free(row_pointers);

    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    return image;
}