Some fixes for cover generation
This commit is contained in:
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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++;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user