gifio: Add dithered output
It's not a great dither, but we're low on CPU time sooo
This commit is contained in:
parent
dc00226143
commit
ef4623dfae
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -41,4 +41,5 @@ typedef struct gifio_gifwriter {
|
|||
size_t cur, size;
|
||||
bool own_file;
|
||||
bool byteswap;
|
||||
bool dither;
|
||||
} gifio_gifwriter_t;
|
||||
|
|
Loading…
Reference in New Issue