Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 47 additions & 20 deletions shared-module/displayio/OnDiskBitmap.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This file is part of the CircuitPython project: https://circuitpython.org
//
// SPDX-FileCopyrightText: Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
// SPDX-FileCopyrightText: Copyright (c) 2025 SamantazFox
//
// SPDX-License-Identifier: MIT

Expand All @@ -15,6 +16,11 @@
#include "py/mperrno.h"
#include "py/runtime.h"


#define DISPLAYIO_ODBMP_DEBUG(...) (void)0
// #define DISPLAYIO_ODBMP_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)


static uint32_t read_word(uint16_t *bmp_header, uint16_t index) {
return bmp_header[index] | bmp_header[index + 1] << 16;
}
Expand All @@ -25,39 +31,63 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
uint16_t bmp_header[69];
f_rewind(&self->file->fp);
UINT bytes_read;
if (f_read(&self->file->fp, bmp_header, 138, &bytes_read) != FR_OK) {

// Read the minimum amount of bytes required to parse a BITMAPCOREHEADER.
// If needed, we will read more bytes down below.
if (f_read(&self->file->fp, bmp_header, 26, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
if (bytes_read != 138 ||
memcmp(bmp_header, "BM", 2) != 0) {
DISPLAYIO_ODBMP_DEBUG("bytes_read: %d\n", bytes_read);
if (bytes_read != 26 || memcmp(bmp_header, "BM", 2) != 0) {
mp_arg_error_invalid(MP_QSTR_file);
}

// We can't cast because we're not aligned.
self->data_offset = read_word(bmp_header, 5);

// Read header size to determine if more header bytes needs to be read.
uint32_t header_size = read_word(bmp_header, 7);
uint16_t bits_per_pixel = bmp_header[14];
DISPLAYIO_ODBMP_DEBUG("header_size: %d\n", header_size);

if (header_size == 40 || header_size == 108 || header_size == 124) {
// Read the remaining header bytes
if (f_read(&self->file->fp, bmp_header + 13, header_size - 12, &bytes_read) != FR_OK) {
mp_raise_OSError(MP_EIO);
}
DISPLAYIO_ODBMP_DEBUG("bytes_read: %d\n", bytes_read);
if (bytes_read != (header_size - 12)) {
mp_arg_error_invalid(MP_QSTR_file);
}
} else if (header_size != 12) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size);
}


uint32_t compression = read_word(bmp_header, 15);
uint32_t number_of_colors = read_word(bmp_header, 23);
DISPLAYIO_ODBMP_DEBUG("compression: %d\n", compression);

// 0 is uncompressed; 3 is bitfield compressed. 1 and 2 are RLE compression.
if (compression != 0 && compression != 3) {
mp_raise_ValueError(MP_ERROR_TEXT("RLE-compressed BMP not supported"));
}

bool indexed = bits_per_pixel <= 8;
// We can't cast because we're not aligned.
self->data_offset = read_word(bmp_header, 5);

self->bitfield_compressed = (compression == 3);
self->bits_per_pixel = bits_per_pixel;
self->bits_per_pixel = bmp_header[14];
self->width = read_word(bmp_header, 9);
self->height = read_word(bmp_header, 11);

DISPLAYIO_ODBMP_DEBUG("data offset: %d\n", self->data_offset);
DISPLAYIO_ODBMP_DEBUG("width: %d\n", self->width);
DISPLAYIO_ODBMP_DEBUG("height: %d\n", self->height);
DISPLAYIO_ODBMP_DEBUG("bpp: %d\n", self->bits_per_pixel);


displayio_colorconverter_t *colorconverter =
mp_obj_malloc(displayio_colorconverter_t, &displayio_colorconverter_type);
common_hal_displayio_colorconverter_construct(colorconverter, false, DISPLAYIO_COLORSPACE_RGB888);
self->colorconverter = colorconverter;

if (bits_per_pixel == 16) {
if (self->bits_per_pixel == 16) {
if (((header_size >= 56)) || (self->bitfield_compressed)) {
self->r_bitmask = read_word(bmp_header, 27);
self->g_bitmask = read_word(bmp_header, 29);
Expand All @@ -68,9 +98,13 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
self->g_bitmask = 0x3e0;
self->b_bitmask = 0x1f;
}
} else if (indexed) {
} else if (self->bits_per_pixel <= 8) { // indexed
uint32_t number_of_colors = 0;
if (header_size >= 40) {
number_of_colors = read_word(bmp_header, 23);
}
if (number_of_colors == 0) {
number_of_colors = 1 << bits_per_pixel;
number_of_colors = 1 << self->bits_per_pixel;
}

displayio_palette_t *palette = mp_obj_malloc(displayio_palette_t, &displayio_palette_type);
Expand Down Expand Up @@ -101,13 +135,6 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
common_hal_displayio_palette_set_color(palette, 1, 0xffffff);
}
self->palette = palette;

} else if (!(header_size == 12 || header_size == 40 || header_size == 108 || header_size == 124)) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size);
}

if (bits_per_pixel == 8 && number_of_colors == 0) {
mp_raise_ValueError_varg(MP_ERROR_TEXT("Only monochrome, indexed 4bpp or 8bpp, and 16bpp or greater BMPs supported: %d bpp given"), bits_per_pixel);
}

uint8_t bytes_per_pixel = (self->bits_per_pixel / 8) ? (self->bits_per_pixel / 8) : 1;
Expand Down