Skip to content

Commit 7d6ac96

Browse files
committed
GifWriter: improve efficiency
* Increase colorspace conversion efficiency. This not only avoids a function call, it avoids the time-consuming switch statement in conver_pixel (replacing it with a single conditional on the byteswap flag + accounting for BGR/RGB during palette creation) * Buffer all the bytes of a single frame together. By reducing low level write calls we get a decent speed increase even though it increases data-shuffling a bit. Together with some other changes that enable "double buffered" camera capture, this gets me to 8.8fps capturing QVGA (320x240) gifs and 11fps capturing 240x240 square gifs.
1 parent 3e020a7 commit 7d6ac96

File tree

2 files changed

+69
-28
lines changed

2 files changed

+69
-28
lines changed

shared-module/gifio/GifWriter.c

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
* THE SOFTWARE.
2727
*/
2828

29+
#include <string.h>
30+
31+
#include "py/gc.h"
2932
#include "py/runtime.h"
3033

3134
#include "shared-module/gifio/GifWriter.h"
@@ -35,16 +38,30 @@
3538

3639
#define BLOCK_SIZE (126) // (2^7) - 2 // (DO NOT CHANGE!)
3740

38-
static void handle_error(const char *what, int error) {
41+
static void handle_error(gifio_gifwriter_t *self) {
42+
if (self->error != 0) {
43+
mp_raise_OSError(self->error);
44+
}
45+
}
46+
47+
static void flush_data(gifio_gifwriter_t *self) {
48+
if (self->cur == 0) {
49+
return;
50+
}
51+
int error = 0;
52+
self->file_proto->write(self->file, self->data, self->cur, &error);
53+
self->cur = 0;
3954
if (error != 0) {
40-
mp_raise_OSError(error);
55+
self->error = error;
4156
}
4257
}
4358

59+
// These "write" calls _MUST_ have enough buffer space available! This is
60+
// ensured by allocating the proper buffer size in construct.
4461
static void write_data(gifio_gifwriter_t *self, const void *data, size_t size) {
45-
int error = 0;
46-
self->file_proto->write(self->file, data, size, &error);
47-
handle_error("write_data", error);
62+
assert(self->cur + size <= self->size);
63+
memcpy(self->data + self->cur, data, size);
64+
self->cur += size;
4865
}
4966

5067
static void write_byte(gifio_gifwriter_t *self, uint8_t value) {
@@ -70,23 +87,44 @@ void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *
7087
self->colorspace = colorspace;
7188
self->own_file = own_file;
7289

90+
size_t nblocks = (width * height + 125) / 126;
91+
self->size = nblocks * 128 + 4;
92+
self->data = gc_alloc(self->size, 0, false);
93+
self->cur = 0;
94+
self->error = 0;
95+
7396
write_data(self, "GIF89a", 6);
7497
write_word(self, width);
7598
write_word(self, height);
7699
write_data(self, (uint8_t []) {0xF6, 0x00, 0x00}, 3);
77100

78-
if (colorspace == DISPLAYIO_COLORSPACE_RGB888) {
79-
mp_raise_TypeError(translate("unsupported colorspace for GifWriter"));
101+
switch (colorspace) {
102+
case DISPLAYIO_COLORSPACE_RGB565:
103+
case DISPLAYIO_COLORSPACE_RGB565_SWAPPED:
104+
case DISPLAYIO_COLORSPACE_BGR565:
105+
case DISPLAYIO_COLORSPACE_BGR565_SWAPPED:
106+
case DISPLAYIO_COLORSPACE_L8:
107+
break;
108+
109+
default:
110+
mp_raise_TypeError(translate("unsupported colorspace for GifWriter"));
80111
}
81112

82113
bool color = (colorspace != DISPLAYIO_COLORSPACE_L8);
83114

115+
bool bgr = (colorspace == DISPLAYIO_COLORSPACE_BGR565 || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
116+
self->byteswap = (colorspace == DISPLAYIO_COLORSPACE_RGB565_SWAPPED || colorspace == DISPLAYIO_COLORSPACE_BGR565_SWAPPED);
117+
84118
if (color) {
85119
for (int i = 0; i < 128; i++) {
86120
int red = (int)(((((i & 0x60) >> 5) * 255) + 1.5) / 3);
87121
int green = (int)(((((i & 0x1C) >> 2) * 255) + 3.5) / 7);
88122
int blue = (int)((((i & 0x3) * 255) + 1.5) / 3);
89-
write_data(self, (uint8_t []) {red, green, blue}, 3);
123+
if (bgr) {
124+
write_data(self, (uint8_t []) {blue, red, green}, 3);
125+
} else {
126+
write_data(self, (uint8_t []) {red, green, blue}, 3);
127+
}
90128
}
91129
} else {
92130
for (int i = 0; i < 128; i++) {
@@ -101,7 +139,8 @@ void shared_module_gifio_gifwriter_construct(gifio_gifwriter_t *self, mp_obj_t *
101139
write_data(self, (uint8_t []) {0x03, 0x01, 0x00, 0x00, 0x00}, 5);
102140
}
103141

104-
142+
flush_data(self);
143+
handle_error(self);
105144
}
106145

107146
bool shared_module_gifio_gifwriter_deinited(gifio_gifwriter_t *self) {
@@ -163,36 +202,34 @@ void shared_module_gifio_gifwriter_add_frame(gifio_gifwriter_t *self, const mp_b
163202

164203
block_data[0] = 1 + block_size;
165204
for (int j = 0; j < block_size; j++) {
166-
int pixel = displayio_colorconverter_convert_pixel(self->colorspace, (*pixels++));
167-
int red = (pixel >> (16 + 6)) & 0x3;
168-
int green = (pixel >> (8 + 5)) & 0x7;
169-
int blue = (pixel >> 6) & 0x3;
205+
int pixel = *pixels++;
206+
if (self->byteswap) {
207+
pixel = __builtin_bswap16(pixel);
208+
}
209+
int red = (pixel >> (11 + (5 - 2))) & 0x3;
210+
int green = (pixel >> (5 + (6 - 3))) & 0x7;
211+
int blue = (pixel >> (0 + (5 - 2))) & 0x3;
170212
block_data[j + 2] = (red << 5) | (green << 2) | blue;
171213
}
172214
write_data(self, block_data, 2 + block_size);
173215
}
174216
}
175217

176218
write_data(self, (uint8_t []) {0x01, 0x81, 0x00}, 3); // end code
177-
178-
int error = 0;
179-
self->file_proto->ioctl(self->file, MP_STREAM_FLUSH, 0, &error);
180-
handle_error("flush", error);
219+
flush_data(self);
220+
handle_error(self);
181221
}
182222

183223
void shared_module_gifio_gifwriter_close(gifio_gifwriter_t *self) {
184-
// we want to ensure the stream is closed even if the first write failed, so we don't use write_data
185-
int error1 = 0;
186-
self->file_proto->write(self->file, ";", 1, &error1);
224+
write_byte(self, ';');
225+
flush_data(self);
187226

188-
int error2 = 0;
189-
if (self->own_file) {
190-
self->file_proto->ioctl(self->file, MP_STREAM_CLOSE, 0, &error2);
191-
} else {
192-
self->file_proto->ioctl(self->file, MP_STREAM_FLUSH, 0, &error2);
193-
}
227+
int error = 0;
228+
self->file_proto->ioctl(self->file, self->own_file ? MP_STREAM_CLOSE : MP_STREAM_FLUSH, 0, &error);
194229
self->file = NULL;
195230

196-
handle_error("write", error1);
197-
handle_error("close", error2);
231+
if (error != 0) {
232+
self->error = error;
233+
}
234+
handle_error(self);
198235
}

shared-module/gifio/GifWriter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ typedef struct gifio_gifwriter {
3636
const mp_stream_p_t *file_proto;
3737
displayio_colorspace_t colorspace;
3838
int width, height;
39+
int error;
40+
uint8_t *data;
41+
size_t cur, size;
3942
bool own_file;
43+
bool byteswap;
4044
} gifio_gifwriter_t;

0 commit comments

Comments
 (0)