Compare commits

...

2 Commits

Author SHA1 Message Date
Dave Allie
647347c21d
Correctly reset pagesUntilFullRefresh each full refresh 2025-12-29 01:49:14 +11:00
Eunchurn Park
76a1c30a87
fix(xtc): address PR review feedback for grayscale rendering
- Use FsHelpers::openFileForRead for consistency (XtcParser.cpp)
- Simplify file extension checking logic (FileSelectionActivity.cpp)
- Fix grayscale rendering bugs (XtcReaderActivity.cpp):
  - Use drawPixel(x, y, false) for LSB/MSB passes (LUT: 0=apply effect)
  - Add cleanupGrayscaleWithFrameBuffer() after re-rendering BW
  - Use conditional HALF_REFRESH based on pagesUntilFullRefresh
  - Decrement refresh counter instead of resetting
- Add cleanupGrayscaleWithFrameBuffer() helper (GfxRenderer)
2025-12-28 20:57:10 +09:00
5 changed files with 32 additions and 22 deletions

View File

@ -369,6 +369,17 @@ void GfxRenderer::restoreBwBuffer() {
Serial.printf("[%lu] [GFX] Restored and freed BW buffer chunks\n", millis());
}
/**
* Cleanup grayscale buffers using the current frame buffer.
* Use this when BW buffer was re-rendered instead of stored/restored.
*/
void GfxRenderer::cleanupGrayscaleWithFrameBuffer() const {
uint8_t* frameBuffer = einkDisplay.getFrameBuffer();
if (frameBuffer) {
einkDisplay.cleanupGrayscaleBuffers(frameBuffer);
}
}
void GfxRenderer::renderChar(const EpdFontFamily& fontFamily, const uint32_t cp, int* x, const int* y,
const bool pixelState, const EpdFontStyle style) const {
const EpdGlyph* glyph = fontFamily.getGlyph(cp, style);

View File

@ -67,6 +67,7 @@ class GfxRenderer {
void displayGrayBuffer() const;
bool storeBwBuffer(); // Returns true if buffer was stored successfully
void restoreBwBuffer();
void cleanupGrayscaleWithFrameBuffer() const;
// Low level functions
uint8_t* getFrameBuffer() const;

View File

@ -7,6 +7,7 @@
#include "XtcParser.h"
#include <FsHelpers.h>
#include <HardwareSerial.h>
#include <cstring>
@ -31,9 +32,7 @@ XtcError XtcParser::open(const char* filepath) {
}
// Open file
m_file = SD.open(filepath, FILE_READ);
if (!m_file) {
Serial.printf("[%lu] [XTC] Failed to open file: %s\n", millis(), filepath);
if (!FsHelpers::openFileForRead("XTC", filepath, m_file)) {
m_lastError = XtcError::FILE_NOT_FOUND;
return m_lastError;
}

View File

@ -40,18 +40,11 @@ void FileSelectionActivity::loadFiles() {
if (file.isDirectory()) {
files.emplace_back(filename + "/");
} else if (filename.length() >= 5 && filename.substr(filename.length() - 5) == ".epub") {
files.emplace_back(filename);
} else if (filename.length() >= 4) {
// Check for XTC format extensions (.xtc, .xtch)
std::string ext4 = filename.substr(filename.length() - 4);
if (ext4 == ".xtc") {
} else {
std::string ext4 = filename.length() >= 4 ? filename.substr(filename.length() - 4) : "";
std::string ext5 = filename.length() >= 5 ? filename.substr(filename.length() - 5) : "";
if (ext5 == ".epub" || ext5 == ".xtch" || ext4 == ".xtc") {
files.emplace_back(filename);
} else if (filename.length() >= 5) {
std::string ext5 = filename.substr(filename.length() - 5);
if (ext5 == ".xtch") {
files.emplace_back(filename);
}
}
}
file.close();

View File

@ -236,29 +236,35 @@ void XtcReaderActivity::renderPage() {
}
}
// Display BW first with half refresh (clean base for grayscale overlay)
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
// Display BW with conditional refresh based on pagesUntilFullRefresh
if (pagesUntilFullRefresh <= 1) {
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
pagesUntilFullRefresh = pagesPerRefresh;
} else {
renderer.displayBuffer();
pagesUntilFullRefresh--;
}
// Pass 2: LSB buffer - mark DARK gray only (XTH value 1)
// README: "mark the **dark** grays pixels with `1`"
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
renderer.clearScreen(0x00);
for (uint16_t y = 0; y < pageHeight; y++) {
for (uint16_t x = 0; x < pageWidth; x++) {
if (getPixelValue(x, y) == 1) { // Dark grey only
renderer.drawPixel(x, y, true);
renderer.drawPixel(x, y, false);
}
}
}
renderer.copyGrayscaleLsbBuffers();
// Pass 3: MSB buffer - mark LIGHT AND DARK gray (XTH value 1 or 2)
// README: "mark the **light and dark** grays pixels with `1`"
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
renderer.clearScreen(0x00);
for (uint16_t y = 0; y < pageHeight; y++) {
for (uint16_t x = 0; x < pageWidth; x++) {
const uint8_t pv = getPixelValue(x, y);
if (pv == 1 || pv == 2) { // Dark grey or Light grey
renderer.drawPixel(x, y, true);
renderer.drawPixel(x, y, false);
}
}
}
@ -277,8 +283,8 @@ void XtcReaderActivity::renderPage() {
}
}
// Reset refresh counter (grayscale display is a full refresh)
pagesUntilFullRefresh = pagesPerRefresh;
// Cleanup grayscale buffers with current frame buffer
renderer.cleanupGrayscaleWithFrameBuffer();
free(pageBuffer);