[escher] Add an image inliner that converts .png files to .cpp

Change-Id: I14bbabae7131ac02e52e9de49bd61095f40d097c
This commit is contained in:
Romain Goyet
2016-09-26 17:56:07 +02:00
parent ab74fe6933
commit fa76920fb7

166
escher/image/inliner.c Normal file
View File

@@ -0,0 +1,166 @@
/* The image inliner converts PNG images to C++ code.
*
* Usage: inliner snake_case_image.png
*
* The inliner creates a .h and a .cpp file in the same directory as the input
* file. The implementation file declares an Image in the ImageStore namespace,
* and the header exposes a pointer to this variable. The Image embedds the
* bitmap data in the RGB565 format. */
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#define ERROR_IF(cond, message) if (cond) { printf(message); return -1; };
#define MAX_FILENAME_LENGTH 255
void generateHeaderFromImage(FILE * file, const char * guardian, const char * variable);
void generateImplementationFromImage(FILE * file, const char * header, const char * variable, uint32_t width, uint32_t height, uint32_t ** pixelsRowPointers);
void fileNameToSnakeCaseName(const char * fileName, char * snakeCaseName, size_t maxLength);
void snakeCaseNameToUpperSnakeName(const char * snakeCaseName, char * upperSnakeCaseName, size_t maxLength);
void camelCaseNameFromSnakeCaseNames(const char * snakeCaseName, const char * upperSnakeCaseName, char * camelCaseName, size_t maxLength);
int main(int argc, char * argv[]) {
ERROR_IF(argc != 2, "Usage: inliner source.png");
const char * inputPath = argv[1];
FILE * inputFile = fopen(inputPath, "rb");
ERROR_IF(inputFile == NULL, "Error: could not open input file.");
unsigned char magic[8];
fread(magic, 1, 8, inputFile);
ERROR_IF(png_sig_cmp(magic, 0, 8), "Error: Input file is not a PNG file");
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
ERROR_IF(png == NULL, "Could not read png struct");
png_infop info = png_create_info_struct(png);
ERROR_IF(info == NULL, "Could not read info struct");
png_init_io(png, inputFile);
png_set_sig_bytes(png, 8);
png_read_info(png, info);
png_uint_32 width = png_get_image_width(png, info);
png_uint_32 height = png_get_image_height(png, info);
png_byte colorType = png_get_color_type(png, info);
png_byte bitDepth = png_get_bit_depth(png, info);
ERROR_IF(colorType != PNG_COLOR_TYPE_RGB_ALPHA, "Error: Inliner only handles RGBA PNG images.");
ERROR_IF(bitDepth != 8, "Error: Inliner only handles RGBA8888 PNG images.");
png_bytep * rowPointers = (png_bytep *)malloc(sizeof(png_bytep)*height);
for (int i=0; i<height; i++) {
rowPointers[i] = (png_byte *)malloc(png_get_rowbytes(png, info));
}
png_read_image(png, rowPointers);
char lowerSnakeCaseName[MAX_FILENAME_LENGTH];
char upperSnakeCaseName[MAX_FILENAME_LENGTH];
char camelCaseName[MAX_FILENAME_LENGTH];
const char * inputFileName = inputPath;
// Let's keep only the last path component
for (const char * currentChar=inputPath; *currentChar != 0; currentChar++) {
if (*currentChar == '/') {
inputFileName = currentChar+1;
}
}
fileNameToSnakeCaseName(inputFileName, lowerSnakeCaseName, MAX_FILENAME_LENGTH);
snakeCaseNameToUpperSnakeName(lowerSnakeCaseName, upperSnakeCaseName, MAX_FILENAME_LENGTH);
camelCaseNameFromSnakeCaseNames(lowerSnakeCaseName, upperSnakeCaseName, camelCaseName, MAX_FILENAME_LENGTH);
char headerPath[MAX_FILENAME_LENGTH];
size_t pathLength = strlen(inputPath);
strcpy(headerPath, inputPath);
// Replace the .png extension with a .h extension
headerPath[pathLength-3] = 'h';
headerPath[pathLength-2] = 0;
char implementationPath[MAX_FILENAME_LENGTH];
strcpy(implementationPath, inputPath);
// Replace the .png extension with a .cpp extension
implementationPath[pathLength-3] = 'c';
implementationPath[pathLength-2] = 'p';
implementationPath[pathLength-1] = 'p';
FILE * header = fopen(headerPath, "w");
generateHeaderFromImage(header, upperSnakeCaseName, camelCaseName);
fclose(header);
FILE * implementation = fopen(implementationPath, "w");
generateImplementationFromImage(implementation, lowerSnakeCaseName, camelCaseName, width, height, (uint32_t **)rowPointers);
fclose(implementation);
fclose(inputFile);
}
void fileNameToSnakeCaseName(const char * fileName, char * snakeCaseName, size_t maxLength) {
for (int i=0; i<maxLength; i++) {
snakeCaseName[i] = fileName[i];
if (fileName[i] == '.') {
snakeCaseName[i] = 0;
break;
}
}
snakeCaseName[maxLength-1] = 0;
}
void snakeCaseNameToUpperSnakeName(const char * snakeCaseName, char * upperSnakeCaseName, size_t maxLength) {
for (int i=0; i<maxLength; i++) {
upperSnakeCaseName[i] = toupper(snakeCaseName[i]);
}
}
void camelCaseNameFromSnakeCaseNames(const char * snakeCaseName, const char * upperSnakeCaseName, char * camelCaseName, size_t maxLength) {
int j=0;
for (int i=0; i<maxLength; i++) {
char nextLetter = snakeCaseName[i];
if (nextLetter == '_') {
continue;
}
if (i==0 || snakeCaseName[i-1] == '_') {
nextLetter = upperSnakeCaseName[i];
}
camelCaseName[j++] = nextLetter;
}
camelCaseName[j] = 0;
}
void generateHeaderFromImage(FILE * file, const char * guardian, const char * variable) {
fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
fprintf(file, "#ifndef IMAGE_STORE_%s_H\n", guardian);
fprintf(file, "#define IMAGE_STORE_%s_H\n\n", guardian);
fprintf(file, "#include <escher/image.h>\n\n");
fprintf(file, "namespace ImageStore {\n\n");
fprintf(file, "extern const Image * %s;\n\n", variable);
fprintf(file, "};\n\n");
fprintf(file, "#endif\n");
}
void generateImplementationFromImage(FILE * file, const char * header, const char * variable, uint32_t width, uint32_t height, uint32_t ** pixelsRowPointers) {
fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
fprintf(file, "#include \"%s.h\"\n\n", header);
fprintf(file, "const unsigned char data[] = {");
for (int j=0; j<height; j++) {
for (int i=0; i<width; i++) {
if ((i+j*width) % 6 == 0) {
fprintf(file, "\n ");
}
uint32_t rgba = pixelsRowPointers[j][i];
uint16_t rgb565 = (((rgba&0xF8000000)>>16)|((rgba&0x00FC0000)>>14)|((rgba&0x0000F800)>>11));
//if (rgba & 0xFF == 0) { rgb565 = 0xFFFF; }
fprintf(file, "0x%02X, 0x%02X", rgb565 >> 8, rgb565 & 0xFF);
if (i!=width-1 || j!= height-1) {
fprintf(file, ", ");
}
}
}
fprintf(file, "\n};\n\n");
fprintf(file, "constexpr Image image = Image(%d, %d, data);\n\n", width, height);
fprintf(file, "const Image * ImageStore::%s = &image;\n", variable);
}