From 51c5c3c0aa665f0852bd8bda42a5d4aa44ae4add Mon Sep 17 00:00:00 2001
From: Maeve Andrews <37351465+maeveynot@users.noreply.github.com>
Date: Tue, 27 Jan 2026 05:59:41 -0600
Subject: [PATCH] fix: rotate origin in drawImage (#557)
## Summary
This was originally a comment in #499, but I'm making it its own PR,
because it doesn't depend on anything there and then I can base that PR
on this one.
Currently, `drawBitmap` is used for covers and sleep wallpaper, and
`drawImage` is used for the boot logo. `drawBitmap` goes row by row and
pixel by pixel, so it respects the renderer orientation. `drawImage`
just calls the `EInkDisplay`'s `drawImage`, which works in the eink
panel's native display orientation.
`drawImage` rotates the x,y coordinates where it's going to draw the
image, but doesn't account for the fact that the northwest corner in
portrait orientation becomes, the southwest corner of the image
rectangle in the native orientation. The boot and sleep activities
currently work around this by calculating the north*east* corner of
where the image should go, which becomes the northwest corner after
`rotateCoordinates`.
I think this wasn't really apparent because the CrossPoint logo is
rotationally symmetrical. The `EInkDisplay` `drawImage` always draws the
image in native orientation, but that looks the same for the "X" image.
If we rotate the origin coordinate in `GfxRenderer`'s `drawImage`, we
can use a much clearer northwest corner coordinate in the boot and sleep
activities. (And then, in #499, we can actually rotate the boot screen
to the user's preferred orientation).
This does *not* yet rotate the actual bits in the image; it's still
displayed in native orientation. This doesn't affect the
rotationally-symmetric logo, but if it's ever changed, we will probably
want to allocate a new `u8int[]` and transpose rows and columns if
necessary.
## Additional Context
I've created an additional branch on top of this to demonstrate by
replacing the logo with a non-rotationally-symmetrical image:
https://github.com/crosspoint-reader/crosspoint-reader/compare/master...maeveynot:rotated-cat
(many thanks to https://notisrac.github.io/FileToCArray/)
As you can see, it is always drawn in native orientation, which makes it
sideways (turned clockwise) in portrait.
---
### AI Usage
No
Co-authored-by: Maeve Andrews
---
lib/GfxRenderer/GfxRenderer.cpp | 17 ++++++++++++++++-
src/activities/boot_sleep/BootActivity.cpp | 2 +-
src/activities/boot_sleep/SleepActivity.cpp | 2 +-
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp
index 08420bf..1dbe8ee 100644
--- a/lib/GfxRenderer/GfxRenderer.cpp
+++ b/lib/GfxRenderer/GfxRenderer.cpp
@@ -145,10 +145,25 @@ void GfxRenderer::fillRect(const int x, const int y, const int width, const int
}
void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const {
- // TODO: Rotate bits
int rotatedX = 0;
int rotatedY = 0;
rotateCoordinates(x, y, &rotatedX, &rotatedY);
+ // Rotate origin corner
+ switch (orientation) {
+ case Portrait:
+ rotatedY = rotatedY - height;
+ break;
+ case PortraitInverted:
+ rotatedX = rotatedX - width;
+ break;
+ case LandscapeClockwise:
+ rotatedY = rotatedY - height;
+ rotatedX = rotatedX - width;
+ break;
+ case LandscapeCounterClockwise:
+ break;
+ }
+ // TODO: Rotate bits
einkDisplay.drawImage(bitmap, rotatedX, rotatedY, width, height);
}
diff --git a/src/activities/boot_sleep/BootActivity.cpp b/src/activities/boot_sleep/BootActivity.cpp
index 65eb6a0..b741c3e 100644
--- a/src/activities/boot_sleep/BootActivity.cpp
+++ b/src/activities/boot_sleep/BootActivity.cpp
@@ -12,7 +12,7 @@ void BootActivity::onEnter() {
const auto pageHeight = renderer.getScreenHeight();
renderer.clearScreen();
- renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
+ renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, (pageHeight - 128) / 2, 128, 128);
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "BOOTING");
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight - 30, CROSSPOINT_VERSION);
diff --git a/src/activities/boot_sleep/SleepActivity.cpp b/src/activities/boot_sleep/SleepActivity.cpp
index 40341e5..c4b9896 100644
--- a/src/activities/boot_sleep/SleepActivity.cpp
+++ b/src/activities/boot_sleep/SleepActivity.cpp
@@ -124,7 +124,7 @@ void SleepActivity::renderDefaultSleepScreen() const {
const auto pageHeight = renderer.getScreenHeight();
renderer.clearScreen();
- renderer.drawImage(CrossLarge, (pageWidth + 128) / 2, (pageHeight - 128) / 2, 128, 128);
+ renderer.drawImage(CrossLarge, (pageWidth - 128) / 2, (pageHeight - 128) / 2, 128, 128);
renderer.drawCenteredText(UI_10_FONT_ID, pageHeight / 2 + 70, "CrossPoint", true, EpdFontFamily::BOLD);
renderer.drawCenteredText(SMALL_FONT_ID, pageHeight / 2 + 95, "SLEEPING");