gifio: Add dithered output

It's not a great dither, but we're low on CPU time sooo
This commit is contained in:
Jeff Epler 2021-10-27 09:37:47 -05:00
parent dc00226143
commit ef4623dfae
4 changed files with 52 additions and 6 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -41,4 +41,5 @@ typedef struct gifio_gifwriter {
size_t cur, size;
bool own_file;
bool byteswap;
bool dither;
} gifio_gifwriter_t;