1
1
// This file is part of the CircuitPython project: https://circuitpython.org
2
2
//
3
3
// SPDX-FileCopyrightText: Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
4
+ // SPDX-FileCopyrightText: Copyright (c) 2025 SamantazFox
4
5
//
5
6
// SPDX-License-Identifier: MIT
6
7
15
16
#include "py/mperrno.h"
16
17
#include "py/runtime.h"
17
18
19
+
20
+ #define DISPLAYIO_ODBMP_DEBUG (...) (void)0
21
+ // #define DISPLAYIO_ODBMP_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__)
22
+
23
+
18
24
static uint32_t read_word (uint16_t * bmp_header , uint16_t index ) {
19
25
return bmp_header [index ] | bmp_header [index + 1 ] << 16 ;
20
26
}
@@ -25,39 +31,63 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
25
31
uint16_t bmp_header [69 ];
26
32
f_rewind (& self -> file -> fp );
27
33
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 ) {
29
38
mp_raise_OSError (MP_EIO );
30
39
}
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 ) {
33
42
mp_arg_error_invalid (MP_QSTR_file );
34
43
}
35
44
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.
39
46
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
+
41
63
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 );
43
65
44
66
// 0 is uncompressed; 3 is bitfield compressed. 1 and 2 are RLE compression.
45
67
if (compression != 0 && compression != 3 ) {
46
68
mp_raise_ValueError (MP_ERROR_TEXT ("RLE-compressed BMP not supported" ));
47
69
}
48
70
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
+
50
74
self -> bitfield_compressed = (compression == 3 );
51
- self -> bits_per_pixel = bits_per_pixel ;
75
+ self -> bits_per_pixel = bmp_header [ 14 ] ;
52
76
self -> width = read_word (bmp_header , 9 );
53
77
self -> height = read_word (bmp_header , 11 );
54
78
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
+
55
85
displayio_colorconverter_t * colorconverter =
56
86
mp_obj_malloc (displayio_colorconverter_t , & displayio_colorconverter_type );
57
87
common_hal_displayio_colorconverter_construct (colorconverter , false, DISPLAYIO_COLORSPACE_RGB888 );
58
88
self -> colorconverter = colorconverter ;
59
89
60
- if (bits_per_pixel == 16 ) {
90
+ if (self -> bits_per_pixel == 16 ) {
61
91
if (((header_size >= 56 )) || (self -> bitfield_compressed )) {
62
92
self -> r_bitmask = read_word (bmp_header , 27 );
63
93
self -> g_bitmask = read_word (bmp_header , 29 );
@@ -68,9 +98,13 @@ void common_hal_displayio_ondiskbitmap_construct(displayio_ondiskbitmap_t *self,
68
98
self -> g_bitmask = 0x3e0 ;
69
99
self -> b_bitmask = 0x1f ;
70
100
}
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
+ }
72
106
if (number_of_colors == 0 ) {
73
- number_of_colors = 1 << bits_per_pixel ;
107
+ number_of_colors = 1 << self -> bits_per_pixel ;
74
108
}
75
109
76
110
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,
101
135
common_hal_displayio_palette_set_color (palette , 1 , 0xffffff );
102
136
}
103
137
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 );
111
138
}
112
139
113
140
uint8_t bytes_per_pixel = (self -> bits_per_pixel / 8 ) ? (self -> bits_per_pixel / 8 ) : 1 ;
0 commit comments