diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index b024fab8..1e9ad982 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -1054,8 +1054,24 @@ void GfxRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, co display.drawImage(bitmap, rotatedX, rotatedY, width, height); } -void GfxRenderer::drawIcon(const uint8_t bitmap[], const int x, const int y, const int width, const int height) const { - display.drawImageTransparent(bitmap, y, getScreenWidth() - width - x, height, width); +void GfxRenderer::drawIcon(const uint8_t bitmap[], const int x, const int y, const int size) const { + // Plot the icon pixel-by-pixel through drawPixel (which applies the orientation + // transform) instead of the byte-aligned framebuffer blit. The blit snaps the + // icon's position to 8px (one byte) along the rotated axis, which prevents it + // from aligning with adjacent text; per-pixel plotting is pixel-precise. + // Icons are square and 1bpp (MSB-first, bit==0 = ink). The (size-1-row, col) + // mapping reproduces the Portrait orientation the blit produced; drawIcon is + // only called by the UI themes, which all render in forced Portrait. + const int rowBytes = (size + 7) / 8; + for (int row = 0; row < size; row++) { + for (int col = 0; col < size; col++) { + const uint8_t byte = bitmap[row * rowBytes + (col >> 3)]; + const bool ink = ((byte >> (7 - (col & 7))) & 1) == 0; + if (ink) { + drawPixel(x + (size - 1 - row), y + col, true); + } + } + } } void GfxRenderer::drawBitmap(const Bitmap& bitmap, const int x, const int y, const int maxWidth, const int maxHeight, diff --git a/lib/GfxRenderer/GfxRenderer.h b/lib/GfxRenderer/GfxRenderer.h index ae000bb2..5aa9ae72 100644 --- a/lib/GfxRenderer/GfxRenderer.h +++ b/lib/GfxRenderer/GfxRenderer.h @@ -183,7 +183,7 @@ class GfxRenderer { void fillRoundedRect(int x, int y, int width, int height, int cornerRadius, bool roundTopLeft, bool roundTopRight, bool roundBottomLeft, bool roundBottomRight, Color color) const; void drawImage(const uint8_t bitmap[], int x, int y, int width, int height) const; - void drawIcon(const uint8_t bitmap[], int x, int y, int width, int height) const; + void drawIcon(const uint8_t bitmap[], int x, int y, int size) const; void drawBitmap(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight, float cropX = 0, float cropY = 0) const; void drawBitmap1Bit(const Bitmap& bitmap, int x, int y, int maxWidth, int maxHeight) const; diff --git a/src/components/themes/lyra/Lyra3CoversTheme.cpp b/src/components/themes/lyra/Lyra3CoversTheme.cpp index fdbed623..f360798c 100644 --- a/src/components/themes/lyra/Lyra3CoversTheme.cpp +++ b/src/components/themes/lyra/Lyra3CoversTheme.cpp @@ -72,7 +72,7 @@ void Lyra3CoversTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con tileY + hPaddingInSelection + (Lyra3CoversMetrics::values.homeCoverHeight / 3), tileWidth - 2 * hPaddingInSelection, 2 * Lyra3CoversMetrics::values.homeCoverHeight / 3, true); - renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32); } } diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index f04f7de0..c5af0a39 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -288,7 +288,7 @@ void LyraTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, const uint8_t* iconBitmap = iconForName(icon, iconSize); if (iconBitmap != nullptr) { renderer.drawIcon(iconBitmap, rect.x + LyraMetrics::values.contentSidePadding + hPaddingInSelection, - itemY + iconY, iconSize, iconSize); + itemY + iconY, iconSize); } } @@ -454,7 +454,7 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: renderer.fillRect(tileX + hPaddingInSelection, tileY + hPaddingInSelection + (LyraMetrics::values.homeCoverHeight / 3), coverWidth, 2 * LyraMetrics::values.homeCoverHeight / 3, true); - renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32, 32); + renderer.drawIcon(CoverIcon, tileX + hPaddingInSelection + 24, tileY + hPaddingInSelection + 24, 32); } coverBufferStored = storeCoverBuffer(); @@ -534,7 +534,7 @@ void LyraTheme::drawButtonMenu(GfxRenderer& renderer, Rect rect, int buttonCount UIIcon icon = rowIcon(i); const uint8_t* iconBitmap = iconForName(icon, mainMenuIconSize); if (iconBitmap != nullptr) { - renderer.drawIcon(iconBitmap, textX, textY + 3, mainMenuIconSize, mainMenuIconSize); + renderer.drawIcon(iconBitmap, textX, textY, mainMenuIconSize); textX += mainMenuIconSize + hPaddingInSelection + 2; } } diff --git a/src/components/themes/roundedraff/RoundedRaffTheme.cpp b/src/components/themes/roundedraff/RoundedRaffTheme.cpp index 50db2a8c..0e609ac9 100644 --- a/src/components/themes/roundedraff/RoundedRaffTheme.cpp +++ b/src/components/themes/roundedraff/RoundedRaffTheme.cpp @@ -165,7 +165,7 @@ void RoundedRaffTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, con // Render empty cover renderer.fillRect(tileX + (tileWidth - coverWidth) / 2, imgY + (RoundedRaffMetrics::values.homeCoverHeight / 3), coverWidth, 2 * RoundedRaffMetrics::values.homeCoverHeight / 3, true); - renderer.drawIcon(CoverIcon, tileX + (tileWidth - coverWidth) / 2 + 24, imgY + 24, 32, 32); + renderer.drawIcon(CoverIcon, tileX + (tileWidth - coverWidth) / 2 + 24, imgY + 24, 32); renderer.maskRoundedRectOutsideCorners(tileX + (tileWidth - coverWidth) / 2, imgY, coverWidth, RoundedRaffMetrics::values.homeCoverHeight, kCoverRadius, Color::LightGray);