Last active
August 29, 2015 14:17
-
-
Save DanielGibson/e0828acfc90f619198cb to your computer and use it in GitHub Desktop.
Crappy benchmark for stb_image, lodepng, libjpeg and libpng
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The Results: http://wp.me/pEPJ4-4S | |
// you can define USE_LIBPNG to decode with libpng XOR USE_LODEPNG to decode with lodepng | |
// XOR USE_LIBJPEG to decode with libjpeg or libjpeg-turbo XOR nothing to use stb_image. | |
// you'll have to provide the corresponding headers and (for libjpeg and libpng) libs to link against | |
// for lodepng also compile lodepng.c into the binary | |
// ex: gcc -O4 -DUSE_LODEPNG imgLoadBench.c lodepng.c -o imgLoadBench_lodepng_gcc_O4 | |
// or: gcc -O0 -DUSE_LIBPNG imgLoadBench.c -o imgLoadBench_libpng_gcc_O0 -lpng | |
// or: gcc -O2 -DUSE_LIBJPEG imgLoadBench.c -o imgLoadBench_libjpeg_gcc_O2 -ljpeg | |
// or: clang -O3 imgLoadBench.c -o imgLoadBench_stb_clang_O3 | |
/* | |
=========================================================================== | |
The libpng png decoding and libjpeg jpg decoding code was adapted from RBDoom3BFG: | |
Doom 3 BFG Edition GPL Source Code | |
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. | |
Copyright (C) 2012-2014 Robert Beckebans | |
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). | |
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. | |
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. | |
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. | |
The stb_image and lodepng jpeg and png decoding code was written by myself | |
and is so trivial that it doesn't deserve a Copyright. | |
=========================================================================== | |
*/ | |
#include <stdio.h> | |
#include <limits.h> // PATH_MAX | |
#include <time.h> | |
#include <stdlib.h> | |
#define eprintf(...) fprintf(stderr, __VA_ARGS__) | |
const char* progName = "imgLoadBench"; | |
typedef unsigned char byte; | |
static void printUsage() | |
{ | |
eprintf("Usage: %s <imgname> [outfilename]\n", progName); | |
eprintf(" e.g.: %s test.png\n", progName); | |
eprintf(" %s /path/to/bla.tga /other/path/to/bla.h\n", progName); | |
} | |
struct image | |
{ | |
unsigned char* data; | |
int w; | |
int h; | |
int format; // 3: RGB, 4: RGBA | |
}; | |
#ifdef USE_LIBPNG | |
// the libpng png loading code is stolen from rbdoom3bfg's LoadPNG() | |
static void freeImage(struct image* img) | |
{ | |
free(img->data); | |
img->data = NULL; | |
} | |
#include <png.h> | |
static void png_Error( png_structp pngPtr, png_const_charp msg ) | |
{ | |
eprintf( "%s\n", msg ); | |
exit(1); | |
} | |
static void png_Warning( png_structp pngPtr, png_const_charp msg ) | |
{ | |
eprintf( "%s\n", msg ); | |
} | |
static void png_ReadData( png_structp pngPtr, png_bytep data, png_size_t length ) | |
{ | |
memcpy( data, ( byte* )pngPtr->io_ptr, length ); | |
pngPtr->io_ptr = ( ( byte* ) pngPtr->io_ptr ) + length; | |
} | |
static struct image loadImage(const char* filename, byte* fbuffer, int len) | |
{ | |
struct image ret = {0}; | |
// create png_struct with the custom error handlers | |
png_structp pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING, ( png_voidp ) NULL, png_Error, png_Warning ); | |
if( !pngPtr ) | |
{ | |
eprintf( "LoadPNG( %s ): png_create_read_struct failed\n", filename ); | |
exit(1); | |
} | |
// allocate the memory for image information | |
png_infop infoPtr = png_create_info_struct( pngPtr ); | |
if( !infoPtr ) | |
{ | |
eprintf( "LoadPNG( %s ): png_create_info_struct failed\n", filename ); | |
exit(1); | |
} | |
png_set_read_fn( pngPtr, fbuffer, png_ReadData ); | |
png_set_sig_bytes( pngPtr, 0 ); | |
png_read_info( pngPtr, infoPtr ); | |
png_uint_32 pngWidth, pngHeight; | |
int bitDepth, colorType, interlaceType; | |
png_get_IHDR( pngPtr, infoPtr, &pngWidth, &pngHeight, &bitDepth, &colorType, &interlaceType, NULL, NULL ); | |
// 16 bit -> 8 bit | |
png_set_strip_16( pngPtr ); | |
// 1, 2, 4 bit -> 8 bit | |
if( bitDepth < 8 ) | |
{ | |
png_set_packing( pngPtr ); | |
} | |
if( colorType & PNG_COLOR_MASK_PALETTE ) | |
{ | |
png_set_expand( pngPtr ); | |
} | |
if( !( colorType & PNG_COLOR_MASK_COLOR ) ) | |
{ | |
png_set_gray_to_rgb( pngPtr ); | |
} | |
// set paletted or RGB images with transparency to full alpha so we get RGBA | |
if( png_get_valid( pngPtr, infoPtr, PNG_INFO_tRNS ) ) | |
{ | |
png_set_tRNS_to_alpha( pngPtr ); | |
} | |
// make sure every pixel has an alpha value | |
if( !( colorType & PNG_COLOR_MASK_ALPHA ) ) | |
{ | |
png_set_filler( pngPtr, 255, PNG_FILLER_AFTER ); | |
} | |
png_read_update_info( pngPtr, infoPtr ); | |
byte* out = ( byte* )malloc( pngWidth * pngHeight * 4 ); | |
ret.data = out; | |
ret.w = pngWidth; | |
ret.h = pngHeight; | |
ret.format = 4; | |
png_uint_32 rowBytes = png_get_rowbytes( pngPtr, infoPtr ); | |
png_bytep* rowPointers = ( png_bytep* ) malloc( sizeof( png_bytep ) * pngHeight ); | |
for( png_uint_32 row = 0; row < pngHeight; row++ ) | |
{ | |
rowPointers[row] = ( png_bytep )( out + ( row * pngWidth * 4 ) ); | |
} | |
png_read_image( pngPtr, rowPointers ); | |
png_read_end( pngPtr, infoPtr ); | |
png_destroy_read_struct( &pngPtr, &infoPtr, NULL ); | |
free( rowPointers ); | |
return ret; | |
} | |
#elif defined(USE_LODEPNG) | |
// the lodepng decoding code is written from scratch | |
#include "lodepng.h" | |
static void freeImage(struct image* img) | |
{ | |
free(img->data); | |
img->data = NULL; | |
} | |
static struct image loadImage(const char* filename, byte* fbuffer, int len) | |
{ | |
struct image ret = {0}; | |
byte* image; | |
unsigned w, h; | |
int error = lodepng_decode32(&image, &w, &h, fbuffer, len); | |
if(error) { | |
eprintf("ERROR: Couldn't load image file %s: %s!\n", filename, lodepng_error_text(error)); | |
exit(1); | |
} | |
ret.data = image; | |
ret.w = w; | |
ret.h = h; | |
ret.format = 4; | |
return ret; | |
} | |
#elif defined(USE_LIBJPEG) | |
// the libjpeg jpeg loading code is stolen from doom3's LoadJPG() | |
#include <jpeglib.h> | |
static void freeImage(struct image* img) | |
{ | |
free(img->data); | |
img->data = NULL; | |
} | |
static struct image loadImage(const char* filename, byte* fbuffer, int len) | |
{ | |
struct image ret = {0}; | |
/* This struct contains the JPEG decompression parameters and pointers to | |
* working space (which is allocated as needed by the JPEG library). | |
*/ | |
struct jpeg_decompress_struct cinfo; | |
/* We use our private extension JPEG error handler. | |
* Note that this struct must live as long as the main JPEG parameter | |
* struct, to avoid dangling-pointer problems. | |
*/ | |
/* This struct represents a JPEG error handler. It is declared separately | |
* because applications often want to supply a specialized error handler | |
* (see the second half of this file for an example). But here we just | |
* take the easy way out and use the standard error handler, which will | |
* print a message on stderr and call exit() if compression fails. | |
* Note that this struct must live as long as the main JPEG parameter | |
* struct, to avoid dangling-pointer problems. | |
*/ | |
struct jpeg_error_mgr jerr; | |
/* More stuff */ | |
JSAMPARRAY buffer; /* Output row buffer */ | |
int row_stride; /* physical row width in output buffer */ | |
unsigned char* out; | |
byte* bbuf; | |
/* Step 1: allocate and initialize JPEG decompression object */ | |
/* We have to set up the error handler first, in case the initialization | |
* step fails. (Unlikely, but it could happen if you are out of memory.) | |
* This routine fills in the contents of struct jerr, and returns jerr's | |
* address which we place into the link field in cinfo. | |
*/ | |
cinfo.err = jpeg_std_error( &jerr ); | |
/* Now we can initialize the JPEG decompression object. */ | |
jpeg_create_decompress( &cinfo ); | |
/* Step 2: specify data source (eg, a file) */ | |
jpeg_mem_src( &cinfo, fbuffer, len ); // DG: this looks better. | |
/* Step 3: read file parameters with jpeg_read_header() */ | |
jpeg_read_header( &cinfo, 1 ); | |
/* We can ignore the return value from jpeg_read_header since | |
* (a) suspension is not possible with the stdio data source, and | |
* (b) we passed TRUE to reject a tables-only JPEG file as an error. | |
* See libjpeg.doc for more info. | |
*/ | |
/* Step 4: set parameters for decompression */ | |
/* In this example, we don't need to change any of the defaults set by | |
* jpeg_read_header(), so we do nothing here. | |
*/ | |
/* Step 5: Start decompressor */ | |
jpeg_start_decompress( &cinfo ); | |
/* We can ignore the return value since suspension is not possible | |
* with the stdio data source. | |
*/ | |
/* We may need to do some setup of our own at this point before reading | |
* the data. After jpeg_start_decompress() we have the correct scaled | |
* output image dimensions available, as well as the output colormap | |
* if we asked for color quantization. | |
* In this example, we need to make an output work buffer of the right size. | |
*/ | |
/* JSAMPLEs per row in output buffer */ | |
row_stride = cinfo.output_width * cinfo.output_components; | |
if( 0 && cinfo.output_components != 4 ) // XXX | |
{ | |
eprintf( "JPG %s is unsupported color depth (%d)", | |
filename, cinfo.output_components ); | |
} | |
out = ( byte* )malloc( cinfo.output_width * cinfo.output_height * 4 ); | |
ret.data = out; | |
ret.w = cinfo.output_width; | |
ret.h = cinfo.output_height; | |
/* Step 6: while (scan lines remain to be read) */ | |
/* jpeg_read_scanlines(...); */ | |
/* Here we use the library's state variable cinfo.output_scanline as the | |
* loop counter, so that we don't have to keep track ourselves. | |
*/ | |
while( cinfo.output_scanline < cinfo.output_height ) | |
{ | |
/* jpeg_read_scanlines expects an array of pointers to scanlines. | |
* Here the array is only one element long, but you could ask for | |
* more than one scanline at a time if that's more convenient. | |
*/ | |
bbuf = ( ( out + ( row_stride * cinfo.output_scanline ) ) ); | |
buffer = &bbuf; | |
jpeg_read_scanlines( &cinfo, buffer, 1 ); | |
} | |
// clear all the alphas to 255 | |
{ | |
int i, j; | |
byte* buf; | |
buf = ret.data; | |
j = cinfo.output_width * cinfo.output_height * 4; | |
for( i = 3 ; i < j ; i += 4 ) | |
{ | |
buf[i] = 255; | |
} | |
} | |
/* Step 7: Finish decompression */ | |
jpeg_finish_decompress( &cinfo ); | |
/* We can ignore the return value since suspension is not possible | |
* with the stdio data source. | |
*/ | |
/* Step 8: Release JPEG decompression object */ | |
/* This is an important step since it will release a good deal of memory. */ | |
jpeg_destroy_decompress( &cinfo ); | |
return ret; | |
} | |
#else // STB | |
// the stb_image decoding code is written from scratch | |
#define STBI_NO_LINEAR // don't need HDR stuff | |
#define STBI_NO_HDR | |
#define STB_IMAGE_IMPLEMENTATION | |
#include "stb_image.h" | |
static void freeImage(struct image* img) | |
{ | |
stbi_image_free(img->data); | |
img->data = NULL; | |
} | |
static struct image loadImage(const char* imgFileName, byte* fbuffer, int len) | |
{ | |
struct image ret = {0}; | |
int format; | |
ret.data = stbi_load_from_memory(fbuffer, len, &ret.w, &ret.h, &format, 4); | |
if(ret.data == NULL) | |
{ | |
eprintf("ERROR: Couldn't load image file %s: %s!\n", imgFileName, stbi_failure_reason()); | |
exit(1); | |
} | |
ret.format = 4; | |
return ret; | |
} | |
#endif // USE_STB | |
int main(int argc, char** argv) | |
{ | |
progName = argv[0]; | |
if(argc < 2) | |
{ | |
printUsage(); | |
exit(1); | |
} | |
const char* filename = argv[1]; | |
byte* fbuffer; | |
int len; | |
// the "load file into buffer code" is stolen from doom3's LoadJPG idFileSystem code (because I was lazy) | |
{ | |
FILE* f = fopen(filename, "r"); | |
if(f == NULL) | |
{ | |
eprintf("ERROR: Couldn't open %s!\n", filename); | |
exit(1); | |
} | |
fseek(f, 0, SEEK_END); | |
len = ftell(f); | |
fseek(f, 0, SEEK_SET); // go back to start | |
fbuffer = ( byte* )malloc( len + 4096 ); | |
byte* buf = fbuffer; | |
int remaining = len; | |
while(remaining) | |
{ | |
int block = remaining; | |
int read = fread( buf, 1, block, f ); | |
if(read < 0) | |
{ | |
eprintf("WTF, error while reading file!\n"); | |
exit(1); | |
} | |
remaining -= read; | |
buf += read; | |
} | |
} | |
// I wrote the following code myself | |
#define numIterations 100 | |
struct timespec before = {0}; | |
struct timespec after = {0}; | |
clock_gettime(CLOCK_MONOTONIC_RAW, &before); | |
int i; | |
for(i=0; i<numIterations; ++i) | |
{ | |
struct image img = loadImage(filename, fbuffer, len); | |
freeImage(&img); | |
} | |
clock_gettime(CLOCK_MONOTONIC_RAW, &after); | |
int secs = after.tv_sec - before.tv_sec; | |
int nsecs = after.tv_nsec - before.tv_nsec; | |
if(nsecs < 0) | |
{ | |
--secs; | |
nsecs += 1000000000; | |
} | |
double ms = 1000.0*secs; | |
ms += nsecs * 0.000001; | |
printf("Decoding %s %d times took %fms => %fms avg\n", filename, numIterations, ms, ms/((float)numIterations)); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment