|
29 | 29 |
|
30 | 30 | #include <stdint.h>
|
31 | 31 |
|
| 32 | +#include "py/binary.h" |
32 | 33 | #include "py/obj.h"
|
33 | 34 | #include "py/runtime.h"
|
34 | 35 |
|
@@ -357,8 +358,163 @@ STATIC mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg
|
357 | 358 | MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_line_obj, 0, bitmaptools_obj_draw_line);
|
358 | 359 | // requires all 6 arguments
|
359 | 360 |
|
| 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 | + |
360 | 514 | STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
|
| 515 | + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) }, |
361 | 516 | { 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) }, |
362 | 518 | { MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) },
|
363 | 519 | { MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
|
364 | 520 | };
|
|
0 commit comments