Skip to content
24 changes: 22 additions & 2 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ msgstr ""
#: extmod/moductypes.c ports/atmel-samd/common-hal/pulseio/PulseIn.c
#: ports/cxd56/common-hal/pulseio/PulseIn.c
#: ports/nrf/common-hal/pulseio/PulseIn.c
#: ports/raspberrypi/common-hal/pulseio/PulseIn.c
#: ports/stm/common-hal/pulseio/PulseIn.c py/obj.c py/objstr.c
#: py/objstrunicode.c
msgid "%q index out of range"
Expand Down Expand Up @@ -962,6 +963,7 @@ msgstr ""
#: ports/cxd56/common-hal/pulseio/PulseIn.c
#: ports/esp32s2/common-hal/pulseio/PulseIn.c
#: ports/nrf/common-hal/pulseio/PulseIn.c
#: ports/raspberrypi/common-hal/pulseio/PulseIn.c
#: ports/stm/common-hal/pulseio/PulseIn.c
#, c-format
msgid "Failed to allocate RX buffer of %d bytes"
Expand Down Expand Up @@ -1247,7 +1249,7 @@ msgstr ""
msgid "Invalid format chunk size"
msgstr ""

#: ports/esp32s2/common-hal/pwmio/PWMOut.c
#: ports/esp32s2/common-hal/busio/I2C.c ports/esp32s2/common-hal/pwmio/PWMOut.c
msgid "Invalid frequency"
msgstr ""

Expand Down Expand Up @@ -2291,7 +2293,7 @@ msgstr ""
msgid "Unsupported format"
msgstr ""

#: py/moduerrno.c
#: ports/raspberrypi/common-hal/pulseio/PulseOut.c py/moduerrno.c
msgid "Unsupported operation"
msgstr ""

Expand Down Expand Up @@ -3206,6 +3208,11 @@ msgstr ""
msgid "invalid arguments"
msgstr ""

#: shared-bindings/bitmaptools/__init__.c
#, c-format
msgid "invalid bits_per_pixel %d, must be, 1, 4, 8, 16, 24, or 32"
msgstr ""

#: extmod/modussl_axtls.c
msgid "invalid cert"
msgstr ""
Expand All @@ -3214,6 +3221,16 @@ msgstr ""
msgid "invalid dupterm index"
msgstr ""

#: shared-bindings/bitmaptools/__init__.c
#, c-format
msgid "invalid element size %d for bits_per_pixel %d\n"
msgstr ""

#: shared-bindings/bitmaptools/__init__.c
#, c-format
msgid "invalid element_size %d, must be, 1, 2, or 4"
msgstr ""

#: extmod/modframebuf.c
msgid "invalid format"
msgstr ""
Expand Down Expand Up @@ -3699,6 +3716,7 @@ msgstr ""
#: ports/atmel-samd/common-hal/pulseio/PulseIn.c
#: ports/cxd56/common-hal/pulseio/PulseIn.c
#: ports/nrf/common-hal/pulseio/PulseIn.c
#: ports/raspberrypi/common-hal/pulseio/PulseIn.c
#: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c
#: shared-bindings/ps2io/Ps2.c
msgid "pop from empty %q"
Expand All @@ -3724,6 +3742,8 @@ msgstr ""
#: ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.h
#: ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.h
#: ports/esp32s2/boards/espressif_saola_1_wrover/mpconfigboard.h
#: ports/esp32s2/boards/franzininho_wifi_wroom/mpconfigboard.h
#: ports/esp32s2/boards/franzininho_wifi_wrover/mpconfigboard.h
#: ports/esp32s2/boards/lilygo_ttgo_t8_s2_st7789/mpconfigboard.h
#: ports/esp32s2/boards/microdev_micro_s2/mpconfigboard.h
#: ports/esp32s2/boards/muselab_nanoesp32_s2/mpconfigboard.h
Expand Down
76 changes: 76 additions & 0 deletions shared-bindings/bitmaptools/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,83 @@ STATIC mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_line_obj, 0, bitmaptools_obj_draw_line);
// requires all 6 arguments

//| def readinto(bitmap: displayio.Bitmap, file: typing.BinaryIO, bits_per_pixel: int, element_size: int = 1, reverse_pixels_in_element: bool = False, swap_bytes_in_element: bool = False) -> None:
//| """Read from a binary file into a bitmap
//| The file must be positioned so that it consists of ``bitmap.height`` rows of pixel data, where each row is the smallest multiple of ``element_size`` bytes that can hold ``bitmap.width`` pixels.
//|
//| The bytes in an element can be optionally swapped, and the pixels in an element can be reversed.
//|
//| This function doesn't parse image headers, but is useful to speed up loading of uncompressed image formats such as PCF glyph data.
//|
//| :param displayio.Bitmap bitmap: A writable bitmap
//| :param typing.BinaryIO file: A file opened in binary mode
//| :param int bits_per_pixel: Number of bits per pixel. Values 1, 2, 4, 8, 16, 24, and 32 are supported;
//| :param int element_size: Number of bytes per element. Values of 1, 2, and 4 are supported, except that 24 ``bits_per_pixel`` requires 1 byte per element.
//| :param bool reverse_pixels_in_element: If set, the first pixel in a word is taken from the Most Signficant Bits; otherwise, it is taken from the Least Significant Bits.
//| :param bool swap_bytes_in_element: If the ``element_size`` is not 1, then reverse the byte order of each element read.
//| """
//| ...
//|

STATIC mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_bitmap, ARG_file, ARG_bits_per_pixel, ARG_element_size, ARG_reverse_pixels_in_element, ARG_swap_bytes_in_element };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_element_size, MP_ARG_INT, { .u_int = 1 } },
{ MP_QSTR_reverse_pixels_in_element, MP_ARG_BOOL, { .u_bool = false } },
{ MP_QSTR_swap_bytes_in_element, MP_ARG_BOOL, { .u_bool = false } },
};

mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

if (!MP_OBJ_IS_TYPE(args[ARG_bitmap].u_obj, &displayio_bitmap_type)) {
mp_raise_TypeError(NULL);
}
displayio_bitmap_t *bitmap = MP_OBJ_TO_PTR(args[ARG_bitmap].u_obj);

if (!MP_OBJ_IS_TYPE(args[ARG_file].u_obj, &mp_type_fileio)) {
mp_raise_TypeError(NULL);
}
pyb_file_obj_t* file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);

int element_size = args[ARG_element_size].u_int;
if (element_size != 1 && element_size != 2 && element_size != 4) {
mp_raise_ValueError_varg(translate("invalid element_size %d, must be, 1, 2, or 4"), element_size);
}

int bits_per_pixel = args[ARG_bits_per_pixel].u_int;
switch (bits_per_pixel) {
case 24:
if (element_size != 1) {
mp_raise_ValueError_varg(translate("invalid element size %d for bits_per_pixel %d\n"), element_size, bits_per_pixel);
}
break;
case 1:
case 2:
case 4:
case 8:
case 16:
case 32:
break;
default:
mp_raise_ValueError_varg(translate("invalid bits_per_pixel %d, must be, 1, 4, 8, 16, 24, or 32"), bits_per_pixel);
}

bool reverse_pixels_in_element = args[ARG_reverse_pixels_in_element].u_bool;
bool swap_bytes_in_element = args[ARG_swap_bytes_in_element].u_bool;

common_hal_bitmaptools_readinto(bitmap, file, element_size, bits_per_pixel, reverse_pixels_in_element, swap_bytes_in_element);

return mp_const_none;
}

MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_readinto_obj, 0, bitmaptools_readinto);

STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) },
{ MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) },
{ MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) },
{ MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
Expand Down
4 changes: 4 additions & 0 deletions shared-bindings/bitmaptools/__init__.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H
#define MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H

#include "shared-module/displayio/Bitmap.h"
#include "py/obj.h"
#include "extmod/vfs_fat.h"

void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy,
int16_t dest_clip0_x, int16_t dest_clip0_y,
Expand All @@ -49,4 +51,6 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
int16_t x1, int16_t y1,
uint32_t value);

void common_hal_bitmaptools_readinto(displayio_bitmap_t *self, pyb_file_obj_t* file, int element_size, int bits_per_pixel, bool reverse_pixels_in_word, bool swap_bytes);

#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H
88 changes: 88 additions & 0 deletions shared-module/bitmaptools/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
*/


#include "shared-bindings/bitmaptools/__init__.h"
#include "shared-bindings/displayio/Bitmap.h"
#include "shared-module/displayio/Bitmap.h"

#include "py/runtime.h"
#include "py/mperrno.h"

#include "math.h"
#include "stdlib.h"
Expand Down Expand Up @@ -336,3 +338,89 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
}
}
}

void common_hal_bitmaptools_readinto(displayio_bitmap_t *self, pyb_file_obj_t* file, int element_size, int bits_per_pixel, bool reverse_pixels_in_element, bool swap_bytes) {
if (self->read_only) {
mp_raise_RuntimeError(translate("Read-only object"));
}

size_t elements_per_row = (self->width * bits_per_pixel + element_size * 8 - 1) / (element_size * 8);
size_t rowsize = element_size * elements_per_row;
size_t rowsize_in_u32 = (rowsize + sizeof(uint32_t) - 1) / sizeof(uint32_t);
size_t rowsize_in_u16 = (rowsize + sizeof(uint16_t) - 1) / sizeof(uint16_t);
for(int y=0; y<self->height; y++) {
uint32_t rowdata32[rowsize_in_u32];
uint16_t *rowdata16 = (uint16_t*)rowdata32;
uint8_t *rowdata8 = (uint8_t*)rowdata32;

UINT bytes_read = 0;
if (f_read(&file->fp, rowdata32, rowsize, &bytes_read) != FR_OK || bytes_read != rowsize) {
mp_raise_OSError(MP_EIO);
}

if (swap_bytes) {
switch(element_size) {
case 2:
for(size_t i=0; i< rowsize_in_u16; i++) {
rowdata16[i] = __builtin_bswap16(rowdata16[i]);
}
break;
case 4:
for(size_t i=0; i< rowsize_in_u32; i++) {
rowdata32[i] = __builtin_bswap32(rowdata32[i]);
}
default:
break;
}
}

for(int x=0; x<self->width; x++) {
int value = 0;
switch(bits_per_pixel) {
case 1:
{
int byte_offset = x / 8;
int bit_offset = reverse_pixels_in_element ? (7 - x % 8) : x % 8;

value = (rowdata8[byte_offset] >> bit_offset) & 1;
break;
}
case 2:
{
int byte_offset = x / 4;
int bit_offset = 2 * (reverse_pixels_in_element ? (3 - x % 4) : x % 4);

value = (rowdata8[byte_offset] >> bit_offset) & 3;
break;
}
case 4:
{
int byte_offset = x / 2;
int bit_offset = 4 * (reverse_pixels_in_element ? (1 - x % 2) : x % 2);

value = (rowdata8[byte_offset] >> bit_offset) & 7;
break;
}
case 8:
value = rowdata8[x];
break;

case 16:
value = rowdata16[x];
break;

case 24:
value = (rowdata8[x * 3] << 16) | (rowdata8[x * 3 + 1] << 8) | (rowdata8[x * 3 + 2] << 8);
break;

case 32:
value = rowdata32[x];
break;
}

displayio_bitmap_write_pixel(self, x, y, value);
}
}

displayio_bitmap_set_dirty_area(self, 0, 0, self->width, self->height);
}