From 73f47423e7cec915a903fe8328aaa4120a184fb6 Mon Sep 17 00:00:00 2001 From: Brackyt <60280126+Brackyt@users.noreply.github.com> Date: Sun, 25 Jan 2026 18:49:42 +0100 Subject: [PATCH] fix: rendering correctly + alpha channel --- lib/GfxRenderer/Bitmap.cpp | 44 +++++++++++++++++++++++++++++---- lib/GfxRenderer/GfxRenderer.cpp | 13 +++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/GfxRenderer/Bitmap.cpp b/lib/GfxRenderer/Bitmap.cpp index 603954d2..4f7173a2 100644 --- a/lib/GfxRenderer/Bitmap.cpp +++ b/lib/GfxRenderer/Bitmap.cpp @@ -155,6 +155,11 @@ BmpReaderError Bitmap::parseHeaders() { if (colorsUsed > 256u) return BmpReaderError::PaletteTooLarge; seekCur(4); + // Robustness Fix: Skip extended header bytes (V4/V5) + if (biSize > 40) { + seekCur(biSize - 40); + } + if (width <= 0 || height <= 0) return BmpReaderError::BadDimensions; constexpr int MAX_IMAGE_WIDTH = 2048; @@ -165,11 +170,33 @@ BmpReaderError Bitmap::parseHeaders() { rowBytes = (width * bpp + 31) / 32 * 4; - for (int i = 0; i < 256; i++) paletteLum[i] = static_cast(i); - if (colorsUsed > 0) { - for (uint32_t i = 0; i < colorsUsed; i++) { + // Initialize safe default palette + if (bpp == 1) { + // For 1-bit, default to Black(0) and White(1) + paletteLum[0] = 0; + paletteLum[1] = 255; + } else if (bpp <= 8) { + int maxIdx = (1 << bpp) - 1; + for (int i = 0; i <= maxIdx; i++) { + paletteLum[i] = (i * 255) / maxIdx; + } + } else { + for (int i = 0; i < 256; i++) paletteLum[i] = static_cast(i); + } + + // If indexed color (<=8bpp), we MUST load the palette. + // The palette is located AFTER the DIB header. + if (bpp <= 8) { + // Explicit seek to palette start + if (!seekSet(14 + biSize)) return BmpReaderError::SeekStartFailed; + + uint32_t colorsToRead = colorsUsed; + if (colorsToRead == 0) colorsToRead = 1 << bpp; + if (colorsToRead > 256) colorsToRead = 256; + + for (uint32_t i = 0; i < colorsToRead; i++) { uint8_t rgb[4]; - readBytes(rgb, 4); + if (readBytes(rgb, 4) != 4) break; paletteLum[i] = (77u * rgb[2] + 150u * rgb[1] + 29u * rgb[0]) >> 8; } } @@ -224,7 +251,14 @@ BmpReaderError Bitmap::readNextRow(uint8_t* data, uint8_t* rowBuffer) const { case 32: { const uint8_t* p = rowBuffer; for (int x = 0; x < width; x++) { - uint8_t lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; + uint8_t lum; // Declare lum here + // Handle Alpha channel (byte 3). If transparent (<128), treat as White. + // This fixes 32-bit icons appearing as black squares on white backgrounds. + if (p[3] < 128) { + lum = 255; + } else { + lum = (77u * p[2] + 150u * p[1] + 29u * p[0]) >> 8; + } packPixel(lum); p += 4; } diff --git a/lib/GfxRenderer/GfxRenderer.cpp b/lib/GfxRenderer/GfxRenderer.cpp index 7d62e33c..d6bcea78 100644 --- a/lib/GfxRenderer/GfxRenderer.cpp +++ b/lib/GfxRenderer/GfxRenderer.cpp @@ -584,7 +584,13 @@ void GfxRenderer::drawBitmap(const Bitmap &bitmap, const int x, const int y, const uint8_t val = outputRow[bmpX / 4] >> (6 - ((bmpX * 2) % 8)) & 0x3; - if (renderMode == BW && val < 3) { + // In BW mode: + // 0 = Black (< 45) + // 1 = Dark Gray (< 70) + // 2 = Light Gray (< 140) + // 3 = White + // Draw black for val < 2 (everything below 70), treating Light Gray as White. + if (renderMode == BW && val < 2) { drawPixel(screenX, screenY); } else if (renderMode == GRAYSCALE_MSB && (val == 1 || val == 2)) { drawPixel(screenX, screenY, false); @@ -631,8 +637,9 @@ void GfxRenderer::draw2BitImage(const uint8_t data[], int x, int y, int w, // Extract 2-bit value (4 pixels per byte) const uint8_t val = (srcRow[col / 4] >> (6 - ((col % 4) * 2))) & 0x3; - // val < 3 means black pixel in 2-bit representation - if (val < 3) { + // val < 2 means black pixel in 2-bit representation (0=Black, 1=DarkGray) + // 2=LightGray, 3=White -> Treat as White + if (val < 2) { // In Portrait: physical Y = DISPLAY_HEIGHT - 1 - screenX const int physY = HalDisplay::DISPLAY_HEIGHT - 1 - screenX; const uint16_t byteIndex =