#include "Display_PNG.h" static PNG png; static File Image_file; static int16_t xpos = 0; static int16_t ypos = 0; // ── PNG callbacks ───────────────────────────────────────────────────────────── static void* pngOpen(const char* filePath, int32_t* size) { Image_file = FFat.open(filePath); if (!Image_file) { printf("PNG: failed to open %s\r\n", filePath); return nullptr; } *size = Image_file.size(); return &Image_file; } static void pngClose(void* handle) { File* fp = (File*)handle; if (fp && *fp) fp->close(); } static int32_t pngRead(PNGFILE* page, uint8_t* buffer, int32_t length) { if (!Image_file) return 0; return Image_file.read(buffer, length); } static int32_t pngSeek(PNGFILE* page, int32_t position) { if (!Image_file) return 0; return Image_file.seek(position); } static uint16_t lineBuffer[MAX_IMAGE_WIDTH]; static void pngDraw(PNGDRAW* pDraw) { png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff); uint32_t w = pDraw->iWidth; for (size_t i = 0; i < w; i++) lineBuffer[i] = __builtin_bswap16(lineBuffer[i]); LCD_addWindow(xpos, pDraw->y, xpos + w - 1, ypos + pDraw->y, lineBuffer); } // ── Screen fill ─────────────────────────────────────────────────────────────── static void clearScreen() { static uint16_t blackLine[MAX_IMAGE_WIDTH]; memset(blackLine, 0, sizeof(blackLine)); for (int y = 0; y < LCD_HEIGHT; y++) LCD_addWindow(0, y, LCD_WIDTH - 1, y, blackLine); } // ── Public API ──────────────────────────────────────────────────────────────── void Display_Image_From_SD(const char* fullPath) { printf("PNG: loading %s\r\n", fullPath); int16_t rc = png.open(fullPath, pngOpen, pngClose, pngRead, pngSeek, pngDraw); if (rc != PNG_SUCCESS) { printf("PNG: open failed, rc=%d\r\n", rc); return; } if (png.getWidth() > MAX_IMAGE_WIDTH) { printf("PNG: image too wide (%d px)\r\n", png.getWidth()); png.close(); return; } clearScreen(); png.decode(NULL, 0); png.close(); } void Display_Memory_Photo(int memoryIndex) { if (memoryIndex < 0 || memoryIndex >= g_memoryCount) { printf("PNG: invalid memory index %d\r\n", memoryIndex); return; } char path[120]; snprintf(path, sizeof(path), "/photos/%s", g_memories[memoryIndex].photo); Display_Image_From_SD(path); } void Draw_Candle_Body() { clearScreen(); // Draw candle body (ivory column): x from 76 to 96 (width 21), y from 175 to 250 (height 75) uint16_t body[21 * 75]; for (int i = 0; i < 21 * 75; i++) body[i] = 0xF7BE; // Warm Ivory / light cream LCD_addWindow(76, 175, 96, 249, body); // Draw candle wick: x = 86, y from 168 to 175 uint16_t wick[1 * 8]; for (int i = 0; i < 8; i++) wick[i] = 0x2104; // Dark gray LCD_addWindow(86, 168, 86, 175, wick); } void Draw_Candle_Flicker() { static uint32_t lastFlickerTime = 0; static uint8_t flickerState = 0; uint32_t now = millis(); if (now - lastFlickerTime < 180) return; // flicker every 180ms lastFlickerTime = now; flickerState = (flickerState + 1) % 4; // Bounding box: width 31, height 43 static uint16_t canvas[31 * 43]; memset(canvas, 0, sizeof(canvas)); // fill black int hOffset = (flickerState % 2 == 0) ? 0 : ((flickerState == 1) ? 1 : -1); int vHeight = 22 + (flickerState % 3); // flame height varies for (int y = 0; y < 43; y++) { for (int x = 0; x < 31; x++) { int dx = abs(x - 15 - hOffset); int dy = 35 - y; if (dy >= 0 && dy < vHeight) { int maxWidth = (dy < 8) ? (dy * 1.2) : (vHeight - dy) * 0.4; if (maxWidth < 1) maxWidth = 1; if (dx <= maxWidth) { if (dx <= maxWidth / 2 && dy < vHeight - 4) { canvas[y * 31 + x] = 0xFFE0; // Yellow } else { canvas[y * 31 + x] = 0xFD20; // Orange } } } } } LCD_addWindow(71, 125, 101, 167, canvas); }