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: Cat-in-a-pan-128-bw 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");