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;
}