Some fixes for cover generation

This commit is contained in:
jpirnay
2026-05-08 18:04:41 +02:00
parent 13e87ff470
commit 79ef2eea91
5 changed files with 29 additions and 15 deletions
+12 -9
View File
@@ -1550,8 +1550,6 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
bool isScaled = false; bool isScaled = false;
int cropPixX = std::floor(bitmap.getWidth() * cropX / 2.0f); int cropPixX = std::floor(bitmap.getWidth() * cropX / 2.0f);
int cropPixY = std::floor(bitmap.getHeight() * cropY / 2.0f); int cropPixY = std::floor(bitmap.getHeight() * cropY / 2.0f);
LOG_DBG("GFX", "Cropping %dx%d by %dx%d pix, is %s", bitmap.getWidth(), bitmap.getHeight(), cropPixX, cropPixY,
bitmap.isTopDown() ? "top-down" : "bottom-up");
const float croppedWidth = (1.0f - cropX) * static_cast<float>(bitmap.getWidth()); const float croppedWidth = (1.0f - cropX) * static_cast<float>(bitmap.getWidth());
const float croppedHeight = (1.0f - cropY) * static_cast<float>(bitmap.getHeight()); const float croppedHeight = (1.0f - cropY) * static_cast<float>(bitmap.getHeight());
@@ -1573,7 +1571,6 @@ void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, con
scale = fitScale; scale = fitScale;
isScaled = true; isScaled = true;
} }
LOG_DBG("GFX", "Scaling by %f - %s", scale, isScaled ? "scaled" : "not scaled");
// Calculate output row size (2 bits per pixel, packed into bytes) // Calculate output row size (2 bits per pixel, packed into bytes)
// IMPORTANT: Use int, not uint8_t, to avoid overflow for images > 1020 pixels wide // IMPORTANT: Use int, not uint8_t, to avoid overflow for images > 1020 pixels wide
@@ -1650,13 +1647,19 @@ void GfxRenderer::drawBitmap1Bit(const Bitmap& bitmap, const int x, const int y,
const int maxHeight) const { const int maxHeight) const {
float scale = 1.0f; float scale = 1.0f;
bool isScaled = false; bool isScaled = false;
if (maxWidth > 0 && bitmap.getWidth() > maxWidth) { if (maxWidth > 0) {
scale = static_cast<float>(maxWidth) / static_cast<float>(bitmap.getWidth()); const float s = static_cast<float>(maxWidth) / static_cast<float>(bitmap.getWidth());
isScaled = true; if (s != 1.0f) {
scale = s;
isScaled = true;
}
} }
if (maxHeight > 0 && bitmap.getHeight() > maxHeight) { if (maxHeight > 0) {
scale = std::min(scale, static_cast<float>(maxHeight) / static_cast<float>(bitmap.getHeight())); const float s = static_cast<float>(maxHeight) / static_cast<float>(bitmap.getHeight());
isScaled = true; if (s < scale) {
scale = s;
isScaled = (scale != 1.0f);
}
} }
// For 1-bit BMP, output is still 2-bit packed (for consistency with readNextRow) // For 1-bit BMP, output is still 2-bit packed (for consistency with readNextRow)
+9 -1
View File
@@ -266,7 +266,15 @@ std::string Xtc::getThumbBmpPath(int width, int height) const {
return cachePath + "/thumb_" + std::to_string(width) + "x" + std::to_string(height) + ".bmp"; return cachePath + "/thumb_" + std::to_string(width) + "x" + std::to_string(height) + ".bmp";
} }
bool Xtc::generateThumbBmp(int height) const { return generateThumbBmp(height * 0.6, height); } bool Xtc::generateThumbBmp(int height) const {
const std::string destPath = getThumbBmpPath(height);
if (Storage.exists(destPath.c_str())) return true;
const int width = static_cast<int>(height * 0.6f);
if (!generateThumbBmp(width, height)) return false;
const std::string srcPath = getThumbBmpPath(width, height);
Storage.rename(srcPath.c_str(), destPath.c_str());
return Storage.exists(destPath.c_str());
}
bool Xtc::generateThumbBmp(int width, int height) const { bool Xtc::generateThumbBmp(int width, int height) const {
if (Storage.exists(getThumbBmpPath(width, height).c_str())) return true; if (Storage.exists(getThumbBmpPath(width, height).c_str())) return true;
+7 -2
View File
@@ -217,6 +217,11 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, ""); RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, "");
book.coverBmpPath = ""; book.coverBmpPath = "";
} else { } else {
// Free the carousel frame cache before converting — PNG/JPEG decode needs ~42 KB
// contiguous heap, which won't be available while the 48 KB frame buffer is held.
// The cache will be rebuilt on the next render.
UITheme::getInstance().getMutableTheme().invalidateFrameCache();
const std::string cacheBase = "/.crosspoint/sidecar_" + std::to_string(std::hash<std::string>{}(book.path)); const std::string cacheBase = "/.crosspoint/sidecar_" + std::to_string(std::hash<std::string>{}(book.path));
const std::string placeholder = cacheBase + "/[HEIGHT].bmp"; const std::string placeholder = cacheBase + "/[HEIGHT].bmp";
bool success = true; bool success = true;
@@ -239,8 +244,8 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
book.coverBmpPath = placeholder; book.coverBmpPath = placeholder;
} else { } else {
LOG_ERR("HOME", "Failed to convert sidecar cover for %s", book.path.c_str()); LOG_ERR("HOME", "Failed to convert sidecar cover for %s", book.path.c_str());
RECENT_BOOKS.updateBook(book.path, book.title, book.author, book.series, ""); // Don't permanently clear the path on failure — keep the raw sidecar path
book.coverBmpPath = ""; // so the next home visit can retry (e.g. after more memory becomes available).
} }
coverRendered = false; coverRendered = false;
nextRecentCoverIndex++; nextRecentCoverIndex++;
-2
View File
@@ -62,13 +62,11 @@ std::string ReaderActivity::sidecarCoverPath(const std::string& bookPath) {
const std::string base = bookPath.substr(0, dot); const std::string base = bookPath.substr(0, dot);
for (const char* ext : {".jpg", ".jpeg", ".png", ".bmp"}) { for (const char* ext : {".jpg", ".jpeg", ".png", ".bmp"}) {
const std::string candidate = base + ext; const std::string candidate = base + ext;
LOG_DBG("SIDECAR", "Checking: %s", candidate.c_str());
if (Storage.exists(candidate.c_str())) { if (Storage.exists(candidate.c_str())) {
LOG_DBG("SIDECAR", "Found sidecar cover: %s", candidate.c_str()); LOG_DBG("SIDECAR", "Found sidecar cover: %s", candidate.c_str());
return candidate; return candidate;
} }
} }
LOG_DBG("SIDECAR", "No sidecar found for: %s", bookPath.c_str());
return ""; return "";
} }
@@ -52,7 +52,7 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con
const float ratio = bitmapWidth / bitmapHeight; const float ratio = bitmapWidth / bitmapHeight;
const float tileRatio = const float tileRatio =
static_cast<float>(tileWidth - 2 * hPaddingInSelection) / static_cast<float>(coverHeight); static_cast<float>(tileWidth - 2 * hPaddingInSelection) / static_cast<float>(coverHeight);
const float cropX = 1.0f - (tileRatio / ratio); const float cropX = std::max(0.0f, 1.0f - (tileRatio / ratio));
renderer.drawBitmap(bitmap, tileX + hPaddingInSelection, tileY + hPaddingInSelection, renderer.drawBitmap(bitmap, tileX + hPaddingInSelection, tileY + hPaddingInSelection,
tileWidth - 2 * hPaddingInSelection, coverHeight, cropX); tileWidth - 2 * hPaddingInSelection, coverHeight, cropX);