Skip to content

Commit

Permalink
gui: allocate correctly sized image buffer when decompressing from flash
Browse files Browse the repository at this point in the history
Prefix the length of the uncompressed image data onto the compressed
data, so we can allocate the correct size buffer to decompress into.
See 5692f51
  • Loading branch information
JamieDriver committed Sep 9, 2024
1 parent ee42117 commit 98fd82f
Show file tree
Hide file tree
Showing 14 changed files with 56 additions and 25 deletions.
6 changes: 5 additions & 1 deletion logo/bmp_to_compressed_bgr565_byteswapped.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ def compress_to_bgr565(bmp_file):
swapped_data.append(bgr565_data[i + 1])
swapped_data.append(bgr565_data[i])

compressed_data = bytes([width]) + swapped_data
data_len = len(swapped_data)
assert data_len < 65536

data_len = data_len.to_bytes(2, 'little')
compressed_data = bytes([width]) + data_len + swapped_data
compressed_data = zlib.compress(compressed_data, level=9)
compressed_file = bmp_file.replace('.bmp', '.bin.gz')
with open(compressed_file, "wb") as file:
Expand Down
Binary file modified logo/ce.bin.gz
Binary file not shown.
15 changes: 7 additions & 8 deletions logo/compressed_bgr565_byteswapped_to_bmp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,22 @@ def extract_to_bmp(filename):
decompressed_data = zlib.decompress(compressed_data)

width = decompressed_data[0]

data_len = int.from_bytes(decompressed_data[1:3], 'little')

swapped_pixel_data = bytearray()
for i in range(0, len(decompressed_data[1:]), 2):
swapped_pixel_data.append(decompressed_data[1:][i + 1])
swapped_pixel_data.append(decompressed_data[1:][i])

decompressed_data = bytes([width]) + swapped_pixel_data
for i in range(0, len(decompressed_data[3:]), 2):
swapped_pixel_data.append(decompressed_data[3:][i + 1])
swapped_pixel_data.append(decompressed_data[3:][i])
assert len(swapped_pixel_data) == data_len

num_pixels = (len(decompressed_data) - 1) // 2
num_pixels = len(swapped_pixel_data) // 2
height = num_pixels // width

image = Image.new("RGB", (width, height))

pixels = image.load()
for i in range(num_pixels):
offset = i * 2 + 1
offset = i * 2
bgr_value = struct.unpack("<H", decompressed_data[offset:offset+2])[0]

r = (bgr_value >> 11) << 3
Expand Down
Binary file modified logo/fcc.bin.gz
Binary file not shown.
Binary file modified logo/icon_qrguide_qvga_large.bin.gz
Binary file not shown.
Binary file modified logo/icon_qrguide_qvga_small.bin.gz
Binary file not shown.
Binary file modified logo/icon_qrguide_vga_large.bin.gz
Binary file not shown.
Binary file modified logo/icon_qrguide_vga_small.bin.gz
Binary file not shown.
Binary file modified logo/splash.bin.gz
Binary file not shown.
Binary file modified logo/statusbar_large.bin.gz
Binary file not shown.
Binary file modified logo/statusbar_small.bin.gz
Binary file not shown.
Binary file modified logo/telec.bin.gz
Binary file not shown.
Binary file modified logo/weee.bin.gz
Binary file not shown.
60 changes: 44 additions & 16 deletions main/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,10 @@ typedef struct {
Icon* icon;
Picture* pic;
size_t written;
uint16_t allocated_size;
} image_deflate_ctx_t;

// Sanity checks
#define MAX_FLASH_PICTURE_SIZE (198 * 62 * sizeof(uint16_t))
#define MAX_FLASH_ICON_SIZE (200 * 200 / 8)

Expand All @@ -313,24 +315,46 @@ static int uncompressed_image_stream_writer(void* ctx, uint8_t* uncompressed, si
image_deflate_ctx_t* const ictx = (image_deflate_ctx_t*)ctx;

// Should be loading a picture or an icon, not both!
JADE_ASSERT((ictx->icon && ictx->icon->data) || (ictx->pic && ictx->pic->data_8));
JADE_ASSERT(!ictx->pic || !ictx->icon);
JADE_ASSERT(!ictx->icon != !ictx->pic);

const size_t max_image_size = ictx->icon ? MAX_FLASH_ICON_SIZE : MAX_FLASH_PICTURE_SIZE;
if (towrite + ictx->written > max_image_size + 1) { // +1 for the prefixed width byte
// larger than we want to handle
return DEFLATE_ERROR;
if (!ictx->written) {
// First iteration
JADE_ASSERT(towrite >= 3); // Assume can read at least 3 bytes
JADE_ASSERT(!ictx->allocated_size);

// NOTE: first byte in uncompressed stream is image width

// Extract next two bytes to get image data length (little-endian)
ictx->allocated_size = uncompressed[1] + (uncompressed[2] << 8);
const size_t max_allowed_size = ictx->icon ? MAX_FLASH_ICON_SIZE : MAX_FLASH_PICTURE_SIZE;
if (!ictx->allocated_size || ictx->allocated_size > max_allowed_size) {
// larger than we want to handle
return DEFLATE_ERROR;
}

// Allocate appropriate data buffer and set image width
if (ictx->icon) {
JADE_ASSERT(!ictx->icon->data);
ictx->icon->data = JADE_MALLOC_PREFER_SPIRAM(ictx->allocated_size);
ictx->icon->width = uncompressed[0];
} else {
JADE_ASSERT(!ictx->pic->data_8);
ictx->pic->data_8 = JADE_MALLOC_PREFER_SPIRAM(ictx->allocated_size);
ictx->pic->width = uncompressed[0];
}

// Count bytes read and bump read pointer
towrite -= 3;
uncompressed += 3;
}

if (!ictx->written) {
// First iteration, extract first byte from uncompressed stream to get the width
uint16_t* const width = ictx->icon ? &ictx->icon->width : (uint16_t*)&ictx->pic->width;
*width = uncompressed[0];
--towrite;
++uncompressed;
if (towrite + ictx->written > ictx->allocated_size) {
// larger than allocation
return DEFLATE_ERROR;
}

uint8_t* const dest = ictx->icon ? (uint8_t*)ictx->icon->data : ictx->pic->data_8;
JADE_ASSERT(dest);
#ifdef CONFIG_IDF_TARGET_ESP32S3
dsps_memcpy_aes3(dest + ictx->written, uncompressed, towrite);
#else
Expand All @@ -339,6 +363,7 @@ static int uncompressed_image_stream_writer(void* ctx, uint8_t* uncompressed, si
ictx->written += towrite;
return DEFLATE_OK;
}

static void decompress_image(const uint8_t* const start, const uint8_t* const end, image_deflate_ctx_t* const ictx)
{
JADE_ASSERT(start);
Expand All @@ -351,6 +376,7 @@ static void decompress_image(const uint8_t* const start, const uint8_t* const en
JADE_ASSERT(!dret);
dret = dctx->write_compressed(dctx, (uint8_t*)start, compressed_size);
JADE_ASSERT(!dret);
JADE_ASSERT(ictx->written == ictx->allocated_size);
free(dctx);
}

Expand All @@ -360,8 +386,8 @@ Picture* get_picture(const uint8_t* const start, const uint8_t* const end)
JADE_ASSERT(end);

// Setup for Picture load
image_deflate_ctx_t ictx = { .icon = NULL, .pic = JADE_MALLOC(sizeof(Picture)), .written = 0 };
ictx.pic->data_8 = JADE_MALLOC_PREFER_SPIRAM(MAX_FLASH_PICTURE_SIZE);
image_deflate_ctx_t ictx = { .icon = NULL, .pic = JADE_MALLOC(sizeof(Picture)), .written = 0, .allocated_size = 0 };
ictx.pic->data_8 = NULL;
ictx.pic->bytes_per_pixel = 2;
ictx.pic->width = 0;
ictx.pic->height = 0;
Expand All @@ -375,6 +401,7 @@ Picture* get_picture(const uint8_t* const start, const uint8_t* const end)
ictx.pic->height = ictx.written / (ictx.pic->width * ictx.pic->bytes_per_pixel);
JADE_ASSERT(ictx.pic->height);
JADE_ASSERT(ictx.pic->height * ictx.pic->width * ictx.pic->bytes_per_pixel == ictx.written);

return ictx.pic;
}

Expand All @@ -384,8 +411,8 @@ Icon* get_icon(const uint8_t* const start, const uint8_t* const end)
JADE_ASSERT(end);

// Setup for Icon load
image_deflate_ctx_t ictx = { .icon = JADE_MALLOC(sizeof(Icon)), .pic = NULL, .written = 0 };
ictx.icon->data = JADE_MALLOC_PREFER_SPIRAM(MAX_FLASH_ICON_SIZE);
image_deflate_ctx_t ictx = { .icon = JADE_MALLOC(sizeof(Icon)), .pic = NULL, .written = 0, .allocated_size = 0 };
ictx.icon->data = NULL;
ictx.icon->width = 0;
ictx.icon->height = 0;
ictx.written = 0;
Expand All @@ -399,6 +426,7 @@ Icon* get_icon(const uint8_t* const start, const uint8_t* const end)
ictx.icon->height = (ictx.written * 8) / ictx.icon->width;
JADE_ASSERT(ictx.icon->height);
JADE_ASSERT(ictx.icon->height * ictx.icon->width == ictx.written * 8);

return ictx.icon;
}

Expand Down

0 comments on commit 98fd82f

Please sign in to comment.