Espruino/libs/graphics/pbf_font.c
Gordon Williams fc3c15a352 Graphics: Add Espruino-specific extension to PBF font loader to handle 2bpp
Graphics: Improve blendColor when outputting to 2/4/8bpp Graphics
Added new font converter - maybe this will end in EspruinoWebTools
2023-06-13 14:00:00 +01:00

142 lines
5.2 KiB
C

/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* Pebble font format parser
* ----------------------------------------------------------------------------
*/
#ifdef ESPR_PBF_FONTS
#include "pbf_font.h"
// https://github.com/pebble-dev/wiki/wiki/Firmware-Font-Format
/* Notes for improvements in a new format:
* remove value in hashtable (un-needed)
* Smaller hashtable, might as well use ~64 or something
* per-codepoint codepoint size?
* Allow mix of 1bpp/2bpp/etc glyphs
* Store all glyphs in one chunk - allow bit-level packing
*/
static uint8_t jspbfGetU8(JsvStringIterator *it) {
return (uint8_t)jsvStringIteratorGetCharAndNext(it);
}
static uint16_t jspbfGetU16(JsvStringIterator *it) {
return (uint16_t)((uint16_t)(uint8_t)jsvStringIteratorGetCharAndNext(it) |
(((uint16_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<8));
}
static uint32_t jspbfGetU32(JsvStringIterator *it) {
return (uint32_t)((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<8) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<16) |
(((uint32_t)(uint8_t)jsvStringIteratorGetCharAndNext(it))<<24));
}
/// Load a .pbf file
void jspbfFontNew(PbfFontLoaderInfo *info, JsVar *font) {
info->var = jsvLockAgain(font);
jsvStringIteratorNew(&info->it, font, 0);
info->version = jspbfGetU8(&info->it); // 0
info->lineHeight = jspbfGetU8(&info->it); // 1
info->glyphCount = jspbfGetU16(&info->it); // 2
jspbfGetU16(&info->it); // wildcard codepoint
info->hashTableOffset = 6;
info->hashTableSize = 255;
info->codepointSize = 2;
if (info->version>=2) {
info->hashTableOffset = 8;
info->hashTableSize = jspbfGetU8(&info->it);
info->codepointSize = jspbfGetU8(&info->it);
}
if (info->version==3) {
info->hashTableOffset = 10;
assert(0); // v3 not loaded yet
}
info->offsetTableOffset = (uint16_t)(info->hashTableOffset + info->hashTableSize*4);
info->glyphTableOffset = (uint16_t)(info->offsetTableOffset + info->glyphCount*6); // cp=2,address=4
}
void jspbfFontFree(PbfFontLoaderInfo *info) {
jsvStringIteratorFree(&info->it);
jsvUnLock(info->var);
info->var = NULL;
}
// Find the font glyph, fill PbfFontLoaderGlyph with info. Iterator is left pointing to glyph
bool jspbfFontFindGlyph(PbfFontLoaderInfo *info, int codepoint, PbfFontLoaderGlyph *result) {
int hash = codepoint % info->hashTableSize;
/*
HashTable
0x00 u8 : Value. In all examples I encountered, this simply counts 0..255
0x01 u8 : Offset Table Size.
0x02 u16 : Offset Table Offset. Measured in bytes from the beginning of the offset table.
*/
jsvStringIteratorGoto(&info->it, info->var, info->hashTableOffset + 4*hash + 1); // +1 because we don't care about value
uint8_t offsetTableSize = jspbfGetU8(&info->it);
uint16_t o = jspbfGetU16(&info->it);
uint16_t offsetTableOffset = o + info->offsetTableOffset;
jsvStringIteratorGoto(&info->it, info->var, offsetTableOffset);
int cp;
while (offsetTableSize--) {
/* Offset Table
0x00 u16/u32 : Unicode codepoint (for example, 0x0622 for ∆).
0x02 or 0x04 u16/u32 : Data offset.
*/
cp = jspbfGetU16(&info->it); assert(info->codepointSize==2); // only cp=2 suppported
uint32_t dataOffset = jspbfGetU32(&info->it); // only address=4 byte supported
if (cp==codepoint) {
jsvStringIteratorGoto(&info->it, info->var, info->glyphTableOffset+dataOffset);
result->w = jspbfGetU8(&info->it);
result->h = jspbfGetU8(&info->it);
result->x = (int8_t)jspbfGetU8(&info->it);
result->y = (int8_t)jspbfGetU8(&info->it);
result->advance = (int8_t)jspbfGetU8(&info->it);
result->bpp = 1;
if (((uint8_t)result->advance)&128) { // extension for Espruino - if advance top bit set (eg. negative), it means we have 2 bpp data
result->advance &= 127;
result->bpp = 2;
}
return true;
}
}
return false;
}
void jspbfFontRenderGlyph(PbfFontLoaderInfo *info, PbfFontLoaderGlyph *glyph, JsGraphics *gfx, int x, int y, bool solidBackground, int scalex, int scaley) {
//bmpOffset *= ch * customBPP;
// now render character
int bmpOffset = 0;
int bpp = glyph->bpp;
int bppRange = (1<<bpp)-1;
int cx,cy;
int citdata = jsvStringIteratorGetCharAndNext(&info->it);
for (cy=0;cy<glyph->h;cy++) {
for (cx=0;cx<glyph->w;cx++) {
int col = citdata&bppRange;
if (solidBackground || col)
graphicsFillRect(gfx,
(x + cx*scalex),
(y + cy*scaley),
(x + cx*scalex + scalex-1),
(y + cy*scaley + scaley-1),
graphicsBlendGfxColor(gfx, (256*col)/bppRange));
bmpOffset += bpp;
citdata >>= bpp;
if (bmpOffset>=8) {
bmpOffset=0;
citdata = jsvStringIteratorGetCharAndNext(&info->it);
}
}
}
}
#endif // ESPR_PBF_FONTS