@@ -134,7 +134,7 @@ STATIC mp_obj_t bitmapfilter_morph(size_t n_args, const mp_obj_t *pos_args, mp_m
134
134
mp_obj_t weights = args [ARG_weights ].u_obj ;
135
135
mp_obj_t obj_len = mp_obj_len (weights );
136
136
if (obj_len == MP_OBJ_NULL || !mp_obj_is_small_int (obj_len )) {
137
- mp_raise_ValueError_varg (MP_ERROR_TEXT ("%q must be of type %q, not %q" ), MP_QSTR_weights , MP_QSTR_Sequence , mp_obj_get_type (weights )-> name );
137
+ mp_raise_ValueError_varg (MP_ERROR_TEXT ("%q must be of type %q, not %q" ), MP_QSTR_weights , MP_QSTR_Sequence , mp_obj_get_type_qstr (weights ));
138
138
}
139
139
140
140
size_t n_weights = MP_OBJ_SMALL_INT_VALUE (obj_len );
@@ -608,8 +608,131 @@ STATIC mp_obj_t bitmapfilter_false_color(size_t n_args, const mp_obj_t *pos_args
608
608
shared_module_bitmapfilter_false_color (bitmap , mask , palette -> colors );
609
609
return args [ARG_bitmap ].u_obj ;
610
610
}
611
-
612
611
MP_DEFINE_CONST_FUN_OBJ_KW (bitmapfilter_false_color_obj , 0 , bitmapfilter_false_color );
612
+
613
+ #define BLEND_TABLE_SIZE (4096)
614
+ STATIC uint8_t * get_blend_table (mp_obj_t lookup , int mode ) {
615
+ mp_buffer_info_t lookup_buf ;
616
+ if (!mp_get_buffer (lookup , & lookup_buf , mode ) || lookup_buf .len != BLEND_TABLE_SIZE ) {
617
+ return NULL ;
618
+ }
619
+ return lookup_buf .buf ;
620
+ }
621
+ //|
622
+ //| BlendFunction = Callable[[float, floa
F438
t], float]
623
+ //| """A function used to blend two images"""
624
+ //|
625
+ //| BlendTable = bytearray
626
+ //| """A precomputed blend table
627
+ //|
628
+ //| There is not actually a BlendTable type. The real type is actually any
629
+ //| buffer 4096 bytes in length."""
630
+ //|
631
+ //| def blend_precompute(lookup: BlendFunction, table: BlendTable | None = None) -> BlendTable:
632
+ //| """Precompute a BlendTable from a BlendFunction
633
+ //|
634
+ //| If the optional ``table`` argument is provided, an existing `BlendTable` is updated
635
+ //| with the new function values.
636
+ //|
637
+ //| The function's two arguments will range from 0 to 1. The returned value should also range from 0 to 1.
638
+ //|
639
+ //| A function to do a 33% blend of each source image could look like this:
640
+ //|
641
+ //| .. code-block:: python
642
+ //|
643
+ //| def blend_one_third(a, b):
644
+ //| return a * .33 + b * .67
645
+ //| """
646
+ //|
647
+ STATIC mp_obj_t blend_precompute (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
648
+ enum { ARG_lookup , ARG_table };
649
+ static const mp_arg_t allowed_args [] = {
650
+ { MP_QSTR_lookup , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
651
+ { MP_QSTR_table , MP_ARG_OBJ , { .u_obj = MP_ROM_NONE } },
652
+ };
653
+ mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
654
+ mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
655
+
656
+ mp_obj_t table = args [ARG_table ].u_obj ;
657
+ if (table == mp_const_none ) {
658
+ table = mp_obj_new_bytearray_of_zeros (BLEND_TABLE_SIZE );
659
+ }
660
+ uint8_t * buf = get_blend_table (table , MP_BUFFER_WRITE );
661
+ if (!buf ) {
662
+ mp_raise_TypeError_varg (MP_ERROR_TEXT ("%q must be of type %q or %q, not %q" ),
663
+ MP_QSTR_table , MP_QSTR_NoneType , MP_QSTR_WritableBuffer ,
664
+ mp_obj_get_type_qs
10000
tr (table ));
665
+ }
666
+ shared_module_bitmapfilter_blend_precompute (args [ARG_lookup ].u_obj , buf );
667
+ return table ;
668
+ }
669
+ MP_DEFINE_CONST_FUN_OBJ_KW (bitmapfilter_blend_precompute_obj , 0 , blend_precompute );
670
+
671
+ //|
672
+ //| def blend(
673
+ //| dest: displayio.Bitmap,
674
+ //| src1: displayio.Bitmap,
675
+ //| src2: displayio.Bitmap,
676
+ //| lookup: BlendFunction | BlendTable,
677
+ //| mask: displayio.Bitmap | None = None,
678
+ //| ) -> displayio.Bitmap:
679
+ //| """Blend the 'src1' and 'src2' images according to lookup function or table 'lookup'
680
+ //|
681
+ //| If ``lookup`` is a function, it is converted to a `BlendTable` by
682
+ //| internally calling blend_precompute. If a blend function is used repeatedly
683
+ //| it can be more efficient to compute it once with `blend_precompute`.
684
+ //|
685
+ //| If the mask is supplied, pixels from ``src1`` are taken unchanged in masked areas.
686
+ //|
687
+ //| The source and destination bitmaps may be the same bitmap.
688
+ //|
689
+ //| The destination bitmap is returned.
690
+ //| """
691
+ //|
692
+
693
+ STATIC mp_obj_t bitmapfilter_blend (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
694
+ enum { ARG_dest , ARG_src1 , ARG_src2 , ARG_lookup , ARG_mask };
695
+ static const mp_arg_t allowed_args [] = {
696
+ { MP_QSTR_dest , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
697
+ { MP_QSTR_src1 , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
698
+ { MP_QSTR_src2 , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
699
+ { MP_QSTR_lookup , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
700
+ { MP_QSTR_mask , MP_ARG_OBJ , { .u_obj = MP_ROM_NONE } },
701
+ };
702
+ mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
703
+ mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
704
+
705
+ mp_arg_validate_type (args [ARG_dest ].u_obj , & displayio_bitmap_type , MP_QSTR_dest );
706
+ displayio_bitmap_t * dest = MP_OBJ_TO_PTR (args [ARG_dest ].u_obj );
707
+
708
+ mp_arg_validate_type (args [ARG_src1 ].u_obj , & displayio_bitmap_type , MP_QSTR_src1 );
709
+ displayio_bitmap_t * src1 = MP_OBJ_TO_PTR (args [ARG_src1 ].u_obj );
710
+
711
+ mp_arg_validate_type (args [ARG_src2 ].u_obj , & displayio_bitmap_type , MP_QSTR_src2 );
712
+ displayio_bitmap_t * src2 = MP_OBJ_TO_PTR (args [ARG_src2 ].u_obj );
713
+
714
+ mp_obj_t lookup = args [ARG_lookup ].u_obj ;
715
+ if (mp_obj_is_callable (lookup )) {
716
+ lookup = mp_call_function_1 (MP_OBJ_FROM_PTR (& bitmapfilter_blend_precompute_obj ), lookup );
717
+ }
718
+ uint8_t * lookup_buf = get_blend_table (lookup , MP_BUFFER_READ );
719
+ if (!lookup_buf ) {
720
+ mp_raise_TypeError_varg (MP_ERROR_TEXT ("%q must be of type %q or %q, not %q" ),
721
+ MP_QSTR_lookup , MP_QSTR_callable , MP_QSTR_ReadableBuffer ,
722
+ mp_obj_get_type_qstr (lookup ));
723
+ }
724
+
725
+ displayio_bitmap_t * mask = NULL ;
726
+ if (args [ARG_mask ].u_obj != mp_const_none ) {
727
+ mp_arg_validate_type (args [ARG_mask ].u_obj , & displayio_bitmap_type , MP_QSTR_mask );
728
+ mask = MP_OBJ_TO_PTR (args [ARG_mask ].u_obj );
729
+ }
730
+
731
+ shared_module_bitmapfilter_blend (dest , src1 , src2 , mask , lookup_buf );
732
+ return args [ARG_dest ].u_obj ;
733
+ }
734
+ MP_DEFINE_CONST_FUN_OBJ_KW (bitmapfilter_blend_obj , 0 , bitmapfilter_blend );
735
+
613
736
STATIC const mp_rom_map_elem_t bitmapfilter_module_globals_table [] = {
614
737
{ MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_bitmapfilter ) },
615
738
{ MP_ROM_QSTR (MP_QSTR_morph ), MP_ROM_PTR (& bitmapfilter_morph_obj ) },
@@ -621,6 +744,8 @@ STATIC const mp_rom_map_elem_t bitmapfilter_module_globals_table[] = {
621
744
{ MP_ROM_QSTR (MP_QSTR_ChannelScaleOffset ), MP_ROM_PTR (& bitmapfilter_channel_scale_offset_type ) },
622
745
{ MP_ROM_QSTR (MP_QSTR_ChannelMixer ), MP_ROM_PTR (& bitmapfilter_channel_mixer_type ) },
623
746
{ MP_ROM_QSTR (MP_QSTR_ChannelMixerOffset ), MP_ROM_PTR (& bitmapfilter_channel_mixer_offset_type ) },
747
+ { MP_ROM_QSTR (MP_QSTR_blend ), MP_ROM_PTR (& bitmapfilter_blend_obj ) },
748
+ { MP_ROM_QSTR (MP_QSTR_blend_precompute ), MP_ROM_PTR (& bitmapfilter_blend_precompute_obj ) },
624
749
};
625
750
STATIC MP_DEFINE_CONST_DICT (bitmapfilter_module_globals , bitmapfilter_module_globals_table );
626
751
0 commit comments