mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2025-12-18 15:17:42 +03:00
Add 2-bit grayscale text and anti-aliased rendering of text
This commit is contained in:
parent
9e046d7086
commit
ba66f36f4c
@ -31,4 +31,5 @@ typedef struct {
|
||||
uint8_t advanceY; ///< Newline distance (y axis)
|
||||
int ascender; ///< Maximal height of a glyph above the base line
|
||||
int descender; ///< Maximal height of a glyph below the base line
|
||||
bool is2Bit;
|
||||
} EpdFontData;
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
* generated by fontconvert.py
|
||||
* name: babyblue
|
||||
* size: 8
|
||||
* mode: 1-bit
|
||||
*/
|
||||
#pragma once
|
||||
#include "EpdFontData.h"
|
||||
@ -500,5 +501,5 @@ static const EpdUnicodeInterval babyblueIntervals[] = {
|
||||
};
|
||||
|
||||
static const EpdFontData babyblue = {
|
||||
babyblueBitmaps, babyblueGlyphs, babyblueIntervals, 5, 17, 13, -4,
|
||||
babyblueBitmaps, babyblueGlyphs, babyblueIntervals, 5, 17, 13, -4, false,
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2608
lib/EpdFont/builtinFonts/bookerly_2b.h
Normal file
2608
lib/EpdFont/builtinFonts/bookerly_2b.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2748
lib/EpdFont/builtinFonts/bookerly_bold_2b.h
Normal file
2748
lib/EpdFont/builtinFonts/bookerly_bold_2b.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2859
lib/EpdFont/builtinFonts/bookerly_bold_italic_2b.h
Normal file
2859
lib/EpdFont/builtinFonts/bookerly_bold_italic_2b.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2680
lib/EpdFont/builtinFonts/bookerly_italic_2b.h
Normal file
2680
lib/EpdFont/builtinFonts/bookerly_italic_2b.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
||||
* generated by fontconvert.py
|
||||
* name: ubuntu_10
|
||||
* size: 10
|
||||
* mode: 1-bit
|
||||
*/
|
||||
#pragma once
|
||||
#include "EpdFontData.h"
|
||||
@ -762,5 +763,5 @@ static const EpdUnicodeInterval ubuntu_10Intervals[] = {
|
||||
};
|
||||
|
||||
static const EpdFontData ubuntu_10 = {
|
||||
ubuntu_10Bitmaps, ubuntu_10Glyphs, ubuntu_10Intervals, 31, 24, 20, -4,
|
||||
ubuntu_10Bitmaps, ubuntu_10Glyphs, ubuntu_10Intervals, 31, 24, 20, -4, false,
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
* generated by fontconvert.py
|
||||
* name: ubuntu_bold_10
|
||||
* size: 10
|
||||
* mode: 1-bit
|
||||
*/
|
||||
#pragma once
|
||||
#include "EpdFontData.h"
|
||||
@ -806,5 +807,5 @@ static const EpdUnicodeInterval ubuntu_bold_10Intervals[] = {
|
||||
};
|
||||
|
||||
static const EpdFontData ubuntu_bold_10 = {
|
||||
ubuntu_bold_10Bitmaps, ubuntu_bold_10Glyphs, ubuntu_bold_10Intervals, 31, 24, 20, -4,
|
||||
ubuntu_bold_10Bitmaps, ubuntu_bold_10Glyphs, ubuntu_bold_10Intervals, 31, 24, 20, -4, false,
|
||||
};
|
||||
|
||||
@ -13,12 +13,14 @@ parser = argparse.ArgumentParser(description="Generate a header file from a font
|
||||
parser.add_argument("name", action="store", help="name of the font.")
|
||||
parser.add_argument("size", type=int, help="font size to use.")
|
||||
parser.add_argument("fontstack", action="store", nargs='+', help="list of font files, ordered by descending priority.")
|
||||
parser.add_argument("--2bit", dest="is2Bit", action="store_true", help="generate 2-bit greyscale bitmap instead of 1-bit black and white.")
|
||||
parser.add_argument("--additional-intervals", dest="additional_intervals", action="append", help="Additional code point intervals to export as min,max. This argument can be repeated.")
|
||||
args = parser.parse_args()
|
||||
|
||||
GlyphProps = namedtuple("GlyphProps", ["width", "height", "advance_x", "left", "top", "data_length", "data_offset", "code_point"])
|
||||
|
||||
font_stack = [freetype.Face(f) for f in args.fontstack]
|
||||
is2Bit = args.is2Bit
|
||||
size = args.size
|
||||
font_name = args.name
|
||||
|
||||
@ -173,26 +175,73 @@ for i_start, i_end in intervals:
|
||||
pixels4g.append(px)
|
||||
px = 0
|
||||
|
||||
# Downsample to 1-bit bitmap - treat any non-zero as black
|
||||
pixelsbw = []
|
||||
px = 0
|
||||
pitch = (bitmap.width // 2) + (bitmap.width % 2)
|
||||
for y in range(bitmap.rows):
|
||||
for x in range(bitmap.width):
|
||||
px = px << 1
|
||||
bm = pixels4g[y * pitch + (x // 2)]
|
||||
px += 1 if ((x & 1) == 0 and bm & 0xF > 0) or ((x & 1) == 1 and bm & 0xF0 > 0) else 0
|
||||
if is2Bit:
|
||||
# 0 = white, 15 black, 8+ dark grey, 7- light grey
|
||||
# Downsample to 2-bit bitmap
|
||||
pixels2b = []
|
||||
px = 0
|
||||
pitch = (bitmap.width // 2) + (bitmap.width % 2)
|
||||
for y in range(bitmap.rows):
|
||||
for x in range(bitmap.width):
|
||||
px = px << 2
|
||||
bm = pixels4g[y * pitch + (x // 2)]
|
||||
bm = (bm >> ((x % 2) * 4)) & 0xF
|
||||
|
||||
if (y * bitmap.width + x) % 8 == 7:
|
||||
pixelsbw.append(px)
|
||||
px = 0
|
||||
if (bitmap.width * bitmap.rows) % 8 != 0:
|
||||
px = px << (8 - (bitmap.width * bitmap.rows) % 8)
|
||||
pixelsbw.append(px)
|
||||
if bm == 15:
|
||||
px += 3
|
||||
elif bm >= 8:
|
||||
px += 2
|
||||
elif bm > 0:
|
||||
px += 1
|
||||
|
||||
if (y * bitmap.width + x) % 4 == 3:
|
||||
pixels2b.append(px)
|
||||
px = 0
|
||||
if (bitmap.width * bitmap.rows) % 4 != 0:
|
||||
px = px << (4 - (bitmap.width * bitmap.rows) % 4) * 2
|
||||
pixels2b.append(px)
|
||||
|
||||
# for y in range(bitmap.rows):
|
||||
# line = ''
|
||||
# for x in range(bitmap.width):
|
||||
# pixelPosition = y * bitmap.width + x
|
||||
# byte = pixels2b[pixelPosition // 4]
|
||||
# bit_index = (3 - (pixelPosition % 4)) * 2
|
||||
# line += '#' if ((byte >> bit_index) & 3) > 0 else '.'
|
||||
# print(line)
|
||||
# print('')
|
||||
else:
|
||||
# Downsample to 1-bit bitmap - treat any non-zero as black
|
||||
pixelsbw = []
|
||||
px = 0
|
||||
pitch = (bitmap.width // 2) + (bitmap.width % 2)
|
||||
for y in range(bitmap.rows):
|
||||
for x in range(bitmap.width):
|
||||
px = px << 1
|
||||
bm = pixels4g[y * pitch + (x // 2)]
|
||||
px += 1 if ((x & 1) == 0 and bm & 0xF > 0) or ((x & 1) == 1 and bm & 0xF0 > 0) else 0
|
||||
|
||||
if (y * bitmap.width + x) % 8 == 7:
|
||||
pixelsbw.append(px)
|
||||
px = 0
|
||||
if (bitmap.width * bitmap.rows) % 8 != 0:
|
||||
px = px << (8 - (bitmap.width * bitmap.rows) % 8)
|
||||
pixelsbw.append(px)
|
||||
|
||||
# for y in range(bitmap.rows):
|
||||
# line = ''
|
||||
# for x in range(bitmap.width):
|
||||
# pixelPosition = y * bitmap.width + x
|
||||
# byte = pixelsbw[pixelPosition // 8]
|
||||
# bit_index = 7 - (pixelPosition % 8)
|
||||
# line += '#' if (byte >> bit_index) & 1 else '.'
|
||||
# print(line)
|
||||
# print('')
|
||||
|
||||
pixels = pixels2b if is2Bit else pixelsbw
|
||||
|
||||
# Build output data
|
||||
packed = bytes(pixelsbw)
|
||||
packed = bytes(pixels)
|
||||
glyph = GlyphProps(
|
||||
width = bitmap.width,
|
||||
height = bitmap.rows,
|
||||
@ -216,7 +265,7 @@ for index, glyph in enumerate(all_glyphs):
|
||||
glyph_data.extend([b for b in packed])
|
||||
glyph_props.append(props)
|
||||
|
||||
print(f"/**\n * generated by fontconvert.py\n * name: {font_name}\n * size: {size}\n */")
|
||||
print(f"/**\n * generated by fontconvert.py\n * name: {font_name}\n * size: {size}\n * mode: {'2-bit' if is2Bit else '1-bit'}\n */")
|
||||
print("#pragma once")
|
||||
print("#include \"EpdFontData.h\"\n")
|
||||
print(f"static const uint8_t {font_name}Bitmaps[{len(glyph_data)}] = {{")
|
||||
@ -244,4 +293,5 @@ print(f" {len(intervals)},")
|
||||
print(f" {norm_ceil(face.size.height)},")
|
||||
print(f" {norm_ceil(face.size.ascender)},")
|
||||
print(f" {norm_floor(face.size.descender)},")
|
||||
print(f" {'true' if is2Bit else 'false'},")
|
||||
print("};")
|
||||
|
||||
@ -6,23 +6,27 @@
|
||||
inline int min(const int a, const int b) { return a < b ? a : b; }
|
||||
inline int max(const int a, const int b) { return a > b ? a : b; }
|
||||
|
||||
enum EpdFontRendererMode { BW, GRAYSCALE_LSB, GRAYSCALE_MSB };
|
||||
|
||||
template <typename Renderable>
|
||||
class EpdFontRenderer {
|
||||
Renderable& renderer;
|
||||
void renderChar(uint32_t cp, int* x, const int* y, bool pixelState, EpdFontStyle style = REGULAR);
|
||||
void renderChar(uint32_t cp, int* x, const int* y, bool pixelState, EpdFontStyle style = REGULAR,
|
||||
EpdFontRendererMode mode = BW);
|
||||
|
||||
public:
|
||||
const EpdFontFamily* fontFamily;
|
||||
explicit EpdFontRenderer(const EpdFontFamily* fontFamily, Renderable& renderer)
|
||||
: fontFamily(fontFamily), renderer(renderer) {}
|
||||
~EpdFontRenderer() = default;
|
||||
void renderString(const char* string, int* x, int* y, bool pixelState = true, EpdFontStyle style = REGULAR);
|
||||
void renderString(const char* string, int* x, int* y, bool pixelState = true, EpdFontStyle style = REGULAR,
|
||||
EpdFontRendererMode mode = BW);
|
||||
void drawPixel(int x, int y, bool pixelState);
|
||||
};
|
||||
|
||||
template <typename Renderable>
|
||||
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const bool pixelState,
|
||||
const EpdFontStyle style) {
|
||||
const EpdFontStyle style, const EpdFontRendererMode mode) {
|
||||
// cannot draw a NULL / empty string
|
||||
if (string == nullptr || *string == '\0') {
|
||||
return;
|
||||
@ -35,7 +39,7 @@ void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int*
|
||||
|
||||
uint32_t cp;
|
||||
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
|
||||
renderChar(cp, x, y, pixelState, style);
|
||||
renderChar(cp, x, y, pixelState, style, mode);
|
||||
}
|
||||
|
||||
*y += fontFamily->getData(style)->advanceY;
|
||||
@ -77,7 +81,7 @@ void EpdFontRenderer<Renderable>::drawPixel(const int x, const int y, const bool
|
||||
|
||||
template <typename Renderable>
|
||||
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, const bool pixelState,
|
||||
const EpdFontStyle style) {
|
||||
const EpdFontStyle style, const EpdFontRendererMode mode) {
|
||||
const EpdGlyph* glyph = fontFamily->getGlyph(cp, style);
|
||||
if (!glyph) {
|
||||
// TODO: Replace with fallback glyph property?
|
||||
@ -90,6 +94,7 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
||||
return;
|
||||
}
|
||||
|
||||
const int is2Bit = fontFamily->getData(style)->is2Bit;
|
||||
const uint32_t offset = glyph->dataOffset;
|
||||
const uint8_t width = glyph->width;
|
||||
const uint8_t height = glyph->height;
|
||||
@ -105,11 +110,26 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
|
||||
const int pixelPosition = glyphY * width + glyphX;
|
||||
int screenX = *x + left + glyphX;
|
||||
|
||||
const uint8_t byte = bitmap[pixelPosition / 8];
|
||||
const uint8_t bit_index = 7 - (pixelPosition % 8);
|
||||
if (is2Bit) {
|
||||
const uint8_t byte = bitmap[pixelPosition / 4];
|
||||
const uint8_t bit_index = (3 - pixelPosition % 4) * 2;
|
||||
|
||||
if ((byte >> bit_index) & 1) {
|
||||
drawPixel(screenX, screenY, pixelState);
|
||||
const uint8_t val = (byte >> bit_index) & 0x3;
|
||||
if (mode == BW && val > 0) {
|
||||
drawPixel(screenX, screenY, pixelState);
|
||||
} else if (mode == GRAYSCALE_MSB && val == 1) {
|
||||
// TODO: Not sure how this anti-aliasing goes on black backgrounds
|
||||
drawPixel(screenX, screenY, false);
|
||||
} else if (mode == GRAYSCALE_LSB && val == 2) {
|
||||
drawPixel(screenX, screenY, false);
|
||||
}
|
||||
} else {
|
||||
const uint8_t byte = bitmap[pixelPosition / 8];
|
||||
const uint8_t bit_index = 7 - (pixelPosition % 8);
|
||||
|
||||
if ((byte >> bit_index) & 1) {
|
||||
drawPixel(screenX, screenY, pixelState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
#include "EpdRenderer.h"
|
||||
|
||||
#include "builtinFonts/babyblue.h"
|
||||
#include "builtinFonts/bookerly.h"
|
||||
#include "builtinFonts/bookerly_bold.h"
|
||||
#include "builtinFonts/bookerly_bold_italic.h"
|
||||
#include "builtinFonts/bookerly_italic.h"
|
||||
#include "builtinFonts/bookerly_2b.h"
|
||||
#include "builtinFonts/bookerly_bold_2b.h"
|
||||
#include "builtinFonts/bookerly_bold_italic_2b.h"
|
||||
#include "builtinFonts/bookerly_italic_2b.h"
|
||||
#include "builtinFonts/ubuntu_10.h"
|
||||
#include "builtinFonts/ubuntu_bold_10.h"
|
||||
|
||||
EpdFont bookerlyFont(&bookerly);
|
||||
EpdFont bookerlyBoldFont(&bookerly_bold);
|
||||
EpdFont bookerlyItalicFont(&bookerly_italic);
|
||||
EpdFont bookerlyBoldItalicFont(&bookerly_bold_italic);
|
||||
EpdFont bookerlyFont(&bookerly_2b);
|
||||
EpdFont bookerlyBoldFont(&bookerly_bold_2b);
|
||||
EpdFont bookerlyItalicFont(&bookerly_italic_2b);
|
||||
EpdFont bookerlyBoldItalicFont(&bookerly_bold_italic_2b);
|
||||
EpdFontFamily bookerlyFontFamily(&bookerlyFont, &bookerlyBoldFont, &bookerlyItalicFont, &bookerlyBoldItalicFont);
|
||||
|
||||
EpdFont smallFont(&babyblue);
|
||||
@ -27,6 +27,7 @@ EpdRenderer::EpdRenderer(EInkDisplay& einkDisplay)
|
||||
marginBottom(30),
|
||||
marginLeft(10),
|
||||
marginRight(10),
|
||||
fontRendererMode(BW),
|
||||
lineCompression(0.95f) {
|
||||
this->regularFontRenderer = new EpdFontRenderer<EInkDisplay>(&bookerlyFontFamily, einkDisplay);
|
||||
this->smallFontRenderer = new EpdFontRenderer<EInkDisplay>(&smallFontFamily, einkDisplay);
|
||||
@ -101,21 +102,21 @@ void EpdRenderer::drawText(const int x, const int y, const char* text, const boo
|
||||
const EpdFontStyle style) const {
|
||||
int ypos = y + getLineHeight() + marginTop;
|
||||
int xpos = x + marginLeft;
|
||||
regularFontRenderer->renderString(text, &xpos, &ypos, state, style);
|
||||
regularFontRenderer->renderString(text, &xpos, &ypos, state, style, fontRendererMode);
|
||||
}
|
||||
|
||||
void EpdRenderer::drawUiText(const int x, const int y, const char* text, const bool state,
|
||||
const EpdFontStyle style) const {
|
||||
int ypos = y + uiFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
|
||||
int xpos = x + marginLeft;
|
||||
uiFontRenderer->renderString(text, &xpos, &ypos, state, style);
|
||||
uiFontRenderer->renderString(text, &xpos, &ypos, state, style, fontRendererMode);
|
||||
}
|
||||
|
||||
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const bool state,
|
||||
const EpdFontStyle style) const {
|
||||
int ypos = y + smallFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
|
||||
int xpos = x + marginLeft;
|
||||
smallFontRenderer->renderString(text, &xpos, &ypos, state, style);
|
||||
smallFontRenderer->renderString(text, &xpos, &ypos, state, style, fontRendererMode);
|
||||
}
|
||||
|
||||
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
|
||||
@ -221,3 +222,9 @@ int EpdRenderer::getSpaceWidth() const { return regularFontRenderer->fontFamily-
|
||||
int EpdRenderer::getLineHeight() const {
|
||||
return regularFontRenderer->fontFamily->getData(REGULAR)->advanceY * lineCompression;
|
||||
}
|
||||
|
||||
void EpdRenderer::copyGrayscaleLsbBuffers() const { einkDisplay.copyGrayscaleLsbBuffers(einkDisplay.getFrameBuffer()); }
|
||||
|
||||
void EpdRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsbBuffers(einkDisplay.getFrameBuffer()); }
|
||||
|
||||
void EpdRenderer::displayGrayBuffer() const { einkDisplay.displayGrayBuffer(); }
|
||||
|
||||
@ -13,6 +13,7 @@ class EpdRenderer {
|
||||
int marginBottom;
|
||||
int marginLeft;
|
||||
int marginRight;
|
||||
EpdFontRendererMode fontRendererMode;
|
||||
float lineCompression;
|
||||
|
||||
public:
|
||||
@ -32,6 +33,9 @@ class EpdRenderer {
|
||||
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||
void drawImageNoMargin(const uint8_t bitmap[], int x, int y, int width, int height) const;
|
||||
|
||||
void copyGrayscaleLsbBuffers() const;
|
||||
void copyGrayscaleMsbBuffers() const;
|
||||
void displayGrayBuffer() const;
|
||||
void clearScreen(uint8_t color = 0xFF) const;
|
||||
void flushDisplay(bool partialUpdate = true) const;
|
||||
void flushArea(int x, int y, int width, int height) const;
|
||||
@ -46,4 +50,5 @@ class EpdRenderer {
|
||||
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
|
||||
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
|
||||
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
|
||||
void setFontRendererMode(const EpdFontRendererMode mode) { this->fontRendererMode = mode; }
|
||||
};
|
||||
|
||||
@ -98,21 +98,15 @@ bool Section::persistPageDataToSD() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Section::renderPage() const {
|
||||
if (0 <= currentPage && currentPage < pageCount) {
|
||||
const auto filePath = "/sd" + cachePath + "/page_" + std::to_string(currentPage) + ".bin";
|
||||
std::ifstream inputFile(filePath);
|
||||
const Page* p = Page::deserialize(inputFile);
|
||||
inputFile.close();
|
||||
p->render(renderer);
|
||||
delete p;
|
||||
} else if (pageCount == 0) {
|
||||
Serial.println("No pages to render");
|
||||
const int width = renderer.getTextWidth("Empty chapter", BOLD);
|
||||
renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Empty chapter", 1, BOLD);
|
||||
} else {
|
||||
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
|
||||
const int width = renderer.getTextWidth("Out of bounds", BOLD);
|
||||
renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Out of bounds", 1, BOLD);
|
||||
Page* Section::loadPageFromSD() const {
|
||||
const auto filePath = "/sd" + cachePath + "/page_" + std::to_string(currentPage) + ".bin";
|
||||
if (!SD.exists(filePath.c_str() + 3)) {
|
||||
Serial.printf("Page file does not exist: %s\n", filePath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::ifstream inputFile(filePath);
|
||||
Page* p = Page::deserialize(inputFile);
|
||||
inputFile.close();
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -26,5 +26,5 @@ class Section {
|
||||
void setupCacheDir() const;
|
||||
void clearCache() const;
|
||||
bool persistPageDataToSD();
|
||||
void renderPage() const;
|
||||
Page* loadPageFromSD() const;
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "EpubReaderScreen.h"
|
||||
|
||||
#include <EpdRenderer.h>
|
||||
#include <Epub/Page.h>
|
||||
#include <SD.h>
|
||||
|
||||
#include "Battery.h"
|
||||
@ -128,7 +129,7 @@ void EpubReaderScreen::displayTaskLoop() {
|
||||
if (updateRequired) {
|
||||
updateRequired = false;
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
renderPage();
|
||||
renderScreen();
|
||||
xSemaphoreGive(renderingMutex);
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
@ -136,7 +137,7 @@ void EpubReaderScreen::displayTaskLoop() {
|
||||
}
|
||||
|
||||
// TODO: Failure handling
|
||||
void EpubReaderScreen::renderPage() {
|
||||
void EpubReaderScreen::renderScreen() {
|
||||
if (!epub) {
|
||||
return;
|
||||
}
|
||||
@ -184,16 +185,27 @@ void EpubReaderScreen::renderPage() {
|
||||
}
|
||||
|
||||
renderer.clearScreen();
|
||||
section->renderPage();
|
||||
renderStatusBar();
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.flushDisplay(false);
|
||||
pagesUntilFullRefresh = PAGES_PER_REFRESH;
|
||||
} else {
|
||||
|
||||
if (section->pageCount == 0) {
|
||||
Serial.println("No pages to render");
|
||||
const int width = renderer.getTextWidth("Empty chapter", BOLD);
|
||||
renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Empty chapter", true, BOLD);
|
||||
renderer.flushDisplay();
|
||||
pagesUntilFullRefresh--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (section->currentPage < 0 || section->currentPage >= section->pageCount) {
|
||||
Serial.printf("Page out of bounds: %d (max %d)\n", section->currentPage, section->pageCount);
|
||||
const int width = renderer.getTextWidth("Out of bounds", BOLD);
|
||||
renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Out of bounds", true, BOLD);
|
||||
renderer.flushDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
const Page* p = section->loadPageFromSD();
|
||||
renderContents(p);
|
||||
delete p;
|
||||
|
||||
File f = SD.open((epub->getCachePath() + "/progress.bin").c_str(), FILE_WRITE);
|
||||
uint8_t data[4];
|
||||
data[0] = currentSpineIndex & 0xFF;
|
||||
@ -204,6 +216,30 @@ void EpubReaderScreen::renderPage() {
|
||||
f.close();
|
||||
}
|
||||
|
||||
void EpubReaderScreen::renderContents(const Page* p) const {
|
||||
p->render(renderer);
|
||||
renderStatusBar();
|
||||
renderer.flushDisplay();
|
||||
|
||||
// grayscale rendering
|
||||
{
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setFontRendererMode(GRAYSCALE_LSB);
|
||||
p->render(renderer);
|
||||
renderer.copyGrayscaleLsbBuffers();
|
||||
|
||||
// Render and copy to MSB buffer
|
||||
renderer.clearScreen(0x00);
|
||||
renderer.setFontRendererMode(GRAYSCALE_MSB);
|
||||
p->render(renderer);
|
||||
renderer.copyGrayscaleMsbBuffers();
|
||||
|
||||
// display grayscale part
|
||||
renderer.displayGrayBuffer();
|
||||
renderer.setFontRendererMode(BW);
|
||||
}
|
||||
}
|
||||
|
||||
void EpubReaderScreen::renderStatusBar() const {
|
||||
const auto pageWidth = renderer.getPageWidth();
|
||||
|
||||
|
||||
@ -20,7 +20,8 @@ class EpubReaderScreen final : public Screen {
|
||||
|
||||
static void taskTrampoline(void* param);
|
||||
[[noreturn]] void displayTaskLoop();
|
||||
void renderPage();
|
||||
void renderScreen();
|
||||
void renderContents(const Page* p) const;
|
||||
void renderStatusBar() const;
|
||||
|
||||
public:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user