Brings ~55 mod-exclusive files to the upstream-based mod/master-resync branch: Activities (migrated to new ActivityManager pattern): - Clock/Time: SetTimeActivity, SetTimezoneOffsetActivity, NtpSyncActivity - Dictionary: DictionaryDefinitionActivity, DictionarySuggestionsActivity, DictionaryWordSelectActivity, LookedUpWordsActivity - Bookmark: EpubReaderBookmarkSelectionActivity - Book management: BookManageMenuActivity, EndOfBookMenuActivity - OPDS: OpdsServerListActivity, OpdsSettingsActivity - Utility: DirectoryPickerActivity, NumericStepperActivity Utilities (unchanged): - BookManager, BookSettings, BookmarkStore, BootNtpSync - Dictionary, LookupHistory, TimeSync, OpdsServerStore Libraries: PlaceholderCover, TableData, ChapterXPathIndexer Scripts: inject_mod_version, generate_book_icon, preview_placeholder_cover Docs: KOReader sync XPath mapping Migration changes: - ActivityWithSubactivity -> Activity base class - Callback constructors -> finish()/setResult() pattern - enterNewActivity() -> startActivityForResult() - Activity::RenderLock&& -> RenderLock&& These files won't compile yet - they reference mod settings and I18n strings that will be added in subsequent phases. Made-with: Cursor
2.9 KiB
2.9 KiB
Letterbox Fill: Replace Dithering with Edge Replication ("Extend Edges")
Date: 2026-02-13
Task Description
After implementing Bayer ordered dithering for the "Blended" letterbox fill mode (replacing earlier noise dithering), the user reported that the problematic cover (The World in a Grain by Vince Beiser) looked even worse. Investigation revealed that any dithering technique is fundamentally incompatible with the e-ink multi-pass rendering pipeline for large uniform fill areas:
- The BW HALF_REFRESH pass creates a visible dark/white pattern in the letterbox
- The subsequent greyscale correction can't cleanly handle mixed-level patterns in large uniform areas
- This causes crosstalk artifacts that can affect adjacent cover rendering
- Uniform fills (all same level, like Solid mode) work fine because all pixels go through identical correction
The user's key insight: the display already renders the cover's grayscale correctly (including Atkinson-dithered mixed levels). Instead of re-dithering an averaged color, replicate the cover's actual boundary pixels into the letterbox -- letting the display render them the same way it renders the cover itself.
Changes Made
src/CrossPointSettings.h
- Renamed
LETTERBOX_BLENDED(value 2) →LETTERBOX_EXTENDED - Updated comment to reflect "None / Solid / Extend Edges"
src/SettingsList.h
- Changed UI label from
"Blended"→"Extend Edges"
src/activities/boot_sleep/SleepActivity.cpp
- Removed:
BAYER_4X4matrix,quantizeBayerDither()function (all Bayer dithering code) - Added:
getPackedPixel(),setPackedPixel()helpers for packed 2-bit array access - Rewrote
LetterboxFillDatastruct:- Added
edgeA/edgeB(dynamically allocated packed 2-bit arrays) for per-pixel edge data - Added
edgePixelCount,scalefor coordinate mapping - Added
freeEdgeData()cleanup method
- Added
- Renamed
computeEdgeAverages()→computeEdgeData():- New
captureEdgePixelsparameter controls whether to allocate and fill edge arrays - For horizontal letterboxing: captures first/last visible BMP rows
- For vertical letterboxing: captures leftmost/rightmost pixel per visible row
- Still computes averages for SOLID mode in the same pass
- New
- Rewrote
drawLetterboxFill():- SOLID mode: unchanged (uniform fill with snapped level)
- EXTENDED mode: maps screen coordinates → BMP coordinates via scale factor, looks up stored 2-bit pixel values from the cover's boundary row/column
- Updated
renderBitmapSleepScreen(): new log messages,freeEdgeData()call at end
Backward Compatibility
- Enum value 2 is unchanged (was
LETTERBOX_BLENDED, nowLETTERBOX_EXTENDED) - Serialized settings files continue to work without migration
Follow-up Items
- Test "Extend Edges" mode with both covers (Power Broker and World in a Grain)
- Test vertical letterboxing (left/right) if applicable covers are available
- Verify SOLID mode still works as expected