2025-12-19 08:45:14 +11:00
|
|
|
#pragma once
|
|
|
|
|
|
2025-12-30 15:09:30 +10:00
|
|
|
#include <SdFat.h>
|
2025-12-19 08:45:14 +11:00
|
|
|
|
2026-01-12 12:36:19 +01:00
|
|
|
#include <cstdint>
|
|
|
|
|
|
|
|
|
|
#include "BitmapHelpers.h"
|
|
|
|
|
|
2026-01-24 20:48:13 -05:00
|
|
|
// Per-edge average luminance values (0-255)
|
|
|
|
|
struct EdgeLuminance {
|
|
|
|
|
uint8_t top;
|
|
|
|
|
uint8_t bottom;
|
|
|
|
|
uint8_t left;
|
|
|
|
|
uint8_t right;
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-19 08:45:14 +11:00
|
|
|
enum class BmpReaderError : uint8_t {
|
|
|
|
|
Ok = 0,
|
|
|
|
|
FileInvalid,
|
|
|
|
|
SeekStartFailed,
|
|
|
|
|
|
|
|
|
|
NotBMP,
|
|
|
|
|
DIBTooSmall,
|
|
|
|
|
|
|
|
|
|
BadPlanes,
|
|
|
|
|
UnsupportedBpp,
|
|
|
|
|
UnsupportedCompression,
|
|
|
|
|
|
|
|
|
|
BadDimensions,
|
2025-12-28 08:38:14 +09:00
|
|
|
ImageTooLarge,
|
2025-12-19 08:45:14 +11:00
|
|
|
PaletteTooLarge,
|
|
|
|
|
|
|
|
|
|
SeekPixelDataFailed,
|
|
|
|
|
BufferTooSmall,
|
|
|
|
|
OomRowBuffer,
|
|
|
|
|
ShortReadRow,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class Bitmap {
|
|
|
|
|
public:
|
|
|
|
|
static const char* errorToString(BmpReaderError err);
|
|
|
|
|
|
2026-01-12 12:36:19 +01:00
|
|
|
explicit Bitmap(FsFile& file, bool dithering = false) : file(file), dithering(dithering) {}
|
2025-12-28 08:38:14 +09:00
|
|
|
~Bitmap();
|
2025-12-19 08:45:14 +11:00
|
|
|
BmpReaderError parseHeaders();
|
2026-01-05 11:07:27 +01:00
|
|
|
BmpReaderError readNextRow(uint8_t* data, uint8_t* rowBuffer) const;
|
2025-12-19 08:45:14 +11:00
|
|
|
BmpReaderError rewindToData() const;
|
2026-01-24 20:48:13 -05:00
|
|
|
EdgeLuminance detectEdgeLuminance(int depth = 2) const;
|
2025-12-19 08:45:14 +11:00
|
|
|
int getWidth() const { return width; }
|
|
|
|
|
int getHeight() const { return height; }
|
|
|
|
|
bool isTopDown() const { return topDown; }
|
|
|
|
|
bool hasGreyscale() const { return bpp > 1; }
|
|
|
|
|
int getRowBytes() const { return rowBytes; }
|
Add cover image display in *Continue Reading* card with framebuffer caching (#200)
## Summary
* **What is the goal of this PR?** (e.g., Fixes a bug in the user
authentication module,
Display the book cover image in the **"Continue Reading"** card on the
home screen, with fast navigation using framebuffer caching.
* **What changes are included?**
- Display book cover image in the "Continue Reading" card on home screen
- Load cover from cached BMP (same as sleep screen cover)
- Add framebuffer store/restore functions (`copyStoredBwBuffer`,
`freeStoredBwBuffer`) for fast navigation after initial render
- Fix `drawBitmap` scaling bug: apply scale to offset only, not to base
coordinates
- Add white text boxes behind title/author/continue reading label for
readability on cover
- Support both EPUB and XTC file cover images
- Increase HomeActivity task stack size from 2048 to 4096 for cover
image rendering
## Additional Context
* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks, specific areas to
focus on).
- Performance: First render loads cover from SD card (~800ms),
subsequent navigation uses cached framebuffer (~instant)
- Memory: Framebuffer cache uses ~48KB (6 chunks × 8KB) while on home
screen, freed on exit
- Fallback: If cover image is not available, falls back to standard
text-only display
- The `drawBitmap` fix corrects a bug where screenY = (y + offset) scale
was incorrectly scaling the base coordinates. Now correctly uses screenY
= y + (offset scale)
2026-01-14 19:24:02 +09:00
|
|
|
bool is1Bit() const { return bpp == 1; }
|
|
|
|
|
uint16_t getBpp() const { return bpp; }
|
2026-01-24 20:48:13 -05:00
|
|
|
uint32_t getFileSize() const { return fileSize; }
|
2025-12-19 08:45:14 +11:00
|
|
|
|
|
|
|
|
private:
|
2025-12-30 15:09:30 +10:00
|
|
|
static uint16_t readLE16(FsFile& f);
|
|
|
|
|
static uint32_t readLE32(FsFile& f);
|
2025-12-19 08:45:14 +11:00
|
|
|
|
2025-12-30 15:09:30 +10:00
|
|
|
FsFile& file;
|
2026-01-12 12:36:19 +01:00
|
|
|
bool dithering = false;
|
2025-12-19 08:45:14 +11:00
|
|
|
int width = 0;
|
|
|
|
|
int height = 0;
|
|
|
|
|
bool topDown = false;
|
|
|
|
|
uint32_t bfOffBits = 0;
|
|
|
|
|
uint16_t bpp = 0;
|
|
|
|
|
int rowBytes = 0;
|
2026-01-24 20:48:13 -05:00
|
|
|
uint32_t fileSize = 0;
|
2025-12-19 08:45:14 +11:00
|
|
|
uint8_t paletteLum[256] = {};
|
2025-12-28 08:38:14 +09:00
|
|
|
|
|
|
|
|
// Floyd-Steinberg dithering state (mutable for const methods)
|
|
|
|
|
mutable int16_t* errorCurRow = nullptr;
|
|
|
|
|
mutable int16_t* errorNextRow = nullptr;
|
2026-01-05 11:07:27 +01:00
|
|
|
mutable int prevRowY = -1; // Track row progression for error propagation
|
2026-01-12 12:36:19 +01:00
|
|
|
|
|
|
|
|
mutable AtkinsonDitherer* atkinsonDitherer = nullptr;
|
|
|
|
|
mutable FloydSteinbergDitherer* fsDitherer = nullptr;
|
2025-12-19 08:45:14 +11:00
|
|
|
};
|