Skip to content

Commit eaf8bc0

Browse files
committed
bitmaptools: add dither
This can convert a BGR565_SWAPPED bitmap to B&W in about 82ms on esp32-s2.
1 parent 5572876 commit eaf8bc0

File tree

4 files changed

+317
-3
lines changed

4 files changed

+317
-3
lines changed

locale/circuitpython.pot

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,10 @@ msgstr ""
26112611
msgid "binary op %q not implemented"
26122612
msgstr ""
26132613

2614+
#: shared-bindings/bitmaptools/__init__.c
2615+
msgid "bitmap sizes must match"
2616+
msgstr ""
2617+
26142618
#: extmod/modurandom.c
26152619
msgid "bits must be 32 or less"
26162620
msgstr ""
@@ -4102,6 +4106,18 @@ msgstr ""
41024106
msgid "source palette too large"
41034107
msgstr ""
41044108

4109+
#: shared-bindings/bitmaptools/__init__.c
4110+
msgid "source_bitmap must have value_count of 2 or 65536"
4111+
msgstr ""
4112+
4113+
#: shared-bindings/bitmaptools/__init__.c
4114+
msgid "source_bitmap must have value_count of 65536"
4115+
msgstr ""
4116+
4117+
#: shared-bindings/bitmaptools/__init__.c
4118+
msgid "source_bitmap must have value_count of 8"
4119+
msgstr ""
4120+
41054121
#: py/objstr.c
41064122
msgid "start/end indices"
41074123
msgstr ""
@@ -4350,6 +4366,10 @@ msgstr ""
43504366
msgid "unsupported colorspace for GifWriter"
43514367
msgstr ""
43524368

4369+
#: shared-bindings/bitmaptools/__init__.c
4370+
msgid "unsupported colorspace for dither"
4371+
msgstr ""
4372+
43534373
#: py/objstr.c
43544374
#, c-format
43554375
msgid "unsupported format character '%c' (0x%x) at index %d"

shared-bindings/bitmaptools/__init__.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
*/
2626

2727
#include "shared-bindings/displayio/Bitmap.h"
28+
#include "shared-bindings/displayio/Palette.h"
29+
#include "shared-bindings/displayio/ColorConverter.h"
2830
#include "shared-bindings/bitmaptools/__init__.h"
2931

3032
#include <stdint.h>
3133

3234
#include "py/binary.h"
35+
#include "py/enum.h"
3336
#include "py/obj.h"
3437
#include "py/runtime.h"
3538

@@ -565,9 +568,92 @@ STATIC mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp
565568

566569
return mp_const_none;
567570
}
568-
569571
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_readinto_obj, 0, bitmaptools_readinto);
570572

573+
//| class DitherAlgorithm:
574+
//| """Identifies the algorith for dither to use"""
575+
//|
576+
//| Atkinson: object
577+
//| """The classic Atkinson dither, often associated with the Hypercard esthetic"""
578+
//|
579+
//| FloydStenberg: object
580+
//| """The Floyd-Stenberg dither"""
581+
//|
582+
MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, Atkinson, DITHER_ALGORITHM_ATKINSON);
583+
MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, FloydStenberg, DITHER_ALGORITHM_ATKINSON);
584+
585+
MAKE_ENUM_MAP(bitmaptools_dither_algorithm) {
586+
MAKE_ENUM_MAP_ENTRY(dither_algorithm, Atkinson),
587+
MAKE_ENUM_MAP_ENTRY(dither_algorithm, FloydStenberg),
588+
};
589+
STATIC MP_DEFINE_CONST_DICT(bitmaptools_dither_algorithm_locals_dict, bitmaptools_dither_algorithm_locals_table);
590+
591+
MAKE_PRINTER(bitmaptools, bitmaptools_dither_algorithm);
592+
593+
MAKE_ENUM_TYPE(bitmaptools, DitherAlgorithm, bitmaptools_dither_algorithm);
594+
595+
//| def dither(dest_bitmap: displayio.Bitmap, source_bitmapp: displayio.Bitmap, source_colorspace: displayio.Colorspace, algorithm: DitherAlgorithm=DitherAlgorithm.Atkinson) -> None:
596+
//| """Convert the input image into a 2-level output image using the given dither algorithm.
597+
//|
598+
//| :param bitmap dest_bitmap: Destination bitmap. It must have a value_count of 2 or 65536. The stored values are 0 and the maximum pixel value.
599+
//| :param bitmap source_bitmap: Source bitmap that contains the graphical region to be dithered. It must have a value_count of 65536.
600+
//| :param colorspace: The colorspace of the image. The supported colorspaces are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, and ``BGR565_SWAPPED``
601+
//| :param algorithm: The dither algorithm to use, one of the `DitherAlgorithm `values.
602+
//| """
603+
//| ...
604+
//|
605+
STATIC mp_obj_t bitmaptools_dither(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
606+
enum { ARG_dest_bitmap, ARG_source_bitmap, ARG_source_colorspace, ARG_algorithm };
607+
static const mp_arg_t allowed_args[] = {
608+
{ MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ },
609+
{ MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ },
610+
{ MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ },
611+
{ MP_QSTR_algorithm, MP_ARG_OBJ, { .u_obj = MP_ROM_PTR((void *)&dither_algorithm_Atkinson_obj) } },
612+
};
613+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
614+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
615+
displayio_bitmap_t *source_bitmap = mp_arg_validate_type(args[ARG_source_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap);
616+
displayio_bitmap_t *dest_bitmap = mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap);
617+
bitmaptools_dither_algorithm_t algorithm = cp_enum_value(&bitmaptools_dither_algorithm_type, args[ARG_algorithm].u_obj);
618+
displayio_colorspace_t colorspace = cp_enum_value(&displayio_colorspace_type, args[ARG_source_colorspace].u_obj);
619+
620+
if (source_bitmap->width != dest_bitmap->width || source_bitmap->height != dest_bitmap->height) {
621+
mp_raise_TypeError(translate("bitmap sizes must match"));
622+
}
623+
624+
if (dest_bitmap->bits_per_value != 16 && dest_bitmap->bits_per_value != 1) {
625+
mp_raise_TypeError(translate("source_bitmap must have value_count of 2 or 65536"));
626+
}
627+
628+
629+
switch (colorspace) {
630+
case DISPLAYIO_COLORSPACE_RGB565:
631+
case DISPLAYIO_COLORSPACE_RGB565_SWAPPED:
632+
case DISPLAYIO_COLORSPACE_BGR565:
633+
case DISPLAYIO_COLORSPACE_BGR565_SWAPPED:
634+
if (source_bitmap->bits_per_value != 16) {
635+
mp_raise_TypeError(translate("source_bitmap must have value_count of 65536"));
636+
}
637+
break;
638+
639+
case DISPLAYIO_COLORSPACE_L8:
640+
if (source_bitmap->bits_per_value != 8) {
641+
mp_raise_TypeError(translate("source_bitmap must have value_count of 8"));
642+
}
643+
break;
644+
645+
default:
646+
mp_raise_TypeError(translate("unsupported colorspace for dither"));
647+
}
648+
649+
650+
common_hal_bitmaptools_dither(dest_bitmap, source_bitmap, colorspace, algorithm);
651+
652+
return mp_const_none;
653+
}
654+
MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_dither_obj, 0, bitmaptools_dither);
655+
656+
571657
STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
572658
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitmaptools) },
573659
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&bitmaptools_readinto_obj) },
@@ -576,6 +662,8 @@ STATIC const mp_rom_map_elem_t bitmaptools_module_globals_table[] = {
576662
{ MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) },
577663
{ MP_ROM_QSTR(MP_QSTR_boundary_fill), MP_ROM_PTR(&bitmaptools_boundary_fill_obj) },
578664
{ MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) },
665+
{ MP_ROM_QSTR(MP_QSTR_dither), MP_ROM_PTR(&bitmaptools_dither_obj) },
666+
{ MP_ROM_QSTR(MP_QSTR_DitherAlgorithm), MP_ROM_PTR(&bitmaptools_dither_algorithm_type) },
579667
};
580668
STATIC MP_DEFINE_CONST_DICT(bitmaptools_module_globals, bitmaptools_module_globals_table);
581669

shared-bindings/bitmaptools/__init__.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@
2828
#define MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H
2929

3030
#include "shared-module/displayio/Bitmap.h"
31+
#include "shared-module/displayio/Palette.h"
32+
#include "shared-bindings/displayio/ColorConverter.h"
3133
#include "py/obj.h"
3234
#include "extmod/vfs_fat.h"
3335

36+
typedef enum {
37+
DITHER_ALGORITHM_ATKINSON, DITHER_ALGORITHM_FLOYD_STENBERG,
38+
} bitmaptools_dither_algorithm_t;
39+
40+
extern const mp_obj_type_t bitmaptools_dither_algorithm_type;
41+
3442
void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16_t oy,
3543
int16_t dest_clip0_x, int16_t dest_clip0_y,
3644
int16_t dest_clip1_x, int16_t dest_clip1_y,
@@ -57,5 +65,6 @@ void common_hal_bitmaptools_draw_line(displayio_bitmap_t *destination,
5765

5866
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, bool reverse_rows);
5967
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);
68+
void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bitmap_t *source_bitmap, displayio_colorspace_t colorspace, bitmaptools_dither_algorithm_t algorithm);
6069

6170
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BITMAPTOOLS__INIT__H

0 commit comments

Comments
 (0)