Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bfc8c89

Browse files
authored
Merge pull request #4403 from jepler/bitmap-read-2
bitmaptools: Add readinto, arrayblit
2 parents 75ba26e + 074ba1d commit bfc8c89

File tree

6 files changed

+295
-1
lines changed

6 files changed

+295
-1
lines changed

locale/circuitpython.pot

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3108,6 +3108,7 @@ msgstr ""
31083108

31093109
#: extmod/ulab/code/numerical/numerical.c
31103110
#: ports/esp32s2/common-hal/pulseio/PulseIn.c py/obj.c
3111+
#: shared-bindings/bitmaptools/__init__.c
31113112
msgid "index out of range"
31123113
msgstr ""
31133114

@@ -3212,6 +3213,11 @@ msgstr ""
32123213
msgid "invalid arguments"
32133214
msgstr ""
32143215

3216+
#: shared-bindings/bitmaptools/__init__.c
3217+
#, c-format
3218+
msgid "invalid bits_per_pixel %d, must be, 1, 4, 8, 16, 24, or 32"
3219+
msgstr ""
3220+
32153221
#: extmod/modussl_axtls.c
32163222
msgid "invalid cert"
32173223
msgstr ""
@@ -3220,6 +3226,16 @@ msgstr ""
32203226
msgid "invalid dupterm index"
32213227
msgstr ""
32223228

3229+
#: shared-bindings/bitmaptools/__init__.c
3230+
#, c-format
3231+
msgid "invalid element size %d for bits_per_pixel %d\n"
3232+
msgstr ""
3233+
3234+
#: shared-bindings/bitmaptools/__init__.c
3235+
#, c-format
3236+
msgid "invalid element_size %d, must be, 1, 2, or 4"
3237+
msgstr ""
3238+
32233239
#: extmod/modframebuf.c
32243240
msgid "invalid format"
32253241
msgstr ""
@@ -3678,7 +3694,7 @@ msgstr ""
36783694
msgid "parameters must be registers in sequence r0 to r3"
36793695
msgstr ""
36803696

3681-
#: shared-bindings/displayio/Bitmap.c
3697+
#: shared-bindings/bitmaptools/__init__.c shared-bindings/displayio/Bitmap.c
36823698
msgid "pixel coordinates out of bounds"
36833699
msgstr ""
36843700

ports/atmel-samd/boards/cp_sapling_m0_spiflash/mpconfigboard.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ EXTERNAL_FLASH_DEVICES = AT25DF081A
1414
CIRCUITPY_AUDIOIO = 0
1515
CIRCUITPY_AUDIOBUSIO = 0
1616
CIRCUITPY_BITBANGIO = 0
17+
CIRCUITPY_BITMAPTOOLS = 0
1718
CIRCUITPY_COUNTIO = 0
1819
CIRCUITPY_FREQUENCYIO = 0
1920
CIRCUITPY_I2CPERIPHERAL = 0

ports/atmel-samd/boards/feather_radiofruit_zigbee/mpconfigboard.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CIRCUITPY_AUDIOBUSIO = 0
1515
# No DAC on SAMR21G
1616
CIRCUITPY_AUDIOIO = 0
1717
CIRCUITPY_BITBANGIO = 0
18+
CIRCUITPY_BITMAPTOOLS = 0
1819
CIRCUITPY_COUNTIO = 0
1920
CIRCUITPY_RTC = 0
2021
CIRCUITPY_FREQUENCYIO = 0

shared-bindings/bitmaptools/__init__.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include <stdint.h>
3131

32+
#include "py/binary.h"
3233
#include "py/obj.h"
3334
#include "py/runtime.h"
3435

@@ -357,8 +358,163 @@ STATIC mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg
357358
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_line_obj, 0, bitmaptools_obj_draw_line);
358359
// requires all 6 arguments
359360

361+
//| def arrayblit(bitmap: displayio.Bitmap, data: ReadableBuffer, x1: int=0, y1: int=0, x2: Optional[int]=None, y2: Optional[int]=None, skip_index:Optional[int]=None) -> None:
362+
//| """Inserts pixels from ``data`` into the rectangle of width×height pixels with the upper left corner at ``(x,y)``
363+
//|
364+
//| The values from ``data`` are taken modulo the number of color values
365+
//| avalable in the destination bitmap.
366+
//|
367+
//| If x1 or y1 are not specified, they are taken as 0. If x2 or y2
368+
//| are not specified, or are given as -1, they are taken as the width
369+
//| and height of the image.
370+
//|
371+
//| The coordinates affected by the blit are ``x1 <= x < x2`` and ``y1 <= y < y2``.
372+
//|
373+
//| ``data`` must contain at least as many elements as required. If it
374+
//| contains excess elements, they are ignored.
375+
//|
376+
//| The blit takes place by rows, so the first elements of ``data`` go
377+
//| to the first row, the next elements to the next row, and so on.
378+
//|
379+
//| :param displayio.Bitmap bitmap: A writable bitmap
380+
//| :param ReadableBuffer data: Buffer containing the source pixel values
381+
//| :param int x1: The left corner of the area to blit into (inclusive)
382+
//| :param int y1: The top corner of the area to blit into (inclusive)
383+
//| :param int x2: The right of the area to blit into (exclusive)
384+
//| :param int y2: The bottom corner of the area to blit into (exclusive)
385+
//| :param int skip_index: Bitmap palette index in the source that will not be copied,
386+
//| set to None to copy all pixels
387+
//| """
388+
//| ...
389+
//|
390+
STATIC mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
391+
enum { ARG_bitmap, ARG_data, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_index };
392+
static const mp_arg_t allowed_args[] = {
393+
{ MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ },
394+
{ MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ },
395+
{ MP_QSTR_x1, MP_ARG_INT, {.u_int = 0} },
396+
{ MP_QSTR_y1, MP_ARG_INT, {.u_int = 0} },
397+
{ MP_QSTR_x2, MP_ARG_INT, {.u_int = -1} },
398+
{ MP_QSTR_y2, MP_ARG_INT, {.u_int = -1} },
399+
{ MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } },
400+
}
401+
;
402+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
403+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
404+
405+
if (!MP_OBJ_IS_TYPE(args[ARG_bitmap].u_obj, &displayio_bitmap_type)) {
406+
mp_raise_TypeError(NULL);
407+
}
408+
displayio_bitmap_t *bitmap = MP_OBJ_TO_PTR(args[ARG_bitmap].u_obj);
409+
410+
mp_buffer_info_t bufinfo;
411+
mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ);
412+
413+
int x1 = args[ARG_x1].u_int;
414+
int y1 = args[ARG_y1].u_int;
415+
int x2 = args[ARG_x2].u_int == -1 ? bitmap->width : args[ARG_x2].u_int;
416+
int y2 = args[ARG_y2].u_int == -1 ? bitmap->height : args[ARG_y2].u_int;
417+
418+
if ((x1 < 0) || (y1 < 0) || (x1 > x2) || (y1 > y2) || (x2 > bitmap->width) || (y2 > bitmap->height)) {
419+
mp_raise_IndexError(translate("pixel coordinates out of bounds"));
420+
}
421+
422+
size_t output_element_count = (x2 - x1) * (y2 - y1);
423+
size_t element_size = mp_binary_get_size('@', bufinfo.typecode, NULL);
424+
size_t input_element_count = bufinfo.len / element_size;
425+
426+
bool skip_specified = args[ARG_skip_index].u_obj != mp_const_none;
427+
uint32_t skip_index = skip_specified ? mp_obj_get_int(args[ARG_skip_index].u_obj) : 0;
428+
if (input_element_count < output_element_count) {
429+
mp_raise_IndexError(translate("index out of range"));
430+
}
431+
432+
common_hal_bitmaptools_arrayblit(bitmap, bufinfo.buf, element_size, x1, y1, x2, y2, skip_specified, skip_index);
433+
434+
return mp_const_none;
435+
}
436+
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_arrayblit_obj, 0, bitmaptools_arrayblit);
437+
438+
439+
//| 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:
440+
//| """Read from a binary file into a bitmap
441+
//| 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.
442+
//|
443+
//| The bytes in an element can be optionally swapped, and the pixels in an element can be reversed.
444+
//|
445+
//| This function doesn't parse image headers, but is useful to speed up loading of uncompressed image formats such as PCF glyph data.
446+
//|
447+
//| :param displayio.Bitmap bitmap: A writable bitmap
448+
//| :param typing.BinaryIO file: A file opened in binary mode
449+
//| :param int bits_per_pixel: Number of bits per pixel. Values 1, 2, 4, 8, 16, 24, and 32 are supported;
450+
//| :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.
451+
//| :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.
452+
//| :param bool swap_bytes_in_element: If the ``element_size`` is not 1, then reverse the byte order of each element read.
453+
//| """
454+
//| ...
455+
//|
456+
457+
STATIC mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
458+
enum { ARG_bitmap, ARG_file, ARG_bits_per_pixel, ARG_element_size, ARG_reverse_pixels_in_element, ARG_swap_bytes_in_element };
459+
static const mp_arg_t allowed_args[] = {
460+
{ MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ },
461+
{ MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ },
462+
{ MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT },
463+
{ MP_QSTR_element_size, MP_ARG_INT, { .u_int = 1 } },
464+
{ MP_QSTR_reverse_pixels_in_element, MP_ARG_BOOL, { .u_bool = false } },
465+
{ MP_QSTR_swap_bytes_in_element, MP_ARG_BOOL, { .u_bool = false } },
466+
};
467+
468+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
469+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
470+
471+
if (!MP_OBJ_IS_TYPE(args[ARG_bitmap].u_obj, &displayio_bitmap_type)) {
472+
mp_raise_TypeError(NULL);
473+
}
474+
displayio_bitmap_t *bitmap = MP_OBJ_TO_PTR(args[ARG_bitmap].u_obj);
475+
476+
if (!MP_OBJ_IS_TYPE(args[ARG_file].u_obj, &mp_type_fileio)) {
477+
mp_raise_TypeError(NULL);
478+
}
479+
pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
480+
481+
int element_size = args[ARG_element_size].u_int;
482+
if (element_size != 1 && element_size != 2 && element_size != 4) {
483+
mp_raise_ValueError_varg(translate("invalid element_size %d, must be, 1, 2, or 4"), element_size);
484+
}
485+
486+
int bits_per_pixel = args[ARG_bits_per_pixel].u_int;
487+
switch (bits_per_pixel) {
488+
case 24:
489+
if (element_size != 1) {
490+
mp_raise_ValueError_varg(translate("invalid element size %d for bits_per_pixel %d\n"), element_size, bits_per_pixel);
491+
}
492+
break;
493+
case 1:
494+
case 2:
495+
case 4:
496+
case 8:
497+
case 16:
498+
case 32:
499+
break;
500+
default:
501+
mp_raise_ValueError_varg(translate("invalid bits_per_pixel %d, must be, 1, 4, 8, 16, 24, or 32"), bits_per_pixel);
502+
}
503+
504+
bool reverse_pixels_in_element = args[ARG_reverse_pixels_in_element].u_bool;
505+
bool swap_bytes_in_element = args[ARG_swap_bytes_in_element].u_bool;
506+
507+
common_hal_bitmaptools_readinto(bitmap, file, element_size, bits_per_pixel, reverse_pixels_in_element, swap_bytes_in_element);
508+
509+
return mp_const_none;
510+
}
511+
512+
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_readinto_obj, 0, bitmaptools_readinto);
513+
360514
STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
515+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) },
361516
{ MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) },
517+
{ MP_ROM_QSTR(MP_QSTR_arrayblit), MP_ROM_PTR(&bitmaptools_arrayblit_obj) },
362518
{ MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) },
363519
{ MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
364520
};

shared-bindings/bitmaptools/__init__.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H
2828
#define MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H
2929

30+
#include "shared-module/displayio/Bitmap.h"
3031
#include "py/obj.h"
32+
#include "extmod/vfs_fat.h"
3133

3234
void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy,
3335
int16_t dest_clip0_x, int16_t dest_clip0_y,
@@ -49,4 +51,7 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
4951
int16_t x1, int16_t y1,
5052
uint32_t value);
5153

54+
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);
55+
void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_index);
56+
5257
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H

shared-module/bitmaptools/__init__.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
*/
2626

2727

28+
#include "shared-bindings/bitmaptools/__init__.h"
2829
#include "shared-bindings/displayio/Bitmap.h"
2930
#include "shared-module/displayio/Bitmap.h"
3031

3132
#include "py/runtime.h"
33+
#include "py/mperrno.h"
3234

3335
#include "math.h"
3436
#include "stdlib.h"
@@ -372,3 +374,116 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
372374
}
373375
}
374376
}
377+
378+
void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int element_size, int x1, int y1, int x2, int y2, bool skip_specified, uint32_t skip_value) {
379+
uint32_t mask = (1 << common_hal_displayio_bitmap_get_bits_per_value(self)) - 1;
380+
381+
for (int y = y1; y < y2; y++) {
382+
for (int x = x1; x < x2; x++) {
383+
uint32_t value;
384+
switch (element_size) {
385+
default:
386+
case 1:
387+
value = *(uint8_t *)data;
388+
data = (void *)((uint8_t *)data + 1);
389+
break;
390+
case 2:
391+
value = *(uint16_t *)data;
392+
data = (void *)((uint16_t *)data + 1);
393+
break;
394+
case 4:
395+
value = *(uint32_t *)data;
396+
data = (void *)((uint32_t *)data + 1);
397+
break;
398+
}
399+
if (!skip_specified || value != skip_value) {
400+
displayio_bitmap_write_pixel(self, x, y, value & mask);
401+
}
402+
}
403+
}
404+
}
405+
406+
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) {
407+
uint32_t mask = (1 << common_hal_displayio_bitmap_get_bits_per_value(self)) - 1;
408+
409+
if (self->read_only) {
410+
mp_raise_RuntimeError(translate("Read-only object"));
411+
}
412+
413+
size_t elements_per_row = (self->width * bits_per_pixel + element_size * 8 - 1) / (element_size * 8);
414+
size_t rowsize = element_size * elements_per_row;
415+
size_t rowsize_in_u32 = (rowsize + sizeof(uint32_t) - 1) / sizeof(uint32_t);
416+
size_t rowsize_in_u16 = (rowsize + sizeof(uint16_t) - 1) / sizeof(uint16_t);
417+
for (int y = 0; y < self->height; y++) {
418+
uint32_t rowdata32[rowsize_in_u32];
419+
uint16_t *rowdata16 = (uint16_t *)rowdata32;
420+
uint8_t *rowdata8 = (uint8_t *)rowdata32;
421+
422+
UINT bytes_read = 0;
423+
if (f_read(&file->fp, rowdata32, rowsize, &bytes_read) != FR_OK || bytes_read != rowsize) {
424+
mp_raise_OSError(MP_EIO);
425+
}
426+
427+
if (swap_bytes) {
428+
switch (element_size) {
429+
case 2:
430+
for (size_t i = 0; i < rowsize_in_u16; i++) {
431+
rowdata16[i] = __builtin_bswap16(rowdata16[i]);
432+
}
433+
break;
434+
case 4:
435+
for (size_t i = 0; i < rowsize_in_u32; i++) {
436+
rowdata32[i] = __builtin_bswap32(rowdata32[i]);
437+
}
438+
default:
439+
break;
440+
}
441+
}
442+
443+
for (int x = 0; x < self->width; x++) {
444+
int value = 0;
445+
switch (bits_per_pixel) {
446+
case 1: {
447+
int byte_offset = x / 8;
448+
int bit_offset = reverse_pixels_in_element ? (7 - x % 8) : x % 8;
449+
450+
value = (rowdata8[byte_offset] >> bit_offset) & 1;
451+
break;
452+
}
453+
case 2: {
454+
int byte_offset = x / 4;
455+
int bit_offset = 2 * (reverse_pixels_in_element ? (3 - x % 4) : x % 4);
456+
457+
value = (rowdata8[byte_offset] >> bit_offset) & 3;
458+
break;
459+
}
460+
case 4: {
461+
int byte_offset = x / 2;
462+
int bit_offset = 4 * (reverse_pixels_in_element ? (1 - x % 2) : x % 2);
463+
464+
value = (rowdata8[byte_offset] >> bit_offset) & 7;
465+
break;
466+
}
467+
case 8:
468+
value = rowdata8[x];
469+
break;
470+
471+
case 16:
472+
value = rowdata16[x];
473+
break;
474+
475+
case 24:
476+
value = (rowdata8[x * 3] << 16) | (rowdata8[x * 3 + 1] << 8) | (rowdata8[x * 3 + 2] << 8);
477+
break;
478+
479+
case 32:
480+
value = rowdata32[x];
481+
break;
482+
}
483+
484+
displayio_bitmap_write_pixel(self, x, y, value & mask);
485+
}
486+
}
487+
488+
displayio_bitmap_set_dirty_area(self, 0, 0, self->width, self->height);
489+
}

0 commit comments

Comments
 (0)