diff --git a/shared-bindings/gifio/GifWriter.c b/shared-bindings/gifio/GifWriter.c index 6131550bf4..11dd43becd 100644 --- a/shared-bindings/gifio/GifWriter.c +++ b/shared-bindings/gifio/GifWriter.c @@ -34,24 +34,27 @@ #include "shared/runtime/context_manager_helpers.h" //| class GifWriter: -//| def __init__(self, file: Union[typing.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True) -> None: +//| def __init__(self, file: Union[typing.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True, dither:bool=False) -> None: //| """Construct a GifWriter object //| //| :param file: Either a file open in bytes mode, or the name of a file to open in bytes mode. //| :param width: The width of the image. All frames must have the same width. //| :param height: The height of the image. All frames must have the same height. -//| :param colorspace: The colorspace of the image. All frames must have the same colorspace. Only 1- and 2-byte colorspace are supported, not ``RGB888``. +//| :param colorspace: The colorspace of the image. All frames must have the same colorspace. The supported colorspaces are ``RGB565``, ``BGR565``, ``RGB565_SWAPPED``, ``BGR565_SWAPPED``, and ``L8`` (greyscale) +//| :param loop: If True, the GIF is marked for looping playback +//| :param dither: If True, and the image is in color, a simple ordered dither is applied. //| """ //| ... //| static mp_obj_t gifio_gifwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop }; + enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop, ARG_dither }; static const mp_arg_t allowed_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} }, { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} }, { MP_QSTR_colorspace, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} }, { MP_QSTR_loop, MP_ARG_BOOL, { .u_bool = true } }, + { MP_QSTR_dither, MP_ARG_BOOL, { .u_bool = false } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -72,6 +75,7 @@ static mp_obj_t gifio_gifwriter_make_new(const mp_obj_type_t *type, size_t n_arg args[ARG_height].u_int, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_colorspace].u_obj), args[ARG_loop].u_bool, + args[ARG_dither].u_bool, own_file); return self; diff --git a/shared-bindings/gifio/GifWriter.h b/shared-bindings/gifio/GifWriter.h index 203169cd75..601bd78679 100644 --- a/shared-bindings/gifio/GifWriter.h +++ b/shared-bindings/gifio/GifWriter.h @@ -33,7 +33,7 @@ typedef enum displayio_colorspace displayio_colorspace_t; extern const mp_obj_type_t gifio_gifwriter_type; -void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool own_file); +void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool dither, bool own_file); void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self); bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self); void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self); diff --git a/shared-module/gifio/GifWriter.c b/shared-module/gifio/GifWriter.c index 5c3dbbfa24..608e522517 100644 --- a/shared-module/gifio/GifWriter.c +++ b/shared-module/gifio/GifWriter.c @@ -76,7 +76,7 @@ static void write_word(gifio_gifwriter_t *self, uint16_t value) { write_data(self, &value, sizeof(value)); } -void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool own_file) { +void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *file, int width, int height, displayio_colorspace_t colorspace, bool loop, bool dither, bool own_file) { self->file = file; self->file_proto = mp_proto_get_or_throw(MP_QSTR_protocol_stream, file); if (self->file_proto->is_text) { @@ -85,6 +85,7 @@ void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t * self->width = width; self->height = height; self->colorspace = colorspace; + self->dither = dither; self->own_file = own_file; size_t nblocks = (width * height + 125) / 126; @@ -159,6 +160,20 @@ void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self) { } } +static const uint8_t rb_bayer[4][4] = { + { 0, 33, 8, 42}, + {50, 16, 58, 25}, + {12, 46, 4, 37}, + {63, 29, 54, 21} +}; + +static const uint8_t g_bayer[4][4] = { + { 0, 16, 4, 20}, + {24, 8, 28, 12}, + { 6, 22, 2, 18}, + {31, 14, 26, 10} +}; + void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *bufinfo, int16_t delay) { if (delay) { write_data(self, (uint8_t []) {'!', 0xF9, 0x04, 0x04}, 4); @@ -191,7 +206,7 @@ void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_b *data++ = (*pixels++) >> 1; } } - } else { + } else if (!self->dither) { mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false); uint16_t *pixels = bufinfo->buf; @@ -212,6 +227,32 @@ void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_b *data++ = (red << 5) | (green << 2) | blue; } } + } else { + mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false); + + uint16_t *pixels = bufinfo->buf; + for (int i = 0; i < blocks; i++) { + int block_size = MIN(BLOCK_SIZE, pixel_count); + pixel_count -= block_size; + + *data++ = 1 + block_size; + *data++ = 0x80; + for (int j = 0; j < block_size; j++) { + int pixel = *pixels++; + if (self->byteswap) { + pixel = __builtin_bswap16(pixel); + } + int red = (pixel >> 8) & 0xf8; + int green = (pixel >> 3) & 0xfc; + int blue = (pixel << 3) & 0xf8; + + red = MAX(0, red - rb_bayer[i % 4][j % 4]); + green = MAX(0, green - g_bayer[i % 4][j % 4]); + blue = MAX(0, blue - rb_bayer[i % 4][j % 4]); + + *data++ = ((red >> 1) & 0x60) | ((green >> 3) & 0x1c) | (blue >> 6); + } + } } self->cur = data - self->data; diff --git a/shared-module/gifio/GifWriter.h b/shared-module/gifio/GifWriter.h index e6676a84e0..5ed57bb022 100644 --- a/shared-module/gifio/GifWriter.h +++ b/shared-module/gifio/GifWriter.h @@ -41,4 +41,5 @@ typedef struct gifio_gifwriter { size_t cur, size; bool own_file; bool byteswap; + bool dither; } gifio_gifwriter_t;