diff --git a/escher/image/inliner.c b/escher/image/inliner.c new file mode 100644 index 000000000..42c81549c --- /dev/null +++ b/escher/image/inliner.c @@ -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 +#include +#include +#include +#include +#include + +#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\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>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 = ℑ\n", variable); +}