Last active
February 6, 2022 23:48
-
-
Save MZachmann/cd64267ede0f3011e55bd94d33c2ca80 to your computer and use it in GitHub Desktop.
Python Code For Bitmap Font Translation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# this is a set of functions to convert from a bitmap image and fnt to | |
# C++ compatible data streams for use by LargeFont | |
import png | |
# This requires a variable named font, which is the font name font='Arial11' | |
Basepath = '/users/mark/my documents/' | |
font = 'Arial11' | |
ClipPng(font) | |
FntToInfo(font) | |
# ------------------------------------------------------- | |
# read the png file, crop it and write it out as hex | |
# remove the black lines top and bottom | |
# crop any black vertical byte-lines from the right | |
# then write the output as hex with one line header | |
# ------------------------------------------------------- | |
def ClipPng(font) : | |
img = png.Reader(Basepath + font + '_0.png') | |
height,width,imgmap,imgmeta = img.asDirect() | |
imgdata = [pi for pi in imgmap] # pixel data as array of arrays of pixel values (bytes) | |
bitdata = [] | |
# convert pixel bytes into bit data | |
imglen = len(imgdata) | |
for i in range(imglen) : | |
bitline = [] | |
bitval = 0x80 | |
bitout = 0 | |
for j in range(len(imgdata[i])) : | |
if(imgdata[i][j]) : | |
bitout = bitout | bitval | |
bitval = bitval >> 1 | |
if(0 == bitval) : | |
bitline.append(bitout) | |
bitval = 0x80 | |
bitout = 0 | |
# leftover data? | |
if(bitval != 0x80) : | |
bitline.append(bitout) | |
bitdata.append(bitline) | |
# | |
# now bitdata is the imgdata but as bits with the last byte rounded up | |
fndx = -1; | |
# crop top and bottom that have no data | |
for i in range(imglen) : | |
x = [im for im in bitdata[i] if im != 0] # find non-zero elements | |
if(len(x)>0) : | |
fndx = i | |
break | |
# | |
for i in range(imglen) : | |
x = [im for im in bitdata[imglen-i-1] if im != 0] # find non-zero elements | |
if(len(x)>0) : | |
fndendx = imglen-i | |
break | |
# | |
imgfnd = bitdata[fndx:fndendx] # these lines have data | |
# find the byte to clip at on the right. keep the left | |
imglen = len(imgfnd) | |
maxx = 0; | |
for i in range(imglen) : | |
rowlen = len(imgfnd[i]) | |
for j in range(rowlen) : | |
if(imgfnd[i][rowlen-j-1] != 0) : | |
if(maxx < rowlen-j) : | |
maxx = rowlen-j | |
break | |
# | |
# clip on the right and create a flat stream of bytes | |
imgstream = [] | |
for i in range(imglen) : | |
imgstream.extend(imgfnd[i][0:maxx]) | |
# | |
# finally we have the clipped data, write it out to file as hex data | |
outp = open(Basepath + font + '_0.hex', 'w') | |
outp.write('// Image Size: ' + str(maxx) + 'bytes x ' + str(imglen) + 'lines\n') | |
outp.write('{\n') | |
idh = ['0x'+'%02x' % x + ',' for x in imgstream] | |
for i in range(len(idh)-1) : | |
outp.write(idh[i]) | |
if(0 == (1+i)%8) : | |
outp.write('\n') | |
# write last one without comma | |
ido = '0x'+'%02x' % imgstream[len(idh)-1] | |
outp.write(ido) | |
outp.write('\n};\n') | |
outp.close() | |
# ------------------------------------------------------- | |
# convert the fnt file to a list of structures (.info) | |
# ------------------------------------------------------- | |
def FntToInfo(font) : | |
textdesc = open(Basepath + font + '.fnt','r') | |
textdout = open(Basepath + font + '.info', 'w') | |
dlines = textdesc.readlines() | |
textdesc.close() | |
dlines = dlines[4::] # remove header stuff | |
textdout.write('{ ') | |
for i in range(len(dlines)) : | |
x = dlines[i].split('=') # one line split by equals | |
textdout.write('{ ' + x[1].split(' ')[0] + ', ') | |
textdout.write(x[2].split(' ')[0] + ', ') | |
textdout.write(x[4].split(' ')[0] + ', ') | |
textdout.write(x[6].split(' ')[0] + ', ') | |
textdout.write(x[8].split(' ')[0] + '}, \n') | |
# | |
textdout.write('{ 0,0,0,0,0} }\n') | |
textdout.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "application.h" | |
// this typeface has a list of descriptors and a list of pixel data (bytes) | |
#include "FontArial11.h" | |
// THIS IS JUST A PIECE OF THE FILE FOR DEMO PURPOSES. Note the ... below. | |
// Arial 11 support (actually 16 high) | |
static struct CharacterInfo Arial11Infos[] = | |
{ { 33, 676, 1, 2, 5}, | |
{ 36, 519, 7, 1, 9}, | |
{ 37, 73, 12, 1, 14}, | |
{ 40, 666, 3, 1, 5}, | |
{ 41, 670, 3, 1, 5}, | |
{ 42, 625, 5, 0, 6}, | |
{ 43, 551, 7, 1, 9}, | |
... | |
{ 153, 46, 13, 2, 12}, | |
{ 169, 86, 12, 0, 12}, | |
{ 186, 631, 5, 0, 5}, | |
{ 188, 99, 12, 1, 13}, | |
{ 189, 60, 12, 1, 13}, | |
{ 190, 32, 13, 0, 13}, | |
{ 0,0,0,0,0} | |
}; | |
// Image Size: 86bytes x 16lines | |
static uint8_t Arial11Pixels[]= | |
{ | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
... | |
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | |
0x00,0x00,0x00,0x00,0x03,0x0a,0x00,0x00 | |
}; | |
// bitmap is 688x15 | |
LargeFont* CreateFontArial11() | |
{ | |
struct FontInfo fif = { | |
Arial11Infos, //struct CharacterInfo* CharacterDescriptors; | |
Arial11Pixels, //uint8_t* PixelData; | |
16, //int Height; // number of scan lines | |
86 //int Width; // width in bytes of the data stream | |
}; | |
return new LargeFont(&fif); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef FONT_ARIAL11_H | |
#define FONT_ARIAL11_H | |
#include "LargeFont.h" | |
LargeFont* CreateFontArial11(); // this constructor | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// --------------------------------------------------------------------- | |
// to get a large font... | |
// first use bitmap font generator (bmfont64) from http://www.angelcode.com/products/bmfont/ | |
// select the chars you want and create a long bitmap (2048x512). Airal, Ansi, 48px high, no kerning, render from truetype | |
// export the glyph into the A field (8 bits) as png | |
// doing Export to bitmap will create two files. One metadata i use the fnt suffix for and one glyph data as png | |
// then run the methods in BmptoHex.py to create data structures from the png and the fnt output | |
// essentially convert the metadata to a list of CharacterDescriptors | |
// and crop the bitmap top/bottom/right then print it as hex | |
// --------------------------------------------------------------------- | |
// These methods are pretty simple: | |
// (a) construct the font and then create a 256 character lookup table | |
// (b) get a pixel from the character glyph | |
#include "LargeFont.h" | |
static bool diagnoseFont = false; | |
// print to the serial device for debugging | |
static void Serialptf(const char* view) | |
{ | |
if(diagnoseFont) | |
{ | |
Serial.println(view); | |
} | |
} | |
// Constructor | |
LargeFont::LargeFont(const struct FontInfo* pFif) | |
{ | |
CharacterDescriptors = pFif->CharacterDescriptors; | |
PixelData = pFif->PixelData; | |
Width = pFif->Width; | |
Height = pFif->Height; | |
CreateCharLookup(); | |
Serialptf("Created Char Lookup A"); | |
} | |
// not really used by the code | |
LargeFont::LargeFont(int width, int height, struct CharacterInfo* chardesc, uint8_t* Pixels) | |
{ | |
CharacterDescriptors = chardesc; | |
PixelData = Pixels; | |
Width = width; | |
Height = height; | |
CreateCharLookup(); | |
Serialptf("Created Char Lookup B"); | |
} | |
// for debugging print a CharacterInfo verbose | |
void PrettyDescriptor(CharacterInfo* pinfo) | |
{ | |
if(diagnoseFont) | |
{ | |
char js[220]; | |
sprintf(js, "Descriptor: %c Xpos=%d, Width=%d, Offset=%d, Advance=%d", pinfo->Thechar, pinfo->Xpos, (int)pinfo->Width, (int)pinfo->Offset, (int)pinfo->Advance); | |
Serialptf(js); | |
} | |
} | |
// create the 256 element lookup table for the CharacterInfo descriptor list (which is sparse) | |
void LargeFont::CreateCharLookup() | |
{ | |
Serialptf("Creating Char Lookup"); | |
int i; | |
for(i=0; i<256; i++) | |
{ | |
int j = 0; | |
// the one with Thechar==0 is the end of the vector and the default | |
while( CharacterDescriptors[j].Thechar != 0) | |
{ | |
if(i == CharacterDescriptors[j].Thechar) | |
{ | |
if(diagnoseFont) | |
{ | |
char js[220]; | |
sprintf(js, "Found descriptor for %c at %d", (char)i, (int)j); | |
PrettyDescriptor(CharacterDescriptors+j); | |
Serialptf(js); | |
} | |
break; | |
} | |
j++; | |
} | |
CharacterLookup[i] = &CharacterDescriptors[j]; | |
} | |
} | |
// for debugging print to serial the pixel request | |
void LargeFont::PrintGetPixel(unsigned char c, bool value, int x, int y) | |
{ | |
if(diagnoseFont) | |
{ | |
char rs[200]; | |
sprintf(rs, "Calling getpixel: %c at %d:%d was %c", c, x, y, value ? '1' : '0'); | |
Serialptf(rs); | |
} | |
} | |
// Get a pixel for this character c at (x,y) | |
bool LargeFont::GetPixel(unsigned char c, uint8_t x, uint8_t y) | |
{ | |
bool rslt = false; | |
CharacterInfo* cinfo = CharacterLookup[c]; | |
PrettyDescriptor(cinfo); | |
int dx = x - cinfo->Offset; | |
int fontWidth = Width; // width in bytes | |
// now we are within the actual pixel data | |
if(dx >= 0 && dx < cinfo->Width) | |
{ | |
int xoff = dx + cinfo->Xpos; // relative to entire bitmap | |
int byteoff = xoff/8; // which byte of the data | |
int xremain = xoff - byteoff * 8; // pixels left over | |
uint8_t bvalue = PixelData[byteoff + y*fontWidth]; | |
if(diagnoseFont) | |
{ | |
char spr[200]; | |
sprintf(spr, "Pixel data: char=%c at %d,%d xval=%d 0x%02x offset %d at %d remain %d", c, (int)x, (int)y, dx, bvalue, (byteoff + y*fontWidth), xoff, xremain); | |
Serialptf(spr); | |
} | |
if(xremain == 0) | |
{ | |
rslt = 0x80 & bvalue; | |
} | |
else | |
{ | |
rslt = (0x80 >> xremain) & bvalue; | |
} | |
} | |
PrintGetPixel(c, rslt, x, y); | |
return rslt; | |
} | |
// Get a pixel line for this character c at (x,y) optimized | |
bool LargeFont::GetPixelLine(unsigned char* pc, unsigned char c, uint8_t y) | |
{ | |
CharacterInfo* cinfo = CharacterLookup[c]; | |
// int dx = x - cinfo->Offset; | |
// now we are within the actual pixel data | |
int cwidth = cinfo->Width; | |
int xoff = cinfo->Xpos; | |
int byteoff = xoff / 8; | |
uint8_t xremain = 0x80 >> (xoff - byteoff * 8); | |
byteoff += y * Width; // move to nth scan line | |
for(int dx = 0; dx < cwidth; dx++) | |
{ | |
// int xoff = dx + cinfo->Xpos; // relative to entire bitmap | |
// int byteoff = xoff/8; // which byte of the data | |
// int xremain = xoff - byteoff * 8; // pixels left over | |
pc[dx] = xremain & PixelData[byteoff]; | |
xremain = xremain >> 1; | |
if(xremain == 0) | |
{ | |
xremain = 0x80; | |
byteoff++; | |
} | |
} | |
return cwidth; | |
} | |
int LargeFont::GetWidth(unsigned char c) | |
{ | |
CharacterInfo* cinfo = CharacterLookup[c]; | |
return cinfo->Width; // how much to move for each character | |
} | |
int LargeFont::GetOffset(unsigned char c) | |
{ | |
CharacterInfo* cinfo = CharacterLookup[c]; | |
return cinfo->Offset; // how much to move for each character | |
} | |
int LargeFont::GetAdvance(unsigned char c) | |
{ | |
CharacterInfo* cinfo = CharacterLookup[c]; | |
return cinfo->Advance; // how much to move for each character | |
} | |
int LargeFont::GetHeight() | |
{ | |
return Height; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _LARGEFONT_H | |
#define _LARGEFONT_H | |
#include "application.h" | |
// for each character here's what we care about | |
struct CharacterInfo | |
{ | |
char Thechar; // the character | |
int Xpos; // pixel where it starts in the image | |
unsigned char Width; // width of char glyph in pixels | |
unsigned char Offset; // left blank space in pixels when drawing it | |
unsigned char Advance; // total width of char in pixels when printed | |
}; | |
// this is the structure used to initialize the large font | |
struct FontInfo | |
{ | |
struct CharacterInfo* CharacterDescriptors; // array of character descriptors for this font. terminated with Thechar==0 entry | |
uint8_t* PixelData; // a stream of the pixel data | |
int Height; // number of scan lines | |
int Width; // width in bytes of the data stream | |
}; | |
// This draws the font on screen | |
class LargeFont | |
{ | |
private: | |
// the FontInfo information | |
struct CharacterInfo* CharacterDescriptors; | |
uint8_t* PixelData; | |
int Height; // number of scan lines | |
int Width; // width in bytes of the data stream | |
public: | |
bool GetPixel(unsigned char c, uint8_t x, uint8_t y); // read a pixel at x,y for character c | |
void PrintGetPixel(unsigned char c, bool value, int x, int y); // for diagnostics print the pixel value | |
int GetWidth(unsigned char c); // return character width in pixels | |
int GetHeight(); // get height of font (fixed) | |
LargeFont(int Width, int Height, struct CharacterInfo* chardesc, uint8_t* Pixels); | |
LargeFont(const struct FontInfo* pFif); | |
private: | |
struct CharacterInfo* CharacterLookup[256]; // lookup table for each character's info | |
void CreateCharLookup(); // on construction build the lookup table | |
}; | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// only changed GFX code for display | |
// write a string | |
size_t Adafruit_GFX::write(uint8_t c) { | |
if (c == '\n') { | |
cursor_y += _myFont->GetHeight(); | |
cursor_x = 0; | |
} else if (c == '\r') { | |
// skip em | |
} else { | |
int ww = _myFont->GetWidth(c); | |
if( ww > 0) | |
{ | |
int off = _myFont->GetOffset(c); | |
drawChar(off+cursor_x, cursor_y, c, textcolor, textbgcolor, textsize); | |
cursor_x += 1 + _myFont->GetAdvance(c); | |
} | |
else | |
{ | |
cursor_x += _myFont->GetWidth('n'); // use n-space for empty char | |
} | |
if (wrap && (cursor_x > (_width - _myFont->GetWidth('@')))) | |
{ | |
cursor_y += _myFont->GetHeight(); | |
cursor_x = 0; | |
} | |
} | |
return 1; | |
} | |
static unsigned char charbufr[100]; // where we put the pixels | |
// Draw a character | |
void Adafruit_GFX::drawChar(int16_t x, int16_t y, unsigned char c, | |
uint16_t color, uint16_t bg, uint8_t size) | |
{ | |
int cWidth = _myFont->GetWidth(c); | |
int cHeight = _myFont->GetHeight(); | |
if((x >= _width) || // Clip right | |
(y >= _height) || // Clip bottom | |
((x + cWidth * size - 1) < 0) || // Clip left | |
((y + cHeight * size - 1) < 0)) // Clip top | |
return; | |
for (int8_t j = 0; j<cHeight; j++) | |
{ | |
_myFont->GetPixelLine(charbufr, c, j); | |
if(size == 1) | |
{ | |
for (int8_t i=0; i<cWidth; i++ ) | |
{ | |
if (charbufr[i]) | |
{ | |
drawPixel(x+i, y+j, color); | |
} | |
else if (bg != color) | |
{ | |
drawPixel(x+i, y+j, bg); | |
} | |
} | |
} | |
else | |
{ | |
for (int8_t i=0; i<cWidth; i++ ) | |
{ | |
if (charbufr[i]) | |
{ | |
fillRect(x+(i*size), y+(j*size), size, size, color); | |
} | |
else if (bg != color) | |
{ | |
fillRect(x+i*size, y+j*size, size, size, bg); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment