/* 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 #include #include #include #include #include #include #include "../../ion/src/external/lz4/lz4hc.h" #define ERROR_IF(cond, message) if (cond) { printf(message "\n"); 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, png_bytep * 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); // TODO: fix inliner to handle any png file // TODO: truncate the app image dimensions to 55x56 pixels int main(int argc, char * argv[]) { ERROR_IF(argc != 4, "Usage: inliner source.png output.h output.cpp"); 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\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, png_bytep * pixelsRowPointers) { int sizeOfPixelBuffer = width * height * sizeof(uint16_t); uint16_t * pixelBuffer = (uint16_t *)malloc(sizeOfPixelBuffer); for (int j=0; j>3)<<11 | (intGreen>>2) << 5 | (intBlue>>3); pixelBuffer[j*width+i] = rgb565value; } } int maxSizeOfCompressedPixelBuffer = LZ4_compressBound(sizeOfPixelBuffer); uint8_t * compressedPixelBuffer = malloc(maxSizeOfCompressedPixelBuffer); int sizeOfCompressedPixelBuffer = LZ4_compress_HC( pixelBuffer, compressedPixelBuffer, sizeOfPixelBuffer, maxSizeOfCompressedPixelBuffer, LZ4HC_CLEVEL_MAX ); assert(sizeOfCompressedPixelBuffer != 0); fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n"); fprintf(file, "#include \"%s.h\"\n\n", header); fprintf(file, "// Compressed %d pixels into %d bytes (%.2f%% compression ratio)/\n", width*height, sizeOfCompressedPixelBuffer, 100.0*sizeOfCompressedPixelBuffer/sizeOfPixelBuffer); fprintf(file, "const uint8_t compressedPixelData[%d] = {", sizeOfCompressedPixelBuffer); for (int i=0; i