Skip to content

Commit ff22baa

Browse files
committed
Add lookup table (LUT)
1 parent 415432e commit ff22baa

6 files changed

Lines changed: 16342 additions & 0 deletions

File tree

shared-bindings/bitmapfilter/__init__.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,90 @@ STATIC mp_obj_t bitmapfilter_solarize(size_t n_args, const mp_obj_t *pos_args, m
269269

270270
MP_DEFINE_CONST_FUN_OBJ_KW(bitmapfilter_solarize_obj, 0, bitmapfilter_solarize);
271271

272+
273+
//| LookupFunction = Callable[[float], float]
274+
//| ThreeLookupFunctions = Tuple[LookupFunction, LookupFunction, LookupFunction]
275+
//|
276+
//| def lookup(
277+
//| bitmap: displayio.Bitmap,
278+
//| lookup: LookupFunction | ThreeLookupFunctions,
279+
//| lookup_g: LookupFunction,
280+
//| lookup_b: LookupFunction,
281+
//| mask: displayio.Bitmap | None,
282+
//| ) -> displayio.Bitmap:
283+
//| """Modify the channels of a bitmap according to a look-up table
284+
//|
285+
//| This can be used to implement non-linear transformations of color values,
286+
//| such as gamma curves.
287+
//|
288+
//| The ``bitmap``, which must be in RGB565_SWAPPED format, is modified
289+
//| according to the values in the ``table``s.
290+
//|
291+
//| Each lookup function is called for each possible channel value from 0 to 1
292+
//| inclusive (64 times for green, 32 times for red or blue), and the return
293+
//| value (also from 0 to 1) is used whenever that color value is returned.
294+
//| """
295+
//|
296+
297+
STATIC int scaled_lut(int maxval, mp_obj_t func, int i) {
298+
mp_obj_t obj = mp_call_function_1(func, mp_obj_new_float(i / (mp_float_t)maxval));
299+
mp_float_t val = mp_obj_get_float(obj);
300+
return (int)MICROPY_FLOAT_C_FUN(round)(val * maxval);
301+
}
302+
303+
STATIC mp_obj_t bitmapfilter_lookup(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
304+
enum { ARG_bitmap, ARG_lookup, ARG_mask };
305+
static const mp_arg_t allowed_args[] = {
306+
{ MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
307+
{ MP_QSTR_lookup, MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
308+
{ MP_QSTR_mask, MP_ARG_OBJ, { .u_obj = MP_ROM_NONE } },
309+
};
310+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
311+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
312+
313+
mp_arg_validate_type(args[ARG_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_bitmap);
314+
displayio_bitmap_t *bitmap = MP_OBJ_TO_PTR(args[ARG_bitmap].u_obj);
315+
316+
mp_obj_t lookup_r, lookup_g, lookup_b;
317+
318+
if (mp_obj_is_tuple_compatible(args[ARG_lookup].u_obj)) {
319+
mp_obj_tuple_t *lookup_tuple = MP_OBJ_TO_PTR(args[ARG_lookup].u_obj);
320+
mp_arg_validate_length(lookup_tuple->len, 3, MP_QSTR_lookup);
321+
lookup_r = lookup_tuple->items[0];
322+
lookup_g = lookup_tuple->items[1];
323+
lookup_b = lookup_tuple->items[2];
324+
} else {
325+
lookup_r = lookup_g = lookup_b = args[ARG_lookup].u_obj;
326+
}
327+
328+
bitmapfilter_lookup_table_t table;
329+
330+
for (int i = 0; i < 32; i++) {
331+
table.r[i] = scaled_lut(31, lookup_r, i);
332+
table.b[i] = lookup_r == lookup_b ? table.r[i] : scaled_lut(31, lookup_b, i);
333+
}
334+
for (int i = 0; i < 64; i++) {
335+
table.g[i] = scaled_lut(63, lookup_g, i);
336+
}
337+
338+
displayio_bitmap_t *mask = NULL;
339+
if (args[ARG_mask].u_obj != mp_const_none) {
340+
mp_arg_validate_type(args[ARG_mask].u_obj, &displayio_bitmap_type, MP_QSTR_mask);
341+
mask = MP_OBJ_TO_PTR(args[ARG_mask].u_obj);
342+
}
343+
344+
shared_module_bitmapfilter_lookup(bitmap, mask, &table);
345+
return args[ARG_bitmap].u_obj;
346+
}
347+
348+
MP_DEFINE_CONST_FUN_OBJ_KW(bitmapfilter_lookup_obj, 0, bitmapfilter_lookup);
349+
272350
STATIC const mp_rom_map_elem_t bitmapfilter_module_globals_table[] = {
273351
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bitmapfilter) },
274352
{ MP_ROM_QSTR(MP_QSTR_morph), MP_ROM_PTR(&bitmapfilter_morph_obj) },
275353
{ MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&bitmapfilter_mix_obj) },
276354
{ MP_ROM_QSTR(MP_QSTR_solarize), MP_ROM_PTR(&bitmapfilter_solarize_obj) },
355+
{ MP_ROM_QSTR(MP_QSTR_lookup), MP_ROM_PTR(&bitmapfilter_lookup_obj) },
277356
};
278357
STATIC MP_DEFINE_CONST_DICT(bitmapfilter_module_globals, bitmapfilter_module_globals_table);
279358

shared-bindings/bitmapfilter/__init__.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ void shared_module_bitmapfilter_solarize(
4848
displayio_bitmap_t *bitmap,
4949
displayio_bitmap_t *mask,
5050
const mp_float_t threshold);
51+
52+
typedef struct {
53+
uint8_t r[32], g[64], b[32];
54+
} bitmapfilter_lookup_table_t;
55+
56+
void shared_module_bitmapfilter_lookup(
57+
displayio_bitmap_t *bitmap,
58+
displayio_bitmap_t *mask,
59+
const bitmapfilter_lookup_table_t *table);

shared-module/bitmapfilter/__init__.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,3 +388,36 @@ void shared_module_bitmapfilter_solarize(
388388
}
389389
}
390390
}
391+
392+
void shared_module_bitmapfilter_lookup(
393+
displayio_bitmap_t *bitmap,
394+
displayio_bitmap_t *mask,
395+
const bitmapfilter_lookup_table_t *table) {
396+
397+
switch (bitmap->bits_per_value) {
398+
default:
399+
mp_raise_ValueError(MP_ERROR_TEXT("unsupported bitmap depth"));
400+
case 16: {
401+
for (int y = 0, yy = bitmap->height; y < yy; y++) {
402+
uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(bitmap, y);
403+
for (int x = 0, xx = bitmap->width; x < xx; x++) {
404+
if (mask && common_hal_displayio_bitmap_get_pixel(mask, x, y)) {
405+
continue; // Short circuit.
406+
}
407+
int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x);
408+
int r = COLOR_RGB565_TO_R5(pixel);
409+
int g = COLOR_RGB565_TO_G6(pixel);
410+
int b = COLOR_RGB565_TO_B5(pixel);
411+
412+
r = table->r[r];
413+
g = table->g[g];
414+
b = table->b[b];
415+
416+
pixel = COLOR_R5_G6_B5_TO_RGB565(r, g, b);
417+
IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, pixel);
418+
}
419+
}
420+
break;
421+
}
422+
}
423+
}

0 commit comments

Comments
 (0)