feat: Slim dictionary (#2583)

Co-authored-by: Ryan Hitchman <hitchmanr@gmail.com>
Co-authored-by: Kurtis Grant <kurtis.b.grant@gmail.com>
Co-authored-by: kemonine <kemonine@kemonine.info>
Co-authored-by: DustinHu <hu.dustin@gmail.com>
Co-authored-by: Justin Mitchell <justin@jmitch.com>
This commit is contained in:
Uri Tauber
2026-07-19 17:57:16 +03:00
committed by GitHub
co-authored by Ryan Hitchman Kurtis Grant kemonine DustinHu Justin Mitchell
parent 5ba1d5747f
commit b1d1d757ba
27 changed files with 1919 additions and 10 deletions
+88
View File
@@ -213,6 +213,61 @@ static inline void rotateCoordinates(const GfxRenderer::Orientation orientation,
}
}
// Output of screenRectToAlignedMemRect: a rectangle in panel-memory
// coordinates whose x and width are guaranteed to be multiples of 8 (the
// SDK's EInkDisplay::displayWindow alignment requirement). `valid == false`
// means the input was empty or fully outside the panel.
struct AlignedMemRect {
uint16_t x = 0;
uint16_t y = 0;
uint16_t w = 0;
uint16_t h = 0;
bool valid = false;
};
// Translate a screen-coordinate rectangle (the coordinate system used by
// fillRect / drawText / the rest of the renderer's public API) into a
// panel-memory rectangle suitable for direct framebuffer indexing. Rotates
// the rectangle's two opposite corners with rotateCoordinates(), takes the
// bounding box (which naturally swaps width/height in Portrait /
// PortraitInverted), then snaps the x extent outward to multiples of 8 and
// clamps to panel bounds. Precondition: panel dims are multiples of 8 (true
// for the 800x480 panel), so clamping cannot re-break alignment.
static AlignedMemRect screenRectToAlignedMemRect(GfxRenderer::Orientation orientation, int sx, int sy, int sw, int sh,
uint16_t panelWidth, uint16_t panelHeight) {
AlignedMemRect out;
if (sw <= 0 || sh <= 0) return out;
int x0, y0, x1, y1;
rotateCoordinates(orientation, sx, sy, &x0, &y0, panelWidth, panelHeight);
rotateCoordinates(orientation, sx + sw - 1, sy + sh - 1, &x1, &y1, panelWidth, panelHeight);
const int memXLo = std::min(x0, x1);
const int memYLo = std::min(y0, y1);
const int memXHi = std::max(x0, x1) + 1; // exclusive upper bound
const int memYHi = std::max(y0, y1) + 1;
// Snap x outward to multiples of 8.
int alignedXLo = memXLo & ~0x7; // round down
int alignedXHi = (memXHi + 7) & ~0x7; // round up
if (alignedXLo < 0) alignedXLo = 0;
if (alignedXHi > panelWidth) alignedXHi = panelWidth;
int clampedYLo = memYLo;
int clampedYHi = memYHi;
if (clampedYLo < 0) clampedYLo = 0;
if (clampedYHi > panelHeight) clampedYHi = panelHeight;
if (alignedXHi <= alignedXLo || clampedYHi <= clampedYLo) return out;
out.x = static_cast<uint16_t>(alignedXLo);
out.y = static_cast<uint16_t>(clampedYLo);
out.w = static_cast<uint16_t>(alignedXHi - alignedXLo);
out.h = static_cast<uint16_t>(clampedYHi - clampedYLo);
out.valid = true;
return out;
}
enum class TextRotation { None, Rotated90CW };
// Shared glyph rendering logic for normal and rotated text.
@@ -1451,6 +1506,39 @@ void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const
display.displayBuffer(refreshMode, fadingFix);
}
size_t GfxRenderer::readFramebufferRegion(int x, int y, int w, int h, uint8_t* dst, size_t dstCapacity) const {
if (dst == nullptr || w <= 0 || h <= 0) return 0;
const AlignedMemRect mem = screenRectToAlignedMemRect(orientation, x, y, w, h, panelWidth, panelHeight);
if (!mem.valid) return 0;
const size_t rowBytes = mem.w / 8; // exact: mem.w is a multiple of 8
const size_t needed = rowBytes * mem.h;
if (needed > dstCapacity) return 0;
for (uint16_t row = 0; row < mem.h; ++row) {
const uint8_t* srcRow = frameBuffer + (static_cast<uint32_t>(mem.y + row) * panelWidthBytes) + (mem.x / 8);
uint8_t* dstRow = dst + (static_cast<size_t>(row) * rowBytes);
memcpy(dstRow, srcRow, rowBytes);
}
return needed;
}
void GfxRenderer::writeFramebufferRegion(int x, int y, int w, int h, const uint8_t* src) {
if (src == nullptr || w <= 0 || h <= 0) return;
const AlignedMemRect mem = screenRectToAlignedMemRect(orientation, x, y, w, h, panelWidth, panelHeight);
if (!mem.valid) return;
const size_t rowBytes = mem.w / 8; // exact: mem.w is a multiple of 8
for (uint16_t row = 0; row < mem.h; ++row) {
const uint8_t* srcRow = src + (static_cast<size_t>(row) * rowBytes);
uint8_t* dstRow = frameBuffer + (static_cast<uint32_t>(mem.y + row) * panelWidthBytes) + (mem.x / 8);
memcpy(dstRow, srcRow, rowBytes);
}
}
std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth,
const EpdFontFamily::Style style) const {
if (!text || maxWidth <= 0) return "";
+9
View File
@@ -189,6 +189,15 @@ class GfxRenderer {
void drawBitmap1Bit(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight) const;
void fillPolygon(const int* xPoints, const int* yPoints, int numPoints, bool state = true) const;
// Snapshot / restore a screen-coordinate framebuffer region (byte-aligned in
// panel memory). readFramebufferRegion returns the bytes written to dst, or
// 0 when the region is empty, offscreen, or exceeds dstCapacity. Pass the
// same rectangle to writeFramebufferRegion to restore the saved pixels.
// Enables partial-repaint patterns (e.g. moving a selection highlight)
// without re-rendering the whole page.
size_t readFramebufferRegion(int x, int y, int w, int h, uint8_t* dst, size_t dstCapacity) const;
void writeFramebufferRegion(int x, int y, int w, int h, const uint8_t* src);
// Text
int getTextWidth(int fontId, const char* text, EpdFontFamily::Style style = EpdFontFamily::REGULAR,
BidiUtils::BidiBaseDir baseDir = BidiUtils::BidiBaseDir::AUTO) const;
+7
View File
@@ -26,6 +26,12 @@ STR_EMPTY_FILE: "Empty file"
STR_OUT_OF_BOUNDS: "Out of bounds"
STR_LOADING: "Loading..."
STR_LOADING_POPUP: "Loading"
STR_LOOKUP: "Look Up"
STR_DICT_LOOKING_UP: "Looking up..."
STR_DICT_INDEXING: "Indexing dictionary..."
STR_DICT_NOT_FOUND: "Not found"
STR_DICT_NO_DICT_SET: "No dictionary set"
STR_DICT_ERROR: "Dictionary error"
STR_WIFI_NETWORKS: "Wi-Fi Networks"
STR_NO_NETWORKS: "No networks found"
STR_NETWORKS_FOUND: "%zu networks found"
@@ -163,6 +169,7 @@ STR_PREV_NEXT: "Prev/Next"
STR_NEXT_PREV: "Next/Prev"
STR_KOSYNC: "KOSync"
STR_BOOKMARK_OPTION: "Bookmark"
STR_DICTIONARY: "Dictionary"
STR_DISABLED: "Disabled"
STR_NOTO_SERIF: "Noto Serif"
STR_NOTO_SANS: "Noto Sans"