Files
crosspoint-reader-mod/mod/prs/sleep-screen-tweaks.md

49 lines
4.3 KiB
Markdown
Raw Normal View History

# feat: Sleep screen letterbox fill and image upscaling
**Branch:** `mod/sleep-screen-tweaks`
## Summary
* **What is the goal of this PR?**
Improve the sleep screen experience for cover images that don't match the display's aspect ratio, and fix Fit mode for images smaller than the screen.
* **What changes are included?**
- Configurable letterbox fill for sleep screen cover images. Four fill modes:
- **Solid** — computes the dominant (average) shade from the image edge and fills the entire letterbox area with that single dithered color.
- **Blended** — fills using per-pixel sampled edge colors with noise dithering (no distance-based interpolation), producing a smooth edge-matching fill that varies along the image border.
- **Gradient** — interpolates per-pixel edge colors toward a configurable target color (white or black) over distance, creating a fade effect.
- **None** — plain white letterbox (original behavior).
- Image upscaling in Fit mode: images smaller than the display are now scaled up to fit while preserving aspect ratio. Previously, small images were simply centered without scaling.
- Edge data caching: the edge-sampling pass (which reads the full bitmap) is performed once per cover and cached as a compact binary file (~1KB) alongside the cover BMP in `.crosspoint/`. Subsequent sleeps load from cache, skipping the bitmap scan entirely. Cache is validated against screen dimensions and auto-regenerated when stale.
- Two new user-facing settings:
- **Letterbox Fill** — None / Solid / Blended / Gradient
- **Gradient Direction** — To White / To Black
## Files Changed
| File | Change |
|------|--------|
| `lib/GfxRenderer/GfxRenderer.cpp` | Unified block-fill scaling in `drawBitmap`/`drawBitmap1Bit` supporting both upscaling and downscaling; added `drawPixelGray` helper |
| `lib/GfxRenderer/GfxRenderer.h` | `drawPixelGray` declaration |
| `lib/GfxRenderer/BitmapHelpers.cpp` | Added `quantizeNoiseDither` — hash-based noise dithering for gradient/solid fills |
| `lib/GfxRenderer/BitmapHelpers.h` | `quantizeNoiseDither` declaration |
| `src/activities/boot_sleep/SleepActivity.cpp` | Edge sampling (`sampleBitmapEdges`), letterbox fill rendering (`drawLetterboxFill`), edge data cache (`loadEdgeCache`/`saveEdgeCache`), integration in `renderBitmapSleepScreen`/`renderCoverSleepScreen`; unconditional aspect-ratio scaling for Fit mode |
| `src/activities/boot_sleep/SleepActivity.h` | Updated `renderBitmapSleepScreen` signature with optional `edgeCachePath` parameter |
| `src/CrossPointSettings.h` | New enums `SLEEP_SCREEN_LETTERBOX_FILL` (4 values) and `SLEEP_SCREEN_GRADIENT_DIR`; new fields `sleepScreenLetterboxFill`, `sleepScreenGradientDir` |
| `src/CrossPointSettings.cpp` | Serialization of new settings fields (incremented `SETTINGS_COUNT`) |
| `src/SettingsList.h` | New "Letterbox Fill" and "Gradient Direction" setting entries in Display category |
## Additional Context
* **Performance:** The edge sampling pass reads the full bitmap row-by-row (~48KB for a full-screen 2-bit image). This is done once per cover image and cached. Subsequent sleeps for the same cover skip this pass entirely, loading ~1KB from the cache file instead.
* **Memory:** Edge sampling uses temporary `uint32_t` accumulator arrays (~10KB peak) which are freed immediately after processing. Final edge data arrays (~1.6KB max) persist only for the duration of rendering. The cache file is ~970 bytes for a 480-wide image.
* **Upscaling approach:** Nearest-neighbor via block-fill — each source pixel maps to a rectangle of destination pixels. No interpolation, which is appropriate for the 4-level grayscale e-ink display.
* **Cache invalidation:** Validated by cache version byte and screen dimensions (width × height). Orientation changes trigger regeneration. Each cover mode (Fit vs Crop) produces a different BMP and thus a different cache path. Cache files live in the book-specific `.crosspoint/epub_<hash>/` directory and are naturally scoped per-book.
* **Potential risks:** The `drawBitmap`/`drawBitmap1Bit` scaling refactor affects all bitmap rendering, not just sleep screens. The logic is functionally equivalent for downscaling (≤1.0) and 1:1 cases; upscaling (>1.0) is new behavior only triggered when both `maxWidth` and `maxHeight` are provided.
---
### AI Usage
Did you use AI tools to help write this code? _**YES**_