From 449b3ca161f501cd7c4029039683609e624abcce Mon Sep 17 00:00:00 2001 From: Dave Allie Date: Tue, 16 Dec 2025 02:16:35 +1100 Subject: [PATCH 1/2] Fixed light gray text rendering --- lib/GfxRenderer/GfxRenderer.cpp | 16 +++++++++++----- lib/GfxRenderer/GfxRenderer.h | 16 +++++++++------- src/screens/EpubReaderScreen.cpp | 6 +++--- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index dd33264..8199bd6 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -207,14 +207,20 @@ void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, if (is2Bit) { const uint8_t byte = bitmap[pixelPosition / 4]; const uint8_t bit_index = (3 - pixelPosition % 4) * 2; + // the direct bit from the font is 0 -> white, 1 -> light gray, 2 -> dark gray, 3 -> black + // we swap this to better match the way images and screen think about colors: + // 0 -> black, 1 -> dark grey, 2 -> light grey, 3 -> white + const uint8_t bmpVal = 3 - (byte >> bit_index) & 0x3; - const uint8_t val = (byte >> bit_index) & 0x3; - if (fontRenderMode == BW && val > 0) { + if (renderMode == BW && bmpVal < 3) { + // Black (also paints over the grays in BW mode) drawPixel(screenX, screenY, pixelState); - } else if (fontRenderMode == GRAYSCALE_MSB && val == 1) { - // TODO: Not sure how this anti-aliasing goes on black backgrounds + } 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); - } else if (fontRenderMode == GRAYSCALE_LSB && val == 2) { + } else if (renderMode == GRAYSCALE_LSB && bmpVal == 1) { + // Dark gray drawPixel(screenX, screenY, false); } } else { diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index d7b5925..720bf70 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -7,17 +7,17 @@ class GfxRenderer { public: - enum FontRenderMode { BW, GRAYSCALE_LSB, GRAYSCALE_MSB }; + enum RenderMode { BW, GRAYSCALE_LSB, GRAYSCALE_MSB }; private: EInkDisplay& einkDisplay; - FontRenderMode fontRenderMode; + RenderMode renderMode; std::map fontMap; void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState, EpdFontStyle style) const; public: - explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), fontRenderMode(BW) {} + explicit GfxRenderer(EInkDisplay& einkDisplay) : einkDisplay(einkDisplay), renderMode(BW) {} ~GfxRenderer() = default; // Setup @@ -41,15 +41,17 @@ class GfxRenderer { 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; - void setFontRenderMode(const FontRenderMode mode) { this->fontRenderMode = mode; } int getSpaceWidth(int fontId) const; int getLineHeight(int fontId) const; + // Grayscale functions + void setRenderMode(const RenderMode mode) { this->renderMode = mode; } + void copyGrayscaleLsbBuffers() const; + void copyGrayscaleMsbBuffers() const; + void displayGrayBuffer() const; + // Low level functions uint8_t* getFrameBuffer() const; void swapBuffers() const; void grayscaleRevert() const; - void copyGrayscaleLsbBuffers() const; - void copyGrayscaleMsbBuffers() const; - void displayGrayBuffer() const; }; diff --git a/src/screens/EpubReaderScreen.cpp b/src/screens/EpubReaderScreen.cpp index 4a42298..f2c51d9 100644 --- a/src/screens/EpubReaderScreen.cpp +++ b/src/screens/EpubReaderScreen.cpp @@ -301,19 +301,19 @@ void EpubReaderScreen::renderContents(std::unique_ptr page) { // TODO: Only do this if font supports it { renderer.clearScreen(0x00); - renderer.setFontRenderMode(GfxRenderer::GRAYSCALE_LSB); + renderer.setRenderMode(GfxRenderer::GRAYSCALE_LSB); page->render(renderer, READER_FONT_ID); renderer.copyGrayscaleLsbBuffers(); // Render and copy to MSB buffer renderer.clearScreen(0x00); - renderer.setFontRenderMode(GfxRenderer::GRAYSCALE_MSB); + renderer.setRenderMode(GfxRenderer::GRAYSCALE_MSB); page->render(renderer, READER_FONT_ID); renderer.copyGrayscaleMsbBuffers(); // display grayscale part renderer.displayGrayBuffer(); - renderer.setFontRenderMode(GfxRenderer::BW); + renderer.setRenderMode(GfxRenderer::BW); } } From c262f222de73352571deae230476e5683a55fe96 Mon Sep 17 00:00:00 2001 From: Dave Allie Date: Tue, 16 Dec 2025 03:15:54 +1100 Subject: [PATCH 2/2] Parse cover image path from content.opf file (#24) --- lib/Epub/Epub.cpp | 3 +++ lib/Epub/Epub/parsers/ContentOpfParser.cpp | 20 +++++++++++++++++--- lib/Epub/Epub/parsers/ContentOpfParser.h | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/Epub/Epub.cpp b/lib/Epub/Epub.cpp index f9273a9..8b4bb9a 100644 --- a/lib/Epub/Epub.cpp +++ b/lib/Epub/Epub.cpp @@ -69,6 +69,9 @@ bool Epub::parseContentOpf(const std::string& contentOpfFilePath) { // Grab data from opfParser into epub title = opfParser.title; + if (!opfParser.coverItemId.empty() && opfParser.items.count(opfParser.coverItemId) > 0) { + coverImageItem = opfParser.items.at(opfParser.coverItemId); + } if (opfParser.items.count("ncx")) { tocNcxItem = opfParser.items.at("ncx"); diff --git a/lib/Epub/Epub/parsers/ContentOpfParser.cpp b/lib/Epub/Epub/parsers/ContentOpfParser.cpp index 1dcdb04..667f679 100644 --- a/lib/Epub/Epub/parsers/ContentOpfParser.cpp +++ b/lib/Epub/Epub/parsers/ContentOpfParser.cpp @@ -90,9 +90,23 @@ void XMLCALL ContentOpfParser::startElement(void* userData, const XML_Char* name return; } - // TODO: Support book cover - // if (self->state == IN_METADATA && (strcmp(name, "meta") == 0 || strcmp(name, "opf:meta") == 0)) { - // } + if (self->state == IN_METADATA && (strcmp(name, "meta") == 0 || strcmp(name, "opf:meta") == 0)) { + bool isCover = false; + std::string coverItemId; + + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "name") == 0 && strcmp(atts[i + 1], "cover") == 0) { + isCover = true; + } else if (strcmp(atts[i], "content") == 0) { + coverItemId = atts[i + 1]; + } + } + + if (isCover) { + self->coverItemId = coverItemId; + } + return; + } if (self->state == IN_MANIFEST && (strcmp(name, "item") == 0 || strcmp(name, "opf:item") == 0)) { std::string itemId; diff --git a/lib/Epub/Epub/parsers/ContentOpfParser.h b/lib/Epub/Epub/parsers/ContentOpfParser.h index 5da16bd..cba4551 100644 --- a/lib/Epub/Epub/parsers/ContentOpfParser.h +++ b/lib/Epub/Epub/parsers/ContentOpfParser.h @@ -28,6 +28,7 @@ class ContentOpfParser final : public Print { public: std::string title; std::string tocNcxPath; + std::string coverItemId; std::map items; std::vector spineRefs;