feat: Add placeholder cover generator for books without covers

Generate styled placeholder covers (title, author, book icon) when a
book has no embedded cover image, instead of showing a blank rectangle.

- Add PlaceholderCoverGenerator lib with 1-bit BMP rendering, scaled
  fonts, word-wrap, and a book icon bitmap
- Integrate as fallback in Epub/Xtc/Txt reader activities and
  SleepActivity after format-specific cover generation fails
- Add fallback in HomeActivity::loadRecentCovers() so the home screen
  also shows placeholder thumbnails when cache is cleared
- Add Txt::getThumbBmpPath() for TXT thumbnail support
- Add helper scripts for icon and layout preview generation

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-02-14 23:38:47 -05:00
parent 5dc9d21bdb
commit 632b76c9ed
12 changed files with 939 additions and 38 deletions

View File

@@ -4,6 +4,7 @@
#include <GfxRenderer.h>
#include <HalStorage.h>
#include <Logging.h>
#include <PlaceholderCoverGenerator.h>
#include <Serialization.h>
#include <Txt.h>
#include <Xtc.h>
@@ -599,6 +600,11 @@ void SleepActivity::renderCoverSleepScreen() const {
}
if (!lastXtc.generateCoverBmp()) {
LOG_DBG("SLP", "XTC cover generation failed, trying placeholder");
PlaceholderCoverGenerator::generate(lastXtc.getCoverBmpPath(), lastXtc.getTitle(), lastXtc.getAuthor(), 480, 800);
}
if (!Storage.exists(lastXtc.getCoverBmpPath().c_str())) {
LOG_ERR("SLP", "Failed to generate XTC cover bmp");
return (this->*renderNoCoverSleepScreen)();
}
@@ -614,6 +620,11 @@ void SleepActivity::renderCoverSleepScreen() const {
}
if (!lastTxt.generateCoverBmp()) {
LOG_DBG("SLP", "TXT cover generation failed, trying placeholder");
PlaceholderCoverGenerator::generate(lastTxt.getCoverBmpPath(), lastTxt.getTitle(), "", 480, 800);
}
if (!Storage.exists(lastTxt.getCoverBmpPath().c_str())) {
LOG_ERR("SLP", "No cover image found for TXT file");
return (this->*renderNoCoverSleepScreen)();
}
@@ -630,6 +641,12 @@ void SleepActivity::renderCoverSleepScreen() const {
}
if (!lastEpub.generateCoverBmp(cropped)) {
LOG_DBG("SLP", "EPUB cover generation failed, trying placeholder");
PlaceholderCoverGenerator::generate(lastEpub.getCoverBmpPath(cropped), lastEpub.getTitle(),
lastEpub.getAuthor(), 480, 800);
}
if (!Storage.exists(lastEpub.getCoverBmpPath(cropped).c_str())) {
LOG_ERR("SLP", "Failed to generate cover bmp");
return (this->*renderNoCoverSleepScreen)();
}