Properly deal with pngs as sleep images
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "SleepActivity.h"
|
||||
|
||||
#include <Epub.h>
|
||||
#include <Epub/converters/PngToFramebufferConverter.h>
|
||||
#include <FsHelpers.h>
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalStorage.h>
|
||||
@@ -23,7 +24,6 @@
|
||||
#include "fontIds.h"
|
||||
#include "images/Logo120.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// Context passed through PNGdec's decode() user-pointer to the per-scanline draw callback.
|
||||
@@ -72,6 +72,87 @@ int32_t pngSleepSeek(PNGFILE* pFile, int32_t pos) {
|
||||
return f->seek(pos);
|
||||
}
|
||||
|
||||
bool renderPngSleepScreen(const std::string& filename, GfxRenderer& renderer, const BookOverlayInfo& overlayInfo) {
|
||||
constexpr size_t MIN_FREE_HEAP = 60 * 1024; // PNG decoder ~42 KB + overhead
|
||||
if (ESP.getFreeHeap() < MIN_FREE_HEAP) {
|
||||
LOG_ERR("SLP", "Not enough heap for PNG sleep image: %s", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const int pageWidth = renderer.getScreenWidth();
|
||||
const int pageHeight = renderer.getScreenHeight();
|
||||
|
||||
renderer.clearScreen();
|
||||
|
||||
RenderConfig config;
|
||||
config.x = 0;
|
||||
config.y = 0;
|
||||
config.maxWidth = pageWidth;
|
||||
config.maxHeight = pageHeight;
|
||||
config.useGrayscale = true;
|
||||
config.useDithering = true;
|
||||
config.ditherMode = ImageDitherMode::Bayer;
|
||||
config.performanceMode = false;
|
||||
config.useExactDimensions = false;
|
||||
|
||||
PngToFramebufferConverter decoder;
|
||||
if (!decoder.decodeToFramebuffer(filename, renderer, config)) {
|
||||
LOG_DBG("SLP", "PNG sleep image decode failed: %s", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!overlayInfo.title.empty() || !overlayInfo.author.empty() || !overlayInfo.progressText.empty()) {
|
||||
const int lineHeight12 = renderer.getLineHeight(BOOKERLY_12_FONT_ID);
|
||||
const int lineHeight10 = renderer.getLineHeight(UI_10_FONT_ID);
|
||||
constexpr int lineSpacing = 3;
|
||||
constexpr int sectionSpacing = 10;
|
||||
|
||||
int textBlockHeight = 0;
|
||||
if (!overlayInfo.title.empty()) {
|
||||
textBlockHeight += lineHeight12;
|
||||
if (!overlayInfo.author.empty()) {
|
||||
textBlockHeight += lineSpacing;
|
||||
} else if (!overlayInfo.progressText.empty()) {
|
||||
textBlockHeight += sectionSpacing;
|
||||
}
|
||||
}
|
||||
if (!overlayInfo.author.empty()) {
|
||||
textBlockHeight += lineHeight10;
|
||||
if (!overlayInfo.progressText.empty()) {
|
||||
textBlockHeight += sectionSpacing;
|
||||
}
|
||||
}
|
||||
if (!overlayInfo.progressText.empty()) {
|
||||
textBlockHeight += lineHeight10;
|
||||
}
|
||||
|
||||
const int overlayY = pageHeight - textBlockHeight - (lineHeight12 / 3) - (lineHeight10 * 2 / 3);
|
||||
int y = overlayY + (lineHeight12 / 3);
|
||||
if (!overlayInfo.title.empty()) {
|
||||
renderer.drawText(BOOKERLY_12_FONT_ID, 10, y, overlayInfo.title.c_str(), true);
|
||||
y += lineHeight12;
|
||||
if (!overlayInfo.author.empty()) {
|
||||
y += lineSpacing;
|
||||
} else if (!overlayInfo.progressText.empty()) {
|
||||
y += sectionSpacing;
|
||||
}
|
||||
}
|
||||
if (!overlayInfo.author.empty()) {
|
||||
renderer.drawText(UI_10_FONT_ID, 10, y, overlayInfo.author.c_str(), true);
|
||||
y += lineHeight10;
|
||||
if (!overlayInfo.progressText.empty()) {
|
||||
y += sectionSpacing;
|
||||
}
|
||||
}
|
||||
if (!overlayInfo.progressText.empty()) {
|
||||
renderer.drawText(UI_10_FONT_ID, 10, y, overlayInfo.progressText.c_str(), true);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Per-scanline draw callback for PNG overlay compositing.
|
||||
// Transparent pixels (alpha < 128) are skipped so the reader page shows through.
|
||||
// Opaque pixels are drawn in their grayscale brightness (dark → black, light → white).
|
||||
@@ -268,28 +349,43 @@ void SleepActivity::renderCustomSleepScreen() const {
|
||||
}
|
||||
explicitSleepFile.close();
|
||||
}
|
||||
if (Storage.openFileForRead("SLP", "/sleep.png", explicitSleepFile)) {
|
||||
explicitSleepFile.close();
|
||||
const BookOverlayInfo resolvedOverlayInfo =
|
||||
shouldLoadOverlayInfo ? getBookOverlayInfo(APP_STATE.openEpubPath) : overlayInfo;
|
||||
if (renderPngSleepScreen("/sleep.png", renderer, resolvedOverlayInfo)) {
|
||||
LOG_DBG("SLP", "Loading explicit custom sleep image: /sleep.png");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect valid BMP files from both /.sleep and /sleep directories (no preference between them)
|
||||
const auto files = collectSleepImages(/*allowPng=*/false);
|
||||
// Collect valid BMP and PNG files from both /.sleep and /sleep directories (no preference between them)
|
||||
const auto files = collectSleepImages(/*allowPng=*/true);
|
||||
const auto numFiles = files.size();
|
||||
if (numFiles > 0) {
|
||||
const auto pickedIndex = pickSleepImageIndex(numFiles);
|
||||
APP_STATE.lastSleepImage = pickedIndex;
|
||||
APP_STATE.saveToFile();
|
||||
const auto& filename = files[pickedIndex];
|
||||
FsFile file;
|
||||
if (Storage.openFileForRead("SLP", filename, file)) {
|
||||
LOG_DBG("SLP", "Loading sleep image: %s", filename.c_str());
|
||||
delay(100);
|
||||
Bitmap bitmap(file, true);
|
||||
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
||||
const BookOverlayInfo resolvedOverlayInfo =
|
||||
shouldLoadOverlayInfo ? getBookOverlayInfo(APP_STATE.openEpubPath) : overlayInfo;
|
||||
renderBitmapSleepScreen(bitmap, resolvedOverlayInfo);
|
||||
file.close();
|
||||
LOG_DBG("SLP", "Loading sleep image: %s", filename.c_str());
|
||||
const BookOverlayInfo resolvedOverlayInfo =
|
||||
shouldLoadOverlayInfo ? getBookOverlayInfo(APP_STATE.openEpubPath) : overlayInfo;
|
||||
if (FsHelpers::hasPngExtension(filename)) {
|
||||
if (renderPngSleepScreen(filename, renderer, resolvedOverlayInfo)) {
|
||||
return;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
FsFile file;
|
||||
if (Storage.openFileForRead("SLP", filename, file)) {
|
||||
delay(100);
|
||||
Bitmap bitmap(file, true);
|
||||
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
||||
renderBitmapSleepScreen(bitmap, resolvedOverlayInfo);
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user