fix: Add framebuffer release/realloc and improved lazy indexing (#2563)

This commit is contained in:
Justin Mitchell
2026-07-12 13:16:48 -04:00
committed by GitHub
parent 859f6cb0d5
commit 444d87de82
24 changed files with 10398 additions and 147 deletions
+18 -22
View File
@@ -2,7 +2,7 @@
#include <HalDisplay.h>
#include <HalStorage.h>
#include <InflateReader.h>
#include <InflateStream.h>
#include <Logging.h>
#include <cstdio>
@@ -174,9 +174,8 @@ void writeBmpHeader2bit(Print& bmpOut, const int width, const int height) {
} // namespace
// Context for streaming PNG decompression
// IMPORTANT: reader must be the first field - the uzlib callback casts uzlib_uncomp* to PngDecodeContext*
struct PngDecodeContext {
InflateReader reader; // Must be first — callback casts uzlib_uncomp* to PngDecodeContext*
InflateStream reader;
HalFile* file;
// PNG image properties
@@ -195,7 +194,7 @@ struct PngDecodeContext {
uint32_t chunkBytesRemaining; // bytes left in current IDAT chunk
bool idatFinished; // no more IDAT chunks
// File read buffer for feeding uzlib
// File read buffer for feeding the inflate stream
uint8_t readBuf[2048];
// Palette for indexed color (type 3)
@@ -229,21 +228,21 @@ static bool findNextIdatChunk(PngDecodeContext& ctx) {
}
}
// uzlib callback: reads the next batch of IDAT data from the file
static int pngIdatReadCallback(uzlib_uncomp* uncomp) {
auto* ctx = reinterpret_cast<PngDecodeContext*>(uncomp);
// Fill callback: reads the next batch of IDAT data from the file
static size_t pngIdatFillCallback(void* vctx, const uint8_t** data) {
auto* ctx = static_cast<PngDecodeContext*>(vctx);
if (ctx->idatFinished) return -1;
if (ctx->idatFinished) return 0;
// Skip 4-byte CRC and find next IDAT chunk when current chunk is exhausted
while (ctx->chunkBytesRemaining == 0) {
if (!ctx->file->seekCur(4)) { // skip 4-byte CRC of previous IDAT
ctx->idatFinished = true;
return -1;
return 0;
}
if (!findNextIdatChunk(*ctx)) {
ctx->idatFinished = true;
return -1;
return 0;
}
}
@@ -251,18 +250,15 @@ static int pngIdatReadCallback(uzlib_uncomp* uncomp) {
size_t toRead = sizeof(ctx->readBuf);
if (toRead > ctx->chunkBytesRemaining) toRead = ctx->chunkBytesRemaining;
int bytesRead = ctx->file->read(ctx->readBuf, toRead);
const int bytesRead = ctx->file->read(ctx->readBuf, toRead);
if (bytesRead <= 0) {
ctx->idatFinished = true;
return -1;
return 0;
}
ctx->chunkBytesRemaining -= bytesRead;
// Give uzlib the buffer (skip first byte since we return it directly)
uncomp->source = ctx->readBuf + 1;
uncomp->source_limit = ctx->readBuf + bytesRead;
return ctx->readBuf[0];
*data = ctx->readBuf;
return static_cast<size_t>(bytesRead);
}
// Decode one scanline: decompress filter byte + raw bytes, then unfilter
@@ -555,16 +551,16 @@ bool PngToBmpConverter::pngFileToBmpStreamInternal(HalFile& pngFile, Print& bmpO
return false;
}
// Initialize streaming decompressor with 32KB ring buffer for back-reference history
// Initialize streaming decompressor with 32KB window for back-reference history
if (!ctx.reader.init(true)) {
LOG_ERR("PNG", "Failed to init inflate reader");
LOG_ERR("PNG", "Failed to init inflate stream");
free(ctx.currentRow);
free(ctx.previousRow);
return false;
}
ctx.reader.setReadCallback(pngIdatReadCallback);
// PNG IDAT data is zlib-wrapped: consume the 2-byte zlib header (CMF + FLG)
ctx.reader.skipZlibHeader();
ctx.reader.setFill(pngIdatFillCallback, &ctx);
// PNG IDAT data is zlib-wrapped (2-byte header + trailing adler32)
ctx.reader.setZlibWrapped();
// Calculate output dimensions (same logic as JpegToBmpConverter)
int outWidth = width;