Numworks Epsilon  1.4.1
Graphing Calculator Operating System
rasterizer.c
Go to the documentation of this file.
1 /* The font rasterizer takes a standard font and converts it into a bitmap
2  * embeddable font. In other words, it takes a .ttf in and outputs a .c/.h pair.
3  *
4  * It can also print a PNG file showing how each glyph has been rendered.
5 *
6  * Usage: rasterizer font_name glyph_width glyph_height
7  * -> Generates a .png image with the font rasterized
8  * -> Generates a .c file with the content of the font */
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <math.h>
13 #include <stdint.h>
14 
15 #include <ft2build.h>
16 #include FT_FREETYPE_H
17 
18 #include "unicode_for_symbol.h"
19 
20 #define ENSURE(action, description...) { if (!(action)) { fprintf(stderr, "Error: "); fprintf(stderr, description); fprintf(stderr, "\n"); exit(-1);}}
21 
22 // Pixel format is RGB888
23 
24 typedef struct {
25  unsigned char red;
26  unsigned char green;
27  unsigned char blue;
28 } pixel_t;
29 
30 typedef struct {
32  int width;
33  int height;
34 } image_t;
35 
36 #ifdef GENERATE_PNG
37 #include <png.h>
38 void writeImageToPNGFile(image_t * image, char * filename);
39 #endif
40 
41 #define CHARACTER_RANGE_START 0x20
42 #define CHARACTER_RANGE_END 0x7E
43 
44 #define GRID_WIDTH 19
45 #define GRID_HEIGHT 8
46 
47 #if (GRID_WIDTH*GRID_HEIGHT < (NUMBER_OF_SYMBOLS+CHARACTER_RANGE_END-CHARACTER_RANGE_START+1))
48 #error Grid too small. Consider increasing GRID_WIDTH or GRID_HEIGHT
49 #endif
50 
51 void drawGlyphInImage(FT_Bitmap * glyphBitmap, image_t * image, int x, int y);
52 
53 int main(int argc, char * argv[]) {
54  FT_Library library;
55  FT_Face face;
56  image_t bitmap_image;
57 
58  int expectedNumberOfArguments = 7;
59 #ifdef GENERATE_PNG
60  expectedNumberOfArguments = 8;
61 #endif
62  if (argc != expectedNumberOfArguments) {
63 #ifdef GENERATE_PNG
64  fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_header output_implementation output_png\n", argv[0]);
65 #else
66  fprintf(stderr, "Usage: %s font_file glyph_width glyph_height font_name output_header output_implementation\n", argv[0]);
67 #endif
68  fprintf(stderr, " font_file: Path of the font file to load\n");
69  fprintf(stderr, " glyph_width: Width of bitmap glyphs, in pixels\n");
70  fprintf(stderr, " glyph_height: Height of bitmap glyphs, in pixels\n");
71  fprintf(stderr, " font_name: name of the loaded font\n");
72  fprintf(stderr, " output_header: Name of the generated C header file\n");
73  fprintf(stderr, " output_implementation: Name of the generated C source file\n");
74 #ifdef GENERATE_PNG
75  fprintf(stderr, " output_png: Name of the generated PNG file\n");
76 #endif
77  exit(1);
78  }
79 
80  char * font_file = argv[1];
81  int requested_glyph_width = atoi(argv[2]);
82  int requested_glyph_height = atoi(argv[3]);
83  char * font_name = argv[4];
84  char * output_header = argv[5];
85  char * output_implementation = argv[6];
86 #ifdef GENERATE_PNG
87  char * output_png = argv[7];
88 #endif
89 
90  ENSURE(!FT_Init_FreeType(&library), "Initializing library");
91 
92  // 0 means we're picking the first face in the provided file
93  ENSURE(!FT_New_Face(library, font_file, 0, &face), "Loading font file %s", font_file);
94 
95  ENSURE(!FT_Set_Pixel_Sizes(face, requested_glyph_width, requested_glyph_height), "Setting face pixel size to %dx%d", requested_glyph_width, requested_glyph_height);
96 
97  /* Glyph metrics are complicated. Here are some useful links:
98  * https://www.freetype.org/freetype2/docs/glyphs/metrics.png
99  * https://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html
100  * https://www.freetype.org/freetype2/docs/reference/ft2-basic_types.html#FT_Bitmap
101  */
102  int maxWidth = 0;
103  int maxAboveBaseline = 0;
104  int maxBelowBaseline = 0;
105  for (unsigned char character = CHARACTER_RANGE_START; character <= CHARACTER_RANGE_END; character++) {
106  ENSURE(!FT_Load_Char(face, character, FT_LOAD_RENDER), "Loading character 0x%02x", character);
107  int aboveBaseline = face->glyph->bitmap_top;
108  int belowBaseline = face->glyph->bitmap.rows - face->glyph->bitmap_top;
109  int width = face->glyph->bitmap_left + face->glyph->bitmap.width;
110  if (width > maxWidth) {
111  maxWidth = width;
112  }
113  if (aboveBaseline > maxAboveBaseline) {
114  maxAboveBaseline = aboveBaseline;
115  }
116  if (belowBaseline > maxBelowBaseline) {
117  maxBelowBaseline = belowBaseline;
118  }
119  }
120  for (int charIndex = 0; charIndex < NUMBER_OF_SYMBOLS; charIndex++) {
121  wchar_t wideChar = codePointForSymbol[charIndex];
122  ENSURE(!FT_Load_Char(face, wideChar, FT_LOAD_RENDER), "Loading character 0x%02x", wideChar);
123  int aboveBaseline = face->glyph->bitmap_top;
124  int belowBaseline = face->glyph->bitmap.rows - face->glyph->bitmap_top;
125  int width = face->glyph->bitmap_left + face->glyph->bitmap.width;
126  if (width > maxWidth) {
127  maxWidth = width;
128  }
129  if (aboveBaseline > maxAboveBaseline) {
130  maxAboveBaseline = aboveBaseline;
131  }
132  if (belowBaseline > maxBelowBaseline) {
133  maxBelowBaseline = belowBaseline;
134  }
135  }
136 
137  int glyph_width = maxWidth-1;
138  int glyph_height = maxAboveBaseline+maxBelowBaseline;
139  //printf("Actual glyph size = %dx%d\n", glyph_width, glyph_height);
140 
141  int grid_size = 1;
142 
143  bitmap_image.width = GRID_WIDTH*glyph_width+(GRID_WIDTH-1)*grid_size;
144  bitmap_image.height = GRID_HEIGHT*glyph_height+(GRID_HEIGHT-1)*grid_size;
145  bitmap_image.pixels = malloc(sizeof(pixel_t)*bitmap_image.width*bitmap_image.height);
146  ENSURE(bitmap_image.pixels != NULL, "Allocating bitmap image of size %dx%d at %ld bytes per pixel", bitmap_image.width, bitmap_image.height, sizeof(pixel_t));
147 
148  // Draw the grid and the background
149  for (int i = 0; i<bitmap_image.width;i++) {
150  for (int j = 0; j<bitmap_image.height;j++) {
151  pixel_t pixel = {.red = 0xFF, .green = 0xFF, .blue = 0xFF};
152  if (i%(glyph_width+grid_size) >= glyph_width || j%(glyph_height+grid_size) >= glyph_height) {
153  pixel = (pixel_t){.red = 0xFF, .green = 0, .blue = 0};
154  }
155  *(bitmap_image.pixels + j*bitmap_image.width + i) = pixel;
156  }
157  }
158 
159  // We're doing the ASCII table, so characters from 0 to 255 inclusive
160  for (unsigned char character = CHARACTER_RANGE_START; character <= CHARACTER_RANGE_END; character++) {
161  int x = (character-CHARACTER_RANGE_START)%(GRID_WIDTH);
162  int y = (character-CHARACTER_RANGE_START)/(GRID_WIDTH);
163  // FT_LOAD_RENDER: Render the glyph upon load
164  ENSURE(!FT_Load_Char(face, character, FT_LOAD_RENDER), "Loading character 0x%02x", character);
165  //printf("Advances = %dx%d\n", face->glyph->bitmap_left, face->glyph->bitmap_top);
166  drawGlyphInImage(&face->glyph->bitmap,
167  &bitmap_image,
168  x*(glyph_width+grid_size) + face->glyph->bitmap_left,
169  y*(glyph_height+grid_size) + maxAboveBaseline - face->glyph->bitmap_top
170  );
171  }
172  // We are now using unicode to access non-ASCII characters
173  for (int charIndex = 0; charIndex < NUMBER_OF_SYMBOLS; charIndex++) {
174  wchar_t wideChar = codePointForSymbol[charIndex];
175  int x = (charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)%(GRID_WIDTH);
176  int y = (charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)/(GRID_WIDTH);
177  // FT_LOAD_RENDER: Render the glyph upon load
178  ENSURE(!FT_Load_Char(face, wideChar, FT_LOAD_RENDER), "Loading character 0x%02x", wideChar);
179  //printf("Advances = %dx%d\n", face->glyph->bitmap_left, face->glyph->bitmap_top);
180  drawGlyphInImage(&face->glyph->bitmap,
181  &bitmap_image,
182  x*(glyph_width+grid_size) + face->glyph->bitmap_left,
183  y*(glyph_height+grid_size) + maxAboveBaseline - face->glyph->bitmap_top
184  );
185  }
186 
187 #if GENERATE_PNG
188  writeImageToPNGFile(&bitmap_image, output_png);
189 #endif
190 
191  FILE * headerFile = fopen(output_header, "w");
192  fprintf(headerFile, "/* Auto-generated by rasterizer */\n\n");
193  fprintf(headerFile, "#define BITMAP_%s_FIRST_CHARACTER 0x%2x\n", font_name, CHARACTER_RANGE_START);
194  fprintf(headerFile, "#define BITMAP_%s_LAST_CHARACTER 0x%2x\n\n", font_name, CHARACTER_RANGE_END+NUMBER_OF_SYMBOLS);
195  fprintf(headerFile, "#define BITMAP_%s_CHARACTER_WIDTH %d\n", font_name, glyph_width);
196  fprintf(headerFile, "#define BITMAP_%s_CHARACTER_HEIGHT %d\n\n", font_name, glyph_height);
197  fprintf(headerFile, "extern const unsigned char bitmap%s[%d][%d][%d];\n", font_name, NUMBER_OF_SYMBOLS+CHARACTER_RANGE_END-CHARACTER_RANGE_START+1, glyph_height, glyph_width);
198  fclose(headerFile);
199 
200  FILE * sourceFile = fopen(output_implementation, "w");
201  fprintf(sourceFile, "/* Auto-generated by rasterizer */\n\n");
202  fprintf(sourceFile, "const unsigned char bitmap%s[%d][%d][%d] = {\n", font_name, NUMBER_OF_SYMBOLS+CHARACTER_RANGE_END-CHARACTER_RANGE_START+1, glyph_height, glyph_width);
203  for (unsigned char character = CHARACTER_RANGE_START; character <= CHARACTER_RANGE_END; character++) {
204  fprintf(sourceFile, " {\n");
205  int characterX = ((character-CHARACTER_RANGE_START)%GRID_WIDTH * (glyph_width+grid_size));
206  int characterY = ((character-CHARACTER_RANGE_START)/GRID_WIDTH * (glyph_height+grid_size));
207  for (int y = 0; y < glyph_height; y++) {
208  fprintf(sourceFile, " {");
209  for (int x = 0; x < glyph_width; x++) {
210  pixel_t * pixel = (bitmap_image.pixels + (y+characterY)*bitmap_image.width + (x+characterX));
211  fprintf(sourceFile, "0x%02x", 0xFF - pixel->green);
212  if (x+1 < glyph_width) {
213  fprintf(sourceFile, ", ");
214  }
215  }
216  fprintf(sourceFile, "}");
217  if (y+1 < glyph_height) {
218  fprintf(sourceFile, ",");
219  }
220  fprintf(sourceFile, "\n");
221  }
222  fprintf(sourceFile, " }");
223  fprintf(sourceFile, ",");
224  fprintf(sourceFile, "\n");
225  }
226  for (int charIndex = 0; charIndex < NUMBER_OF_SYMBOLS; charIndex++) {
227  wchar_t wideChar = codePointForSymbol[charIndex];
228  fprintf(sourceFile, " {\n");
229  int characterX = ((charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)%GRID_WIDTH * (glyph_width+grid_size));
230  int characterY = ((charIndex+1+CHARACTER_RANGE_END-CHARACTER_RANGE_START)/GRID_WIDTH * (glyph_height+grid_size));
231  for (int y = 0; y < glyph_height; y++) {
232  fprintf(sourceFile, " {");
233  for (int x = 0; x < glyph_width; x++) {
234  pixel_t * pixel = (bitmap_image.pixels + (y+characterY)*bitmap_image.width + (x+characterX));
235  fprintf(sourceFile, "0x%02x", 0xFF - pixel->green);
236  if (x+1 < glyph_width) {
237  fprintf(sourceFile, ", ");
238  }
239  }
240  fprintf(sourceFile, "}");
241  if (y+1 < glyph_height) {
242  fprintf(sourceFile, ",");
243  }
244  fprintf(sourceFile, "\n");
245  }
246  fprintf(sourceFile, " }");
247  if (charIndex < NUMBER_OF_SYMBOLS - 1) {
248  fprintf(sourceFile, ",");
249  }
250  fprintf(sourceFile, "\n");
251  }
252 
253  fprintf(sourceFile, "};\n");
254  fclose(sourceFile);
255 
256  free(bitmap_image.pixels);
257 
258  return 0;
259 }
260 
261 void drawGlyphInImage(FT_Bitmap * glyphBitmap, image_t * image, int x, int y) {
262  // printf("Drawing glyph. Size is %dx%d, pitch is %d\n", glyphBitmap->width, glyphBitmap->rows, glyphBitmap->pitch);
263  ENSURE(glyphBitmap->pixel_mode == FT_PIXEL_MODE_GRAY, "Checking glyph is in FT_PIXEL_MODE_GRAY");
264  for (int j=0;j<glyphBitmap->rows;j++) {
265  for (int i=0;i<glyphBitmap->width;i++) {
266  uint8_t glyphPixel = *(glyphBitmap->buffer + j*glyphBitmap->pitch + i);
267  pixel_t * currentPixelPointer = (image->pixels + (y+j)*image->width + (x+i));
268  *currentPixelPointer = (pixel_t){.red = (0xFF-glyphPixel), .green = (0xFF-glyphPixel), .blue = (0xFF-glyphPixel)}; // Alpha blending
269  }
270  }
271 }
272 
273 #if GENERATE_PNG
274 
275 void writeImageToPNGFile(image_t * image, char * filename) {
276  FILE * file = fopen(filename, "wb"); // Write in binary mode
277  ENSURE(file != NULL, "Opening file %s", filename);
278 
279  png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
280  ENSURE(png != NULL, "Allocating PNG write structure");
281 
282  png_infop info = png_create_info_struct(png);
283  ENSURE(info != NULL, "Allocating info structure");
284 
285  png_init_io(png, file);
286 
287  png_set_IHDR(png, info,
288  image->width, image->height,
289  8, // Number of bits per channel
290  PNG_COLOR_TYPE_RGB,
291  PNG_INTERLACE_NONE,
292  PNG_COMPRESSION_TYPE_DEFAULT,
293  PNG_FILTER_TYPE_DEFAULT);
294 
295  png_write_info(png, info);
296 
297  for (int j=0;j<image->height;j++) {
298  png_write_row(png, (png_bytep)(image->pixels+j*image->width));
299  }
300 
301  png_write_end(png, NULL);
302 
303  png_free_data(png, info, PNG_FREE_ALL, -1); // -1 = all items
304  png_destroy_write_struct(&png, (png_infopp)NULL);
305  fclose(file);
306 }
307 #endif
#define GRID_HEIGHT
Definition: rasterizer.c:45
unsigned char green
Definition: rasterizer.c:26
#define NUMBER_OF_SYMBOLS
void drawGlyphInImage(FT_Bitmap *glyphBitmap, image_t *image, int x, int y)
Definition: rasterizer.c:261
int main(int argc, char *argv[])
Definition: rasterizer.c:53
LIBA_BEGIN_DECLS void free(void *ptr)
Definition: malloc.c:33
unsigned char red
Definition: rasterizer.c:25
unsigned char uint8_t
Definition: stdint.h:4
int height
Definition: rasterizer.c:33
#define CHARACTER_RANGE_END
Definition: rasterizer.c:42
#define ENSURE(action, description...)
Definition: rasterizer.c:20
unsigned char blue
Definition: rasterizer.c:27
pixel_t * pixels
Definition: rasterizer.c:31
#define NULL
Definition: stddef.h:4
wchar_t codePointForSymbol[NUMBER_OF_SYMBOLS]
#define GRID_WIDTH
Definition: rasterizer.c:44
void * malloc(size_t size)
Definition: malloc.c:44
int width
Definition: rasterizer.c:32
void FILE
Definition: stdio.h:8
#define CHARACTER_RANGE_START
Definition: rasterizer.c:41