Created
July 18, 2015 02:30
-
-
Save DanielGibson/eb322f8054c2dfef06a9 to your computer and use it in GitHub Desktop.
Hacky cmdline app that converts to png with several encoders for comparison
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
/* | |
* Encodes a given file to png using stb_image_write, miniz, lodepng and libpng | |
* | |
* Useful to compare the compression ratios achievable with the different encoders. | |
* Blog post with results: http://wp.me/pEPJ4-5U | |
* | |
* Needs: | |
* - stb_image.h and stb_image_write.h from https://github.com/nothings/stb | |
* - lodepng.c and lodepng.h from http://lodev.org/lodepng/ | |
* - miniz.c from https://github.com/richgel999/miniz | |
* - libpng from http://libpng.org/ or your Linux distro or whatever (tested v1.2.50) | |
* | |
* Can be built with "gcc -o encPng encPng.c lodepng.c -lpng" (or use clang instead of gcc). | |
* Building with MSVC (as console application) is probably possible, but untested. | |
* | |
* writePngLibPng() is based on writeImage() from Dr. Andrew Greensted's makePNG.c | |
* (C) Dr. Andrew Greensted | |
* see http://www.labbookpages.co.uk/software/imgProc/libPNG.html#write | |
* http://www.labbookpages.co.uk/home/licences.html says I can use it as long as | |
* I mention him and his homepage, which I've hereby done :-) | |
* | |
* Rest of the code: | |
* (C) 2015 Daniel Gibson | |
* | |
* License (for everything except for writePngLibPng()): | |
* This software is in the public domain. Where that dedication is not | |
* recognized, you are granted a perpetual, irrevocable license to copy | |
* and modify this file however you want. | |
* No warranty implied; use at your own risk. | |
*/ | |
#include <stdio.h> | |
#include <limits.h> // PATH_MAX | |
#ifndef PATH_MAX | |
// should be defined by limits.h, but maybe it's not on windows, not sure... | |
#define PATH_MAX 4096 | |
#endif | |
#define STBI_NO_LINEAR // don't need HDR stuff | |
#define STBI_NO_HDR | |
#define STB_IMAGE_IMPLEMENTATION | |
#include "stb_image.h" | |
#define eprintf(...) fprintf(stderr, __VA_ARGS__) | |
const char* progName = "encPng"; | |
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/converted/conv_bla\n", progName); | |
} | |
struct image | |
{ | |
unsigned char* data; | |
int w; | |
int h; | |
int format; // 3: RGB, 4: RGBA | |
}; | |
static struct image loadImage(const char* imgFileName) | |
{ | |
struct image ret = {0}; | |
int inforet = stbi_info(imgFileName, &ret.w, &ret.h, &ret.format); | |
if(!inforet) | |
{ | |
eprintf("ERROR: Couldn't load image file %s: %s!\n", imgFileName, stbi_failure_reason()); | |
exit(1); | |
} | |
int bppToUse = 4; | |
// no alpha => use RGB, else use RGBA | |
if(ret.format == 1 || ret.format == 3) bppToUse = 3; | |
ret.data = stbi_load(imgFileName, &ret.w, &ret.h, &ret.format, bppToUse); | |
if(ret.data == NULL) | |
{ | |
eprintf("ERROR: Couldn't load image file %s: %s!\n", imgFileName, stbi_failure_reason()); | |
exit(1); | |
} | |
ret.format = bppToUse; | |
return ret; | |
} | |
#define STB_IMAGE_WRITE_IMPLEMENTATION | |
#include "stb_image_write.h" | |
static void writePngStb(struct image img, const char* outFilePrefix) | |
{ | |
char outFileBuf[PATH_MAX]; | |
strcpy(outFileBuf, outFilePrefix); | |
strcat(outFileBuf, "_stb.png"); | |
stbi_write_png(outFileBuf, img.w, img.h, img.format, img.data, 0); | |
} | |
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES // prevent conflict with libpng/zlib | |
#include "miniz.c" | |
static void writePngMiniz(struct image img, const char* outFilePrefix) | |
{ | |
char outFileBuf[PATH_MAX]; | |
strcpy(outFileBuf, outFilePrefix); | |
strcat(outFileBuf, "_miniz.png"); | |
size_t outLen = 0; | |
int comprLevel = MZ_BEST_COMPRESSION; // MZ_DEFAULT_LEVEL | |
void* data = tdefl_write_image_to_png_file_in_memory_ex(img.data, img.w, img.h, img.format, &outLen, comprLevel, 0); | |
if(data == NULL) | |
{ | |
eprintf("WTF, miniz's png compression failed!\n"); | |
return; | |
} | |
FILE* f = fopen(outFileBuf, "wb"); | |
if(f == NULL) | |
{ | |
eprintf("WTF, couldn't open outfile: %s\n", outFileBuf); | |
return; | |
} | |
fwrite(data, outLen, 1, f); | |
fclose(f); | |
} | |
#include "lodepng.h" | |
static void writePngLode(struct image img, const char* outFilePrefix) | |
{ | |
char outFileBuf[PATH_MAX]; | |
strcpy(outFileBuf, outFilePrefix); | |
strcat(outFileBuf, "_lode.png"); | |
LodePNGColorType colortype = LCT_RGB; | |
if(img.format == STBI_rgb_alpha) colortype = LCT_RGBA; | |
lodepng_encode_file(outFileBuf, img.data, img.w, img.h, colortype, 8); | |
} | |
#include <libpng/png.h> | |
// the following function is based on writeImage() from A. Greensted's makePNG.c | |
// see http://www.labbookpages.co.uk/software/imgProc/libPNG.html#write | |
// !! this function is *not* in the public domain, but can still be freely used !! | |
// !! see http://www.labbookpages.co.uk/home/licences.html !! | |
static void writePngLibPng(struct image img, const char* outFilePrefix) | |
{ | |
char outFileBuf[PATH_MAX]; | |
strcpy(outFileBuf, outFilePrefix); | |
strcat(outFileBuf, "_libPng.png"); | |
if(img.format != 3) { | |
// support for RGBA shouldn't be hard, but it's an additional case I don't care about right now | |
eprintf("the libpng code only supports RGB, not RGBA!\n"); | |
return; | |
} | |
int width = img.w, height = img.h; | |
int code = 0; | |
// DG: with all the gotos, we should initialize things! | |
FILE *fp = NULL; | |
png_structp png_ptr = NULL; | |
png_infop info_ptr = NULL; | |
png_bytep row = NULL; | |
// Open file for writing (binary mode) | |
fp = fopen(outFileBuf, "wb"); | |
if (fp == NULL) { | |
fprintf(stderr, "Could not open file %s for writing\n", outFileBuf); | |
code = 1; | |
goto finalise; | |
} | |
// Initialize write structure | |
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if (png_ptr == NULL) { | |
fprintf(stderr, "Could not allocate write struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// DG: set highest compression level | |
png_set_compression_level(png_ptr, 9); | |
// Initialize info structure | |
info_ptr = png_create_info_struct(png_ptr); | |
if (info_ptr == NULL) { | |
fprintf(stderr, "Could not allocate info struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// Setup Exception handling | |
if (setjmp(png_jmpbuf(png_ptr))) { | |
fprintf(stderr, "Error during png creation\n"); | |
code = 1; | |
goto finalise; | |
} | |
png_init_io(png_ptr, fp); | |
// Write header (8 bit colour depth) | |
png_set_IHDR(png_ptr, info_ptr, width, height, | |
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
// Set title | |
/* | |
if (title != NULL) { | |
png_text title_text; | |
title_text.compression = PNG_TEXT_COMPRESSION_NONE; | |
title_text.key = "Title"; | |
title_text.text = title; | |
png_set_text(png_ptr, info_ptr, &title_text, 1); | |
} | |
*/ | |
png_write_info(png_ptr, info_ptr); | |
// Write image data | |
int y; | |
for (y=0 ; y<height ; y++) { | |
row = img.data + y*img.w*3; | |
png_write_row(png_ptr, row); | |
} | |
// End write | |
png_write_end(png_ptr, NULL); | |
finalise: | |
if (fp != NULL) fclose(fp); | |
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); | |
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |
} | |
int main(int argc, char** argv) | |
{ | |
progName = argv[0]; | |
if(argc < 2) | |
{ | |
printUsage(); | |
exit(1); | |
} | |
const char* fileName = argv[1]; | |
struct image img = loadImage(fileName); | |
char outFileBuf[PATH_MAX]; | |
const char* outFilePrefix = outFileBuf; | |
if(argc > 2) | |
{ | |
outFilePrefix = argv[2]; | |
} | |
else | |
{ | |
strncpy(outFileBuf, fileName, PATH_MAX); | |
outFileBuf[PATH_MAX-1] = '\0'; | |
char* ext = strrchr(outFileBuf, '.'); | |
if(ext == NULL) | |
{ | |
eprintf("ERROR: Image file %s has no file extension that could be replaced with .c!\n", fileName); | |
exit(1); | |
} | |
*ext = '\0'; | |
} | |
writePngStb(img, outFilePrefix); | |
writePngMiniz(img, outFilePrefix); | |
writePngLode(img, outFilePrefix); | |
writePngLibPng(img, outFilePrefix); | |
stbi_image_free(img.data); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment