feat: Overhaul font format into CrossPoint font

This commit is contained in:
Dave Allie
2026-01-28 23:30:39 +11:00
parent da4d3b5ea5
commit 567fa6e1e2
142 changed files with 254312 additions and 263366 deletions

View File

@@ -0,0 +1,147 @@
#include "CrossPointFont.h"
#include <Utf8.h>
#include <algorithm>
#include <cmath>
#define FONT_SCALE 2
namespace {
// Number of set bits from 0->15
uint8_t bitCount[] = {
0, // 0b0000,
1, // 0b0001,
1, // 0b0010,
2, // 0b0011,
1, // 0b0100,
2, // 0b0101,
2, // 0b0110,
3, // 0b0111,
1, // 0b1000,
2, // 0b1001,
2, // 0b1010,
3, // 0b1011,
2, // 0b1100,
3, // 0b1101,
3, // 0b1110,
4, // 0b1111,
};
} // namespace
void CrossPointFont::getTextBounds(const char* string, const Style style, const int startX, const int startY, int* minX,
int* minY, int* maxX, int* maxY) const {
*minX = startX;
*minY = startY;
*maxX = startX;
*maxY = startY;
if (*string == '\0') {
return;
}
int cursorX = startX;
const int cursorY = startY;
uint32_t cp;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
const CrossPointFontGlyph* glyph = getGlyph(cp, style);
if (!glyph) {
glyph = getGlyph(REPLACEMENT_GLYPH, style);
}
if (!glyph) {
// TODO: Better handle this?
continue;
}
*minX = std::min(*minX, cursorX + glyph->xOffset / FONT_SCALE);
*maxX = std::max(*maxX, cursorX + (glyph->xOffset + glyph->width) / FONT_SCALE);
*minY = std::min(*minY, cursorY + (glyph->yOffset - glyph->height) / FONT_SCALE);
*maxY = std::max(*maxY, cursorY + glyph->yOffset / FONT_SCALE);
cursorX += glyph->xAdvance / FONT_SCALE;
}
}
void CrossPointFont::getTextDimensions(const char* string, const Style style, int* w, int* h) const {
int minX = 0, minY = 0, maxX = 0, maxY = 0;
getTextBounds(string, style, 0, 0, &minX, &minY, &maxX, &maxY);
*w = maxX - minX;
*h = maxY - minY;
}
uint8_t CrossPointFont::styleGroup(const Style style) const {
if (style == REGULAR) return 0;
if (data.header.styles == 0b0001) {
// Only have regular font, show regular
return 0;
}
if (data.header.styles == 0b0011) {
// Only have bold and regular font
// Show bold if style is bold or bold_italic
return style == BOLD || style == BOLD_ITALIC ? 1 : 0;
}
if (data.header.styles == 0b0101) {
// Only have italic and regular font
// Show italic if style is italic or bold_italic
return style == ITALIC || style == BOLD_ITALIC ? 1 : 0;
}
if (data.header.styles == 0b1001) {
// Only have bold_italic and regular font
// Show bold_italic if style is any non-regular
return style == BOLD_ITALIC || style == BOLD || style == ITALIC ? 1 : 0;
}
if (data.header.styles == 0b0111) {
// Have all but bold_italic
// Show bold if style is bold_italic, otherwise show the requested style
return style == BOLD_ITALIC ? 1 : style;
}
if (data.header.styles == 0b1011) {
// Have all but italic
// Show bold_italic if style is italic, otherwise show the requested style
return style == ITALIC ? 2 : style;
}
if (data.header.styles == 0b1101) {
// Have all but bold
// Show bold_italic if style is bold, otherwise show the requested style
return style == BOLD ? 2 : style;
}
if (data.header.styles == 0b1111) {
return style;
}
return 0;
}
const CrossPointFontGlyph* CrossPointFont::getGlyph(const uint32_t cp, const Style style) const {
const CrossPointFontUnicodeInterval* intervals = data.intervals;
const int count = data.header.intervalCount;
if (count == 0) return nullptr;
// Binary search for O(log n) lookup instead of O(n)
// Critical for Korean fonts with many unicode intervals
int left = 0;
int right = count - 1;
while (left <= right) {
const int mid = left + (right - left) / 2;
const CrossPointFontUnicodeInterval* interval = &intervals[mid];
if (cp < interval->first) {
right = mid - 1;
} else if (cp > interval->last) {
left = mid + 1;
} else {
// Found: cp >= interval->first && cp <= interval->last
const uint32_t index =
interval->offset + (cp - interval->first) * bitCount[data.header.styles] + styleGroup(style);
return &data.glyphs[index];
}
}
return nullptr;
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include "CrossPointFontFormat.h"
#include "Group5/Group5.h"
class CrossPointFont {
public:
enum Style : uint8_t { REGULAR = 0, BOLD = 1, ITALIC = 2, BOLD_ITALIC = 3 };
CrossPointFontData data;
explicit CrossPointFont(void* rawData) {
data.header = *static_cast<CrossPointFontHeader*>(rawData);
data.intervals = (CrossPointFontUnicodeInterval*)(static_cast<uint8_t*>(rawData) + sizeof(CrossPointFontHeader));
data.glyphs = (CrossPointFontGlyph*)((uint8_t*)data.intervals +
sizeof(CrossPointFontUnicodeInterval) * data.header.intervalCount);
data.bitmap = (uint8_t*)data.glyphs + sizeof(CrossPointFontGlyph) * data.header.glyphCount;
}
~CrossPointFont() = default;
void getTextDimensions(const char* string, Style style, int* w, int* h) const;
const CrossPointFontGlyph* getGlyph(uint32_t cp, Style style) const;
private:
void getTextBounds(const char* string, Style style, int startX, int startY, int* minX, int* minY, int* maxX,
int* maxY) const;
uint8_t styleGroup(Style style) const;
};
// TODO: CrossPointFontSmall

View File

@@ -0,0 +1,57 @@
#pragma once
// 16-bit marker at the start of a CrossPoint font file
// (CrossPoint Font Format)
#define CPF_FONT_MARKER 0xCFF1
#define CPF_FONT_MARKER_SMALL 0xCFF2
// Font info per large character (glyph)
typedef struct {
uint32_t bitmapOffset; /// Offset to compressed bitmap data for this glyph
uint16_t width; /// bitmap width in pixels
uint16_t height; /// bitmap height in pixels
uint16_t xAdvance; /// total width in pixels (bitmap + padding)
int16_t xOffset; /// left padding to upper left corner
int16_t yOffset; /// top padding to upper left corner
} CrossPointFontGlyph;
// Font info per small character (glyph)
typedef struct {
uint32_t bitmapOffset; /// Offset to compressed bitmap data for this glyph
uint8_t width; /// bitmap width in pixels
uint8_t height; /// bitmap height in pixels
uint8_t xAdvance; /// total width in pixels (bitmap + padding)
int8_t xOffset; /// left padding to upper left corner
int16_t yOffset; /// top padding to upper left corner
} CrossPointFontSmallGlyph;
/// Glyph interval structure
typedef struct {
uint32_t first; /// The first unicode code point of the interval
uint32_t last; /// The last unicode code point of the interval
uint32_t offset; /// Index of the first code point into the glyph array
} CrossPointFontUnicodeInterval;
typedef struct {
uint16_t u16Marker; /// CPF_FONT_MARKER / CPF_FONT_MARKER_SMALL
uint16_t height; /// Newline distance (y axis)
uint16_t ascender; /// Maximal height of a glyph above the base line
uint8_t styles; /// Regular = 0x01, Bold = 0x02, Italic = 0x04, BoldItalic = 0x08, can be OR'd together
uint16_t intervalCount; /// Number of unicode intervals.
uint32_t glyphCount; /// Number of total glyphs across all styles
} CrossPointFontHeader;
/// Data stored for FONT AS A WHOLE
typedef struct {
CrossPointFontHeader header;
CrossPointFontUnicodeInterval* intervals; /// Valid unicode intervals for this font
CrossPointFontGlyph* glyphs; /// Glyph array
uint8_t* bitmap; /// Glyph bitmaps, concatenated
} CrossPointFontData;
typedef struct {
CrossPointFontHeader header;
CrossPointFontUnicodeInterval* intervals; /// Valid unicode intervals for this font
CrossPointFontSmallGlyph* glyphs; /// Glyph array
uint8_t* bitmap; /// Glyph bitmaps, concatenated
} CrossPointFontDataSmall;

View File

@@ -0,0 +1,51 @@
#include "g5enc.inl"
#include "g5dec.inl"
//
// Group5 1-bit image compression library
// Written by Larry Bank (bitbank@pobox.com)
// Decoder C++ wrapper functions
//
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
int G5DECODER::init(int iWidth, int iHeight, uint8_t *pData, int iDataSize)
{
return g5_decode_init(&_g5dec, iWidth, iHeight, pData, iDataSize);
} /* init() */
int G5DECODER::decodeLine(uint8_t *pOut)
{
return g5_decode_line(&_g5dec, pOut);
} /* decodeLine() */
//
// Encoder C++ wrapper functions
//
int G5ENCODER::init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize)
{
return g5_encode_init(&_g5enc, iWidth, iHeight, pOut, iOutSize);
} /* init() */
int G5ENCODER::encodeLine(uint8_t *pPixels)
{
return g5_encode_encodeLine(&_g5enc, pPixels);
} /* encodeLine() */
int G5ENCODER::size()
{
return g5_encode_getOutSize(&_g5enc);
} /* size() */

View File

@@ -0,0 +1,171 @@
#ifndef __GROUP5__
#define __GROUP5__
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __AVR__
#include <avr/pgmspace.h>
#endif
//
// Group5 1-bit image compression library
// Written by Larry Bank (bitbank@pobox.com)
//
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// The name "Group5" is derived from the CCITT Group4 standard
// This code is based on a lot of the good ideas from CCITT T.6
// for FAX image compression, but modified to work in a very
// constrained environment. The Huffman tables for horizontal
// mode have been replaced with a simple 2-bit flag followed by
// short or long counts of a fixed length. The short codes are
// always 3 bits (run lengths 0-7) and the long codes are the
// number of bits needed to encode the width of the image.
// For example, if a 320 pixel wide image is being compressed,
// the longest horizontal run needed is 320, which requires 9
// bits to encode. The 2 prefix bits have the following meaning:
// 00 = short, short (3+3 bits)
// 01 = short, long (3+N bits)
// 10 = long, short (N+3 bits)
// 11 = long, long (N+N bits)
// The rest of the code works identically to Group4 2D FAX
//
// Caution - this is the maximum number of color changes per line
// The default value is set low to work embedded systems with little RAM
// for font compression, this is plenty since each line of a character should have
// a maximum of 7 color changes
// You can define this in your compiler macros to override the default vlaue
//
#ifndef MAX_IMAGE_FLIPS
#ifdef __AVR__
#define MAX_IMAGE_FLIPS 32
#else
#define MAX_IMAGE_FLIPS 512
#endif // __AVR__
#endif
// Horizontal prefix bits
enum {
HORIZ_SHORT_SHORT=0,
HORIZ_SHORT_LONG,
HORIZ_LONG_SHORT,
HORIZ_LONG_LONG
};
// Return code for encoder and decoder
enum {
G5_SUCCESS = 0,
G5_INVALID_PARAMETER,
G5_DECODE_ERROR,
G5_UNSUPPORTED_FEATURE,
G5_ENCODE_COMPLETE,
G5_DECODE_COMPLETE,
G5_NOT_INITIALIZED,
G5_DATA_OVERFLOW,
G5_MAX_FLIPS_EXCEEDED
};
//
// Decoder state
//
typedef struct g5_dec_image_tag
{
int iWidth, iHeight; // image size
int iError;
int y; // last y value drawn
int iVLCSize;
int iHLen; // length of 'long' horizontal codes for this image
int iPitch; // width in bytes of output buffer
uint32_t u32Accum; // fractional scaling accumulator
uint32_t ulBitOff, ulBits; // vlc decode variables
uint8_t *pSrc, *pBuf; // starting & current buffer pointer
int16_t *pCur, *pRef; // current state of current vs reference flips
int16_t CurFlips[MAX_IMAGE_FLIPS];
int16_t RefFlips[MAX_IMAGE_FLIPS];
} G5DECIMAGE;
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#ifdef __AVR__
// assume PROGMEM as the source of data
inline uint32_t TIFFMOTOLONG(uint8_t *p)
{
uint32_t u32 = pgm_read_dword(p);
return __builtin_bswap32(u32);
}
#else
#define TIFFMOTOLONG(p) (((uint32_t)(*p)<<24UL) + ((uint32_t)(*(p+1))<<16UL) + ((uint32_t)(*(p+2))<<8UL) + (uint32_t)(*(p+3)))
#endif // __AVR__
#define TOP_BIT 0x80000000
#define MAX_VALUE 0xffffffff
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#define BIGUINT uint32_t
//
// G5 Encoder
//
typedef struct g5_buffered_bits
{
unsigned char *pBuf; // buffer pointer
uint32_t ulBits; // buffered bits
uint32_t ulBitOff; // current bit offset
uint32_t ulDataSize; // available data
} G5_BUFFERED_BITS;
//
// Encoder state
//
typedef struct g5_enc_image_tag
{
int iWidth, iHeight; // image size
int iError;
int y; // last y encoded
int iOutSize;
int iDataSize; // generated output size
uint8_t *pOutBuf;
int16_t *pCur, *pRef; // pointers to swap current and reference lines
G5_BUFFERED_BITS bb;
int16_t CurFlips[MAX_IMAGE_FLIPS];
int16_t RefFlips[MAX_IMAGE_FLIPS];
} G5ENCIMAGE;
#ifdef __cplusplus
//
// The G5 classes wrap portable C code which does the actual work
//
class G5ENCODER
{
public:
int init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize);
int encodeLine(uint8_t *pPixels);
int size();
private:
G5ENCIMAGE _g5enc;
};
class G5DECODER
{
public:
int init(int iWidth, int iHeight, uint8_t *pData, int iDataSize);
int decodeLine(uint8_t *pOut);
private:
G5DECIMAGE _g5dec;
};
#endif // __cplusplus
#endif // __GROUP5__

View File

@@ -0,0 +1,346 @@
//
// Group5
// A 1-bpp image decoder
//
// Written by Larry Bank (bitbank@pobox.com)
//
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "Group5.h"
/*
The code tree that follows has: bit_length, decode routine
These codes are for Group 4 (MMR) decoding
01 = vertneg1, 11h = vert1, 20h = horiz, 30h = pass, 12h = vert2
02 = vertneg2, 13h = vert3, 03 = vertneg3, 90h = trash
*/
static const uint8_t code_table[128] =
{0x90, 0, 0x40, 0, /* trash, uncompr mode - codes 0 and 1 */
3, 7, /* V(-3) pos = 2 */
0x13, 7, /* V(3) pos = 3 */
2, 6, 2, 6, /* V(-2) pos = 4,5 */
0x12, 6, 0x12, 6, /* V(2) pos = 6,7 */
0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4, /* pass pos = 8->F */
0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4,
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, /* horiz pos = 10->1F */
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3,
/* V(-1) pos = 20->2F */
1, 3, 1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3, 1, 3,
1, 3, 1, 3, 1, 3, 1, 3,
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3, /* V(1) pos = 30->3F */
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3,
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3,
0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3};
static int g5_decode_init(G5DECIMAGE *pImage, int iWidth, int iHeight, uint8_t *pData, int iDataSize)
{
if (pImage == NULL || iWidth < 1 || iHeight < 1 || pData == NULL || iDataSize < 1)
return G5_INVALID_PARAMETER;
pImage->iVLCSize = iDataSize;
pImage->pSrc = pData;
pImage->ulBitOff = 0;
pImage->y = 0;
pImage->ulBits = TIFFMOTOLONG(pData); // preload the first 32 bits of data
pImage->iWidth = iWidth;
pImage->iHeight = iHeight;
return G5_SUCCESS;
} /* g5_decode_init() */
static void G5DrawLine(G5DECIMAGE *pPage, int16_t *pCurFlips, uint8_t *pOut)
{
int x, len, run;
uint8_t lBit, rBit, *p;
int iStart = 0, xright = pPage->iWidth;
uint8_t *pDest;
iStart = 0;
pDest = pOut;
len = (xright+7)>>3; // number of bytes to generate
for (x=0; x<len; x++) {
pOut[x] = 0xff; // start with white and only draw the black runs
}
x = 0;
while (x < xright) { // while the scaled x is within the window bounds
x = *pCurFlips++; // black starting point
run = *pCurFlips++ - x; // get the black run
x -= iStart;
if (x >= xright || run == 0)
break;
if ((x + run) > 0) { /* If the run is visible, draw it */
if (x < 0) {
run += x; /* draw only visible part of run */
x = 0;
}
if ((x + run) > xright) { /* Don't let it go off right edge */
run = xright - x;
}
/* Draw this run */
lBit = 0xff << (8 - (x & 7));
rBit = 0xff >> ((x + run) & 7);
len = ((x+run)>>3) - (x >> 3);
p = &pDest[x >> 3];
if (len == 0) {
lBit |= rBit;
*p &= lBit;
} else {
*p++ &= lBit;
while (len > 1) {
*p++ = 0;
len--;
}
*p = rBit;
}
} // visible run
} /* while drawing line */
} /* G5DrawLine() */
//
// Initialize internal structures to decode the image
//
static void Decode_Begin(G5DECIMAGE *pPage)
{
int i, xsize;
int16_t *CurFlips, *RefFlips;
xsize = pPage->iWidth;
RefFlips = pPage->RefFlips;
CurFlips = pPage->CurFlips;
/* Seed the current and reference line with XSIZE for V(0) codes */
for (i=0; i<MAX_IMAGE_FLIPS-2; i++) {
RefFlips[i] = xsize;
CurFlips[i] = xsize;
}
/* Prefill both current and reference lines with 7fff to prevent it from
walking off the end if the data gets bunged and the current X is > XSIZE
3-16-94 */
CurFlips[i] = RefFlips[i] = 0x7fff;
CurFlips[i+1] = RefFlips[i+1] = 0x7fff;
pPage->pCur = CurFlips;
pPage->pRef = RefFlips;
pPage->pBuf = pPage->pSrc;
pPage->ulBits = TIFFMOTOLONG(pPage->pSrc); // load 32 bits to start
pPage->ulBitOff = 0;
// Calculate the number of bits needed for a long horizontal code
#ifdef __AVR__
pPage->iHLen = 16 - __builtin_clz(pPage->iWidth);
#else
pPage->iHLen = 32 - __builtin_clz(pPage->iWidth);
#endif
} /* Decode_Begin() */
//
// Decode a single line of G5 data (private function)
//
static int DecodeLine(G5DECIMAGE *pPage)
{
signed int a0, a0_p, b1;
int16_t *pCur, *pRef, *RefFlips, *CurFlips;
int xsize, tot_run=0, tot_run1 = 0;
int32_t sCode;
uint32_t lBits;
uint32_t ulBits, ulBitOff;
uint8_t *pBuf/*, *pBufEnd*/;
uint32_t u32HMask, u32HLen; // horizontal code mask and length
pCur = CurFlips = pPage->pCur;
pRef = RefFlips = pPage->pRef;
ulBits = pPage->ulBits;
ulBitOff = pPage->ulBitOff;
pBuf = pPage->pBuf;
// pBufEnd = &pPage->pSrc[pPage->iVLCSize];
u32HLen = pPage->iHLen;
u32HMask = (1 << u32HLen) - 1;
a0 = -1;
xsize = pPage->iWidth;
while (a0 < xsize) { /* Decode this line */
if (ulBitOff > (REGISTER_WIDTH-8)) { // need at least 7 unused bits
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = TIFFMOTOLONG(pBuf);
}
if ((int32_t)(ulBits << ulBitOff) < 0) { /* V(0) code is the most frequent case (1 bit) */
a0 = *pRef++;
ulBitOff++; // length = 1 bit
*pCur++ = a0;
} else { /* Slower method for the less frequence codes */
lBits = (ulBits >> ((REGISTER_WIDTH - 8) - ulBitOff)) & 0xfe; /* Only the first 7 bits are useful */
sCode = code_table[lBits]; /* Get the code type as an 8-bit value */
ulBitOff += code_table[lBits+1]; /* Get the code length */
switch (sCode) {
case 1: /* V(-1) */
case 2: /* V(-2) */
case 3: /* V(-3) */
a0 = *pRef - sCode; /* A0 = B1 - x */
*pCur++ = a0;
if (pRef == RefFlips) {
pRef += 2;
}
pRef--;
while (a0 >= *pRef) {
pRef += 2;
}
break;
case 0x11: /* V(1) */
case 0x12: /* V(2) */
case 0x13: /* V(3) */
a0 = *pRef++; /* A0 = B1 */
b1 = a0;
a0 += sCode & 7; /* A0 = B1 + x */
if (b1 != xsize && a0 < xsize) {
while (a0 >= *pRef) {
pRef += 2;
}
}
if (a0 > xsize) {
a0 = xsize;
}
*pCur++ = a0;
break;
case 0x20: /* Horizontal codes */
if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = TIFFMOTOLONG(pBuf);
}
a0_p = a0;
if (a0 < 0) {
a0_p = 0;
}
lBits = (ulBits >> ((REGISTER_WIDTH - 2) - ulBitOff)) & 0x3; // get 2-bit prefix for code type
// There are 4 possible horizontal cases: short/short, short/long, long/short, long/long
// These are encoded in a 2-bit prefix code, followed by 3 bits for short or N bits for long code
// N is the log base 2 of the image width (e.g. 320 pixels requires 9 bits)
ulBitOff += 2;
switch (lBits) {
case HORIZ_SHORT_SHORT:
tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
ulBitOff += 3;
tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
ulBitOff += 3;
break;
case HORIZ_SHORT_LONG:
tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
ulBitOff += 3;
tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
ulBitOff += u32HLen;
break;
case HORIZ_LONG_SHORT:
tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
ulBitOff += u32HLen;
tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length
ulBitOff += 3;
break;
case HORIZ_LONG_LONG:
tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
ulBitOff += u32HLen;
if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits
pBuf += (ulBitOff >> 3);
ulBitOff &= 7;
ulBits = TIFFMOTOLONG(pBuf);
}
tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length
ulBitOff += u32HLen;
break;
} // switch on lBits
a0 = a0_p + tot_run;
*pCur++ = a0;
a0 += tot_run1;
if (a0 < xsize) {
while (a0 >= *pRef) {
pRef += 2;
}
}
*pCur++ = a0;
break;
case 0x30: /* Pass code */
pRef++; /* A0 = B2, iRef+=2 */
a0 = *pRef++;
break;
default: /* ERROR */
pPage->iError = G5_DECODE_ERROR;
goto pilreadg5z;
} /* switch */
} /* Slow climb */
}
/*--- Convert flips data into run lengths ---*/
*pCur++ = xsize; /* Terminate the line properly */
*pCur++ = xsize;
pilreadg5z:
// Save the current VLC decoder state
pPage->ulBits = ulBits;
pPage->ulBitOff = ulBitOff;
pPage->pBuf = pBuf;
return pPage->iError;
} /* DecodeLine() */
//
// Decompress the VLC data
//
static int g5_decode_line(G5DECIMAGE *pPage, uint8_t *pOut)
{
int rc;
uint8_t *pBufEnd;
int16_t *t1;
if (pPage == NULL || pOut == NULL)
return G5_INVALID_PARAMETER;
if (pPage->y >= pPage->iHeight)
return G5_DECODE_COMPLETE;
pPage->iError = G5_SUCCESS;
if (pPage->y == 0) { // first time through
Decode_Begin(pPage);
}
pBufEnd = &pPage->pSrc[pPage->iVLCSize];
if (pPage->pBuf >= pBufEnd) { // read past the end, error
pPage->iError = G5_DECODE_ERROR;
return G5_DECODE_ERROR;
}
rc = DecodeLine(pPage);
if (rc == G5_SUCCESS) {
// Draw the current line
G5DrawLine(pPage, pPage->pCur, pOut);
/*--- Swap current and reference lines ---*/
t1 = pPage->pRef;
pPage->pRef = pPage->pCur;
pPage->pCur = t1;
pPage->y++;
if (pPage->y >= pPage->iHeight) {
pPage->iError = G5_DECODE_COMPLETE;
}
} else {
pPage->iError = rc;
}
return pPage->iError;
} /* Decode() */

View File

@@ -0,0 +1,315 @@
//
// G5 Encoder
// A 1-bpp image encoding library
//
// Written by Larry Bank (bitbank@pobox.com)
//
// SPDX-FileCopyrightText: 2024 BitBank Software, Inc.
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "Group5.h"
/* Number of consecutive 1 bits in a byte from MSB to LSB */
static uint8_t bitcount[256] =
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0-15 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16-31 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 32-47 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 48-63 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 64-79 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80-95 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 96-111 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 112-127 */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 128-143 */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 144-159 */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 160-175 */
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 176-191 */
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 192-207 */
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 208-223 */
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 224-239 */
4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,8}; /* 240-255 */
/* Table of vertical codes for G5 encoding */
/* code followed by length, starting with v(-3) */
static const uint8_t vtable[14] =
{3,7, /* V(-3) = 0000011 */
3,6, /* V(-2) = 000011 */
3,3, /* V(-1) = 011 */
1,1, /* V(0) = 1 */
2,3, /* V(1) = 010 */
2,6, /* V(2) = 000010 */
2,7}; /* V(3) = 0000010 */
static void G5ENCInsertCode(G5_BUFFERED_BITS *bb, BIGUINT ulCode, int iLen)
{
if ((bb->ulBitOff + iLen) > REGISTER_WIDTH) { // need to write data
bb->ulBits |= (ulCode >> (bb->ulBitOff + iLen - REGISTER_WIDTH)); // partial bits on first word
*(BIGUINT *)bb->pBuf = __builtin_bswap32(bb->ulBits);
bb->pBuf += sizeof(BIGUINT);
bb->ulBits = ulCode << ((REGISTER_WIDTH*2) - (bb->ulBitOff + iLen));
bb->ulBitOff += iLen - REGISTER_WIDTH;
} else {
bb->ulBits |= (ulCode << (REGISTER_WIDTH - bb->ulBitOff - iLen));
bb->ulBitOff += iLen;
}
} /* G5ENCInsertCode() */
//
// Flush any buffered bits to the output
//
static void G5ENCFlushBits(G5_BUFFERED_BITS *bb)
{
while (bb->ulBitOff >= 8)
{
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
bb->ulBits <<= 8;
bb->ulBitOff -= 8;
}
if (bb->ulBitOff) { // partial byte?
*bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8));
}
bb->ulBitOff = 0;
bb->ulBits = 0;
} /* G5ENCFlushBits() */
//
// Initialize the compressor
// This must be called before adding data to the output
//
static int g5_encode_init(G5ENCIMAGE *pImage, int iWidth, int iHeight, uint8_t *pOut, int iOutSize)
{
int iError = G5_SUCCESS;
if (pImage == NULL || iHeight <= 0)
return G5_INVALID_PARAMETER;
pImage->iWidth = iWidth; // image size
pImage->iHeight = iHeight;
pImage->pCur = pImage->CurFlips;
pImage->pRef = pImage->RefFlips;
pImage->pOutBuf = pOut; // optional output buffer
pImage->iOutSize = iOutSize; // output buffer pre-allocated size
pImage->iDataSize = 0; // no data yet
pImage->y = 0;
for (int i=0; i<MAX_IMAGE_FLIPS; i++) {
pImage->RefFlips[i] = iWidth;
pImage->CurFlips[i] = iWidth;
}
pImage->bb.pBuf = pImage->pOutBuf;
pImage->bb.ulBits = 0;
pImage->bb.ulBitOff = 0;
pImage->iError = iError;
return iError;
} /* g5_encode_init() */
//
// Internal function to convert uncompressed 1-bit per pixel data
// into the run-end data needed to feed the G5 encoder
//
static int G5ENCEncodeLine(unsigned char *buf, int xsize, int16_t *pDest)
{
int iCount, xborder;
uint8_t i, c;
int8_t cBits;
int iLen;
int16_t x;
int16_t *pLimit = pDest + (MAX_IMAGE_FLIPS-4);
xborder = xsize;
iCount = (xsize + 7) >> 3; /* Number of bytes per line */
cBits = 8;
iLen = 0; /* Current run length */
x = 0;
c = *buf++; /* Get the first byte to start */
iCount--;
while (iCount >=0) {
if (pDest >= pLimit) return G5_MAX_FLIPS_EXCEEDED;
i = bitcount[c]; /* Get the number of consecutive bits */
iLen += i; /* Add this length to total run length */
c <<= i;
cBits -= i; /* Minus the number in a byte */
if (cBits <= 0)
{
iLen += cBits; /* Adjust length */
cBits = 8;
c = *buf++; /* Get another data byte */
iCount--;
continue; /* Keep doing white until color change */
}
c = ~c; /* flip color to count black pixels */
/* Store the white run length */
xborder -= iLen;
if (xborder < 0)
{
iLen += xborder; /* Make sure run length is not past end */
break;
}
x += iLen;
*pDest++ = x;
iLen = 0;
doblack:
i = bitcount[c]; /* Get consecutive bits */
iLen += i; /* Add to total run length */
c <<= i;
cBits -= i;
if (cBits <= 0)
{
iLen += cBits; /* Adjust length */
cBits = 8;
c = *buf++; /* Get another data byte */
c = ~c; /* Flip color to find black */
iCount--;
if (iCount < 0)
break;
goto doblack;
}
/* Store the black run length */
c = ~c; /* Flip color again to find white pixels */
xborder -= iLen;
if (xborder < 0)
{
iLen += xborder; /* Make sure run length is not past end */
break;
}
x += iLen;
*pDest++ = x;
iLen = 0;
} /* while */
if (pDest >= pLimit) return G5_MAX_FLIPS_EXCEEDED;
*pDest++ = xsize;
*pDest++ = xsize; // Store a few more XSIZE to end the line
*pDest++ = xsize; // so that the compressor doesn't go past
*pDest++ = xsize; // the end of the line
return G5_SUCCESS;
} /* G5ENCEncodeLine() */
//
// Compress a line of pixels and add it to the output
// the input format is expected to be MSB (most significant bit) first
// for example, pixel 0 is in byte 0 at bit 7 (0x80)
// Returns G5ENC_SUCCESS for each line if all is well and G5ENC_IMAGE_COMPLETE
// for the last line
//
static int g5_encode_encodeLine(G5ENCIMAGE *pImage, uint8_t *pPixels)
{
int16_t a0, a0_c, b2, a1;
int dx, run1, run2;
int xsize, iErr, iHighWater;
int iCur, iRef, iLen;
int iHLen; // number of bits for long horizontal codes
int16_t *CurFlips, *RefFlips;
G5_BUFFERED_BITS bb;
if (pImage == NULL || pPixels == NULL)
return G5_INVALID_PARAMETER;
iHighWater = pImage->iOutSize - 32;
iHLen = 32 - __builtin_clz(pImage->iWidth);
memcpy(&bb, &pImage->bb, sizeof(G5_BUFFERED_BITS)); // keep local copy
CurFlips = pImage->pCur;
RefFlips = pImage->pRef;
xsize = pImage->iWidth; /* For performance reasons */
// Convert the incoming line of pixels into run-end data
iErr = G5ENCEncodeLine(pPixels, pImage->iWidth, CurFlips);
if (iErr != G5_SUCCESS) return iErr; // exceeded the maximum number of color changes
/* Encode this line as G5 */
a0 = a0_c = 0;
iCur = iRef = 0;
while (a0 < xsize) {
b2 = RefFlips[iRef+1];
a1 = CurFlips[iCur];
if (b2 < a1) { /* Is b2 to the left of a1? */
/* yes, do pass mode */
a0 = b2;
iRef += 2;
G5ENCInsertCode(&bb, 1, 4); /* Pass code = 0001 */
} else { /* Try vertical and horizontal mode */
dx = RefFlips[iRef] - a1; /* b1 - a1 */
if (dx > 3 || dx < -3) { /* Horizontal mode */
G5ENCInsertCode(&bb, 1, 3); /* Horizontal code = 001 */
run1 = CurFlips[iCur] - a0;
run2 = CurFlips[iCur+1] - CurFlips[iCur];
if (run1 < 8) {
if (run2 < 8) { // short, short
G5ENCInsertCode(&bb, HORIZ_SHORT_SHORT, 2); /* short, short = 00 */
G5ENCInsertCode(&bb, run1, 3);
G5ENCInsertCode(&bb, run2, 3);
} else { // short, long
G5ENCInsertCode(&bb, HORIZ_SHORT_LONG, 2); /* short, long = 01 */
G5ENCInsertCode(&bb, run1, 3);
G5ENCInsertCode(&bb, run2, iHLen);
}
} else { // first run is long
if (run2 < 8) { // long, short
G5ENCInsertCode(&bb, HORIZ_LONG_SHORT, 2); /* long, short = 10 */
G5ENCInsertCode(&bb, run1, iHLen);
G5ENCInsertCode(&bb, run2, 3);
} else { // long, long
G5ENCInsertCode(&bb, HORIZ_LONG_LONG, 2); /* long, long = 11 */
G5ENCInsertCode(&bb, run1, iHLen);
G5ENCInsertCode(&bb, run2, iHLen);
}
}
a0 = CurFlips[iCur+1]; /* a0 = a2 */
if (a0 != xsize) {
iCur += 2; /* Skip two color flips */
while (RefFlips[iRef] != xsize && RefFlips[iRef] <= a0) {
iRef += 2;
}
}
} else { /* Vertical mode */
dx = (dx + 3) * 2; /* Convert to index table */
G5ENCInsertCode(&bb, vtable[dx], vtable[dx+1]);
a0 = a1;
a0_c = 1-a0_c;
if (a0 != xsize) {
if (iRef != 0) {
iRef -= 2;
}
iRef++; /* Skip a color change in cur and ref */
iCur++;
while (RefFlips[iRef] <= a0 && RefFlips[iRef] != xsize) {
iRef += 2;
}
}
} /* vertical mode */
} /* horiz/vert mode */
} /* while x < xsize */
iLen = (int)(bb.pBuf-pImage->pOutBuf);
if (iLen >= iHighWater) { // not enough space
pImage->iError = iErr = G5_DATA_OVERFLOW; // we don't have a better error
return iErr;
}
if (pImage->y == pImage->iHeight-1) { // last line of image
G5ENCFlushBits(&bb); // output the final buffered bits
// wrap up final output
pImage->iDataSize = 1 + (int)(bb.pBuf-pImage->pOutBuf);
iErr = G5_ENCODE_COMPLETE;
}
pImage->pCur = RefFlips; // swap current and reference lines
pImage->pRef = CurFlips;
pImage->y++;
memcpy(&pImage->bb, &bb, sizeof(bb));
return iErr;
} /* g5_encode_encodeLine() */
//
// Returns the number of bytes of G5 created by the encoder
//
static int g5_encode_getOutSize(G5ENCIMAGE *pImage)
{
int iSize = 0;
if (pImage != NULL)
iSize = pImage->iDataSize;
return iSize;
} /* g5_encode_getOutSize() */

View File

@@ -0,0 +1,17 @@
#pragma once
#include <builtinFonts/bookerly_12.h>
#include <builtinFonts/bookerly_14.h>
#include <builtinFonts/bookerly_16.h>
#include <builtinFonts/bookerly_18.h>
#include <builtinFonts/notosans_8.h>
#include <builtinFonts/notosans_12.h>
#include <builtinFonts/notosans_14.h>
#include <builtinFonts/notosans_16.h>
#include <builtinFonts/notosans_18.h>
#include <builtinFonts/opendyslexic_8.h>
#include <builtinFonts/opendyslexic_10.h>
#include <builtinFonts/opendyslexic_12.h>
#include <builtinFonts/opendyslexic_14.h>
#include <builtinFonts/ubuntu_10.h>
#include <builtinFonts/ubuntu_12.h>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
echo "// The contents of this file are generated by ./lib/CrossPointFont/builtinFonts/build-font-ids.sh"
echo "#pragma once"
echo ""
echo "#define BOOKERLY_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define BOOKERLY_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define BOOKERLY_16_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_16.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define BOOKERLY_18_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./bookerly_18.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define NOTOSANS_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define NOTOSANS_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define NOTOSANS_16_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_16.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define NOTOSANS_18_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_18.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define OPENDYSLEXIC_8_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_8.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define OPENDYSLEXIC_10_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_10.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define OPENDYSLEXIC_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define OPENDYSLEXIC_14_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./opendyslexic_14.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define UI_10_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./ubuntu_10.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define UI_12_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./ubuntu_12.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"
echo "#define SMALL_FONT_ID ($(ruby -rdigest -e 'puts Digest::SHA256.hexdigest(File.read("./notosans_8.h")).to_i(16) % (2 ** 32) - (2 ** 31)'))"

View File

@@ -0,0 +1,42 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"
BOOKERLY_FONT_SIZES=(12 14 16 18)
NOTOSANS_FONT_SIZES=(12 14 16 18)
OPENDYSLEXIC_FONT_SIZES=(8 10 12 14)
for size in ${BOOKERLY_FONT_SIZES[@]}; do
font_name="bookerly_${size}"
font_path_prefix="./source/Bookerly/Bookerly-"
output_path="./${font_name}.h"
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -i "${font_path_prefix}Italic.ttf" -bi "${font_path_prefix}BoldItalic.ttf" -o $output_path -p $size
done
for size in ${NOTOSANS_FONT_SIZES[@]}; do
font_name="notosans_${size}"
font_path_prefix="./source/NotoSans/NotoSans-"
output_path="./${font_name}.h"
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -i "${font_path_prefix}Italic.ttf" -bi "${font_path_prefix}BoldItalic.ttf" -o $output_path -p $size
done
for size in ${OPENDYSLEXIC_FONT_SIZES[@]}; do
font_name="opendyslexic_${size}"
font_path_prefix="./source/OpenDyslexic/OpenDyslexic-"
output_path="./${font_name}.h"
../fontconvert/fontconvert "${font_path_prefix}Regular.otf" -b "${font_path_prefix}Bold.otf" -i "${font_path_prefix}Italic.otf" -bi "${font_path_prefix}BoldItalic.otf" -o $output_path -p $size
done
UI_FONT_SIZES=(10 12)
UI_FONT_STYLES=("Regular" "Bold")
for size in ${UI_FONT_SIZES[@]}; do
font_name="ubuntu_${size}"
font_path_prefix="./source/Ubuntu/Ubuntu-"
output_path="./${font_name}.h"
../fontconvert/fontconvert "${font_path_prefix}Regular.ttf" -b "${font_path_prefix}Bold.ttf" -o $output_path -p $size
done
../fontconvert/fontconvert ./source/NotoSans/NotoSans-Regular.ttf -o ./notosans_8.h -p 8

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,94 @@
Copyright (c) 2019-07-29, Abbie Gonzalez (https://abbiecod.es|support@abbiecod.es),
with Reserved Font Name OpenDyslexic.
Copyright (c) 12/2012 - 2019
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -0,0 +1,96 @@
-------------------------------
UBUNTU FONT LICENCE Version 1.0
-------------------------------
PREAMBLE
This licence allows the licensed fonts to be used, studied, modified and
redistributed freely. The fonts, including any derivative works, can be
bundled, embedded, and redistributed provided the terms of this licence
are met. The fonts and derivatives, however, cannot be released under
any other licence. The requirement for fonts to remain under this
licence does not require any document created using the fonts or their
derivatives to be published under this licence, as long as the primary
purpose of the document is not to be a vehicle for the distribution of
the fonts.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this licence and clearly marked as such. This may
include source files, build scripts and documentation.
"Original Version" refers to the collection of Font Software components
as received under this licence.
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to
a new environment.
"Copyright Holder(s)" refers to all individuals and companies who have a
copyright ownership of the Font Software.
"Substantially Changed" refers to Modified Versions which can be easily
identified as dissimilar to the Font Software by users of the Font
Software comparing the Original Version with the Modified Version.
To "Propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification and with or without charging
a redistribution fee), making available to the public, and in some
countries other activities as well.
PERMISSION & CONDITIONS
This licence does not grant any rights under trademark law and all such
rights are reserved.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the Font Software, to propagate the Font Software, subject to
the below conditions:
1) Each copy of the Font Software must contain the above copyright
notice and this licence. These can be included either as stand-alone
text files, human-readable headers or in the appropriate machine-
readable metadata fields within text or binary files as long as those
fields can be easily viewed by the user.
2) The font name complies with the following:
(a) The Original Version must retain its name, unmodified.
(b) Modified Versions which are Substantially Changed must be renamed to
avoid use of the name of the Original Version or similar names entirely.
(c) Modified Versions which are not Substantially Changed must be
renamed to both (i) retain the name of the Original Version and (ii) add
additional naming elements to distinguish the Modified Version from the
Original Version. The name of such Modified Versions must be the name of
the Original Version, with "derivative X" where X represents the name of
the new work, appended to that name.
3) The name(s) of the Copyright Holder(s) and any contributor to the
Font Software shall not be used to promote, endorse or advertise any
Modified Version, except (i) as required by this licence, (ii) to
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
their explicit written permission.
4) The Font Software, modified or unmodified, in part or in whole, must
be distributed entirely under this licence, and must not be distributed
under any other licence. The requirement for fonts to remain under this
licence does not affect any document created using the Font Software,
except any version of the Font Software extracted from a document
created using the Font Software may only be distributed under this
licence.
TERMINATION
This licence becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
DEALINGS IN THE FONT SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
fontconvert

View File

@@ -0,0 +1,12 @@
all: fontconvert
CC = gcc
CFLAGS = -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include -I/opt/homebrew/include/freetype2
LIBS = -L/opt/homebrew/lib -lfreetype
fontconvert: main.c ../Group5/Group5.h
$(CC) $(CFLAGS) main.c $(LIBS) -o fontconvert
strip fontconvert
clean:
rm -f fontconvert

View File

@@ -0,0 +1,553 @@
//
// TrueType to CrossPoint font converter
// Copyright (c) 2024 BitBank Software, inc.
// Written by Larry Bank, adapted by Dave Allie for CrossPoint
// August 31, 2024
// The CrossPoint font format is a losslessly compressed bitmap font of a single point size with multiple variants
// This was built entirely on the back of Larry Bank's bb_font format.
// The data is compressed with a compression scheme based on CCITT T.6
// The font structure includes overall size, per-character glyph info and then the
// compressed image data at the end.
// The font file format is designed to allow both dynamic loading of font data from
// external memory/disk or compiling the data as const into a progarm.
//
// Example usage:
// ./fontconvert <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p <pt size> -o <out.cpf>
// ./fontconvert <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p <pt size> -o <out.h>
//
// This code requires the freetype library
// found here: www.freetype.org
//
#ifndef ARDUINO
#include <ctype.h>
#include <ft2build.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../CrossPointFontFormat.h"
#include FT_GLYPH_H
#include FT_MODULE_H
#include FT_TRUETYPE_DRIVER_H
#include "../Group5/g5enc.inl" // Group5 image compression library
G5ENCIMAGE g5enc; // Group5 encoder state
#define DPI 150 // Approximate resolution of common displays
#define OUTBUF_SIZE 1048576 // 1MB
#define MAX_INTERVALS 65536
#define FONT_SCALE_FACTOR 2
// TODO: Re-enable small font
// Disabled small font generation to get this working, but want to re-enable
#define SMALL_FONT_ENABLED 0
uint32_t raw_intervals[][2] = {
/* Basic Latin */
// ASCII letters, digits, punctuation, control characters
{0x0000, 0x007F},
/* Latin-1 Supplement */
// Accented characters for Western European languages
{0x0080, 0x00FF},
/* Latin Extended-A */
// Eastern European and Baltic languages
{0x0100, 0x017F},
/* General Punctuation (core subset) */
// Smart quotes, en dash, em dash, ellipsis, NO-BREAK SPACE
{0x2000, 0x206F},
/* Basic Symbols From "Latin-1 + Misc" */
// dashes, quotes, prime marks
{0x2010, 0x203A},
// misc punctuation
{0x2040, 0x205F},
// common currency symbols
{0x20A0, 0x20CF},
/* Combining Diacritical Marks (minimal subset) */
// Needed for proper rendering of many extended Latin languages
{0x0300, 0x036F},
/* Greek & Coptic */
// Used in science, maths, philosophy, some academic texts
// {0x0370, 0x03FF},
/* Cyrillic */
// Russian, Ukrainian, Bulgarian, etc.
{0x0400, 0x04FF},
/* Math Symbols (common subset) */
// Superscripts and Subscripts
{0x2070, 0x209F},
// General math operators
{0x2200, 0x22FF},
// Arrows
{0x2190, 0x21FF},
/* CJK */
// Core Unified Ideographs
// {0x4E00, 0x9FFF},
// Extension A
// {0x3400, 0x4DBF},
// Extension B
// {0x20000, 0x2A6DF},
// Extension CF
// {0x2A700, 0x2EBEF},
// Extension G
// {0x30000, 0x3134F},
// Hiragana
// {0x3040, 0x309F},
// Katakana
// {0x30A0, 0x30FF},
// Katakana Phonetic Extensions
// {0x31F0, 0x31FF},
// Halfwidth Katakana
// {0xFF60, 0xFF9F},
// Hangul Syllables
// {0xAC00, 0xD7AF},
// Hangul Jamo
// {0x1100, 0x11FF},
// Hangul Compatibility Jamo
// {0x3130, 0x318F},
// Hangul Jamo Extended-A
// {0xA960, 0xA97F},
// Hangul Jamo Extended-B
// {0xD7B0, 0xD7FF},
// CJK Radicals Supplement
// {0x2E80, 0x2EFF},
// Kangxi Radicals
// {0x2F00, 0x2FDF},
// CJK Symbols and Punctuation
// {0x3000, 0x303F},
// CJK Compatibility Forms
// {0xFE30, 0xFE4F},
// CJK Compatibility Ideographs
// {0xF900, 0xFAFF},
/* Specials */
// Replacement Character
{0xFFFD, 0xFFFD},
};
//
// Comparison function for qsort
//
int compareIntervals(const void* a, const void* b) {
const uint32_t* ia = (uint32_t*)a;
const uint32_t* ib = (uint32_t*)b;
if (ia[0] < ib[0]) return -1;
if (ia[0] > ib[0]) return 1;
return 0;
}
//
// Sort and merge adjacent intervals
// Returns the number of intervals after merging
//
int sortAndMergeIntervals(uint32_t intervals[][2], const int count) {
int merged_count = 0;
// Sort intervals by start value
qsort(intervals, count, sizeof(uint32_t) * 2, compareIntervals);
// Merge overlapping/adjacent intervals
for (int i = 0; i < count; i++) {
if (merged_count > 0 && intervals[i][0] <= intervals[merged_count - 1][1] + 1) {
// Merge with previous interval
if (intervals[i][1] > intervals[merged_count - 1][1]) {
intervals[merged_count - 1][1] = intervals[i][1];
}
} else {
// Add as new interval
if (merged_count != i) {
intervals[merged_count][0] = intervals[i][0];
intervals[merged_count][1] = intervals[i][1];
}
merged_count++;
}
}
return merged_count;
}
//
// Create the comments and const array boilerplate for the hex data bytes
//
void StartHexFile(FILE* f, int iLen, const char* fname, int size) {
int i, j;
char szTemp[256];
fprintf(f, "#pragma once\n\n");
fprintf(f, "//\n// Created with fontconvert, written by Larry Bank, updated for CrossPoint by Dave Allie\n");
fprintf(f, "// Point size: %d (scaled %dx)\n", size, FONT_SCALE_FACTOR);
fprintf(f, "// compressed font data size = %d bytes\n//\n", iLen);
strcpy(szTemp, fname);
i = strlen(szTemp);
if (szTemp[i - 2] == '.') szTemp[i - 2] = 0; // get the leaf name for the data
j = i;
// go backwards to get rid trim off just the leaf name
while (j > 0 && szTemp[j] != '/') {
j--;
}
if (szTemp[j] == '/') j++;
fprintf(f, "static const uint8_t %s[] = {\n", &szTemp[j]);
} /* StartHexFile() */
//
// Add N bytes of hex data to the output
// The data will be arranged in rows of 16 bytes each
//
void AddHexBytes(FILE* f, void* pData, int iLen, int bLast) {
static int iCount = 0; // number of bytes processed so far
int i;
uint8_t* s = (uint8_t*)pData;
for (i = 0; i < iLen; i++) { // process the given data
fprintf(f, "0x%02x", *s++);
iCount++;
if (i < iLen - 1 || !bLast) fprintf(f, ",");
if ((iCount & 15) == 0) fprintf(f, "\n"); // next row of 16
}
if (bLast) {
fprintf(f, "};\n");
}
} /* AddHexBytes() */
int loadCodePoint(FT_Face face, uint32_t code_point, CrossPointFontGlyph* pGlyphs, uint8_t* pBitmap,
uint32_t* glyph_index, uint32_t* iOffset) {
uint8_t* s;
int iPitch, err;
FT_Glyph glyph;
// MONO renderer provides clean image with perfect crop
// (no wasted pixels) via bitmap struct.
if ((err = FT_Load_Char(face, code_point, FT_LOAD_TARGET_MONO))) {
printf("Error %d loading char U+%04X\n", err, code_point);
(*glyph_index)++;
return 0;
}
if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) {
printf("Error %d rendering char U+%04X\n", err, code_point);
(*glyph_index)++;
return 0;
}
if ((err = FT_Get_Glyph(face->glyph, &glyph))) {
printf("Error %d getting glyph U+%04X\n", err, code_point);
(*glyph_index)++;
return 0;
}
FT_Bitmap* bitmap = &face->glyph->bitmap;
FT_BitmapGlyphRec* g = (FT_BitmapGlyphRec*)glyph;
// TODO: Restore small font
if (0 /* bSmallFont */) {
#if SMALL_FONT_ENABLED == 1
printf("Stubbed\n");
return 1;
#else
printf("Small font has been disabled\n");
return 1;
#endif
} else {
pGlyphs[*glyph_index].bitmapOffset = *iOffset;
pGlyphs[*glyph_index].width = bitmap->width;
pGlyphs[*glyph_index].height = bitmap->rows;
pGlyphs[*glyph_index].xAdvance = (face->glyph->advance.x >> 6);
pGlyphs[*glyph_index].xOffset = g->left;
pGlyphs[*glyph_index].yOffset = g->top;
}
s = bitmap->buffer;
iPitch = bitmap->pitch;
g5_encode_init(&g5enc, bitmap->width, bitmap->rows, &pBitmap[*iOffset], OUTBUF_SIZE - *iOffset);
for (int y = 0; y < bitmap->rows; y++) {
g5_encode_encodeLine(&g5enc, &s[y * iPitch]);
} // for y
int iLen = g5_encode_getOutSize(&g5enc);
*iOffset += iLen;
FT_Done_Glyph(glyph);
(*glyph_index)++;
return 0;
}
int main(int argc, char* argv[]) {
int i, err, size = 0;
uint32_t iLen, iOffset = 0;
FILE* fOut;
// TrueType library structures
FT_Library library;
char* regularFaceFile;
char* boldFaceFile = NULL;
char* italicFaceFile = NULL;
char* boldItalicFaceFile = NULL;
const char* outputFile = NULL;
FT_Face faceRegular;
FT_Face faceBold;
FT_Face faceItalic;
FT_Face faceBoldItalic;
int bSmallFont = 0; // indicates if we're creating a normal or small font file
CrossPointFontUnicodeInterval* pIntervals;
CrossPointFontGlyph* pGlyphs;
#if SMALL_FONT_ENABLED == 1
CrossPointFontSmallGlyph* pSmallGlyphs;
#endif
uint8_t* pBitmap;
CrossPointFontHeader epdFontHeader;
int bHFile; // flag indicating if the output will be a .H file of hex data
// Process intervals
uint32_t intervals[MAX_INTERVALS][2];
int intervalCount = sizeof(raw_intervals) / sizeof(raw_intervals[0]);
uint32_t totalGlyphs = 0;
if (argc < 6 || argc % 2 == 1) {
printf(
"Usage: %s <regular.ttf> [-b <bold.ttf>] [-i <italic.ttf>] [-bi <bold-italic.ttf>] -p point_size -o <out.cpf "
"or out.h>\n",
argv[0]);
return 1;
}
regularFaceFile = argv[1];
for (int i = 2; i < argc; i += 2) {
if (strcmp(argv[i], "-b") == 0) {
// Bold font
boldFaceFile = argv[i + 1];
} else if (strcmp(argv[i], "-i") == 0) {
// Italic font
italicFaceFile = argv[i + 1];
} else if (strcmp(argv[i], "-bi") == 0) {
// Bold-Italic font
boldItalicFaceFile = argv[i + 1];
} else if (strcmp(argv[i], "-p") == 0) {
// Point size
size = atoi(argv[i + 1]);
} else if (strcmp(argv[i], "-o") == 0) {
// Output file
outputFile = argv[i + 1];
// output an H file?
bHFile = outputFile[strlen(outputFile) - 1] == 'H' || outputFile[strlen(outputFile) - 1] == 'h';
} else {
printf("Unknown argument: %s\n", argv[i]);
return 1;
}
}
if (!outputFile) {
printf("No output file specified\n");
return 1;
}
if (size <= 0) {
printf("Invalid point size: %d\n", size);
return 1;
}
size = size * FONT_SCALE_FACTOR;
bSmallFont = (size < 60) && SMALL_FONT_ENABLED == 1; // Glyph info can fit in signed 8-bit values
int fontVariants = 1; // Always at least one variant we treat as regular
if (boldFaceFile) fontVariants += 1;
if (italicFaceFile) fontVariants += 1;
if (boldItalicFaceFile) fontVariants += 1;
// Copy and sort/merge intervals
if (intervalCount > MAX_INTERVALS) {
printf("Error: too many intervals (max %d)\n", MAX_INTERVALS);
return 1;
}
for (i = 0; i < intervalCount; i++) {
intervals[i][0] = raw_intervals[i][0];
intervals[i][1] = raw_intervals[i][1];
}
intervalCount = sortAndMergeIntervals(intervals, intervalCount);
// Calculate total number of glyphs
for (i = 0; i < intervalCount; i++) {
totalGlyphs += intervals[i][1] - intervals[i][0] + 1;
}
totalGlyphs *= fontVariants;
printf("Processed intervals: %d, total glyphs: %u\n", intervalCount, totalGlyphs);
// Allocate memory for intervals
pIntervals = (CrossPointFontUnicodeInterval*)malloc(intervalCount * sizeof(CrossPointFontUnicodeInterval));
if (!pIntervals) {
printf("Error allocating memory for interval data\n");
return 1;
}
// Allocate memory for glyphs
if (bSmallFont) {
#if SMALL_FONT_ENABLED == 1
pSmallGlyphs = (CrossPointFontSmallGlyph*)malloc(totalGlyphs * sizeof(CrossPointFontSmallGlyph));
if (!pSmallGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
}
#else
printf("Small font has been disabled\n");
return 1;
#endif
} else {
pGlyphs = (CrossPointFontGlyph*)malloc(totalGlyphs * sizeof(CrossPointFontGlyph));
if (!pGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
}
}
pBitmap = (uint8_t*)malloc(OUTBUF_SIZE); // Enough to hold the output
if (!pBitmap) {
printf("Error allocating memory for bitmap data\n");
return 1;
}
// Init FreeType lib, load font
if ((err = FT_Init_FreeType(&library))) {
printf("FreeType init error: %d", err);
return err;
}
// Use TrueType engine version 35, without subpixel rendering.
// This improves clarity of fonts since this library does not
// support rendering multiple levels of gray in a glyph.
// See https://github.com/adafruit/Adafruit-GFX-Library/issues/103
FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35;
FT_Property_Set(library, "truetype", "interpreter-version", &interpreter_version);
if ((err = FT_New_Face(library, regularFaceFile, 0, &faceRegular))) {
printf("Font load error: %d\n", err);
FT_Done_FreeType(library);
return err;
}
if (italicFaceFile && (err = FT_New_Face(library, italicFaceFile, 0, &faceItalic))) {
printf("Font load error: %d\n", err);
FT_Done_FreeType(library);
return err;
}
if (boldFaceFile && (err = FT_New_Face(library, boldFaceFile, 0, &faceBold))) {
printf("Font load error: %d\n", err);
FT_Done_FreeType(library);
return err;
}
if (boldItalicFaceFile && (err = FT_New_Face(library, boldItalicFaceFile, 0, &faceBoldItalic))) {
printf("Font load error: %d\n", err);
FT_Done_FreeType(library);
return err;
}
// Shift the size left by 6 because the library uses '26dot6' fixed-point format
FT_Set_Char_Size(faceRegular, size << 6, 0, DPI, 0);
if (boldFaceFile) FT_Set_Char_Size(faceBold, size << 6, 0, DPI, 0);
if (italicFaceFile) FT_Set_Char_Size(faceItalic, size << 6, 0, DPI, 0);
if (boldItalicFaceFile) FT_Set_Char_Size(faceBoldItalic, size << 6, 0, DPI, 0);
// Build intervals with offsets and process glyphs
uint32_t glyph_index = 0;
for (int iInterval = 0; iInterval < intervalCount; iInterval++) {
const uint32_t intervalStart = intervals[iInterval][0];
const uint32_t intervalEnd = intervals[iInterval][1];
// Store interval with offset
pIntervals[iInterval].first = intervalStart;
pIntervals[iInterval].last = intervalEnd;
pIntervals[iInterval].offset = glyph_index;
// Process each glyph in this interval
// Load the codepoint for each style variant
for (uint32_t codePoint = intervalStart; codePoint <= intervalEnd; codePoint++) {
loadCodePoint(faceRegular, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
if (boldFaceFile) loadCodePoint(faceBold, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
if (italicFaceFile) loadCodePoint(faceItalic, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
if (boldItalicFaceFile) loadCodePoint(faceBoldItalic, codePoint, pGlyphs, pBitmap, &glyph_index, &iOffset);
} // for each code point in interval
} // for each interval
// Try to create the output file
fOut = fopen(outputFile, "w+b");
if (!fOut) {
printf("Error creating output file: %s\n", outputFile);
return 1;
}
epdFontHeader.height = faceRegular->size->metrics.height >> 6;
epdFontHeader.ascender = faceRegular->size->metrics.ascender >> 6;
epdFontHeader.styles = 0b0001;
if (boldFaceFile) epdFontHeader.styles |= 0b0010;
if (italicFaceFile) epdFontHeader.styles |= 0b0100;
if (boldItalicFaceFile) epdFontHeader.styles |= 0b1000;
epdFontHeader.intervalCount = intervalCount;
epdFontHeader.glyphCount = totalGlyphs;
// Write the file header
if (bSmallFont) {
#if SMALL_FONT_ENABLED == 1
epdFontHeader.u16Marker = CPF_FONT_MARKER_SMALL;
if (faceRegular->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
epdFontHeader.height = pSmallGlyphs[0].height;
}
iLen = sizeof(CrossPointFontHeader) + intervalCount * sizeof(CrossPointFontUnicodeInterval) +
totalGlyphs * sizeof(CrossPointFontSmallGlyph) + iOffset;
if (bHFile) { // create an H file of hex values
StartHexFile(fOut, iLen, outputFile, size);
AddHexBytes(fOut, &epdFontHeader, sizeof(CrossPointFontHeader), 0);
// Write the intervals
AddHexBytes(fOut, pIntervals, sizeof(CrossPointFontUnicodeInterval) * intervalCount, 0);
// Write the glyph table
AddHexBytes(fOut, pSmallGlyphs, sizeof(CrossPointFontSmallGlyph) * totalGlyphs, 0);
// Write the compressed bitmap data
AddHexBytes(fOut, pBitmap, iOffset, 1);
} else {
fwrite(&epdFontHeader, 1, sizeof(CrossPointFontHeader), fOut);
// Write the intervals
fwrite(pIntervals, 1, intervalCount * sizeof(CrossPointFontUnicodeInterval), fOut);
// Write the glyph table
fwrite(pSmallGlyphs, 1, totalGlyphs * sizeof(CrossPointFontSmallGlyph), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
}
#else
printf("Small font has been disabled\n");
return 1;
#endif
} else {
epdFontHeader.u16Marker = CPF_FONT_MARKER;
if (faceRegular->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
epdFontHeader.height = pGlyphs[0].height;
}
iLen = sizeof(CrossPointFontHeader) + intervalCount * sizeof(CrossPointFontUnicodeInterval) +
totalGlyphs * sizeof(CrossPointFontGlyph) + iOffset;
if (bHFile) { // create an H file of hex values
StartHexFile(fOut, iLen, outputFile, size);
AddHexBytes(fOut, &epdFontHeader, sizeof(CrossPointFontHeader), 0);
// Write the intervals
AddHexBytes(fOut, pIntervals, sizeof(CrossPointFontUnicodeInterval) * intervalCount, 0);
// Write the glyph table
AddHexBytes(fOut, pGlyphs, sizeof(CrossPointFontGlyph) * totalGlyphs, 0);
// Write the compressed bitmap data
AddHexBytes(fOut, pBitmap, iOffset, 1);
} else {
fwrite(&epdFontHeader, 1, sizeof(CrossPointFontHeader), fOut);
// Write the intervals
fwrite(pIntervals, 1, intervalCount * sizeof(CrossPointFontUnicodeInterval), fOut);
// Write the glyph table
fwrite(pGlyphs, 1, totalGlyphs * sizeof(CrossPointFontGlyph), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
}
} // large fonts
fflush(fOut);
fclose(fOut); // done!
FT_Done_FreeType(library);
printf("Success!\nFont file size: %d bytes (%d glyphs)\n", iLen, totalGlyphs);
return 0;
} /* main() */
#endif