Files

91 lines
4.0 KiB
Arduino

//
// Compressed font example
// written by Larry Bank
//
// This sketch demonstrates how to work with BBF (BitBank Font) files
// and render the characters to a color LCD display
// This should definitely be considered an advanced topic. Some of this functionality will be baked into
// my display libraries eventually, but the paid vs free divide makes it necessary to demonstrate how to
// work with the compressed font files created by the Group5 fontconvert tool
//
#include <Group5.h>
#include <bb_spi_lcd.h>
#include "Roboto_Black_40.h" // BBF (BitBank Font file included as hex data to compile with the sketch)
BB_SPI_LCD lcd; // class instance of bb_spi_lcd display library
G5DECODER g5dec; // class instance of Group5 image decoder
uint16_t u16Temp[256]; // holds one line of a character being displayed
//
// Draw a string of characters in the given font at the given position and color
// The y coordinate is relative to the baseline of the character, not the top
// This means that drawing at coordinate 0,0 on the display will usually get clipped 100%
// For this example, a 40pt font is about 64 pixels tall, so the baseline should be top+64
//
void DrawString(const char *szMsg, const BB_FONT *pFont, int x, int y, uint16_t u16FG, uint16_t u16BG)
{
int i, rc, iBitOff, dx, dy, cx, cy, tx, ty;
char c;
BB_GLYPH *pGlyph;
uint8_t *s, u8, u8Mask, *pG5, u8Temp[32]; // 32 bytes can hold 256 1-bit pixels (of each character)
uint16_t *d;
u16FG = __builtin_bswap16(u16FG); // swap for big endian (the SPI LCD data format)
u16BG = __builtin_bswap16(u16BG);
i = 0;
cx = (pFont->last - pFont->first + 1); // Glyph count
pG5 = (uint8_t *)&pFont->glyphs[cx]; // compressed data starts after the last glyph
while (szMsg[i] != 0 && x < lcd.width()) {
c = szMsg[i++];
if (c < pFont->first || c > pFont->last) // undefined character
continue; // skip it
c -= pFont->first; // subtract first char of font defined to start from index 0
pGlyph = (BB_GLYPH *)&pFont->glyphs[c];
// set up the destination window (rectangle) on the display
dx = x + pGlyph->xOffset; // offset from character UL to start drawing
dy = y + pGlyph->yOffset;
cx = pGlyph->width;
cy = pGlyph->height;
if (dy + cy > lcd.height())
cy = lcd.height() - dy; // clip bottom edge
if (dx + cx > lcd.width())
cx = lcd.width() - dx; // clip right edge
lcd.setAddrWindow(dx, dy, cx, cy); // tell the LCD the memory window in advance of writing the pixels
s = pG5 + pGlyph->bitmapOffset; // start of bitmap data
ty = (pGlyph[1].bitmapOffset - pGlyph[0].bitmapOffset); // compressed size
if (ty < 0 || ty > 4096) ty = 4096; // DEBUG
rc = g5dec.init(pGlyph->width, cy, s, ty);
if (rc != G5_SUCCESS) return;
for (ty=0; ty<cy && rc == G5_SUCCESS; ty++) { // loop over the lines (all equal width)
rc = g5dec.decodeLine(u8Temp); // decode the 1-bit image one line at a time
s = u8Temp;
d = u16Temp; // point to start of output buffer
u8Mask = 0x80; // MSB first
u8 = *s++; // preload first byte
for (tx=0; tx<cx; tx++) { // loop over the pixels on this line and convert from 1 to 16-bits
*d++ = (u8 & u8Mask) ? u16FG : u16BG; // 1/0 foreground or background color is written
u8Mask >>= 1;
if (u8Mask == 0) { // next source byte
u8 = *s++;
u8Mask = 0x80;
}
} // for x
lcd.pushPixels(u16Temp, cx); // write to the memory window one line at a time
} // for ty
x += pGlyph->xAdvance;
} // while characters
} /* DrawString() */
void setup()
{
lcd.begin(DISPLAY_CYD_2USB); // easy initialization of a named display type (ESP32 w/ 2.8" 240x320 ST7789)
lcd.fillScreen(TFT_BLACK);
DrawString("Hello", (BB_FONT *)Roboto_Black_40, 0, 70, TFT_GREEN, TFT_BLACK);
DrawString("World!", (BB_FONT *)Roboto_Black_40, 0, 140, TFT_BLUE, TFT_BLACK);
} /* setup() */
void loop()
{
} /* loop() */