mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2025-12-18 15:17:42 +03:00
Wrap up multiple font styles into EpdFontFamily
This commit is contained in:
parent
fa0f27df6a
commit
05a027e2bf
37
lib/EpdFont/EpdFontFamily.cpp
Normal file
37
lib/EpdFont/EpdFontFamily.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "EpdFontFamily.h"
|
||||||
|
|
||||||
|
const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
|
||||||
|
if (style == BOLD && bold) {
|
||||||
|
return bold;
|
||||||
|
}
|
||||||
|
if (style == ITALIC && italic) {
|
||||||
|
return italic;
|
||||||
|
}
|
||||||
|
if (style == BOLD_ITALIC) {
|
||||||
|
if (boldItalic) {
|
||||||
|
return boldItalic;
|
||||||
|
}
|
||||||
|
if (bold) {
|
||||||
|
return bold;
|
||||||
|
}
|
||||||
|
if (italic) {
|
||||||
|
return italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return regular;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdFontFamily::getTextDimensions(const char* string, int* w, int* h, const EpdFontStyle style) const {
|
||||||
|
getFont(style)->getTextDimensions(string, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EpdFontFamily::hasPrintableChars(const char* string, const EpdFontStyle style) const {
|
||||||
|
return getFont(style)->hasPrintableChars(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
const EpdFontData* EpdFontFamily::getData(const EpdFontStyle style) const { return getFont(style)->data; }
|
||||||
|
|
||||||
|
const EpdGlyph* EpdFontFamily::getGlyph(const uint32_t cp, const EpdFontStyle style) const {
|
||||||
|
return getFont(style)->getGlyph(cp);
|
||||||
|
};
|
||||||
24
lib/EpdFont/EpdFontFamily.h
Normal file
24
lib/EpdFont/EpdFontFamily.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "EpdFont.h"
|
||||||
|
|
||||||
|
enum EpdFontStyle { REGULAR, BOLD, ITALIC, BOLD_ITALIC };
|
||||||
|
|
||||||
|
class EpdFontFamily {
|
||||||
|
const EpdFont* regular;
|
||||||
|
const EpdFont* bold;
|
||||||
|
const EpdFont* italic;
|
||||||
|
const EpdFont* boldItalic;
|
||||||
|
|
||||||
|
const EpdFont* getFont(EpdFontStyle style) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit EpdFontFamily(const EpdFont* regular, const EpdFont* bold = nullptr, const EpdFont* italic = nullptr,
|
||||||
|
const EpdFont* boldItalic = nullptr)
|
||||||
|
: regular(regular), bold(bold), italic(italic), boldItalic(boldItalic) {}
|
||||||
|
~EpdFontFamily() = default;
|
||||||
|
void getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style = REGULAR) const;
|
||||||
|
bool hasPrintableChars(const char* string, EpdFontStyle style = REGULAR) const;
|
||||||
|
|
||||||
|
const EpdFontData* getData(EpdFontStyle style = REGULAR) const;
|
||||||
|
const EpdGlyph* getGlyph(uint32_t cp, EpdFontStyle style = REGULAR) const;
|
||||||
|
};
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <EpdFont.h>
|
#include <EpdFontFamily.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
#include <Utf8.h>
|
#include <Utf8.h>
|
||||||
#include <miniz.h>
|
#include <miniz.h>
|
||||||
@ -12,13 +12,14 @@ static tinfl_decompressor decomp;
|
|||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
class EpdFontRenderer {
|
class EpdFontRenderer {
|
||||||
Renderable* renderer;
|
Renderable* renderer;
|
||||||
void renderChar(uint32_t cp, int* x, const int* y, uint16_t color);
|
void renderChar(uint32_t cp, int* x, const int* y, uint16_t color, EpdFontStyle style = REGULAR);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const EpdFont* font;
|
const EpdFontFamily* fontFamily;
|
||||||
explicit EpdFontRenderer(const EpdFont* font, Renderable* renderer);
|
explicit EpdFontRenderer(const EpdFontFamily* fontFamily, Renderable* renderer)
|
||||||
|
: fontFamily(fontFamily), renderer(renderer) {}
|
||||||
~EpdFontRenderer() = default;
|
~EpdFontRenderer() = default;
|
||||||
void renderString(const char* string, int* x, int* y, uint16_t color);
|
void renderString(const char* string, int* x, int* y, uint16_t color, EpdFontStyle style = REGULAR);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* source, size_t sourceSize) {
|
inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* source, size_t sourceSize) {
|
||||||
@ -38,37 +39,33 @@ inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* sou
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
EpdFontRenderer<Renderable>::EpdFontRenderer(const EpdFont* font, Renderable* renderer) {
|
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color,
|
||||||
this->font = font;
|
const EpdFontStyle style) {
|
||||||
this->renderer = renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Renderable>
|
|
||||||
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color) {
|
|
||||||
// cannot draw a NULL / empty string
|
// cannot draw a NULL / empty string
|
||||||
if (string == nullptr || *string == '\0') {
|
if (string == nullptr || *string == '\0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no printable characters
|
// no printable characters
|
||||||
if (!font->hasPrintableChars(string)) {
|
if (!fontFamily->hasPrintableChars(string, style)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t cp;
|
uint32_t cp;
|
||||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
||||||
renderChar(cp, x, y, color);
|
renderChar(cp, x, y, color, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
*y += font->data->advanceY;
|
*y += fontFamily->getData(style)->advanceY;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Renderable>
|
template <typename Renderable>
|
||||||
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color) {
|
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color,
|
||||||
const EpdGlyph* glyph = font->getGlyph(cp);
|
const EpdFontStyle style) {
|
||||||
|
const EpdGlyph* glyph = fontFamily->getGlyph(cp, style);
|
||||||
if (!glyph) {
|
if (!glyph) {
|
||||||
// TODO: Replace with fallback glyph property?
|
// TODO: Replace with fallback glyph property?
|
||||||
glyph = font->getGlyph('?');
|
glyph = fontFamily->getGlyph('?', style);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no glyph?
|
// no glyph?
|
||||||
@ -86,17 +83,17 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
|||||||
const unsigned long bitmapSize = byteWidth * height;
|
const unsigned long bitmapSize = byteWidth * height;
|
||||||
const uint8_t* bitmap = nullptr;
|
const uint8_t* bitmap = nullptr;
|
||||||
|
|
||||||
if (font->data->compressed) {
|
if (fontFamily->getData(style)->compressed) {
|
||||||
auto* tmpBitmap = static_cast<uint8_t*>(malloc(bitmapSize));
|
auto* tmpBitmap = static_cast<uint8_t*>(malloc(bitmapSize));
|
||||||
if (tmpBitmap == nullptr && bitmapSize) {
|
if (tmpBitmap == nullptr && bitmapSize) {
|
||||||
Serial.println("Failed to allocate memory for decompression buffer");
|
Serial.println("Failed to allocate memory for decompression buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uncompress(tmpBitmap, bitmapSize, &font->data->bitmap[offset], glyph->compressedSize);
|
uncompress(tmpBitmap, bitmapSize, &fontFamily->getData(style)->bitmap[offset], glyph->compressedSize);
|
||||||
bitmap = tmpBitmap;
|
bitmap = tmpBitmap;
|
||||||
} else {
|
} else {
|
||||||
bitmap = &font->data->bitmap[offset];
|
bitmap = &fontFamily->getData(style)->bitmap[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap != nullptr) {
|
if (bitmap != nullptr) {
|
||||||
@ -123,7 +120,7 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (font->data->compressed) {
|
if (fontFamily->getData(style)->compressed) {
|
||||||
free(const_cast<uint8_t*>(bitmap));
|
free(const_cast<uint8_t*>(bitmap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,13 @@
|
|||||||
#include "builtinFonts/bookerly_italic.h"
|
#include "builtinFonts/bookerly_italic.h"
|
||||||
|
|
||||||
EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
||||||
|
const auto bookerlyFontFamily = new EpdFontFamily(new EpdFont(&bookerly), new EpdFont(&bookerly_bold),
|
||||||
|
new EpdFont(&bookerly_italic), new EpdFont(&bookerly_bold_italic));
|
||||||
|
const auto babyblueFontFamily = new EpdFontFamily(new EpdFont(&babyblue));
|
||||||
|
|
||||||
this->display = display;
|
this->display = display;
|
||||||
this->regularFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly), display);
|
this->regularFontRenderer = new EpdFontRenderer<XteinkDisplay>(bookerlyFontFamily, display);
|
||||||
this->boldFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold), display);
|
this->smallFontRenderer = new EpdFontRenderer<XteinkDisplay>(babyblueFontFamily, display);
|
||||||
this->italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_italic), display);
|
|
||||||
this->bold_italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold_italic), display);
|
|
||||||
this->smallFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&babyblue), display);
|
|
||||||
|
|
||||||
this->marginTop = 11;
|
this->marginTop = 11;
|
||||||
this->marginBottom = 30;
|
this->marginBottom = 30;
|
||||||
@ -21,50 +22,38 @@ EpdRenderer::EpdRenderer(XteinkDisplay* display) {
|
|||||||
this->lineCompression = 0.95f;
|
this->lineCompression = 0.95f;
|
||||||
}
|
}
|
||||||
|
|
||||||
EpdFontRenderer<XteinkDisplay>* EpdRenderer::getFontRenderer(const bool bold, const bool italic) const {
|
int EpdRenderer::getTextWidth(const char* text, const EpdFontStyle style) const {
|
||||||
if (bold && italic) {
|
|
||||||
return bold_italicFont;
|
|
||||||
}
|
|
||||||
if (bold) {
|
|
||||||
return boldFont;
|
|
||||||
}
|
|
||||||
if (italic) {
|
|
||||||
return italicFont;
|
|
||||||
}
|
|
||||||
return regularFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
int EpdRenderer::getTextWidth(const char* text, const bool bold, const bool italic) const {
|
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
|
|
||||||
getFontRenderer(bold, italic)->font->getTextDimensions(text, &w, &h);
|
regularFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpdRenderer::getSmallTextWidth(const char* text) const {
|
int EpdRenderer::getSmallTextWidth(const char* text, const EpdFontStyle style) const {
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
|
|
||||||
smallFont->font->getTextDimensions(text, &w, &h);
|
smallFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
|
||||||
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawText(const int x, const int y, const char* text, const bool bold, const bool italic,
|
void EpdRenderer::drawText(const int x, const int y, const char* text, const uint16_t color,
|
||||||
const uint16_t color) const {
|
const EpdFontStyle style) const {
|
||||||
int ypos = y + getLineHeight() + marginTop;
|
int ypos = y + getLineHeight() + marginTop;
|
||||||
int xpos = x + marginLeft;
|
int xpos = x + marginLeft;
|
||||||
getFontRenderer(bold, italic)->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
regularFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color) const {
|
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color,
|
||||||
int ypos = y + smallFont->font->data->advanceY + marginTop;
|
const EpdFontStyle style) const {
|
||||||
|
int ypos = y + smallFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
|
||||||
int xpos = x + marginLeft;
|
int xpos = x + marginLeft;
|
||||||
smallFont->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
smallFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
|
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
|
||||||
const bool bold, const bool italic) const {
|
const EpdFontStyle style) const {
|
||||||
const size_t length = text.length();
|
const size_t length = text.length();
|
||||||
// fit the text into the box
|
// fit the text into the box
|
||||||
int start = 0;
|
int start = 0;
|
||||||
@ -72,7 +61,7 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
|
|||||||
int ypos = 0;
|
int ypos = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
if (end >= length) {
|
if (end >= length) {
|
||||||
drawText(x, y + ypos, text.substr(start, length - start).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, length - start).c_str(), 1, style);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,15 +70,15 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (text[end - 1] == '\n') {
|
if (text[end - 1] == '\n') {
|
||||||
drawText(x, y + ypos, text.substr(start, end - start).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, end - start).c_str(), 1, style);
|
||||||
ypos += getLineHeight();
|
ypos += getLineHeight();
|
||||||
start = end;
|
start = end;
|
||||||
end = start + 1;
|
end = start + 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getTextWidth(text.substr(start, end - start).c_str(), bold, italic) > width) {
|
if (getTextWidth(text.substr(start, end - start).c_str(), style) > width) {
|
||||||
drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), bold, italic);
|
drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), 1, style);
|
||||||
ypos += getLineHeight();
|
ypos += getLineHeight();
|
||||||
start = end - 1;
|
start = end - 1;
|
||||||
continue;
|
continue;
|
||||||
@ -108,11 +97,23 @@ void EpdRenderer::drawRect(const int x, const int y, const int width, const int
|
|||||||
display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EpdRenderer::fillRect(const int x, const int y, const int width, const int height,
|
void EpdRenderer::fillRect(const int x, const int y, const int width, const int height, const uint16_t color) const {
|
||||||
const uint16_t color = 0) const {
|
|
||||||
display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawCircle(const int x, const int y, const int radius, const uint16_t color) const {
|
||||||
|
display->drawCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::fillCircle(const int x, const int y, const int radius, const uint16_t color) const {
|
||||||
|
display->fillCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EpdRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
|
||||||
|
const bool invert) const {
|
||||||
|
display->drawImage(bitmap, x + marginLeft, y + marginTop, width, height, invert);
|
||||||
|
}
|
||||||
|
|
||||||
void EpdRenderer::clearScreen(const bool black) const {
|
void EpdRenderer::clearScreen(const bool black) const {
|
||||||
Serial.println("Clearing screen");
|
Serial.println("Clearing screen");
|
||||||
display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
|
display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
|
||||||
@ -121,30 +122,15 @@ void EpdRenderer::clearScreen(const bool black) const {
|
|||||||
void EpdRenderer::flushDisplay(const bool partialUpdate) const { display->display(partialUpdate); }
|
void EpdRenderer::flushDisplay(const bool partialUpdate) const { display->display(partialUpdate); }
|
||||||
|
|
||||||
void EpdRenderer::flushArea(const int x, const int y, const int width, const int height) const {
|
void EpdRenderer::flushArea(const int x, const int y, const int width, const int height) const {
|
||||||
display->displayWindow(x, y, width, height);
|
display->displayWindow(x + marginLeft, y + marginTop, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; }
|
int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; }
|
||||||
|
|
||||||
int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; }
|
int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; }
|
||||||
|
|
||||||
int EpdRenderer::getSpaceWidth() const { return regularFont->font->getGlyph(' ')->advanceX; }
|
int EpdRenderer::getSpaceWidth() const { return regularFontRenderer->fontFamily->getGlyph(' ', REGULAR)->advanceX; }
|
||||||
|
|
||||||
int EpdRenderer::getLineHeight() const { return regularFont->font->data->advanceY * lineCompression; }
|
int EpdRenderer::getLineHeight() const {
|
||||||
|
return regularFontRenderer->fontFamily->getData(REGULAR)->advanceY * lineCompression;
|
||||||
// deep sleep helper - persist any state to disk that may be needed on wake
|
}
|
||||||
bool EpdRenderer::dehydrate() {
|
|
||||||
// TODO: Implement
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// deep sleep helper - retrieve any state from disk after wake
|
|
||||||
bool EpdRenderer::hydrate() {
|
|
||||||
// TODO: Implement
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// really really clear the screen
|
|
||||||
void EpdRenderer::reset() {
|
|
||||||
// TODO: Implement
|
|
||||||
};
|
|
||||||
|
|||||||
@ -8,30 +8,28 @@
|
|||||||
|
|
||||||
class EpdRenderer {
|
class EpdRenderer {
|
||||||
XteinkDisplay* display;
|
XteinkDisplay* display;
|
||||||
EpdFontRenderer<XteinkDisplay>* regularFont;
|
EpdFontRenderer<XteinkDisplay>* regularFontRenderer;
|
||||||
EpdFontRenderer<XteinkDisplay>* boldFont;
|
EpdFontRenderer<XteinkDisplay>* smallFontRenderer;
|
||||||
EpdFontRenderer<XteinkDisplay>* italicFont;
|
|
||||||
EpdFontRenderer<XteinkDisplay>* bold_italicFont;
|
|
||||||
EpdFontRenderer<XteinkDisplay>* smallFont;
|
|
||||||
int marginTop;
|
int marginTop;
|
||||||
int marginBottom;
|
int marginBottom;
|
||||||
int marginLeft;
|
int marginLeft;
|
||||||
int marginRight;
|
int marginRight;
|
||||||
float lineCompression;
|
float lineCompression;
|
||||||
EpdFontRenderer<XteinkDisplay>* getFontRenderer(bool bold, bool italic) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit EpdRenderer(XteinkDisplay* display);
|
explicit EpdRenderer(XteinkDisplay* display);
|
||||||
~EpdRenderer() = default;
|
~EpdRenderer() = default;
|
||||||
int getTextWidth(const char* text, bool bold = false, bool italic = false) const;
|
int getTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
|
||||||
int getSmallTextWidth(const char* text) const;
|
int getSmallTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
|
||||||
void drawText(int x, int y, const char* text, bool bold = false, bool italic = false, uint16_t color = 1) const;
|
void drawText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
|
||||||
void drawSmallText(int x, int y, const char* text, uint16_t color = 1) const;
|
void drawSmallText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
|
||||||
void drawTextBox(int x, int y, const std::string& text, int width, int height, bool bold = false,
|
void drawTextBox(int x, int y, const std::string& text, int width, int height, EpdFontStyle style = REGULAR) const;
|
||||||
bool italic = false) const;
|
void drawLine(int x1, int y1, int x2, int y2, uint16_t color = 1) const;
|
||||||
void drawLine(int x1, int y1, int x2, int y2, uint16_t color) const;
|
void drawRect(int x, int y, int width, int height, uint16_t color = 1) const;
|
||||||
void drawRect(int x, int y, int width, int height, uint16_t color) const;
|
void fillRect(int x, int y, int width, int height, uint16_t color = 1) const;
|
||||||
void fillRect(int x, int y, int width, int height, uint16_t color) const;
|
void drawCircle(int x, int y, int radius, uint16_t color = 1) const;
|
||||||
|
void fillCircle(int x, int y, int radius, uint16_t color = 1) const;
|
||||||
|
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false) const;
|
||||||
void clearScreen(bool black = false) const;
|
void clearScreen(bool black = false) const;
|
||||||
void flushDisplay(bool partialUpdate = true) const;
|
void flushDisplay(bool partialUpdate = true) const;
|
||||||
void flushArea(int x, int y, int width, int height) const;
|
void flushArea(int x, int y, int width, int height) const;
|
||||||
@ -45,12 +43,4 @@ class EpdRenderer {
|
|||||||
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
|
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
|
||||||
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
|
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
|
||||||
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
|
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
|
||||||
// deep sleep helper - persist any state to disk that may be needed on wake
|
|
||||||
bool dehydrate();
|
|
||||||
// deep sleep helper - retrieve any state from disk after wake
|
|
||||||
bool hydrate();
|
|
||||||
// really really clear the screen
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
uint8_t temperature = 20;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -107,11 +107,11 @@ void Section::renderPage() {
|
|||||||
delete p;
|
delete p;
|
||||||
} else if (pageCount == 0) {
|
} else if (pageCount == 0) {
|
||||||
Serial.println("No pages to render");
|
Serial.println("No pages to render");
|
||||||
const int width = renderer->getTextWidth("Empty chapter", true);
|
const int width = renderer->getTextWidth("Empty chapter", BOLD);
|
||||||
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", true);
|
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", 1, BOLD);
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
|
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
|
||||||
const int width = renderer->getTextWidth("Out of bounds", true);
|
const int width = renderer->getTextWidth("Out of bounds", BOLD);
|
||||||
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", true);
|
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", 1, BOLD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,17 @@ std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer* renderer) {
|
|||||||
uint16_t wordWidths[totalWordCount];
|
uint16_t wordWidths[totalWordCount];
|
||||||
for (int i = 0; i < words.size(); i++) {
|
for (int i = 0; i < words.size(); i++) {
|
||||||
// measure the word
|
// measure the word
|
||||||
const int width = renderer->getTextWidth(words[i].c_str(), wordStyles[i] & BOLD_SPAN, wordStyles[i] & ITALIC_SPAN);
|
EpdFontStyle fontStyle = REGULAR;
|
||||||
|
if (wordStyles[i] & BOLD_SPAN) {
|
||||||
|
if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = BOLD_ITALIC;
|
||||||
|
} else {
|
||||||
|
fontStyle = BOLD;
|
||||||
|
}
|
||||||
|
} else if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = ITALIC;
|
||||||
|
}
|
||||||
|
const int width = renderer->getTextWidth(words[i].c_str(), fontStyle);
|
||||||
wordWidths[i] = width;
|
wordWidths[i] = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +192,18 @@ void TextBlock::render(const EpdRenderer* renderer, const int x, const int y) co
|
|||||||
// get the style
|
// get the style
|
||||||
const uint8_t wordStyle = wordStyles[i];
|
const uint8_t wordStyle = wordStyles[i];
|
||||||
// render the word
|
// render the word
|
||||||
renderer->drawText(x + wordXpos[i], y, words[i].c_str(), wordStyle & BOLD_SPAN, wordStyle & ITALIC_SPAN);
|
EpdFontStyle fontStyle = REGULAR;
|
||||||
|
if (wordStyles[i] & BOLD_SPAN) {
|
||||||
|
if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = BOLD_ITALIC;
|
||||||
|
} else {
|
||||||
|
fontStyle = BOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (wordStyles[i] & ITALIC_SPAN) {
|
||||||
|
fontStyle = ITALIC;
|
||||||
|
}
|
||||||
|
renderer->drawText(x + wordXpos[i], y, words[i].c_str(), 1, fontStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ void waitForNoButton() {
|
|||||||
|
|
||||||
// Enter deep sleep mode
|
// Enter deep sleep mode
|
||||||
void enterDeepSleep() {
|
void enterDeepSleep() {
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", true, false, true));
|
enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", BOLD, true));
|
||||||
|
|
||||||
Serial.println("Power button released after a long press. Entering deep sleep.");
|
Serial.println("Power button released after a long press. Entering deep sleep.");
|
||||||
delay(1000); // Allow Serial buffer to empty and display to update
|
delay(1000); // Allow Serial buffer to empty and display to update
|
||||||
@ -137,7 +137,7 @@ void setup() {
|
|||||||
display.setTextColor(GxEPD_BLACK);
|
display.setTextColor(GxEPD_BLACK);
|
||||||
Serial.println("Display initialized");
|
Serial.println("Display initialized");
|
||||||
|
|
||||||
enterNewScreen(new FullScreenMessageScreen(renderer, "Booting...", true));
|
enterNewScreen(new FullScreenMessageScreen(renderer, "Booting...", BOLD));
|
||||||
|
|
||||||
// SD Card Initialization
|
// SD Card Initialization
|
||||||
SD.begin(SD_SPI_CS, SPI, SPI_FQ);
|
SD.begin(SD_SPI_CS, SPI, SPI_FQ);
|
||||||
|
|||||||
@ -134,8 +134,8 @@ void EpubReaderScreen::renderPage() {
|
|||||||
const int h = renderer->getLineHeight() + margin * 2;
|
const int h = renderer->getLineHeight() + margin * 2;
|
||||||
renderer->fillRect(x, y, w, h, 0);
|
renderer->fillRect(x, y, w, h, 0);
|
||||||
renderer->drawText(x + margin, y + margin, "Indexing...");
|
renderer->drawText(x + margin, y + margin, "Indexing...");
|
||||||
renderer->drawRect(x + 5, y + 5, w - 10, h - 10, 1);
|
renderer->drawRect(x + 5, y + 5, w - 10, h - 10);
|
||||||
renderer->flushArea(x - 20, y - 20, w + 40, h + 40);
|
renderer->flushArea(x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
section->setupCacheDir();
|
section->setupCacheDir();
|
||||||
@ -199,23 +199,23 @@ void EpubReaderScreen::renderStatusBar() const {
|
|||||||
constexpr int y = 772;
|
constexpr int y = 772;
|
||||||
|
|
||||||
// Top line
|
// Top line
|
||||||
renderer->drawLine(x, y, x + batteryWidth - 4, y, 1);
|
renderer->drawLine(x, y, x + batteryWidth - 4, y);
|
||||||
// Bottom line
|
// Bottom line
|
||||||
renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1, 1);
|
renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
|
||||||
// Left line
|
// Left line
|
||||||
renderer->drawLine(x, y, x, y + batteryHeight - 1, 1);
|
renderer->drawLine(x, y, x, y + batteryHeight - 1);
|
||||||
// Battery end
|
// Battery end
|
||||||
renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1, 1);
|
renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
|
||||||
renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3);
|
||||||
renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3);
|
||||||
renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3, 1);
|
renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
|
||||||
|
|
||||||
// The +1 is to round up, so that we always fill at least one pixel
|
// The +1 is to round up, so that we always fill at least one pixel
|
||||||
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
|
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
|
||||||
if (filledWidth > batteryWidth - 5) {
|
if (filledWidth > batteryWidth - 5) {
|
||||||
filledWidth = batteryWidth - 5; // Ensure we don't overflow
|
filledWidth = batteryWidth - 5; // Ensure we don't overflow
|
||||||
}
|
}
|
||||||
renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2, 1);
|
renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
|
||||||
|
|
||||||
// Page width minus existing content with 30px padding on each side
|
// Page width minus existing content with 30px padding on each side
|
||||||
const int leftMargin = 20 + percentageTextWidth + 30;
|
const int leftMargin = 20 + percentageTextWidth + 30;
|
||||||
|
|||||||
@ -91,14 +91,14 @@ void FileSelectionScreen::render() const {
|
|||||||
renderer->clearScreen();
|
renderer->clearScreen();
|
||||||
|
|
||||||
const auto pageWidth = renderer->getPageWidth();
|
const auto pageWidth = renderer->getPageWidth();
|
||||||
const auto titleWidth = renderer->getTextWidth("CrossPoint Reader", true);
|
const auto titleWidth = renderer->getTextWidth("CrossPoint Reader", BOLD);
|
||||||
renderer->drawText((pageWidth - titleWidth) / 2, 0, "CrossPoint Reader", true);
|
renderer->drawText((pageWidth - titleWidth) / 2, 0, "CrossPoint Reader", 1, BOLD);
|
||||||
|
|
||||||
if (files.empty()) {
|
if (files.empty()) {
|
||||||
renderer->drawSmallText(50, 50, "No EPUBs found");
|
renderer->drawSmallText(50, 50, "No EPUBs found");
|
||||||
} else {
|
} else {
|
||||||
// Draw selection
|
// Draw selection
|
||||||
renderer->fillRect(0, 50 + selectorIndex * 20 + 2, pageWidth - 1, 20, 1);
|
renderer->fillRect(0, 50 + selectorIndex * 20 + 2, pageWidth - 1, 20);
|
||||||
|
|
||||||
for (size_t i = 0; i < files.size(); i++) {
|
for (size_t i = 0; i < files.size(); i++) {
|
||||||
const auto file = files[i];
|
const auto file = files[i];
|
||||||
|
|||||||
@ -21,7 +21,7 @@ class FileSelectionScreen final : public Screen {
|
|||||||
void render() const;
|
void render() const;
|
||||||
void loadFiles();
|
void loadFiles();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileSelectionScreen(EpdRenderer* renderer, const std::function<void(const std::string&)>& onSelect)
|
explicit FileSelectionScreen(EpdRenderer* renderer, const std::function<void(const std::string&)>& onSelect)
|
||||||
: Screen(renderer), onSelect(onSelect) {}
|
: Screen(renderer), onSelect(onSelect) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
|||||||
@ -3,13 +3,13 @@
|
|||||||
#include <EpdRenderer.h>
|
#include <EpdRenderer.h>
|
||||||
|
|
||||||
void FullScreenMessageScreen::onEnter() {
|
void FullScreenMessageScreen::onEnter() {
|
||||||
const auto width = renderer->getTextWidth(text.c_str(), bold, italic);
|
const auto width = renderer->getTextWidth(text.c_str(), style);
|
||||||
const auto height = renderer->getLineHeight();
|
const auto height = renderer->getLineHeight();
|
||||||
const auto left = (renderer->getPageWidth() - width) / 2;
|
const auto left = (renderer->getPageWidth() - width) / 2;
|
||||||
const auto top = (renderer->getPageHeight() - height) / 2;
|
const auto top = (renderer->getPageHeight() - height) / 2;
|
||||||
|
|
||||||
renderer->clearScreen(invert);
|
renderer->clearScreen(invert);
|
||||||
renderer->drawText(left, top, text.c_str(), bold, italic, invert ? 0 : 1);
|
renderer->drawText(left, top, text.c_str(), invert ? 0 : 1, style);
|
||||||
// If inverted, do a full screen update to ensure no ghosting
|
// If inverted, do a full screen update to ensure no ghosting
|
||||||
renderer->flushDisplay(!invert);
|
renderer->flushDisplay(!invert);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,17 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "EpdFontFamily.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
|
|
||||||
class FullScreenMessageScreen final : public Screen {
|
class FullScreenMessageScreen final : public Screen {
|
||||||
std::string text;
|
std::string text;
|
||||||
bool bold;
|
EpdFontStyle style;
|
||||||
bool italic;
|
|
||||||
bool invert;
|
bool invert;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const bool bold = false,
|
explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const EpdFontStyle style = REGULAR,
|
||||||
const bool italic = false, const bool invert = false)
|
const bool invert = false)
|
||||||
: Screen(renderer), text(std::move(text)), bold(bold), italic(italic), invert(invert) {}
|
: Screen(renderer), text(std::move(text)), style(style), invert(invert) {}
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user