feat: Add custom font selection from SD card
Allow users to select custom fonts (.epdfont files) from the /.crosspoint/fonts/ directory on the SD card for EPUB/TXT reading. Features: - New FontSelectionActivity for browsing and selecting fonts - SdFont and SdFontFamily classes for loading fonts from SD card - Dynamic font reloading without device reboot - Reader cache invalidation when font changes - Hash-based font ID generation for proper cache management The custom fonts use the .epdfont binary format which supports: - 2-bit antialiasing for smooth text rendering - Efficient on-demand glyph loading with LRU cache - Memory-optimized design for ESP32-C3 constraints
This commit is contained in:
@@ -2,7 +2,40 @@
|
||||
|
||||
#include <Utf8.h>
|
||||
|
||||
void GfxRenderer::insertFont(const int fontId, EpdFontFamily font) { fontMap.insert({fontId, font}); }
|
||||
void GfxRenderer::insertFont(const int fontId, const EpdFontFamily* font) {
|
||||
fontMap[fontId] = std::unique_ptr<UnifiedFontFamily>(new UnifiedFontFamily(font));
|
||||
}
|
||||
|
||||
void GfxRenderer::insertSdFont(const int fontId, SdFontFamily* font) {
|
||||
fontMap[fontId] = std::unique_ptr<UnifiedFontFamily>(new UnifiedFontFamily(font));
|
||||
}
|
||||
|
||||
bool GfxRenderer::removeFont(const int fontId) {
|
||||
auto it = fontMap.find(fontId);
|
||||
if (it == fontMap.end()) {
|
||||
return false;
|
||||
}
|
||||
fontMap.erase(it);
|
||||
Serial.printf("[%lu] [GFX] Removed font %d\n", millis(), fontId);
|
||||
return true;
|
||||
}
|
||||
|
||||
int GfxRenderer::getEffectiveFontId(const int fontId) const {
|
||||
if (fontMap.find(fontId) != fontMap.end()) {
|
||||
return fontId;
|
||||
}
|
||||
// Custom font IDs are negative (hash-based), map to CUSTOM_FONT_ID slot (-999999)
|
||||
constexpr int CUSTOM_FONT_ID = -999999;
|
||||
if (fontId < 0 && fontMap.find(CUSTOM_FONT_ID) != fontMap.end()) {
|
||||
return CUSTOM_FONT_ID;
|
||||
}
|
||||
// Font not found, return fallback
|
||||
if (fallbackFontId != 0 && fontMap.find(fallbackFontId) != fontMap.end()) {
|
||||
return fallbackFontId;
|
||||
}
|
||||
// No fallback set or fallback not found, return original (will fail gracefully)
|
||||
return fontId;
|
||||
}
|
||||
|
||||
void GfxRenderer::rotateCoordinates(const int x, const int y, int* rotatedX, int* rotatedY) const {
|
||||
switch (orientation) {
|
||||
@@ -66,26 +99,46 @@ void GfxRenderer::drawPixel(const int x, const int y, const bool state) const {
|
||||
}
|
||||
}
|
||||
|
||||
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontFamily::Style style) const {
|
||||
if (fontMap.count(fontId) == 0) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
int GfxRenderer::getTextWidth(const int fontId, const char* text, const EpdFontStyle style) const {
|
||||
const int effectiveId = getEffectiveFontId(fontId);
|
||||
auto it = fontMap.find(effectiveId);
|
||||
if (it == fontMap.end()) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found (no fallback)\n", millis(), fontId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w = 0, h = 0;
|
||||
fontMap.at(fontId).getTextDimensions(text, &w, &h, style);
|
||||
it->second->getTextDimensions(text, &w, &h, style);
|
||||
|
||||
// Add 1px per character for synthetic bold (bold requested but no bold font)
|
||||
const bool syntheticBold = (style == BOLD || style == BOLD_ITALIC) && !it->second->hasBold();
|
||||
if (syntheticBold && text != nullptr) {
|
||||
// Count UTF-8 characters
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text);
|
||||
int charCount = 0;
|
||||
while (*ptr) {
|
||||
// Count UTF-8 start bytes (not continuation bytes 10xxxxxx)
|
||||
if ((*ptr & 0xC0) != 0x80) {
|
||||
charCount++;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
w += charCount;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
void GfxRenderer::drawCenteredText(const int fontId, const int y, const char* text, const bool black,
|
||||
const EpdFontFamily::Style style) const {
|
||||
const EpdFontStyle style) const {
|
||||
const int x = (getScreenWidth() - getTextWidth(fontId, text, style)) / 2;
|
||||
drawText(fontId, x, y, text, black, style);
|
||||
}
|
||||
|
||||
void GfxRenderer::drawText(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||
const EpdFontFamily::Style style) const {
|
||||
const int yPos = y + getFontAscenderSize(fontId);
|
||||
const EpdFontStyle style) const {
|
||||
const int effectiveId = getEffectiveFontId(fontId);
|
||||
const int yPos = y + getFontAscenderSize(effectiveId);
|
||||
int xpos = x;
|
||||
|
||||
// cannot draw a NULL / empty string
|
||||
@@ -93,11 +146,12 @@ void GfxRenderer::drawText(const int fontId, const int x, const int y, const cha
|
||||
return;
|
||||
}
|
||||
|
||||
if (fontMap.count(fontId) == 0) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
auto it = fontMap.find(effectiveId);
|
||||
if (it == fontMap.end()) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found (no fallback)\n", millis(), fontId);
|
||||
return;
|
||||
}
|
||||
const auto font = fontMap.at(fontId);
|
||||
const auto& font = *(it->second);
|
||||
|
||||
// no printable characters
|
||||
if (!font.hasPrintableChars(text, style)) {
|
||||
@@ -401,7 +455,7 @@ void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) cons
|
||||
}
|
||||
|
||||
std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth,
|
||||
const EpdFontFamily::Style style) const {
|
||||
const EpdFontStyle style) const {
|
||||
std::string item = text;
|
||||
int itemWidth = getTextWidth(fontId, item.c_str(), style);
|
||||
while (itemWidth > maxWidth && item.length() > 8) {
|
||||
@@ -441,37 +495,41 @@ int GfxRenderer::getScreenHeight() const {
|
||||
}
|
||||
|
||||
int GfxRenderer::getSpaceWidth(const int fontId) const {
|
||||
if (fontMap.count(fontId) == 0) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
const int effectiveId = getEffectiveFontId(fontId);
|
||||
auto it = fontMap.find(effectiveId);
|
||||
if (it == fontMap.end()) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found (no fallback)\n", millis(), fontId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fontMap.at(fontId).getGlyph(' ', EpdFontFamily::REGULAR)->advanceX;
|
||||
const EpdGlyph* glyph = it->second->getGlyph(' ', REGULAR);
|
||||
return glyph ? glyph->advanceX : 0;
|
||||
}
|
||||
|
||||
int GfxRenderer::getFontAscenderSize(const int fontId) const {
|
||||
if (fontMap.count(fontId) == 0) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
const int effectiveId = getEffectiveFontId(fontId);
|
||||
auto it = fontMap.find(effectiveId);
|
||||
if (it == fontMap.end()) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found (no fallback)\n", millis(), fontId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
||||
return it->second->getAscender(REGULAR);
|
||||
}
|
||||
|
||||
int GfxRenderer::getLineHeight(const int fontId) const {
|
||||
if (fontMap.count(fontId) == 0) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
const int effectiveId = getEffectiveFontId(fontId);
|
||||
auto it = fontMap.find(effectiveId);
|
||||
if (it == fontMap.end()) {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found (no fallback)\n", millis(), fontId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->advanceY;
|
||||
return it->second->getAdvanceY(REGULAR);
|
||||
}
|
||||
|
||||
void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char* btn2, const char* btn3,
|
||||
const char* btn4) {
|
||||
const Orientation orig_orientation = getOrientation();
|
||||
setOrientation(Orientation::Portrait);
|
||||
|
||||
const char* btn4) const {
|
||||
const int pageHeight = getScreenHeight();
|
||||
constexpr int buttonWidth = 106;
|
||||
constexpr int buttonHeight = 40;
|
||||
@@ -484,15 +542,12 @@ void GfxRenderer::drawButtonHints(const int fontId, const char* btn1, const char
|
||||
// Only draw if the label is non-empty
|
||||
if (labels[i] != nullptr && labels[i][0] != '\0') {
|
||||
const int x = buttonPositions[i];
|
||||
fillRect(x, pageHeight - buttonY, buttonWidth, buttonHeight, false);
|
||||
drawRect(x, pageHeight - buttonY, buttonWidth, buttonHeight);
|
||||
const int textWidth = getTextWidth(fontId, labels[i]);
|
||||
const int textX = x + (buttonWidth - 1 - textWidth) / 2;
|
||||
drawText(fontId, textX, pageHeight - buttonY + textYOffset, labels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
setOrientation(orig_orientation);
|
||||
}
|
||||
|
||||
void GfxRenderer::drawSideButtonHints(const int fontId, const char* topBtn, const char* bottomBtn) const {
|
||||
@@ -551,7 +606,7 @@ int GfxRenderer::getTextHeight(const int fontId) const {
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
return 0;
|
||||
}
|
||||
return fontMap.at(fontId).getData(EpdFontFamily::REGULAR)->ascender;
|
||||
return fontMap.at(fontId)->getAscender(EpdFontFamily::REGULAR);
|
||||
}
|
||||
|
||||
void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y, const char* text, const bool black,
|
||||
@@ -565,10 +620,10 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
|
||||
Serial.printf("[%lu] [GFX] Font %d not found\n", millis(), fontId);
|
||||
return;
|
||||
}
|
||||
const auto font = fontMap.at(fontId);
|
||||
const auto& font = fontMap.at(fontId);
|
||||
|
||||
// No printable characters
|
||||
if (!font.hasPrintableChars(text, style)) {
|
||||
if (!font->hasPrintableChars(text, style)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -580,22 +635,22 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
|
||||
|
||||
uint32_t cp;
|
||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&text)))) {
|
||||
const EpdGlyph* glyph = font.getGlyph(cp, style);
|
||||
const EpdGlyph* glyph = font->getGlyph(cp, style);
|
||||
if (!glyph) {
|
||||
glyph = font.getGlyph('?', style);
|
||||
glyph = font->getGlyph('?', style);
|
||||
}
|
||||
if (!glyph) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int is2Bit = font.getData(style)->is2Bit;
|
||||
const uint32_t offset = glyph->dataOffset;
|
||||
const bool is2BitFont = font->is2Bit(style);
|
||||
const uint8_t width = glyph->width;
|
||||
const uint8_t height = glyph->height;
|
||||
const int left = glyph->left;
|
||||
const int top = glyph->top;
|
||||
const int ascender = font->getAscender(style);
|
||||
|
||||
const uint8_t* bitmap = &font.getData(style)->bitmap[offset];
|
||||
const uint8_t* bitmap = font->getGlyphBitmap(cp, style);
|
||||
|
||||
if (bitmap != nullptr) {
|
||||
for (int glyphY = 0; glyphY < height; glyphY++) {
|
||||
@@ -605,10 +660,10 @@ void GfxRenderer::drawTextRotated90CW(const int fontId, const int x, const int y
|
||||
// 90° clockwise rotation transformation:
|
||||
// screenX = x + (ascender - top + glyphY)
|
||||
// screenY = yPos - (left + glyphX)
|
||||
const int screenX = x + (font.getData(style)->ascender - top + glyphY);
|
||||
const int screenX = x + (ascender - top + glyphY);
|
||||
const int screenY = yPos - left - glyphX;
|
||||
|
||||
if (is2Bit) {
|
||||
if (is2BitFont) {
|
||||
const uint8_t byte = bitmap[pixelPosition / 4];
|
||||
const uint8_t bit_index = (3 - pixelPosition % 4) * 2;
|
||||
const uint8_t bmpVal = 3 - (byte >> bit_index) & 0x3;
|
||||
@@ -745,6 +800,38 @@ void GfxRenderer::restoreBwBuffer() {
|
||||
Serial.printf("[%lu] [GFX] Restored and freed BW buffer chunks\n", millis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy stored BW buffer to framebuffer without freeing the stored chunks.
|
||||
* Use this when you want to restore the buffer but keep it for later reuse.
|
||||
* Returns true if buffer was copied successfully.
|
||||
*/
|
||||
bool GfxRenderer::copyStoredBwBuffer() {
|
||||
// Check if all chunks are allocated
|
||||
for (const auto& bwBufferChunk : bwBufferChunks) {
|
||||
if (!bwBufferChunk) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* frameBuffer = einkDisplay.getFrameBuffer();
|
||||
if (!frameBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < BW_BUFFER_NUM_CHUNKS; i++) {
|
||||
const size_t offset = i * BW_BUFFER_CHUNK_SIZE;
|
||||
memcpy(frameBuffer + offset, bwBufferChunks[i], BW_BUFFER_CHUNK_SIZE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the stored BW buffer chunks manually.
|
||||
* Use this when you no longer need the stored buffer.
|
||||
*/
|
||||
void GfxRenderer::freeStoredBwBuffer() { freeBwBufferChunks(); }
|
||||
|
||||
/**
|
||||
* Cleanup grayscale buffers using the current frame buffer.
|
||||
* Use this when BW buffer was re-rendered instead of stored/restored.
|
||||
@@ -756,11 +843,11 @@ void GfxRenderer::cleanupGrayscaleWithFrameBuffer() const {
|
||||
}
|
||||
}
|
||||
|
||||
void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
|
||||
const bool pixelState, const EpdFontFamily::Style style) const {
|
||||
void GfxRenderer::renderChar(const UnifiedFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
|
||||
const bool pixelState, const EpdFontStyle style) const {
|
||||
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);
|
||||
if (!glyph) {
|
||||
// TODO: Replace with fallback glyph property?
|
||||
// Try fallback glyph
|
||||
glyph = fontFamily.getGlyph('?', style);
|
||||
}
|
||||
|
||||
@@ -770,14 +857,20 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp,
|
||||
return;
|
||||
}
|
||||
|
||||
const int is2Bit = fontFamily.getData(style)->is2Bit;
|
||||
const uint32_t offset = glyph->dataOffset;
|
||||
const bool is2Bit = fontFamily.is2Bit(style);
|
||||
const uint8_t width = glyph->width;
|
||||
const uint8_t height = glyph->height;
|
||||
const int left = glyph->left;
|
||||
|
||||
const uint8_t* bitmap = nullptr;
|
||||
bitmap = &fontFamily.getData(style)->bitmap[offset];
|
||||
// Check if we need synthetic bold (bold requested but no bold font available)
|
||||
const bool syntheticBold = (style == BOLD || style == BOLD_ITALIC) && !fontFamily.hasBold();
|
||||
|
||||
// Get bitmap data (works for both flash and SD fonts)
|
||||
const uint8_t* bitmap = fontFamily.getGlyphBitmap(cp, style);
|
||||
if (!bitmap) {
|
||||
// Try fallback
|
||||
bitmap = fontFamily.getGlyphBitmap('?', style);
|
||||
}
|
||||
|
||||
if (bitmap != nullptr) {
|
||||
for (int glyphY = 0; glyphY < height; glyphY++) {
|
||||
@@ -797,13 +890,22 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp,
|
||||
if (renderMode == BW && bmpVal < 3) {
|
||||
// Black (also paints over the grays in BW mode)
|
||||
drawPixel(screenX, screenY, pixelState);
|
||||
if (syntheticBold) {
|
||||
drawPixel(screenX + 1, screenY, pixelState); // Draw again 1px to the right
|
||||
}
|
||||
} else if (renderMode == GRAYSCALE_MSB && (bmpVal == 1 || bmpVal == 2)) {
|
||||
// Light gray (also mark the MSB if it's going to be a dark gray too)
|
||||
// We have to flag pixels in reverse for the gray buffers, as 0 leave alone, 1 update
|
||||
drawPixel(screenX, screenY, false);
|
||||
if (syntheticBold) {
|
||||
drawPixel(screenX + 1, screenY, false);
|
||||
}
|
||||
} else if (renderMode == GRAYSCALE_LSB && bmpVal == 1) {
|
||||
// Dark gray
|
||||
drawPixel(screenX, screenY, false);
|
||||
if (syntheticBold) {
|
||||
drawPixel(screenX + 1, screenY, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const uint8_t byte = bitmap[pixelPosition / 8];
|
||||
@@ -811,13 +913,17 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp,
|
||||
|
||||
if ((byte >> bit_index) & 1) {
|
||||
drawPixel(screenX, screenY, pixelState);
|
||||
if (syntheticBold) {
|
||||
drawPixel(screenX + 1, screenY, pixelState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*x += glyph->advanceX;
|
||||
// Advance by glyph width, adding 1px for synthetic bold
|
||||
*x += glyph->advanceX + (syntheticBold ? 1 : 0);
|
||||
}
|
||||
|
||||
void GfxRenderer::getOrientedViewableTRBL(int* outTop, int* outRight, int* outBottom, int* outLeft) const {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <EInkDisplay.h>
|
||||
#include <EpdFontFamily.h>
|
||||
#include <SdFontFamily.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "Bitmap.h"
|
||||
|
||||
@@ -29,9 +30,10 @@ class GfxRenderer {
|
||||
RenderMode renderMode;
|
||||
Orientation orientation;
|
||||
uint8_t* bwBufferChunks[BW_BUFFER_NUM_CHUNKS] = {nullptr};
|
||||
std::map<int, EpdFontFamily> fontMap;
|
||||
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
||||
EpdFontFamily::Style style) const;
|
||||
std::map<int, std::unique_ptr<UnifiedFontFamily>> fontMap;
|
||||
int fallbackFontId = 0; // Default fallback font ID (set after fonts are loaded)
|
||||
void renderChar(const UnifiedFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
||||
EpdFontStyle style) const;
|
||||
void freeBwBufferChunks();
|
||||
void rotateCoordinates(int x, int y, int* rotatedX, int* rotatedY) const;
|
||||
|
||||
@@ -44,8 +46,18 @@ class GfxRenderer {
|
||||
static constexpr int VIEWABLE_MARGIN_BOTTOM = 3;
|
||||
static constexpr int VIEWABLE_MARGIN_LEFT = 3;
|
||||
|
||||
// Setup
|
||||
void insertFont(int fontId, EpdFontFamily font);
|
||||
// Setup - Flash fonts (EpdFontFamily) - stores pointer to global font
|
||||
void insertFont(int fontId, const EpdFontFamily* font);
|
||||
// Setup - SD card fonts (SdFontFamily) - takes ownership
|
||||
void insertSdFont(int fontId, SdFontFamily* font);
|
||||
// Set fallback font ID (used when requested font is not found)
|
||||
void setFallbackFont(int fontId) { fallbackFontId = fontId; }
|
||||
// Check if a font is registered
|
||||
bool hasFont(int fontId) const { return fontMap.find(fontId) != fontMap.end(); }
|
||||
// Remove a font from the registry (frees memory for SD fonts)
|
||||
bool removeFont(int fontId);
|
||||
// Get effective font ID (returns fallback if requested font not found)
|
||||
int getEffectiveFontId(int fontId) const;
|
||||
|
||||
// Orientation control (affects logical width/height and coordinate transforms)
|
||||
void setOrientation(const Orientation o) { orientation = o; }
|
||||
@@ -72,19 +84,16 @@ class GfxRenderer {
|
||||
void fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state = true) const;
|
||||
|
||||
// Text
|
||||
int getTextWidth(int fontId, const char* text, EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||
void drawCenteredText(int fontId, int y, const char* text, bool black = true,
|
||||
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||
void drawText(int fontId, int x, int y, const char* text, bool black = true,
|
||||
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||
int getTextWidth(int fontId, const char* text, EpdFontStyle style = REGULAR) const;
|
||||
void drawCenteredText(int fontId, int y, const char* text, bool black = true, EpdFontStyle style = REGULAR) const;
|
||||
void drawText(int fontId, int x, int y, const char* text, bool black = true, EpdFontStyle style = REGULAR) const;
|
||||
int getSpaceWidth(int fontId) const;
|
||||
int getFontAscenderSize(int fontId) const;
|
||||
int getLineHeight(int fontId) const;
|
||||
std::string truncatedText(int fontId, const char* text, int maxWidth,
|
||||
EpdFontFamily::Style style = EpdFontFamily::REGULAR) const;
|
||||
std::string truncatedText(int fontId, const char* text, int maxWidth, EpdFontStyle style = REGULAR) const;
|
||||
|
||||
// UI Components
|
||||
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4);
|
||||
void drawButtonHints(int fontId, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const;
|
||||
void drawSideButtonHints(int fontId, const char* topBtn, const char* bottomBtn) const;
|
||||
|
||||
private:
|
||||
@@ -99,8 +108,10 @@ class GfxRenderer {
|
||||
void copyGrayscaleLsbBuffers() const;
|
||||
void copyGrayscaleMsbBuffers() const;
|
||||
void displayGrayBuffer() const;
|
||||
bool storeBwBuffer(); // Returns true if buffer was stored successfully
|
||||
void restoreBwBuffer(); // Restore and free the stored buffer
|
||||
bool storeBwBuffer(); // Returns true if buffer was stored successfully
|
||||
void restoreBwBuffer(); // Restore and free the stored buffer
|
||||
bool copyStoredBwBuffer(); // Copy stored buffer to framebuffer without freeing
|
||||
void freeStoredBwBuffer(); // Free the stored buffer manually
|
||||
void cleanupGrayscaleWithFrameBuffer() const;
|
||||
|
||||
// Low level functions
|
||||
|
||||
Reference in New Issue
Block a user