8#include "TinyGPU/Font/IFont.h"
9#include "TinyGPU/Surface/ISurface.h"
10#include "TinyGPU/Color/RGB565.h"
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29template <uint8_t Width, uint8_t Height,
typename RowT,
typename RGB_T =
RGB565>
33 using Glyph = std::array<RowT, Height>;
35 using CodePoint = uint32_t;
46 BitmapFont(
const Glyph* table, size_t glyphCount,
char firstChar =
' ')
51 void setProportional(
bool proportional)
override { proportional_ = proportional; }
60 glyphColumnBounds(codePoint, left, right);
71 if (!glyphColumnBounds(codePoint, left, right)) {
74 return static_cast<uint8_t>(right - left + 1);
84 const Glyph&
glyph(
char character)
const {
85 return glyph(
static_cast<CodePoint>(
static_cast<
unsigned char>(character)));
94 virtual const Glyph&
glyph(CodePoint codePoint)
const {
95 const auto first =
static_cast<
unsigned char>(firstChar_);
96 if (codePoint >= first && codePoint < first + glyphCount_) {
97 return table_[codePoint - first];
99 return replacementGlyph();
103 bool pixel(CodePoint codePoint, uint8_t x, uint8_t y)
const {
107 const RowT rowMask = glyph(codePoint)[y];
108 return (rowMask &
static_cast<RowT>(RowT(1) << (
kGlyphWidth - 1 - x))) != 0;
114 CodePoint codePoint, RGB_T foreground,
115 RGB_T background = RGB_T(0),
bool opaque =
false,
116 uint8_t scale = 1)
const {
121 uint8_t startColumn = 0;
126 if (glyphColumnBounds(codePoint, left, right)) {
128 endColumn =
static_cast<uint8_t>(right + 1);
135 for (uint8_t column = startColumn; column < endColumn; ++column) {
136 const int16_t pixelX =
137 static_cast<int16_t>(x + ((column - startColumn) * scale));
138 const int16_t pixelY =
static_cast<int16_t>(y + (row * scale));
141 target.fillRect(pixelX, pixelY, scale, scale, foreground);
143 target.fillRect(pixelX, pixelY, scale, scale, background);
150 void drawChar(ISurface<RGB_T>& target, int16_t x, int16_t y,
char character,
151 RGB_T foreground, RGB_T background = RGB_T(0),
152 bool opaque =
false, uint8_t scale = 1)
const {
154 static_cast<CodePoint>(
static_cast<
unsigned char>(character))
,
155 foreground
, background
, opaque
, scale
);
159 void drawText(ISurface<RGB_T>& target, int16_t x, int16_t y,
const char* text,
160 RGB_T foreground, RGB_T background = RGB_T(0),
161 bool opaque =
false, uint8_t scale = 1, uint8_t spacing = 1,
162 uint8_t lineSpacing = 1)
const override {
163 if (text ==
nullptr) {
170 const int16_t advanceY =
171 static_cast<int16_t>((
kGlyphHeight * scale) + lineSpacing);
175 const char* current = text;
176 while (*current !=
'\0') {
177 if (*current ==
'\n') {
180 cursorY =
static_cast<int16_t>(cursorY + advanceY);
184 const CodePoint codePoint = decodeNextUtf8(current);
187 const int16_t advanceX =
static_cast<int16_t>(
189 cursorX =
static_cast<int16_t>(cursorX + advanceX);
196 if (text ==
nullptr || *text ==
'\0') {
203 size_t lineWidth = 0;
204 size_t lineChars = 0;
205 size_t longestLineWidth = 0;
206 const char* current = text;
207 while (*current !=
'\0') {
208 if (*current ==
'\n') {
210 if (lineWidth > longestLineWidth) {
211 longestLineWidth = lineWidth;
216 const CodePoint codePoint = decodeNextUtf8(current);
218 lineWidth += spacing;
220 lineWidth +=
static_cast<size_t>(glyphAdvanceWidth(codePoint)) * scale;
225 if (lineWidth > longestLineWidth) {
226 longestLineWidth = lineWidth;
229 return longestLineWidth;
235 if (text ==
nullptr || *text ==
'\0') {
243 for (
const char* current = text; *current !=
'\0'; ++current) {
244 if (*current ==
'\n') {
249 return (lines * kGlyphHeight * scale) + ((lines - 1) * lineSpacing);
261 bool proportional_ =
true;
266 bool glyphColumnBounds(CodePoint codePoint, uint8_t& leftColumn,
267 uint8_t& rightColumn)
const {
268 const Glyph& glyphData = glyph(codePoint);
273 const RowT rowMask = glyphData[row];
277 for (uint8_t column = 0; column <
kGlyphWidth; ++column) {
278 if ((rowMask &
static_cast<RowT>(RowT(1) << (
kGlyphWidth - 1 - column))) !=
284 if (column > right) {
299 const Glyph& replacementGlyph()
const {
300 const auto first =
static_cast<
unsigned char>(firstChar_);
301 const auto question =
static_cast<
unsigned char>(
'?');
302 if (question >= first && question < first + glyphCount_) {
303 return table_[question - first];
308 CodePoint decodeNextUtf8(
const char*& current)
const {
309 const unsigned char firstByte =
static_cast<
unsigned char>(*current);
310 if (firstByte == 0) {
314 if ((firstByte & 0x80U) == 0) {
316 return static_cast<CodePoint>(firstByte);
319 uint8_t sequenceLength = 0;
320 CodePoint codePoint = 0;
321 if ((firstByte & 0xE0U) == 0xC0U) {
323 codePoint =
static_cast<CodePoint>(firstByte & 0x1FU);
324 }
else if ((firstByte & 0xF0U) == 0xE0U) {
326 codePoint =
static_cast<CodePoint>(firstByte & 0x0FU);
327 }
else if ((firstByte & 0xF8U) == 0xF0U) {
329 codePoint =
static_cast<CodePoint>(firstByte & 0x07U);
332 return static_cast<CodePoint>(
'?');
335 for (uint8_t index = 1; index < sequenceLength; ++index) {
336 const unsigned char nextByte =
static_cast<
unsigned char>(current[index]);
337 if (nextByte == 0 || (nextByte & 0xC0U) != 0x80U) {
339 return static_cast<CodePoint>(
'?');
342 codePoint =
static_cast<CodePoint>((codePoint << 6) | (nextByte & 0x3FU));
345 current += sequenceLength;
Fixed-size bitmap font backed by a caller-supplied glyph table.
Definition: BitmapFont.h:30
virtual const Glyph & glyph(CodePoint codePoint) const
Definition: BitmapFont.h:94
void drawChar(ISurface< RGB_T > &target, int16_t x, int16_t y, char character, RGB_T foreground, RGB_T background=RGB_T(0), bool opaque=false, uint8_t scale=1) const
Draws a single character.
Definition: BitmapFont.h:150
size_t getHeight(uint8_t scale) const override
Returns the scaled glyph height in pixels.
Definition: BitmapFont.h:253
static constexpr uint8_t kGlyphWidth
Width of a glyph in pixels.
Definition: BitmapFont.h:38
static constexpr uint8_t kBlankGlyphWidth
Definition: BitmapFont.h:43
uint8_t glyphAdvanceWidth(CodePoint codePoint) const
Definition: BitmapFont.h:79
uint8_t glyphEffectiveWidth(CodePoint codePoint) const
Definition: BitmapFont.h:68
void drawText(ISurface< RGB_T > &target, int16_t x, int16_t y, const char *text, RGB_T foreground, RGB_T background=RGB_T(0), bool opaque=false, uint8_t scale=1, uint8_t spacing=1, uint8_t lineSpacing=1) const override
Draws a UTF-8 text string.
Definition: BitmapFont.h:159
static constexpr uint8_t kGlyphHeight
Height of a glyph in pixels.
Definition: BitmapFont.h:40
bool pixel(CodePoint codePoint, uint8_t x, uint8_t y) const
Returns whether a pixel is set in a code point glyph.
Definition: BitmapFont.h:103
size_t measureTextHeight(const char *text, uint8_t scale=1, uint8_t lineSpacing=1) const override
Returns the total text height in pixels.
Definition: BitmapFont.h:233
void drawCodePoint(ISurface< RGB_T > &target, int16_t x, int16_t y, CodePoint codePoint, RGB_T foreground, RGB_T background=RGB_T(0), bool opaque=false, uint8_t scale=1) const
Definition: BitmapFont.h:113
size_t measureTextWidth(const char *text, uint8_t scale=1, uint8_t spacing=1) const override
Returns the width of the longest text line in pixels.
Definition: BitmapFont.h:194
void setProportional(bool proportional) override
Definition: BitmapFont.h:51
bool isProportional() const override
Returns whether proportional spacing is enabled.
Definition: BitmapFont.h:53
BitmapFont(const Glyph *table, size_t glyphCount, char firstChar=' ')
Creates a fixed bitmap font over a contiguous glyph table.
Definition: BitmapFont.h:46
const Glyph & glyph(char character) const
Returns the glyph for an 8-bit character.
Definition: BitmapFont.h:84
uint8_t glyphLeftOffset(CodePoint codePoint) const
Definition: BitmapFont.h:57
Font rendering interface for TinyGPU-compatible framebuffers.
Definition: IFont.h:19
RGB color stored in 16-bit RGB565 format, byte-swapped from the conventional bit layout.
Definition: RGB565.h:24
Definition: DSIBusESP32.h:19