Skip to content

Commit

Permalink
Merge pull request #10097 from SamantazFox/fix-small-bitmaps
Browse files Browse the repository at this point in the history
shared-module/OnDiskBitmap.c: Fix loading small monochrome bitmaps
  • Loading branch information
tannewt authored Feb 25, 2025
2 parents 0654cdf + c099022 commit d857656
Showing 1 changed file with 47 additions and 20 deletions.
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

0 comments on commit d857656

Please sign in to comment.