Numworks Epsilon  1.4.1
Graphing Calculator Operating System
inliner.c
Go to the documentation of this file.
1 /* The image inliner converts PNG images to C++ code.
2  *
3  * Usage: inliner snake_case_image.png
4  *
5  * The inliner creates a .h and a .cpp file in the same directory as the input
6  * file. The implementation file declares an Image in the ImageStore namespace,
7  * and the header exposes a pointer to this variable. The Image embedds the
8  * bitmap data in the RGB565 format. */
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <png.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include <ctype.h>
16 
17 #define ERROR_IF(cond, message) if (cond) { printf(message); return -1; };
18 #define MAX_FILENAME_LENGTH 255
19 
20 void generateHeaderFromImage(FILE * file, const char * guardian, const char * variable);
21 void generateImplementationFromImage(FILE * file, const char * header, const char * variable, uint32_t width, uint32_t height, png_bytep * pixelsRowPointers);
22 void fileNameToSnakeCaseName(const char * fileName, char * snakeCaseName, size_t maxLength);
23 void snakeCaseNameToUpperSnakeName(const char * snakeCaseName, char * upperSnakeCaseName, size_t maxLength);
24 void camelCaseNameFromSnakeCaseNames(const char * snakeCaseName, const char * upperSnakeCaseName, char * camelCaseName, size_t maxLength);
25 
26 // TODO: fix inliner to handle any png file
27 // TODO: truncate the app image dimensions to 55x56 pixels
28 
29 int main(int argc, char * argv[]) {
30  ERROR_IF(argc != 2, "Usage: inliner source.png");
31  const char * inputPath = argv[1];
32 
33  FILE * inputFile = fopen(inputPath, "rb");
34  ERROR_IF(inputFile == NULL, "Error: could not open input file.");
35 
36  unsigned char magic[8];
37  fread(magic, 1, 8, inputFile);
38  ERROR_IF(png_sig_cmp(magic, 0, 8), "Error: Input file is not a PNG file");
39 
40  png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
41  ERROR_IF(png == NULL, "Could not read png struct");
42 
43  png_infop info = png_create_info_struct(png);
44  ERROR_IF(info == NULL, "Could not read info struct");
45 
46  png_init_io(png, inputFile);
47  png_set_sig_bytes(png, 8);
48 
49  png_read_info(png, info);
50 
51  png_uint_32 width = png_get_image_width(png, info);
52  png_uint_32 height = png_get_image_height(png, info);
53  png_byte colorType = png_get_color_type(png, info);
54  png_byte bitDepth = png_get_bit_depth(png, info);
55 
56  ERROR_IF(colorType != PNG_COLOR_TYPE_RGB_ALPHA, "Error: Inliner only handles RGBA PNG images.");
57  ERROR_IF(bitDepth != 8, "Error: Inliner only handles RGBA8888 PNG images.");
58 
59  png_bytep * rowPointers = (png_bytep *)malloc(sizeof(png_bytep)*height);
60  for (int i=0; i<height; i++) {
61  rowPointers[i] = (png_byte *)malloc(png_get_rowbytes(png, info));
62  }
63  png_read_image(png, rowPointers);
64 
65  char lowerSnakeCaseName[MAX_FILENAME_LENGTH];
66  char upperSnakeCaseName[MAX_FILENAME_LENGTH];
67  char camelCaseName[MAX_FILENAME_LENGTH];
68  const char * inputFileName = inputPath;
69  // Let's keep only the last path component
70  for (const char * currentChar=inputPath; *currentChar != 0; currentChar++) {
71  if (*currentChar == '/') {
72  inputFileName = currentChar+1;
73  }
74  }
75 
76  fileNameToSnakeCaseName(inputFileName, lowerSnakeCaseName, MAX_FILENAME_LENGTH);
77  snakeCaseNameToUpperSnakeName(lowerSnakeCaseName, upperSnakeCaseName, MAX_FILENAME_LENGTH);
78  camelCaseNameFromSnakeCaseNames(lowerSnakeCaseName, upperSnakeCaseName, camelCaseName, MAX_FILENAME_LENGTH);
79 
80  char headerPath[MAX_FILENAME_LENGTH];
81  size_t pathLength = strlen(inputPath);
82  strcpy(headerPath, inputPath);
83  // Replace the .png extension with a .h extension
84  headerPath[pathLength-3] = 'h';
85  headerPath[pathLength-2] = 0;
86 
87  char implementationPath[MAX_FILENAME_LENGTH];
88  strcpy(implementationPath, inputPath);
89  // Replace the .png extension with a .cpp extension
90  implementationPath[pathLength-3] = 'c';
91  implementationPath[pathLength-2] = 'p';
92  implementationPath[pathLength-1] = 'p';
93 
94  FILE * header = fopen(headerPath, "w");
95  generateHeaderFromImage(header, upperSnakeCaseName, camelCaseName);
96  fclose(header);
97 
98  FILE * implementation = fopen(implementationPath, "w");
99  generateImplementationFromImage(implementation, lowerSnakeCaseName, camelCaseName, width, height, rowPointers);
100  fclose(implementation);
101 
102  fclose(inputFile);
103 }
104 
105 void fileNameToSnakeCaseName(const char * fileName, char * snakeCaseName, size_t maxLength) {
106  for (int i=0; i<maxLength; i++) {
107  snakeCaseName[i] = fileName[i];
108  if (fileName[i] == '.') {
109  snakeCaseName[i] = 0;
110  break;
111  }
112  }
113  snakeCaseName[maxLength-1] = 0;
114 }
115 
116 void snakeCaseNameToUpperSnakeName(const char * snakeCaseName, char * upperSnakeCaseName, size_t maxLength) {
117  for (int i=0; i<maxLength; i++) {
118  upperSnakeCaseName[i] = toupper(snakeCaseName[i]);
119  }
120 }
121 
122 void camelCaseNameFromSnakeCaseNames(const char * snakeCaseName, const char * upperSnakeCaseName, char * camelCaseName, size_t maxLength) {
123  int j=0;
124  for (int i=0; i<maxLength; i++) {
125  char nextLetter = snakeCaseName[i];
126  if (nextLetter == '_') {
127  continue;
128  }
129  if (i==0 || snakeCaseName[i-1] == '_') {
130  nextLetter = upperSnakeCaseName[i];
131  }
132  camelCaseName[j++] = nextLetter;
133  }
134  camelCaseName[j] = 0;
135 }
136 
137 void generateHeaderFromImage(FILE * file, const char * guardian, const char * variable) {
138  fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
139  fprintf(file, "#ifndef IMAGE_STORE_%s_H\n", guardian);
140  fprintf(file, "#define IMAGE_STORE_%s_H\n\n", guardian);
141  fprintf(file, "#include <escher/image.h>\n\n");
142  fprintf(file, "namespace ImageStore {\n\n");
143  fprintf(file, "extern const Image * %s;\n\n", variable);
144  fprintf(file, "};\n\n");
145  fprintf(file, "#endif\n");
146 }
147 
148 void generateImplementationFromImage(FILE * file, const char * header, const char * variable, uint32_t width, uint32_t height, png_bytep * pixelsRowPointers) {
149  fprintf(file, "// This file is auto-generated by Inliner. Do not edit manually.\n");
150  fprintf(file, "#include \"%s.h\"\n\n", header);
151  fprintf(file, "#define P(c) KDColor::RGB24(c)\n\n");
152  fprintf(file, "const KDColor pixels[] = {");
153  for (int j=0; j<height; j++) {
154  png_bytep pixelRow = pixelsRowPointers[j];
155  for (int i=0; i<width; i++) {
156  if ((i+j*width) % 6 == 0) {
157  fprintf(file, "\n ");
158  } else {
159  fprintf(file, " ");
160  }
161  png_bytep pixel = &(pixelRow[i*4]);
162  double red = pixel[0]/255.0;
163  double green = pixel[1]/255.0;
164  double blue = pixel[2]/255.0;
165  double alpha = pixel[3]/255.0;
166  // Assume a white background (1.0, 1.0, 1.0) in the blending
167  double blendedRed = red*alpha + 1.0*(1.0-alpha);
168  double blendedGreen = green*alpha + 1.0*(1.0-alpha);
169  double blendedBlue = blue*alpha + 1.0*(1.0-alpha);
170  fprintf(file, "P(0x%02X%02X%02X)", (int)(blendedRed*0xFF), (int)(blendedGreen*0xFF), (int)(blendedBlue*0xFF));
171  if (i!=width-1 || j!= height-1) {
172  fprintf(file, ",");
173  }
174  }
175  }
176  fprintf(file, "\n};\n\n");
177  fprintf(file, "constexpr Image image = Image(%d, %d, pixels);\n\n", width, height);
178  fprintf(file, "const Image * ImageStore::%s = &image;\n", variable);
179 }
#define ERROR_IF(cond, message)
Definition: inliner.c:17
void generateHeaderFromImage(FILE *file, const char *guardian, const char *variable)
Definition: inliner.c:137
int main(int argc, char *argv[])
Definition: inliner.c:29
void camelCaseNameFromSnakeCaseNames(const char *snakeCaseName, const char *upperSnakeCaseName, char *camelCaseName, size_t maxLength)
Definition: inliner.c:122
size_t strlen(const char *s)
Definition: strlen.c:3
unsigned int uint32_t
Definition: stdint.h:6
#define NULL
Definition: stddef.h:4
void * malloc(size_t size)
Definition: malloc.c:44
void FILE
Definition: stdio.h:8
#define MAX_FILENAME_LENGTH
Definition: inliner.c:18
void generateImplementationFromImage(FILE *file, const char *header, const char *variable, uint32_t width, uint32_t height, png_bytep *pixelsRowPointers)
Definition: inliner.c:148
void fileNameToSnakeCaseName(const char *fileName, char *snakeCaseName, size_t maxLength)
Definition: inliner.c:105
void snakeCaseNameToUpperSnakeName(const char *snakeCaseName, char *upperSnakeCaseName, size_t maxLength)
Definition: inliner.c:116