feat: Add displayGrayscaleBase method for differential refresh
(#2334) Introduces a new display method that prepares the framebuffer as a base frame for grayscale overlays. On X3 panels, this uses the OEM differential base waveform (AA-pre-BW) without forcing a resync. Other panels fall back to normal display with configurable refresh mode. Dependent upon matching SDK commit to work
This commit is contained in:
@@ -1587,6 +1587,30 @@ size_t GfxRenderer::getBufferSize() const { return frameBufferSize; }
|
|||||||
// unused
|
// unused
|
||||||
// void GfxRenderer::grayscaleRevert() const { display.grayscaleRevert(); }
|
// void GfxRenderer::grayscaleRevert() const { display.grayscaleRevert(); }
|
||||||
|
|
||||||
|
void GfxRenderer::displayGrayscaleBase(HalDisplay::RefreshMode fallback) const {
|
||||||
|
display.displayGrayscaleBase(fallback, fadingFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GfxRenderer::preconditionGrayscale() const { display.preconditionGrayscale(); }
|
||||||
|
|
||||||
|
void GfxRenderer::preconditionGrayscale(int x, int y, int w, int h) const {
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
// Rotate the logical rect's opposite corners to physical panel coords; the
|
||||||
|
// physical bbox stays axis-aligned for all four orientations.
|
||||||
|
int ax, ay, bx, by;
|
||||||
|
rotateCoordinates(orientation, x, y, &ax, &ay, panelWidth, panelHeight);
|
||||||
|
rotateCoordinates(orientation, x + w - 1, y + h - 1, &bx, &by, panelWidth, panelHeight);
|
||||||
|
int x0 = ax < bx ? ax : bx, x1 = ax > bx ? ax : bx;
|
||||||
|
int y0 = ay < by ? ay : by, y1 = ay > by ? ay : by;
|
||||||
|
if (x0 < 0) x0 = 0;
|
||||||
|
if (y0 < 0) y0 = 0;
|
||||||
|
if (x1 >= panelWidth) x1 = panelWidth - 1;
|
||||||
|
if (y1 >= panelHeight) y1 = panelHeight - 1;
|
||||||
|
if (x1 < x0 || y1 < y0) return;
|
||||||
|
display.preconditionGrayscale(static_cast<uint16_t>(x0), static_cast<uint16_t>(y0),
|
||||||
|
static_cast<uint16_t>(x1 - x0 + 1), static_cast<uint16_t>(y1 - y0 + 1));
|
||||||
|
}
|
||||||
|
|
||||||
void GfxRenderer::copyGrayscaleLsbBuffers() const { display.copyGrayscaleLsbBuffers(frameBuffer); }
|
void GfxRenderer::copyGrayscaleLsbBuffers() const { display.copyGrayscaleLsbBuffers(frameBuffer); }
|
||||||
|
|
||||||
void GfxRenderer::copyGrayscaleMsbBuffers() const { display.copyGrayscaleMsbBuffers(frameBuffer); }
|
void GfxRenderer::copyGrayscaleMsbBuffers() const { display.copyGrayscaleMsbBuffers(frameBuffer); }
|
||||||
|
|||||||
@@ -218,6 +218,16 @@ class GfxRenderer {
|
|||||||
// Grayscale functions
|
// Grayscale functions
|
||||||
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
||||||
RenderMode getRenderMode() const { return renderMode; }
|
RenderMode getRenderMode() const { return renderMode; }
|
||||||
|
// Grayscale preconditioning settle pass (no-op on X4). The rect overload
|
||||||
|
// takes the gray region in LOGICAL screen coordinates and rotates it to the
|
||||||
|
// panel; the no-arg overload settles the full frame. Call after the BW base
|
||||||
|
// frame is displayed and before the grayscale planes are written.
|
||||||
|
void preconditionGrayscale() const;
|
||||||
|
void preconditionGrayscale(int x, int y, int w, int h) const;
|
||||||
|
// Display the framebuffer as the base frame for a grayscale overlay that
|
||||||
|
// follows (X3: OEM differential base waveform; others: plain display with
|
||||||
|
// `fallback`).
|
||||||
|
void displayGrayscaleBase(HalDisplay::RefreshMode fallback = HalDisplay::HALF_REFRESH) const;
|
||||||
void copyGrayscaleLsbBuffers() const;
|
void copyGrayscaleLsbBuffers() const;
|
||||||
void copyGrayscaleMsbBuffers() const;
|
void copyGrayscaleMsbBuffers() const;
|
||||||
void displayGrayBuffer() const;
|
void displayGrayBuffer() const;
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ void HalDisplay::copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* m
|
|||||||
einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer);
|
einkDisplay.copyGrayscaleBuffers(lsbBuffer, msbBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HalDisplay::displayGrayscaleBase(RefreshMode fallback, bool turnOffScreen) {
|
||||||
|
einkDisplay.displayGrayscaleBase(convertRefreshMode(fallback), turnOffScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HalDisplay::preconditionGrayscale() { einkDisplay.preconditionGrayscale(); }
|
||||||
|
|
||||||
|
void HalDisplay::preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
|
||||||
|
einkDisplay.preconditionGrayscale(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); }
|
void HalDisplay::copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer) { einkDisplay.copyGrayscaleLsbBuffers(lsbBuffer); }
|
||||||
|
|
||||||
void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); }
|
void HalDisplay::copyGrayscaleMsbBuffers(const uint8_t* msbBuffer) { einkDisplay.copyGrayscaleMsbBuffers(msbBuffer); }
|
||||||
|
|||||||
@@ -47,6 +47,19 @@ class HalDisplay {
|
|||||||
// Access to frame buffer
|
// Access to frame buffer
|
||||||
uint8_t* getFrameBuffer() const;
|
uint8_t* getFrameBuffer() const;
|
||||||
|
|
||||||
|
// X3 grayscale preconditioning (OEM "AA-pre-BW(mid)" settle pass), windowed
|
||||||
|
// to the gray region in physical panel coordinates (no-arg = full frame).
|
||||||
|
// Call after the BW base frame is displayed and before the grayscale planes
|
||||||
|
// are written; no-op on X4. See EInkDisplay::preconditionGrayscale.
|
||||||
|
void preconditionGrayscale();
|
||||||
|
void preconditionGrayscale(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
|
||||||
|
|
||||||
|
// Display the framebuffer as the base frame for a grayscale overlay that
|
||||||
|
// follows. X3 uses the OEM differential base waveform ("AA-pre-BW(mid)");
|
||||||
|
// other panels display normally with `fallback` mode (previous behavior).
|
||||||
|
// Deliberately does NOT force the X3 resync that displayBuffer(HALF) does.
|
||||||
|
void displayGrayscaleBase(RefreshMode fallback = HALF_REFRESH, bool turnOffScreen = false);
|
||||||
|
|
||||||
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
void copyGrayscaleBuffers(const uint8_t* lsbBuffer, const uint8_t* msbBuffer);
|
||||||
void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer);
|
void copyGrayscaleLsbBuffers(const uint8_t* lsbBuffer);
|
||||||
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer);
|
void copyGrayscaleMsbBuffers(const uint8_t* msbBuffer);
|
||||||
|
|||||||
+1
-1
Submodule open-x4-sdk updated: 26648d643a...198ad26721
@@ -218,7 +218,15 @@ void SleepActivity::renderBitmapSleepScreen(const Bitmap& bitmap) const {
|
|||||||
renderer.invertScreen();
|
renderer.invertScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasGreyscale) {
|
||||||
|
// OEM grayscale pipeline base: on X3 this displays the frame with the
|
||||||
|
// dedicated "AA-pre-BW(mid)" differential waveform, leaving every pixel
|
||||||
|
// in the calibrated state the gray nudge refresh expects; on X4 it is a
|
||||||
|
// plain HALF refresh (previous behavior).
|
||||||
|
renderer.displayGrayscaleBase(HalDisplay::HALF_REFRESH);
|
||||||
|
} else {
|
||||||
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasGreyscale) {
|
if (hasGreyscale) {
|
||||||
bitmap.rewindToData();
|
bitmap.rewindToData();
|
||||||
|
|||||||
@@ -294,7 +294,19 @@ void XtcReaderActivity::renderPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReaderUtils::displayWithRefreshCycle(renderer, pagesUntilFullRefresh);
|
if (pagesUntilFullRefresh <= 1) {
|
||||||
|
// Periodic ghost cleanup: scrub via the normal path, then run the
|
||||||
|
// settle flavor of the grayscale base pass (DTM planes are equal after
|
||||||
|
// the display sync, so only the gentle reinforcement cells fire).
|
||||||
|
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
|
||||||
|
renderer.preconditionGrayscale();
|
||||||
|
pagesUntilFullRefresh = SETTINGS.getRefreshFrequency();
|
||||||
|
} else {
|
||||||
|
// OEM grayscale pipeline base: differential "AA-pre-BW(mid)" update as
|
||||||
|
// the page turn on X3; plain FAST refresh on X4 (previous behavior).
|
||||||
|
renderer.displayGrayscaleBase(HalDisplay::FAST_REFRESH);
|
||||||
|
pagesUntilFullRefresh--;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass 2: LSB buffer - mark DARK gray only (XTH value 1)
|
// Pass 2: LSB buffer - mark DARK gray only (XTH value 1)
|
||||||
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
|
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
|
||||||
|
|||||||
Reference in New Issue
Block a user