381 lines
10 KiB
C
381 lines
10 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include FT_GLYPH_H
|
|
#include FT_TRUETYPE_DRIVER_H
|
|
#include FT_MODULE_H
|
|
#include <getopt.h>
|
|
|
|
#define OPCODE_REPEAT 253
|
|
#define OPCODE_EOL 254
|
|
#define OPCODE_EOC 255
|
|
|
|
#define MAX_RLE 80 /* Maximum number of repeats */
|
|
|
|
|
|
/****************************************************************************
|
|
* BYTEPOINTER FUNCTIONS
|
|
****************************************************************************/
|
|
struct histogram {
|
|
int position;
|
|
int count;
|
|
};
|
|
struct bytepointer {
|
|
unsigned char *p;
|
|
unsigned char *start;
|
|
unsigned char *end;
|
|
struct histogram histogram[256];
|
|
};
|
|
|
|
struct bitoutput {
|
|
FILE *fp;
|
|
int col;
|
|
int byte_count;
|
|
uint8_t byte;
|
|
uint8_t bit;
|
|
};
|
|
|
|
void init_bytepointer (struct bytepointer *bp, unsigned char *buffer, int n) {
|
|
int i;
|
|
bp->p = buffer;
|
|
bp->start = buffer;
|
|
bp->end = buffer + n;
|
|
for (i=0; i<256; i++) {
|
|
bp->histogram[i].position = i;
|
|
bp->histogram[i].count = 0;
|
|
}
|
|
}
|
|
|
|
void save_byte (struct bytepointer *bp, unsigned char byte) {
|
|
if (bp->p == bp->end) {
|
|
printf ("OUT OF BITPOINTER MEMORY.\n");
|
|
exit (2);
|
|
}
|
|
|
|
// If byte is more than MAX_RLE, break into MAX_RLE, 0, remainder
|
|
while (byte < OPCODE_REPEAT && byte > MAX_RLE) {
|
|
* (bp->p++) = MAX_RLE;
|
|
* (bp->p++) = 0;
|
|
byte -= MAX_RLE;
|
|
}
|
|
* (bp->p++) = byte;
|
|
bp->histogram[byte].count++;
|
|
}
|
|
|
|
int histcmp (const void *p, const void *q) {
|
|
struct histogram *p0 = (struct histogram *) p;
|
|
struct histogram *q0 = (struct histogram *) q;
|
|
return q0->count - p0->count;
|
|
}
|
|
|
|
void save_bits (struct bitoutput *bits, int bit) {
|
|
if (bit) bits->byte |= bits->bit;
|
|
bits->bit >>= 1;
|
|
if (!bits->bit) {
|
|
if (bits->col == 12) { fprintf (bits->fp, ",\n\t"); bits->col = 0; }
|
|
else if (bits->col != 0) fprintf (bits->fp, ", ");
|
|
fprintf (bits->fp, "0x%02X", bits->byte);
|
|
bits->col++;
|
|
bits->byte_count++;
|
|
bits->bit = 0x80;
|
|
bits->byte = 0;
|
|
}
|
|
}
|
|
|
|
void output_bitstream (struct bytepointer *bp, int max_width, int scaled) {
|
|
unsigned char *p;
|
|
struct bitoutput bits;
|
|
int col, i, v, lookup[256], offset, offsets[96];
|
|
|
|
qsort (bp->histogram, 256, sizeof (struct histogram), histcmp);
|
|
for (i=0; i<256; i++) {
|
|
lookup[bp->histogram[i].position] = i;
|
|
}
|
|
|
|
bits.fp = fopen ("font.h", "w");
|
|
bits.byte = 0;
|
|
bits.bit = 0x80;
|
|
bits.col = 0;
|
|
bits.byte_count = 0;
|
|
|
|
offset = 0;
|
|
offsets[0] = 0;
|
|
|
|
fprintf (bits.fp,
|
|
"/********************************************************************\n"
|
|
"* Font File format:\n"
|
|
"* 2 bits: =0, opcode lut index 0\n"
|
|
"* =1, opcode lut index is in next 3 bits + 1\n"
|
|
"* =2, opcode lut index is in next 4 bits + 9\n"
|
|
"* =3, opcode lut index is in next 6 bits + 25\n"
|
|
"********************************************************************/\n");
|
|
fprintf (bits.fp, "unsigned char mfont[] = {\n\t");
|
|
for (p = bp->start; p <= bp->p; p++) {
|
|
v = lookup[*p];
|
|
if (v == 0) {
|
|
save_bits (&bits, 0);
|
|
save_bits (&bits, 0);
|
|
} else if (v < 9) {
|
|
v = v-1;
|
|
save_bits (&bits, 0);
|
|
save_bits (&bits, 1);
|
|
save_bits (&bits, v & 4);
|
|
save_bits (&bits, v & 2);
|
|
save_bits (&bits, v & 1);
|
|
} else if (v < 25) {
|
|
v = v-9;
|
|
save_bits (&bits, 1);
|
|
save_bits (&bits, 0);
|
|
save_bits (&bits, v & 8);
|
|
save_bits (&bits, v & 4);
|
|
save_bits (&bits, v & 2);
|
|
save_bits (&bits, v & 1);
|
|
} else {
|
|
v = v-25;
|
|
save_bits (&bits, 1);
|
|
save_bits (&bits, 1);
|
|
save_bits (&bits, v & 32);
|
|
save_bits (&bits, v & 16);
|
|
save_bits (&bits, v & 8);
|
|
save_bits (&bits, v & 4);
|
|
save_bits (&bits, v & 2);
|
|
save_bits (&bits, v & 1);
|
|
}
|
|
if (*p == OPCODE_EOC) {
|
|
while (bits.bit != 0x80) save_bits (&bits, 0);
|
|
offsets[++offset] = bits.byte_count;
|
|
}
|
|
|
|
}
|
|
fprintf (bits.fp, "};\n");
|
|
|
|
fprintf (bits.fp, "unsigned char flut[] = {\n\t");
|
|
for (i=0, col=0; bp->histogram[i].count; i++) {
|
|
if (col == 14) { fprintf (bits.fp, ",\n\t"); col = 0; }
|
|
else if (col != 0) fprintf (bits.fp, ", ");
|
|
fprintf (bits.fp, "%3d", bp->histogram[i].position);
|
|
col++;
|
|
bits.byte_count++;
|
|
}
|
|
fprintf (bits.fp, "};\n");
|
|
|
|
fprintf (bits.fp, "unsigned int foffs[] = {\n\t");
|
|
for (i=0, col=0; i<offset; i++) {
|
|
if (col == 10) { fprintf (bits.fp, ",\n\t"); col = 0; }
|
|
else if (col != 0) fprintf (bits.fp, ", ");
|
|
fprintf (bits.fp, "%5d", offsets[i]);
|
|
bits.byte_count += 2;
|
|
col++;
|
|
}
|
|
fprintf (bits.fp, "};\n\n");
|
|
|
|
|
|
if (scaled) max_width += max_width;
|
|
fprintf (bits.fp, "#define FONT_MAXWIDTH %d\n", max_width);
|
|
fprintf (bits.fp, "#define FONT_DOUBLED %d\n\n", scaled);
|
|
fprintf (bits.fp, "#define OPCODE_REPEAT %d\n", OPCODE_REPEAT);
|
|
fprintf (bits.fp, "#define OPCODE_EOL %d\n", OPCODE_EOL);
|
|
fprintf (bits.fp, "#define OPCODE_EOC %d\n", OPCODE_EOC);
|
|
|
|
printf ("%d bytes.\n", bits.byte_count);
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char *argv[]) {
|
|
int error;
|
|
FT_Library library;
|
|
FT_Face face;
|
|
FT_GlyphSlot slot;
|
|
FT_Glyph glyph;
|
|
FT_Bitmap *bitmap;
|
|
int advance, byte, c, x, y, y0, v, offset;
|
|
uint8_t bit;
|
|
int max_decender = 0;
|
|
int max_width = 0;
|
|
|
|
struct bytepointer bp;
|
|
unsigned char buffer[48*1024];
|
|
int color, scaled, verbose;
|
|
char *font = NULL;
|
|
int ch, width, height;
|
|
|
|
struct option longopts[] = {
|
|
{ "font", required_argument, NULL, 'f' },
|
|
{ "width", required_argument, NULL, 'w' },
|
|
{ "double", no_argument, &scaled, 1 },
|
|
{ "verbose", no_argument, &verbose, 1 },
|
|
{ NULL, 0, NULL, 0}
|
|
};
|
|
|
|
width = 80;
|
|
while ( (ch = getopt_long (argc, argv, "f:h:", longopts, NULL)) != -1) {
|
|
switch (ch) {
|
|
case 'f':
|
|
font = optarg;
|
|
break;
|
|
case 'w':
|
|
width = atoi (optarg);
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
fprintf (stderr, "mkfont: --font FONTNAME [--width HEIGHT]\n");
|
|
exit (2);
|
|
}
|
|
}
|
|
|
|
if (!font) {
|
|
fprintf (stderr, "No font specified. Use --font option\n");
|
|
exit (2);
|
|
}
|
|
|
|
/* Adjust width to account for 0 base */
|
|
if (scaled) width /= 2;
|
|
width--;
|
|
height = width * 6 / 10;
|
|
|
|
init_bytepointer (&bp, buffer, sizeof (buffer));
|
|
|
|
if ( (error = FT_Init_FreeType (&library))) {
|
|
fprintf (stderr, "Error initializing FreeType");
|
|
exit (2);
|
|
}
|
|
|
|
FT_UInt version = TT_INTERPRETER_VERSION_35;
|
|
FT_Property_Set (library, "truetype", "interpreter-version",
|
|
&version);
|
|
|
|
if ( (error = FT_New_Face (library, font, 0, &face))) {
|
|
fprintf (stderr, "Error loading Face");
|
|
exit (2);
|
|
}
|
|
|
|
// Height, width reversed below because characters are rotated
|
|
if ( (error = FT_Set_Pixel_Sizes (face, height, width))) {
|
|
fprintf (stderr, "Error setting pixel size");
|
|
exit (2);
|
|
}
|
|
|
|
/* Find maximum decender and width */
|
|
for (c=32; c < 127; c++) {
|
|
if ( (error = FT_Load_Char (face, c, FT_LOAD_TARGET_MONO))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
slot = face->glyph;
|
|
if ( (error = FT_Render_Glyph (slot, FT_RENDER_MODE_MONO))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
if ( (error = FT_Get_Glyph (slot, &glyph))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
bitmap = &slot->bitmap;
|
|
if ( (int) (slot->bitmap_top - bitmap->rows) < max_decender) {
|
|
max_decender = slot->bitmap_top - bitmap->rows;
|
|
}
|
|
if (bitmap->rows > max_width)
|
|
max_width = bitmap->rows;
|
|
}
|
|
max_width++;
|
|
|
|
/* Generate glyphs and encode */
|
|
for (c=32; c < 127; c++) {
|
|
if ( (error = FT_Load_Char (face, c, FT_LOAD_TARGET_MONO))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
slot = face->glyph;
|
|
if ( (error = FT_Render_Glyph (slot, FT_RENDER_MODE_MONO))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
if ( (error = FT_Get_Glyph (slot, &glyph))) {
|
|
fprintf (stderr, "Error loading char");
|
|
exit (2);
|
|
}
|
|
|
|
bitmap = &slot->bitmap;
|
|
advance = slot->bitmap_left;
|
|
if (advance < 0) advance = 0;
|
|
|
|
color = 0;
|
|
for (; advance > 0; advance--) {
|
|
save_byte (&bp, OPCODE_EOL);
|
|
if (verbose) printf ("\n");
|
|
}
|
|
for (x=0; x < bitmap->width; x++) {
|
|
int repeat = 0;
|
|
|
|
// Check for repeating line
|
|
if (x > 0) {
|
|
int v0;
|
|
for (y = bitmap->rows-1; y >= 0; y--) {
|
|
byte = (x-1) /8;
|
|
bit = 0x80 >> ( (x-1) &7);
|
|
v0 = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0;
|
|
byte = x/8;
|
|
bit = 0x80 >> (x&7);
|
|
v = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0;
|
|
if (v0 != v)
|
|
break;
|
|
}
|
|
if (y < 0) repeat = 1;
|
|
}
|
|
// Find end of spaces
|
|
for (y0=0; y0<bitmap->rows; y0++) {
|
|
byte = x/8;
|
|
bit = 0x80 >> (x&7);
|
|
v = (bitmap->buffer[y0*bitmap->pitch + byte]&bit) ? 1: 0;
|
|
if (v) break;
|
|
}
|
|
offset = -max_decender+ (slot->bitmap_top - bitmap->rows);
|
|
if (y0 == bitmap->rows) offset = 0;
|
|
if (verbose) {
|
|
for (y=0; y<offset; y++) {
|
|
printf (" ");
|
|
}
|
|
}
|
|
for (y=bitmap->rows-1; y>=y0; y--) {
|
|
byte = x/8;
|
|
bit = 0x80 >> (x&7);
|
|
v = (bitmap->buffer[y*bitmap->pitch + byte]&bit) ? 1: 0;
|
|
if (verbose) {
|
|
if (v)
|
|
printf (repeat ? "*" : "#");
|
|
else
|
|
printf (" ");
|
|
}
|
|
|
|
if (v==color) offset++;
|
|
else {
|
|
if (!repeat) save_byte (&bp, offset);
|
|
color ^= 1;
|
|
offset = 1;
|
|
}
|
|
}
|
|
if (repeat) {
|
|
save_byte (&bp, OPCODE_REPEAT);
|
|
} else {
|
|
save_byte (&bp, offset);
|
|
save_byte (&bp, OPCODE_EOL);
|
|
}
|
|
color = 0;
|
|
if (verbose) printf ("\n");
|
|
}
|
|
advance = (slot->advance.x >> 6) - bitmap->width -
|
|
( (slot->bitmap_left > 0) ? slot->bitmap_left : 0);
|
|
if (advance < 0) advance = 0;
|
|
for (; advance > 0; advance--) {
|
|
save_byte (&bp, OPCODE_EOL);
|
|
if (verbose) printf ("\n");
|
|
}
|
|
save_byte (&bp, OPCODE_EOC);
|
|
FT_Done_Glyph (glyph);
|
|
}
|
|
output_bitstream (&bp, max_width, scaled);
|
|
}
|