feat: 1614 lut pxc#13
Open
pablohc wants to merge 68 commits into
Open
Conversation
Add GRAY2 absolute encoding for 4-shade e-ink rendering via factory LUTs. Introduce FactoryFast and FactoryQuality GrayscaleMode variants with X3 fallback to Differential. Add direct-pixel BMP rendering path, 2-bit XTC plane support in Xtc parser, and HALF_REFRESH display mode in HalDisplay. displayXtchPlanes uses Differential on X3 (factory LUT not calibrated). Skip fadingFix power-down on first BW render after factory LUT to avoid redundant power cycle (display already off after 0xC7 sequence).
Add PxcViewerActivity for PXC image display using factory LUT grayscale. Update BmpViewerActivity and SleepActivity with X3-aware grayscale mode. EPUB reader uses FactoryQuality on X4 for image pages, Differential on X3. XTC reader pre-flashes to white on entry; periodic FULL_REFRESH every 32 pages. Add UITheme metrics for PXC viewer layout.
Add ScreenshotUtil to detect factory LUT display state and re-render the current page via renderGrayscale for accurate screenshot output. X3 falls back to Differential mode for all grayscale paths.
…m Home When entering sleep from Home (not Reader), the display particle state is in BW mode from FAST_REFRESH renders. Without pre-conditioning, the subsequent factory LUT render produces a dirty appearance. Added a clearScreen + HALF_REFRESH before displayXtchPlanes to reset particles to a known state.
Replace the 2-bit BMP thumbnail generation for XTCH files with 1-bit BMP using Atkinson dithering (matching EPUB thumbnail quality). Key changes: - Load both XTCH bitplanes separately for area-averaged downsampling - Apply Atkinson 1-bit dithering (3 error rows, 6 neighbors, 6/8 error) - Apply 1.2x contrast boost for better tonal separation - Inverted bitplane luminance to match display polarity - Falls back to 2-bit BMP on memory allocation failure
Replace hash-based noise dithering with Atkinson 1-bit dithering to match EPUB thumbnail quality. Key changes: - Atkinson 1-bit dithering (3 error rows, 6 neighbors, 6/8 error) - 1.2x contrast boost for better edge definition - Same BMP convention as XTCH path (memset 0xFF + clear bit for dark)
Eliminate ~80 lines of duplicated code: - Replace inline Atkinson dithering with Atkinson1BitDitherer class (same class used by EPUB thumbnail generation) - Extract computeSrcRange() helper for downscale coordinate calc (was duplicated 4 times across XTCH/XTC paths)
After reader sessions, heap fragmentation reduces MaxAlloc to ~65KB, preventing the primary path from allocating both planes simultaneously (96KB). The fallback produced a raw 2-bit BMP with no dithering, resulting in poor visual quality on the Home Screen. Unify to a single sequential path: load plane1 (48KB) -> majority-vote -> free, load plane2 (48KB) -> combine -> Atkinson dithering -> 1-bit BMP. Peak memory ~52KB, always within MaxAlloc constraints. This matches the same approach used by XTC 1-bit thumbnails, ensuring consistent quality across all themes and memory states.
Standardize all grayscale rendering to use FactoryQuality mode instead of conditionally using Differential mode for X3 devices. Also inverts the useFactoryGray logic in EpubReaderActivity to enable factory grayscale for X3 devices on image pages.
Remove X3-specific factory gray rendering path and associated pre-flash logic. Factory gray mode now only applies to image pages when text anti-aliasing is enabled, regardless of device type.
Remove obsolete comments about factory gray mode and text-only antialiasing display handling that are no longer relevant to the current implementation.
The 1-bit XTC sleep path unconditionally cleared the screen and issued a HALF_REFRESH before calling displayXtcBwPage, which itself runs clearScreen + FAST_REFRESH. Match the 2-bit branch by skipping the pre-flash when the previous activity was the reader, avoiding the redundant refresh. Also dedupes a few render callbacks while in the area: - Extract the 'Entering sleep' overlay lambda into drawEnteringSleepOverlay. - Promote PageRenderCtx + grayFn to a private nested struct and static renderPageCallback shared by renderContents and onScreenshotRequest. - Consolidate PxcCtx and the rendering/overlay callbacks into file-scope statics behind a renderPxcToFramebuffer helper used by both onEnter and renderGrayscaleImage.
- Reject PXC files whose dimensions don't exactly match the runtime screen size. The previous abs(w-sw) > 1 tolerance allowed a one-pixel oversize, which would cause out-of-bounds writes in DirectPixelWriter (no bounds checking on writePixel). Strict equality is correct per device since renderer.getScreenWidth/Height are device-aware (X4 800x480 vs X3 792x528). - Make renderPxcSleepScreen return bool so the /sleep.pxc path can fall through to /sleep.bmp when the PXC is missing/invalid, instead of short-circuiting straight to the default sleep screen. Other callers (random wallpaper picker, screenshot replay) explicitly fall back to renderDefaultSleepScreen on failure to preserve prior behaviour. - In onScreenshotRequest, store the BW page before the factory-gray render and restore it before cleanupGrayscaleWithFrameBuffer, so the controller's BW state is synced to the actual page rather than a cleared framebuffer. Mirrors the pattern in the normal render path.
The clearScreen + cleanupGrayscaleWithFrameBuffer in onScreenshotRequest intentionally puts the controller into a 'BW = white' state. This is the correct precondition for the HALF_REFRESH pre-flash done by the next SleepActivity render (triggered by goToSleep on the way into deep sleep) and the panel resets across deep sleep, so no preservation of the displayed image is needed here.
# Conflicts: # src/activities/reader/XtcReaderActivity.cpp
- Remove BW/Inverted filter path for PXC (bypassed factory LUT) - PXC now always uses FactoryQuality grayscale mode - DirectPixelWriter handles orientation transforms automatically - Filter settings (BLACK_AND_WHITE/INVERTED) ignored for PXC - Consistent with PxcViewerActivity behavior - Maintains PR 1614 intent (factory LUT + PXC support)
# Conflicts: # src/activities/reader/XtcReaderActivity.cpp # src/activities/reader/XtcReaderActivity.h
- GfxRenderer.h: merge factory LUT grayscale (PR) + SD card fonts (master) - EpubReaderActivity.cpp: keep PR's refactored renderGrayscale() approach
Two coupled changes for the new SDK contract: - After displayGrayBuffer in GfxRenderer::renderGrayscale and renderGrayscaleSinglePass (both the malloc'd path and the two-pass fallback), call display.clearGrayscaleModeFlag(). Differential mode rendering was leaving inGrayscaleMode=true, which (post-SDK fix) triggered an automatic ~700ms revert refresh on the next BW page turn. The renderer already manages cleanup via cleanupGrayscaleBuffers inside restoreBwBuffer, so the SDK auto-revert is unwanted overhead. - Drop the redundant cleanupGrayscaleWithFrameBuffer call after restoreBwBuffer in EpubReaderActivity (page render path and onScreenshotRequest). restoreBwBuffer already calls cleanupGrayscaleBuffers(frameBuffer), so the explicit second call wrote RED RAM with identical data twice per page. RED RAM is now rebased exactly once per gray-to-BW transition. Bumps SDK submodule to ccfd37b (community-sdk PR #4 head): the clearGrayscaleModeFlag() accessor and idempotent grayscaleRevert contract this code depends on.
Mirrors the BMP viewer feature set for .pxc files: Up/Down navigates siblings in the current folder, Confirm copies the current image to /sleep.pxc and switches sleepScreen to CUSTOM.
Exposes the SSD1677 factory LUT trade-off for XTC reading: Speed uses lut_factory_fast (default), Quality uses lut_factory_quality. Selectable under Settings -> Reader as "2-bit XTC". Stored as xtcRenderQuality in the JSON settings file. displayXtchPlanes() gains an optional GrayscaleMode parameter (default FactoryFast) and selects the LUT at the displayGrayBuffer call. XtcReaderActivity reads SETTINGS.xtcRenderQuality at the call site to pick FactoryFast or FactoryQuality.
Ports upstream crosspoint-reader#1852 prev/next arrow labels onto BmpViewer and mirrors the same behavior on PxcViewer for parity with the recently-added sibling navigation. Both viewers now show '<' / '>' next to BACK and SET_SLEEP_COVER when adjacent siblings exist, and accept Left/Right alongside Up/Down for sibling navigation. PxcViewer's renderPxcToFramebuffer() gains hasPrevious/hasNext bools so the interactive onEnter path receives arrow labels while the screenshot path passes false/false (matches BMP screenshot behavior).
# Conflicts: # open-x4-sdk # src/activities/util/BmpViewerActivity.cpp
Refactor: - Rename GfxRenderer::GrayscaleMode -> GrayscaleDriveMode to separate caller intent (which LUT, fast vs quality vs differential) from low-level panel plumbing (RenderMode values, lut pointer, factoryMode flag, g_differentialQuantize). - Add file-private GrayscaleDriveSpec + resolveGrayscaleDrive() in GfxRenderer.cpp. Replaces four duplicated ternary clusters across renderGrayscale, renderGrayscaleSinglePass (main + malloc-failed fallback), and displayXtchPlanes. - Tidy SleepActivity displayXtchPlanes call: drop two inert args (preFlash is false, so passes count and refresh mode were unreachable). Add a one-line comment pointing to the external precondition. Stock-firmware experiments (PXC sleep ghost investigation, see docs/v559-disassembly-findings.md): - renderGrayscaleSinglePass factory path now uses the split SDK API (displayGrayBufferFactorySetup + RAM writes + displayGrayBufferFactoryActivate) to match stock's SPI order: LUT load -> Border Waveform -> RAM writes -> CTRL1/CTRL2/MASTER_ACTIVATION. - Precondition (runFactorySleepPrecondition) commented out in all three SleepActivity render paths (PXC, BMP grayscale, XTCH planes) to test whether the precondition's post-FULL_REFRESH RED RAM sync was contributing. - Bump open-x4-sdk pointer to pick up the matching SDK experiments (FACTORY_GRAY CTRL2 = 0xCC, Border Waveform per render, BOOSTER and BORDER init bytes matching stock, deepSleep factory branch early-return, factory LUT voltages set to stock's byte-exact 0x00/0x00/0x01/0x22/0x22 sequence).
Picks up the fix for displayGrayBufferFactoryActivate not restoring VCOM after a non-default-VCOM LUT load. See SDK commit 4e949dc.
Picks up the revert of the VCOM restore in displayGrayBufferFactoryActivate. Preserves byte-exact stock V5.5.9 SPI sequence for the PXC sleep ghost experiment. See SDK commit 7c8afd0.
Re-enable runFactorySleepPrecondition in all three SleepActivity render paths (PXC, BMP grayscale, XTCH planes). The disable was an experiment that didn't move the ghost; restoring it to test against stock's actual pre-LUT sequence. Switch the precondition itself from renderer.clearScreen + renderer.displayBuffer(FULL_REFRESH) to renderer.displayBufferPrecondition() — a new HAL/SDK method that fires CTRL2 = 0xF7 (full power cycle, matches stock V5.5.9) and skips the SINGLE_BUFFER_MODE post-RED-sync. Wrappers added to HalDisplay and GfxRenderer. Bump open-x4-sdk pointer to 4982527 which contains the new EInkDisplay::displayBufferPrecondition implementation. See docs/v559-disassembly-findings.md for the full byte-match rationale.
The stock byte-exact lut_factory_quality swap caused all-white renders on device. Reverted in SDK; bumping pointer to match. The "OTP-preserve for 0x00 voltages" hypothesis from Difference #8 is falsified by this empirical result. See docs/v559-disassembly-findings.md.
Previously the split SDK API (displayGrayBufferFactorySetup + displayGrayBufferFactoryActivate, matching stock V5.5.9's LUT-load-before-RAM-writes SPI order — Difference #4) was applied only to renderGrayscaleSinglePass. PxcViewer and BmpViewer use that singlepass path so they got the patch implicitly, but the other factory render callers did not: - renderGrayscale (two-pass) → used by EpubReaderActivity for inline grayscale images - displayXtchPlanes → used by XtcReaderActivity and SleepActivity's XTCH cover path Refactor both to use the split SDK API for factory mode: - renderGrayscale: insert displayGrayBufferFactorySetup before the first copyGrayscaleLsbBuffers, replace the trailing displayGrayBuffer with displayGrayBufferFactoryActivate. Differential mode keeps the combined displayGrayBuffer (no change). - displayXtchPlanes: resolve drive spec early, call Setup before the plane1 RAM write, call Activate after the plane2 RAM write. Net result: all 6 factory render call sites (PXC viewer, BMP viewer, EPUB inline images, XTC reader, sleep XTCH path, sleep PXC/BMP path) now follow the stock SPI order. See docs/v559-disassembly-findings.md Difference #4.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Additional Context
specific areas to focus on).
AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? < YES | PARTIALLY | NO >