Skip to content

Commit eb2e431

Browse files
authored
Merge pull request micropython#11027 from makermelissa-piclaw/alphablend-mask
bitmaptools.alphablend: add optional L8 mask= kwarg
2 parents 6b4970b + 8cabd2a commit eb2e431

4 files changed

Lines changed: 65 additions & 7 deletions

File tree

locale/circuitpython.pot

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,14 @@ msgstr ""
14411441
msgid "Mapping must be a tuple"
14421442
msgstr ""
14431443

1444+
#: shared-bindings/bitmaptools/__init__.c
1445+
msgid "Mask bitmap must have 8 bits per pixel"
1446+
msgstr ""
1447+
1448+
#: shared-bindings/bitmaptools/__init__.c
1449+
msgid "Mask bitmap size must match the other bitmaps"
1450+
msgstr ""
1451+
14441452
#: py/persistentcode.c
14451453
msgid "MicroPython .mpy file; use CircuitPython mpy-cross"
14461454
msgstr ""

shared-bindings/bitmaptools/__init__.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ MAKE_ENUM_TYPE(bitmaptools, BlendMode, bitmaptools_blendmode);
311311
//| blendmode: Optional[BlendMode] = BlendMode.Normal,
312312
//| skip_source1_index: Union[int, None] = None,
313313
//| skip_source2_index: Union[int, None] = None,
314+
//| mask: Optional[displayio.Bitmap] = None,
314315
//| ) -> None:
315316
//| """Alpha blend the two source bitmaps into the destination.
316317
//|
@@ -326,14 +327,19 @@ MAKE_ENUM_TYPE(bitmaptools, BlendMode, bitmaptools_blendmode);
326327
//| :param bitmaptools.BlendMode blendmode: The blend mode to use. Default is Normal.
327328
//| :param int skip_source1_index: Bitmap palette or luminance index in source_bitmap_1 that will not be blended, set to None to blend all pixels
328329
//| :param int skip_source2_index: Bitmap palette or luminance index in source_bitmap_2 that will not be blended, set to None to blend all pixels
330+
//| :param displayio.Bitmap mask: Optional 8-bits-per-value grayscale mask bitmap controlling per-pixel opacity of ``source_bitmap_2``.
331+
//| The mask must have the same width and height as the other bitmaps and a ``bits_per_value`` of 8. A mask value of 0
332+
//| means ``source_bitmap_2`` is fully transparent at that pixel (only ``source_bitmap_1`` contributes); a value of 255 means
333+
//| ``source_bitmap_2`` is fully opaque at that pixel (subject to ``factor2``). Intermediate values scale ``factor2`` linearly.
334+
//| Pass ``None`` to disable per-pixel masking.
329335
//|
330336
//| For the L8 colorspace, the bitmaps must have a bits-per-value of 8.
331337
//| For the RGB colorspaces, they must have a bits-per-value of 16."""
332338
//|
333339
//|
334340

335341
static mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
336-
enum {ARG_dest_bitmap, ARG_source_bitmap_1, ARG_source_bitmap_2, ARG_colorspace, ARG_factor_1, ARG_factor_2, ARG_blendmode, ARG_skip_source1_index, ARG_skip_source2_index};
342+
enum {ARG_dest_bitmap, ARG_source_bitmap_1, ARG_source_bitmap_2, ARG_colorspace, ARG_factor_1, ARG_factor_2, ARG_blendmode, ARG_skip_source1_index, ARG_skip_source2_index, ARG_mask};
337343

338344
static const mp_arg_t allowed_args[] = {
339345
{MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = NULL}},
@@ -345,6 +351,7 @@ static mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args,
345351
{MP_QSTR_blendmode, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = (void *)&bitmaptools_blendmode_Normal_obj}},
346352
{MP_QSTR_skip_source1_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
347353
{MP_QSTR_skip_source2_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
354+
{MP_QSTR_mask, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} },
348355
};
349356
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
350357
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
@@ -411,8 +418,19 @@ static mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args,
411418
skip_source2_index_none = false;
412419
}
413420

421+
displayio_bitmap_t *mask = NULL;
422+
if (args[ARG_mask].u_obj != mp_const_none) {
423+
mask = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_mask].u_obj, &displayio_bitmap_type, MP_QSTR_mask));
424+
if (mask->width != destination->width || mask->height != destination->height) {
425+
mp_raise_ValueError(MP_ERROR_TEXT("Mask bitmap size must match the other bitmaps"));
426+
}
427+
if (mask->bits_per_value != 8) {
428+
mp_raise_ValueError(MP_ERROR_TEXT("Mask bitmap must have 8 bits per pixel"));
429+
}
430+
}
431+
414432
common_hal_bitmaptools_alphablend(destination, source1, source2, colorspace, factor1, factor2, blendmode, skip_source1_index,
415-
skip_source1_index_none, skip_source2_index, skip_source2_index_none);
433+
skip_source1_index_none, skip_source2_index, skip_source2_index_none, mask);
416434

417435
return mp_const_none;
418436
}

shared-bindings/bitmaptools/__init__.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ void common_hal_bitmaptools_arrayblit(displayio_bitmap_t *self, void *data, int
6969
void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bitmap_t *source_bitmap, displayio_colorspace_t colorspace, bitmaptools_dither_algorithm_t algorithm);
7070

7171
void common_hal_bitmaptools_alphablend(displayio_bitmap_t *destination, displayio_bitmap_t *source1, displayio_bitmap_t *source2, displayio_colorspace_t colorspace, mp_float_t factor1, mp_float_t factor2,
72-
bitmaptools_blendmode_t blendmode, uint32_t skip_source1_index, bool skip_source1_index_none, uint32_t skip_source2_index, bool skip_source2_index_none);
72+
bitmaptools_blendmode_t blendmode, uint32_t skip_source1_index, bool skip_source1_index_none, uint32_t skip_source2_index, bool skip_source2_index_none,
73+
displayio_bitmap_t *mask);
7374

7475
typedef struct {
7576
union {

shared-module/bitmaptools/__init__.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -857,23 +857,36 @@ void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bi
857857
}
858858

859859
void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitmap_t *source1, displayio_bitmap_t *source2, displayio_colorspace_t colorspace, mp_float_t factor1, mp_float_t factor2,
860-
bitmaptools_blendmode_t blendmode, uint32_t skip_source1_index, bool skip_source1_index_none, uint32_t skip_source2_index, bool skip_source2_index_none) {
860+
bitmaptools_blendmode_t blendmode, uint32_t skip_source1_index, bool skip_source1_index_none, uint32_t skip_source2_index, bool skip_source2_index_none,
861+
displayio_bitmap_t *mask) {
861862
displayio_area_t a = {0, 0, dest->width, dest->height, NULL};
862863
displayio_bitmap_set_dirty_area(dest, &a);
863864

864-
int ifactor1 = (int)(factor1 * 256);
865-
int ifactor2 = (int)(factor2 * 256);
865+
int ifactor1_base = (int)(factor1 * 256);
866+
int ifactor2_base = (int)(factor2 * 256);
867+
int ifactor1 = ifactor1_base;
868+
int ifactor2 = ifactor2_base;
866869
bool blend_source1, blend_source2;
867870

868871
if (colorspace == DISPLAYIO_COLORSPACE_L8) {
869872
for (int y = 0; y < dest->height; y++) {
870873
uint8_t *dptr = (uint8_t *)(dest->data + y * dest->stride);
871874
uint8_t *sptr1 = (uint8_t *)(source1->data + y * source1->stride);
872875
uint8_t *sptr2 = (uint8_t *)(source2->data + y * source2->stride);
876+
uint8_t *mptr = mask ? (uint8_t *)(mask->data + y * mask->stride) : NULL;
873877
int pixel;
874878
for (int x = 0; x < dest->width; x++) {
875879
blend_source1 = skip_source1_index_none || *sptr1 != (uint8_t)skip_source1_index;
876880
blend_source2 = skip_source2_index_none || *sptr2 != (uint8_t)skip_source2_index;
881+
if (mptr) {
882+
uint8_t m = *mptr;
883+
// Scale source2's contribution by the mask (0..255)
884+
ifactor2 = (ifactor2_base * m + 127) / 255;
885+
if (m == 0) {
886+
// Mask says fully transparent: drop source2 entirely
887+
blend_source2 = false;
888+
}
889+
}
877890
if (blend_source1 && blend_source2) {
878891
// Premultiply by the alpha factor
879892
int sda = *sptr1++ *ifactor1;
@@ -886,7 +899,8 @@ void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitma
886899
blend = sca + sda * (256 - ifactor2) / 256;
887900
}
888901
// Divide by the alpha factor
889-
pixel = (blend / (ifactor1 + ifactor2 - ifactor1 * ifactor2 / 256));
902+
int denom = ifactor1 + ifactor2 - ifactor1 * ifactor2 / 256;
903+
pixel = (denom > 0) ? (blend / denom) : 0;
890904
} else if (blend_source1) {
891905
// Apply iFactor1 to source1 only
892906
pixel = *sptr1++ *ifactor1 / 256;
@@ -898,6 +912,9 @@ void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitma
898912
pixel = *dptr;
899913
}
900914
*dptr++ = MIN(255, MAX(0, pixel));
915+
if (mptr) {
916+
mptr++;
917+
}
901918
}
902919
}
903920
} else {
@@ -907,6 +924,7 @@ void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitma
907924
uint16_t *dptr = (uint16_t *)(dest->data + y * dest->stride);
908925
uint16_t *sptr1 = (uint16_t *)(source1->data + y * source1->stride);
909926
uint16_t *sptr2 = (uint16_t *)(source2->data + y * source2->stride);
927+
uint8_t *mptr = mask ? (uint8_t *)(mask->data + y * mask->stride) : NULL;
910928
for (int x = 0; x < dest->width; x++) {
911929
int spix1 = *sptr1++;
912930
int spix2 = *sptr2++;
@@ -922,11 +940,24 @@ void common_hal_bitmaptools_alphablend(displayio_bitmap_t *dest, displayio_bitma
922940
blend_source1 = skip_source1_index_none || spix1 != (int)skip_source1_index;
923941
blend_source2 = skip_source2_index_none || spix2 != (int)skip_source2_index;
924942

943+
if (mptr) {
944+
uint8_t m = *mptr++;
945+
ifactor2 = (ifactor2_base * m + 127) / 255;
946+
if (m == 0) {
947+
blend_source2 = false;
948+
}
949+
}
950+
925951
if (blend_source1 && blend_source2) {
926952
// Blend based on the SVG alpha compositing specs
927953
// https://dev.w3.org/SVG/modules/compositing/master/#alphaCompositing
928954

929955
int ifactor_blend = ifactor1 + ifactor2 - ifactor1 * ifactor2 / 256;
956+
if (ifactor_blend <= 0) {
957+
// Both factors are zero at this pixel; keep destination.
958+
dptr++;
959+
continue;
960+
}
930961

931962
// Premultiply the colors by the alpha factor
932963
int red_dca = ((spix1 & r_mask) >> 8) * ifactor1;

0 commit comments

Comments
 (0)