Skip to content

Commit c099022

Browse files
committed
shared-module/OnDiskBitmap.c: Fix loading small monochrome bitmaps
1 parent 4cce4fa commit c099022

File tree

1 file changed

+47
-20
lines changed

1 file changed

+47
-20
lines changed

shared-module/displayio/OnDiskBitmap.c

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// This file is part of the CircuitPython project: https://circuitpython.org
22
//
33
// SPDX-FileCopyrightText: Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
4+
// SPDX-FileCopyrightText: Copyright (c) 2025 SamantazFox
45
//
56
// SPDX-License-Identifier: MIT
67

@@ -15,6 +16,11 @@
1516
#include "py/mperrno.h"
1617
#include "py/runtime.h"
1718

19+
20+
#define DISPLAYIO_ODBMP_DEBUG(...) (void)0
21+
// #define DISPLAYIO_ODBMP_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
22+
23+
1824
static uint32_t read_word(uint16_t *bmp_header, uint16_t index) {
1925
return bmp_header[index] | bmp_header[index + 1] << 16;
2026
}
@@ -25,39 +31,63 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
2531
uint16_t bmp_header[69];
2632
f_rewind(&self->file->fp);
2733
UINT bytes_read;
28-
if (f_read(&self->file->fp, bmp_header, 138, &bytes_read) != FR_OK) {
34+
35+
// Read the minimum amount of bytes required to parse a BITMAPCOREHEADER.
36+
// If needed, we will read more bytes down below.
37+
if (f_read(&self->file->fp, bmp_header, 26, &bytes_read) != FR_OK) {
2938
mp_raise_OSError(MP_EIO);
3039
}
31-
if (bytes_read != 138 ||
32-
memcmp(bmp_header, "BM", 2) != 0) {
40+
DISPLAYIO_ODBMP_DEBUG("bytes_read: %d\n", bytes_read);
41+
if (bytes_read != 26 || memcmp(bmp_header, "BM", 2) != 0) {
3342
mp_arg_error_invalid(MP_QSTR_file);
3443
}
3544

36-
// We can't cast because we're not aligned.
37-
self->data_offset = read_word(bmp_header, 5);
38-
45+
// Read header size to determine if more header bytes needs to be read.
3946
uint32_t header_size = read_word(bmp_header, 7);
40-
uint16_t bits_per_pixel = bmp_header[14];
47+
DISPLAYIO_ODBMP_DEBUG("header_size: %d\n", header_size);
48+
49+
if (header_size == 40 || header_size == 108 || header_size == 124) {
50+
// Read the remaining header bytes
51+
if (f_read(&self->file->fp, bmp_header + 13, header_size - 12, &bytes_read) != FR_OK) {
52+
mp_raise_OSError(MP_EIO);
53+
}
54+
DISPLAYIO_ODBMP_DEBUG("bytes_read: %d\n", bytes_read);
55+
if (bytes_read != (header_size - 12)) {
56+
mp_arg_error_invalid(MP_QSTR_file);
57+
}
58+
} else if (header_size != 12) {
59+
mp_raise_ValueError_varg(MP_ERROR_TEXT("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size);
60+
}
61+
62+
4163
uint32_t compression = read_word(bmp_header, 15);
42-
uint32_t number_of_colors = read_word(bmp_header, 23);
64+
DISPLAYIO_ODBMP_DEBUG("compression: %d\n", compression);
4365

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

49-
bool indexed = bits_per_pixel <= 8;
71+
// We can't cast because we're not aligned.
72+
self->data_offset = read_word(bmp_header, 5);
73+
5074
self->bitfield_compressed = (compression == 3);
51-
self->bits_per_pixel = bits_per_pixel;
75+
self->bits_per_pixel = bmp_header[14];
5276
self->width = read_word(bmp_header, 9);
5377
self->height = read_word(bmp_header, 11);
5478

79+
DISPLAYIO_ODBMP_DEBUG("data offset: %d\n", self->data_offset);
80+
DISPLAYIO_ODBMP_DEBUG("width: %d\n", self->width);
81+
DISPLAYIO_ODBMP_DEBUG("height: %d\n", self->height);
82+
DISPLAYIO_ODBMP_DEBUG("bpp: %d\n", self->bits_per_pixel);
83+
84+
5585
displayio_colorconverter_t *colorconverter =
5686
mp_obj_malloc(displayio_colorconverter_t, &displayio_colorconverter_type);
5787
common_hal_displayio_colorconverter_construct(colorconverter, false, DISPLAYIO_COLORSPACE_RGB888);
5888
self->colorconverter = colorconverter;
5989

60-
if (bits_per_pixel == 16) {
90+
if (self->bits_per_pixel == 16) {
6191
if (((header_size >= 56)) || (self->bitfield_compressed)) {
6292
self->r_bitmask = read_word(bmp_header, 27);
6393
self->g_bitmask = read_word(bmp_header, 29);
@@ -68,9 +98,13 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
6898
self->g_bitmask = 0x3e0;
6999
self->b_bitmask = 0x1f;
70100
}
71-
} else if (indexed) {
101+
} else if (self->bits_per_pixel <= 8) { // indexed
102+
uint32_t number_of_colors = 0;
103+
if (header_size >= 40) {
104+
number_of_colors = read_word(bmp_header, 23);
105+
}
72106
if (number_of_colors == 0) {
73-
number_of_colors = 1 << bits_per_pixel;
107+
number_of_colors = 1 << self->bits_per_pixel;
74108
}
75109

76110
displayio_palette_t *palette = mp_obj_malloc(displayio_palette_t, &displayio_palette_type);
@@ -101,13 +135,6 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
101135
common_hal_displayio_palette_set_color(palette, 1, 0xffffff);
102136
}
103137
self->palette = palette;
104-
105-
} else if (!(header_size == 12 || header_size == 40 || header_size == 108 || header_size == 124)) {
106-
mp_raise_ValueError_varg(MP_ERROR_TEXT("Only Windows format, uncompressed BMP supported: given header size is %d"), header_size);
107-
}
108-
109-
if (bits_per_pixel == 8 && number_of_colors == 0) {
110-
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);
111138
}
112139

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

0 commit comments

Comments
 (0)