-
-
Notifications
You must be signed in to change notification settings - Fork 46
Add clipping support with CrossPoint PR #1742 adaptations #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8ad98e1
26958a6
4d9e306
198b9d7
02822e0
3338695
b8a5152
ef98d44
741dd89
59c8d39
eb92f2e
0a211b5
bd279ad
281752c
92346ca
e9ab936
88ad649
846a620
08efb36
48cfb66
a59d708
c517777
f773c2c
ee06284
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -504,6 +504,41 @@ void GfxRenderer::fillRectDither(const int x, const int y, const int width, cons | |
| } | ||
| } | ||
|
|
||
| void GfxRenderer::maskRoundedRectOutsideCorners(const int x, const int y, const int width, const int height, | ||
| const int radius, const Color color) const { | ||
| if (radius <= 0 || color == Color::Clear) { | ||
| return; | ||
| } | ||
|
|
||
| const int rr = radius - 1; | ||
| const int rr2 = rr * rr; | ||
| for (int dy = 0; dy < radius; dy++) { | ||
| for (int dx = 0; dx < radius; dx++) { | ||
| const int tx = rr - dx; | ||
| const int ty = rr - dy; | ||
| if (tx * tx + ty * ty > rr2) { | ||
| if (color == Color::White || color == Color::Black) { | ||
| bool state = color == Color::Black; | ||
| drawPixel(x + dx, y + dy, state); // top-left | ||
| drawPixel(x + width - 1 - dx, y + dy, state); // top-right | ||
| drawPixel(x + dx, y + height - 1 - dy, state); // bottom-left | ||
| drawPixel(x + width - 1 - dx, y + height - 1 - dy, state); // bottom-right | ||
| } else if (color == Color::LightGray) { | ||
| drawPixelDither<Color::LightGray>(x + dx, y + dy); // top-left | ||
| drawPixelDither<Color::LightGray>(x + width - 1 - dx, y + dy); // top-right | ||
| drawPixelDither<Color::LightGray>(x + dx, y + height - 1 - dy); // bottom-left | ||
| drawPixelDither<Color::LightGray>(x + width - 1 - dx, y + height - 1 - dy); // bottom-right | ||
| } else if (color == Color::DarkGray) { | ||
| drawPixelDither<Color::DarkGray>(x + dx, y + dy); // top-left | ||
| drawPixelDither<Color::DarkGray>(x + width - 1 - dx, y + dy); // top-right | ||
| drawPixelDither<Color::DarkGray>(x + dx, y + height - 1 - dy); // bottom-left | ||
| drawPixelDither<Color::DarkGray>(x + width - 1 - dx, y + height - 1 - dy); // bottom-right | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| template <Color color> | ||
| void GfxRenderer::fillArc(const int maxRadius, const int cx, const int cy, const int xDir, const int yDir) const { | ||
| if (maxRadius <= 0) return; | ||
|
|
@@ -908,6 +943,41 @@ void GfxRenderer::displayBuffer(const HalDisplay::RefreshMode refreshMode) const | |
| display.displayBuffer(refreshMode, fadingFix); | ||
| } | ||
|
|
||
| void GfxRenderer::displayWindow(int x, int y, int width, int height) const { | ||
| int phyX, phyY, phyW, phyH; | ||
| switch (orientation) { | ||
| case Portrait: | ||
| phyX = x; | ||
| phyY = panelHeight - y - height; | ||
| phyW = width; | ||
| phyH = height; | ||
| break; | ||
| case LandscapeClockwise: | ||
| phyX = panelWidth - x - width; | ||
| phyY = panelHeight - y - height; | ||
| phyW = width; | ||
| phyH = height; | ||
| break; | ||
| case PortraitInverted: | ||
| phyX = panelWidth - x - width; | ||
| phyY = y; | ||
| phyW = width; | ||
| phyH = height; | ||
| break; | ||
| case LandscapeCounterClockwise: | ||
| default: | ||
| phyX = x; | ||
| phyY = y; | ||
| phyW = width; | ||
| phyH = height; | ||
| break; | ||
| } | ||
| // Align to 8-pixel (byte) boundaries required by e-ink panel DMA | ||
| const int alignedX = (phyX / 8) * 8; | ||
| const int alignedW = ((phyX + phyW + 7) / 8) * 8 - alignedX; | ||
| display.displayWindow(alignedX, phyY, alignedW, phyH); | ||
| } | ||
|
Comment on lines
+946
to
+979
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle 90° orientations when converting partial-update rectangles. The current transform only works for 0°/180° cases. Suggested fix void GfxRenderer::displayWindow(int x, int y, int width, int height) const {
int phyX, phyY, phyW, phyH;
switch (orientation) {
case Portrait:
- phyX = x;
- phyY = panelHeight - y - height;
- phyW = width;
- phyH = height;
+ phyX = y;
+ phyY = panelHeight - x - width;
+ phyW = height;
+ phyH = width;
break;
case LandscapeClockwise:
phyX = panelWidth - x - width;
phyY = panelHeight - y - height;
phyW = width;
phyH = height;
break;
case PortraitInverted:
- phyX = panelWidth - x - width;
- phyY = y;
- phyW = width;
- phyH = height;
+ phyX = panelWidth - y - height;
+ phyY = x;
+ phyW = height;
+ phyH = width;
break;
case LandscapeCounterClockwise:
default:
phyX = x;
phyY = y;🤖 Prompt for AI Agents |
||
|
|
||
| std::string GfxRenderer::truncatedText(const int fontId, const char* text, const int maxWidth, | ||
| const EpdFontFamily::Style style) const { | ||
| if (!text || maxWidth <= 0) return ""; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -230,6 +230,7 @@ STR_GO_TO_PERCENT: "%-ке өту" | |
| STR_GO_HOME_BUTTON: "Басты бетке өту" | ||
| STR_SYNC_PROGRESS: "Үлгерімді синхрондау" | ||
| STR_DELETE_CACHE: "Кітап кэшін жою" | ||
| STR_SAVE_CLIPPING: "Үзінді сақтау" | ||
| STR_CHAPTER_PREFIX: "Тарау: " | ||
|
Comment on lines
232
to
234
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Find which translation files already define STR_SAVE_CLIPPING
rg -n --glob 'lib/I18n/translations/*.ya?ml' '^\s*STR_SAVE_CLIPPING\s*:' lib/I18n/translations || true
# Fail if any locale file is missing the key
missing=0
for f in lib/I18n/translations/*.ya?ml; do
if ! rg -n '^\s*STR_SAVE_CLIPPING\s*:' "$f" >/dev/null 2>&1; then
echo "MISSING: $f"
missing=1
fi
done
if [ "$missing" -ne 0 ]; then
echo "One or more locale files are missing STR_SAVE_CLIPPING."
exit 1
fiRepository: uxjulia/CrossInk Length of output: 155 🏁 Script executed: # List all translation files in the expected directory
find lib/I18n/translations -name '*.yaml' -o -name '*.yml' | sortRepository: uxjulia/CrossInk Length of output: 875 🏁 Script executed: # For each translation file, check if STR_SAVE_CLIPPING exists
for f in $(find lib/I18n/translations -name '*.yaml' -o -name '*.yml' | sort); do
echo "=== $f ==="
if rg -n 'STR_SAVE_CLIPPING' "$f"; then
echo "FOUND"
else
echo "NOT FOUND"
fi
doneRepository: uxjulia/CrossInk Length of output: 2104 Kazakh translation is correct, but Vietnamese locale is missing
🤖 Prompt for AI Agents |
||
| STR_PAGES_SEPARATOR: " бет | " | ||
| STR_BOOK_PREFIX: "Кітап: " | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,16 @@ void HalDisplay::displayBuffer(HalDisplay::RefreshMode mode, bool turnOffScreen) | |
| einkDisplay.displayBuffer(convertRefreshMode(mode), turnOffScreen); | ||
| } | ||
|
|
||
| void HalDisplay::displayWindow(int x, int y, int w, int h) { | ||
| if (x < 0) x = 0; | ||
| if (y < 0) y = 0; | ||
| if (w <= 0 || h <= 0 || x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) return; | ||
| if (x + w > DISPLAY_WIDTH) w = DISPLAY_WIDTH - x; | ||
| if (y + h > DISPLAY_HEIGHT) h = DISPLAY_HEIGHT - y; | ||
| einkDisplay.displayWindow(static_cast<uint16_t>(x), static_cast<uint16_t>(y), static_cast<uint16_t>(w), | ||
| static_cast<uint16_t>(h)); | ||
|
Comment on lines
+61
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clip top/left overflow instead of shifting the window. When Suggested fix void HalDisplay::displayWindow(int x, int y, int w, int h) {
- if (x < 0) x = 0;
- if (y < 0) y = 0;
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
if (w <= 0 || h <= 0 || x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) return;
if (x + w > DISPLAY_WIDTH) w = DISPLAY_WIDTH - x;
if (y + h > DISPLAY_HEIGHT) h = DISPLAY_HEIGHT - y;
einkDisplay.displayWindow(static_cast<uint16_t>(x), static_cast<uint16_t>(y), static_cast<uint16_t>(w),
static_cast<uint16_t>(h));
}🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| void HalDisplay::refreshDisplay(HalDisplay::RefreshMode mode, bool turnOffScreen) { | ||
| if (gpio.deviceIsX3() && mode == RefreshMode::HALF_REFRESH) { | ||
| einkDisplay.requestResync(1); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clamp
radiusbefore mirroring the corner mask.This helper assumes
radiusfits inside the rectangle. If it doesn't, the mirrored writes cross past the opposite edge and start touching pixels outside the requested box. Since this is a public drawing helper, it should defensively cap the radius first.Suggested fix
void GfxRenderer::maskRoundedRectOutsideCorners(const int x, const int y, const int width, const int height, const int radius, const Color color) const { - if (radius <= 0 || color == Color::Clear) { + if (width <= 0 || height <= 0 || radius <= 0 || color == Color::Clear) { return; } - const int rr = radius - 1; + const int clippedRadius = std::min({radius, width / 2, height / 2}); + if (clippedRadius <= 0) { + return; + } + + const int rr = clippedRadius - 1; const int rr2 = rr * rr; - for (int dy = 0; dy < radius; dy++) { - for (int dx = 0; dx < radius; dx++) { + for (int dy = 0; dy < clippedRadius; dy++) { + for (int dx = 0; dx < clippedRadius; dx++) { const int tx = rr - dx; const int ty = rr - dy; if (tx * tx + ty * ty > rr2) {📝 Committable suggestion
🤖 Prompt for AI Agents