mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2025-12-17 06:37:42 +03:00
Use single buffer mode for EInkDisplay (#34)
## Summary * Frees up 48kB of statically allocated RAM in exchange for 48kB just when grayscale rendering is needed ## Additional Context * Upstream changes: https://github.com/open-x4-epaper/community-sdk/pull/7
This commit is contained in:
parent
5d68c8b305
commit
c287aa03a4
@ -132,13 +132,19 @@ void GfxRenderer::displayBuffer(const EInkDisplay::RefreshMode refreshMode) cons
|
||||
einkDisplay.displayBuffer(refreshMode);
|
||||
}
|
||||
|
||||
// TODO: Support partial window update
|
||||
// void GfxRenderer::flushArea(const int x, const int y, const int width, const int height) const {
|
||||
// const int rotatedX = y;
|
||||
// const int rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x;
|
||||
//
|
||||
// einkDisplay.displayBuffer(EInkDisplay::FAST_REFRESH, rotatedX, rotatedY, height, width);
|
||||
// }
|
||||
void GfxRenderer::displayWindow(const int x, const int y, const int width, const int height) const {
|
||||
// Rotate coordinates from portrait (480x800) to landscape (800x480)
|
||||
// Rotation: 90 degrees clockwise
|
||||
// Portrait coordinates: (x, y) with dimensions (width, height)
|
||||
// Landscape coordinates: (rotatedX, rotatedY) with dimensions (rotatedWidth, rotatedHeight)
|
||||
|
||||
const int rotatedX = y;
|
||||
const int rotatedY = EInkDisplay::DISPLAY_HEIGHT - 1 - x - width + 1;
|
||||
const int rotatedWidth = height;
|
||||
const int rotatedHeight = width;
|
||||
|
||||
einkDisplay.displayWindow(rotatedX, rotatedY, rotatedWidth, rotatedHeight);
|
||||
}
|
||||
|
||||
// Note: Internal driver treats screen in command orientation, this library treats in portrait orientation
|
||||
int GfxRenderer::getScreenWidth() { return EInkDisplay::DISPLAY_HEIGHT; }
|
||||
@ -164,7 +170,7 @@ int GfxRenderer::getLineHeight(const int fontId) const {
|
||||
|
||||
uint8_t* GfxRenderer::getFrameBuffer() const { return einkDisplay.getFrameBuffer(); }
|
||||
|
||||
void GfxRenderer::swapBuffers() const { einkDisplay.swapBuffers(); }
|
||||
size_t GfxRenderer::getBufferSize() { return EInkDisplay::BUFFER_SIZE; }
|
||||
|
||||
void GfxRenderer::grayscaleRevert() const { einkDisplay.grayscaleRevert(); }
|
||||
|
||||
@ -174,6 +180,35 @@ void GfxRenderer::copyGrayscaleMsbBuffers() const { einkDisplay.copyGrayscaleMsb
|
||||
|
||||
void GfxRenderer::displayGrayBuffer() const { einkDisplay.displayGrayBuffer(); }
|
||||
|
||||
/**
|
||||
* This should be called before grayscale buffers are populated.
|
||||
* A `restoreBwBuffer` call should always follow the grayscale render if this method was called.
|
||||
*/
|
||||
void GfxRenderer::storeBwBuffer() {
|
||||
if (bwBuffer) {
|
||||
Serial.printf("[%lu] [GFX] !! BW buffer already stored - this is likely a bug, freeing it\n", millis());
|
||||
free(bwBuffer);
|
||||
}
|
||||
|
||||
bwBuffer = static_cast<uint8_t*>(malloc(EInkDisplay::BUFFER_SIZE));
|
||||
memcpy(bwBuffer, einkDisplay.getFrameBuffer(), EInkDisplay::BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* This can only be called if `storeBwBuffer` was called prior to the grayscale render.
|
||||
* It should be called to restore the BW buffer state after grayscale rendering is complete.
|
||||
*/
|
||||
void GfxRenderer::restoreBwBuffer() {
|
||||
if (!bwBuffer) {
|
||||
Serial.printf("[%lu] [GFX] !! BW buffer not stored - this is likely a bug\n", millis());
|
||||
return;
|
||||
}
|
||||
|
||||
einkDisplay.cleanupGrayscaleBuffers(bwBuffer);
|
||||
free(bwBuffer);
|
||||
bwBuffer = nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -12,6 +12,7 @@ class GfxRenderer {
|
||||
private:
|
||||
EInkDisplay& einkDisplay;
|
||||
RenderMode renderMode;
|
||||
uint8_t* bwBuffer = nullptr;
|
||||
std::map<int, EpdFontFamily> fontMap;
|
||||
void renderChar(const EpdFontFamily& fontFamily, uint32_t cp, int* x, const int* y, bool pixelState,
|
||||
EpdFontStyle style) const;
|
||||
@ -27,6 +28,8 @@ class GfxRenderer {
|
||||
static int getScreenWidth();
|
||||
static int getScreenHeight();
|
||||
void displayBuffer(EInkDisplay::RefreshMode refreshMode = EInkDisplay::FAST_REFRESH) const;
|
||||
// EXPERIMENTAL: Windowed update - display only a rectangular region (portrait coordinates)
|
||||
void displayWindow(int x, int y, int width, int height) const;
|
||||
void invertScreen() const;
|
||||
void clearScreen(uint8_t color = 0xFF) const;
|
||||
|
||||
@ -49,9 +52,11 @@ class GfxRenderer {
|
||||
void copyGrayscaleLsbBuffers() const;
|
||||
void copyGrayscaleMsbBuffers() const;
|
||||
void displayGrayBuffer() const;
|
||||
void storeBwBuffer();
|
||||
void restoreBwBuffer();
|
||||
|
||||
// Low level functions
|
||||
uint8_t* getFrameBuffer() const;
|
||||
void swapBuffers() const;
|
||||
static size_t getBufferSize();
|
||||
void grayscaleRevert() const;
|
||||
};
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 4d0dcd5ff87fcd86eb2966a123e85b03284a03db
|
||||
Subproject commit 98a5aa1f8969ccd317c9b45bf0fa84b6c82e167f
|
||||
@ -20,6 +20,7 @@ build_flags =
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1
|
||||
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1
|
||||
# https://libexpat.github.io/doc/api/latest/#XML_GE
|
||||
-DXML_GE=0
|
||||
-DXML_CONTEXT_BYTES=1024
|
||||
|
||||
@ -31,7 +31,6 @@ void EpubReaderScreen::onEnter() {
|
||||
|
||||
epub->setupCacheDir();
|
||||
|
||||
// TODO: Move this to a state object
|
||||
if (SD.exists((epub->getCachePath() + "/progress.bin").c_str())) {
|
||||
File f = SD.open((epub->getCachePath() + "/progress.bin").c_str());
|
||||
uint8_t data[4];
|
||||
@ -210,20 +209,20 @@ void EpubReaderScreen::renderScreen() {
|
||||
Serial.printf("[%lu] [ERS] Cache not found, building...\n", millis());
|
||||
|
||||
{
|
||||
renderer.grayscaleRevert();
|
||||
|
||||
const int textWidth = renderer.getTextWidth(READER_FONT_ID, "Indexing...");
|
||||
constexpr int margin = 20;
|
||||
const int x = (GfxRenderer::getScreenWidth() - textWidth - margin * 2) / 2;
|
||||
constexpr int y = 50;
|
||||
const int w = textWidth + margin * 2;
|
||||
const int h = renderer.getLineHeight(READER_FONT_ID) + margin * 2;
|
||||
renderer.grayscaleRevert();
|
||||
uint8_t* fb1 = renderer.getFrameBuffer();
|
||||
renderer.swapBuffers();
|
||||
memcpy(fb1, renderer.getFrameBuffer(), EInkDisplay::BUFFER_SIZE);
|
||||
renderer.fillRect(x, y, w, h, 0);
|
||||
// Round all coordinates to 8 pixel boundaries
|
||||
const int x = ((GfxRenderer::getScreenWidth() - textWidth - margin * 2) / 2 + 7) / 8 * 8;
|
||||
constexpr int y = 56;
|
||||
const int w = (textWidth + margin * 2 + 7) / 8 * 8;
|
||||
const int h = (renderer.getLineHeight(READER_FONT_ID) + margin * 2 + 7) / 8 * 8;
|
||||
renderer.fillRect(x, y, w, h, false);
|
||||
renderer.drawText(READER_FONT_ID, x + margin, y + margin, "Indexing...");
|
||||
renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
|
||||
renderer.displayBuffer();
|
||||
// EXPERIMENTAL: Still suffers from ghosting
|
||||
renderer.displayWindow(x, y, w, h);
|
||||
pagesUntilFullRefresh = 0;
|
||||
}
|
||||
|
||||
@ -297,6 +296,9 @@ void EpubReaderScreen::renderContents(std::unique_ptr<Page> page) {
|
||||
pagesUntilFullRefresh--;
|
||||
}
|
||||
|
||||
// Save bw buffer to reset buffer state after grayscale data sync
|
||||
renderer.storeBwBuffer();
|
||||
|
||||
// grayscale rendering
|
||||
// TODO: Only do this if font supports it
|
||||
{
|
||||
@ -315,6 +317,9 @@ void EpubReaderScreen::renderContents(std::unique_ptr<Page> page) {
|
||||
renderer.displayGrayBuffer();
|
||||
renderer.setRenderMode(GfxRenderer::BW);
|
||||
}
|
||||
|
||||
// restore the bw data
|
||||
renderer.restoreBwBuffer();
|
||||
}
|
||||
|
||||
void EpubReaderScreen::renderStatusBar() const {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user