crosspoint-reader/src/activities/reader/ReaderActivity.cpp
Eunchurn Park 16568932cf
Improve EPUB cover image quality with pre-scaling and Atkinson dithering
Pre-scaling (critical fix):
- Add pre-scaling to fit display dimensions (480x800) before dithering
  to prevent post-downsampling artifacts that destroy dithering patterns
- Use fixed-point (16.16) math for sub-pixel accurate scaling
- Implement area averaging for smooth downsampling

Dithering improvements:
- Add Atkinson dithering (75% error diffusion) for cleaner results
- Add Floyd-Steinberg dithering option with serpentine scanning
- Keep clustered-dot halftone and Bayer as compile-time options

Image adjustments:
- Add brightness, contrast, and gamma adjustments for better visibility
- Adjust RGB to grayscale conversion (25-50-25) to reduce blue darkness

Other changes:
- Fix MCU block indexing bug for correct picojpeg buffer access
- Pre-generate cover BMP when EPUB is loaded for faster sleep screen
2025-12-25 00:33:15 +09:00

72 lines
2.0 KiB
C++

#include "ReaderActivity.h"
#include <SD.h>
#include "Epub.h"
#include "EpubReaderActivity.h"
#include "FileSelectionActivity.h"
#include "activities/util/FullScreenMessageActivity.h"
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
if (!SD.exists(path.c_str())) {
Serial.printf("[%lu] [ ] File does not exist: %s\n", millis(), path.c_str());
return nullptr;
}
auto epub = std::unique_ptr<Epub>(new Epub(path, "/.crosspoint"));
if (epub->load()) {
// Pre-generate cover BMP for sleep screen (so it's cached when entering sleep)
if (!epub->generateCoverBmp()) {
Serial.printf("[%lu] [RDR] Cover BMP generation skipped or failed\n", millis());
}
return epub;
}
Serial.printf("[%lu] [ ] Failed to load epub\n", millis());
return nullptr;
}
void ReaderActivity::onSelectEpubFile(const std::string& path) {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Loading..."));
auto epub = loadEpub(path);
if (epub) {
onGoToEpubReader(std::move(epub));
} else {
exitActivity();
enterNewActivity(new FullScreenMessageActivity(renderer, inputManager, "Failed to load epub", REGULAR,
EInkDisplay::HALF_REFRESH));
delay(2000);
onGoToFileSelection();
}
}
void ReaderActivity::onGoToFileSelection() {
exitActivity();
enterNewActivity(new FileSelectionActivity(
renderer, inputManager, [this](const std::string& path) { onSelectEpubFile(path); }, onGoBack));
}
void ReaderActivity::onGoToEpubReader(std::unique_ptr<Epub> epub) {
exitActivity();
enterNewActivity(new EpubReaderActivity(renderer, inputManager, std::move(epub), [this] { onGoToFileSelection(); }));
}
void ReaderActivity::onEnter() {
ActivityWithSubactivity::onEnter();
if (initialEpubPath.empty()) {
onGoToFileSelection();
return;
}
auto epub = loadEpub(initialEpubPath);
if (!epub) {
onGoBack();
return;
}
onGoToEpubReader(std::move(epub));
}