Skip to content

Commit ef4623d

Browse files
committed
gifio: Add dithered output
It's not a great dither, but we're low on CPU time sooo
1 parent dc00226 commit ef4623d

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed

shared-bindings/gifio/GifWriter.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,27 @@
3434
#include "shared/runtime/context_manager_helpers.h"
3535

3636
//| class GifWriter:
37-
//| def __init__(self, file: Union[typing.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True) -> None:
37+
//| def __init__(self, file: Union[typing.BinaryIO, str], width:int, height:int, colorspace: displayio.Colorspace, loop:bool=True, dither:bool=False) -> None:
3838
//| """Construct a GifWriter object
3939
//|
4040
//| :param file: Either a file open in bytes mode, or the name of a file to open in bytes mode.
4141
//| :param width: The width of the image. All frames must have the same width.
4242
//| :param height: The height of the image. All frames must have the same height.
43-
//| :param colorspace: The colorspace of the image. All frames must have the same colorspace. Only 1- and 2-byte colorspace are supported, not ``RGB888``.
43+
//| :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)
44+
//| :param loop: If True, the GIF is marked for looping playback
45+
//| :param dither: If True, and the image is in color, a simple ordered dither is applied.
4446
//| """
4547
//| ...
4648
//|
4749
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) {
48-
enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop };
50+
enum { ARG_file, ARG_width, ARG_height, ARG_colorspace, ARG_loop, ARG_dither };
4951
static const mp_arg_t allowed_args[] = {
5052
{ MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} },
5153
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
5254
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED, {.u_int = 0} },
5355
{ MP_QSTR_colorspace, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL} },
5456
{ MP_QSTR_loop, MP_ARG_BOOL, { .u_bool = true } },
57+
{ MP_QSTR_dither, MP_ARG_BOOL, { .u_bool = false } },
5558
};
5659
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
5760
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
7275
args[ARG_height].u_int,
7376
(displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_colorspace].u_obj),
7477
args[ARG_loop].u_bool,
78+
args[ARG_dither].u_bool,
7579
own_file);
7680

7781
return self;

shared-bindings/gifio/GifWriter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ typedef enum displayio_colorspace displayio_colorspace_t;
3333

3434
extern const mp_obj_type_t gifio_gifwriter_type;
3535

36-
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);
36+
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);
3737
void shared_module_gifio_gifwriter_check_for_deinit(gifio_gifwriter_t *self);
3838
bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self);
3939
void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self);

shared-module/gifio/GifWriter.c

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static void write_word(gifio_gifwriter_t *self, uint16_t value) {
7676
write_data(self, &value, sizeof(value));
7777
}
7878

79-
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) {
79+
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) {
8080
self->file = file;
8181
self->file_proto = mp_proto_get_or_throw(MP_QSTR_protocol_stream, file);
8282
if (self->file_proto->is_text) {
@@ -85,6 +85,7 @@ void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *
8585
self->width = width;
8686
self->height = height;
8787
self->colorspace = colorspace;
88+
self->dither = dither;
8889
self->own_file = own_file;
8990

9091
size_t nblocks = (width * height + 125) / 126;
@@ -159,6 +160,20 @@ void shared_module_gifio_gifwriter_deinit(gifio_gifwriter_t *self) {
159160
}
160161
}
161162

163+
static const uint8_t rb_bayer[4][4] = {
164+
{ 0, 33, 8, 42},
165+
{50, 16, 58, 25},
166+
{12, 46, 4, 37},
167+
{63, 29, 54, 21}
168+
};
169+
170+
static const uint8_t g_bayer[4][4] = {
171+
{ 0, 16, 4, 20},
172+
{24, 8, 28, 12},
173+
{ 6, 22, 2, 18},
174+
{31, 14, 26, 10}
175+
};
176+
162177
void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_buffer_info_t *bufinfo, int16_t delay) {
163178
if (delay) {
164179
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
191206
*data++ = (*pixels++) >> 1;
192207
}
193208
}
194-
} else {
209+
} else if (!self->dither) {
195210
mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false);
196211

197212
uint16_t *pixels = bufinfo->buf;
@@ -212,6 +227,32 @@ void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_b
212227
*data++ = (red << 5) | (green << 2) | blue;
213228
}
214229
}
230+
} else {
231+
mp_get_index(&mp_type_memoryview, bufinfo->len, MP_OBJ_NEW_SMALL_INT(2 * pixel_count - 1), false);
232+
233+
uint16_t *pixels = bufinfo->buf;
234+
for (int i = 0; i < blocks; i++) {
235+
int block_size = MIN(BLOCK_SIZE, pixel_count);
236+
pixel_count -= block_size;
237+
238+
*data++ = 1 + block_size;
239+
*data++ = 0x80;
240+
for (int j = 0; j < block_size; j++) {
241+
int pixel = *pixels++;
242+
if (self->byteswap) {
243+
pixel = __builtin_bswap16(pixel);
244+
}
245+
int red = (pixel >> 8) & 0xf8;
246+
int green = (pixel >> 3) & 0xfc;
247+
int blue = (pixel << 3) & 0xf8;
248+
249+
red = MAX(0, red - rb_bayer[i % 4][j % 4]);
250+
green = MAX(0, green - g_bayer[i % 4][j % 4]);
251+
blue = MAX(0, blue - rb_bayer[i % 4][j % 4]);
252+
253+
*data++ = ((red >> 1) & 0x60) | ((green >> 3) & 0x1c) | (blue >> 6);
254+
}
255+
}
215256
}
216257

217258
self->cur = data - self->data;

shared-module/gifio/GifWriter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ typedef struct gifio_gifwriter {
4141
size_t cur, size;
4242
bool own_file;
4343
bool byteswap;
44+
bool dither;
4445
} gifio_gifwriter_t;

0 commit comments

Comments
 (0)