Optimize sixel_print. Previously, the algorithm scanned each pixel several

times; once to find out which colors are active, and then once for every single
active color to actually construct the output string.

Now it constructs the compressed sixel patterns in the first pass (now x * 12
iters), so we can reduce the second pass (the really expensive part, at active
colors * x * 6 iters) to just appending these to the output buffer.

From nincsnevem662 at gmail dot com in GitHub issue 4184.
This commit is contained in:
Nicholas Marriott 2024-10-14 11:05:59 +01:00
parent cb00e869ea
commit 8ff65230e1

View File

@ -52,6 +52,19 @@ struct sixel_image {
struct sixel_line *lines; struct sixel_line *lines;
}; };
struct sixel_chunk {
u_int next_x;
u_int next_y;
u_int count;
char pattern;
char next_pattern;
size_t len;
size_t used;
char *data;
};
static int static int
sixel_parse_expand_lines(struct sixel_image *si, u_int y) sixel_parse_expand_lines(struct sixel_image *si, u_int y)
{ {
@ -496,13 +509,65 @@ sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
} }
} }
static void
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
u_int y, u_int *active, u_int *nactive)
{
u_int i, x, c, dx, colors[6];
struct sixel_chunk *chunk = NULL;
struct sixel_line *sl;
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
colors[i] = 0;
if (y + i < si->y) {
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0) {
colors[i] = sl->data[x];
c = sl->data[x] - 1;
chunks[c].next_pattern |= 1 << i;
}
}
}
for (i = 0; i < 6; i++) {
if (colors[i] == 0)
continue;
c = colors[i] - 1;
chunk = &chunks[c];
if (chunk->next_x == x + 1)
continue;
if (chunk->next_y < y + 1) {
chunk->next_y = y + 1;
active[(*nactive)++] = c;
}
dx = x - chunk->next_x;
if (chunk->pattern != chunk->next_pattern || dx != 0) {
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_repeat(&chunk->data, &chunk->len,
&chunk->used, dx, '?');
chunk->pattern = chunk->next_pattern;
chunk->count = 0;
}
chunk->count++;
chunk->next_pattern = 0;
chunk->next_x = x + 1;
}
}
}
char * char *
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size) sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
{ {
char *buf, tmp[64], *contains, data, last = 0; char *buf, tmp[64];
size_t len, used = 0, tmplen; size_t len, used = 0, tmplen;
u_int *colours, ncolours, i, c, x, y, count; u_int *colours, ncolours, i, c, y, *active, nactive;
struct sixel_line *sl; struct sixel_chunk *chunks, *chunk;
if (map != NULL) { if (map != NULL) {
colours = map->colours; colours = map->colours;
@ -514,7 +579,6 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (ncolours == 0) if (ncolours == 0)
return (NULL); return (NULL);
contains = xcalloc(1, ncolours);
len = 8192; len = 8192;
buf = xmalloc(len); buf = xmalloc(len);
@ -528,59 +592,42 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
sixel_print_add(&buf, &len, &used, tmp, tmplen); sixel_print_add(&buf, &len, &used, tmp, tmplen);
} }
chunks = xcalloc(ncolours, sizeof *chunks);
active = xcalloc(ncolours, sizeof *active);
for (i = 0; i < ncolours; i++) { for (i = 0; i < ncolours; i++) {
c = colours[i]; c = colours[i];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u", tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
sixel_print_add(&buf, &len, &used, tmp, tmplen); sixel_print_add(&buf, &len, &used, tmp, tmplen);
chunk = &chunks[i];
chunk->len = 8;
chunk->data = xmalloc(chunk->len);
} }
for (y = 0; y < si->y; y += 6) { for (y = 0; y < si->y; y += 6) {
memset(contains, 0, ncolours); nactive = 0;
for (x = 0; x < si->x; x++) { sixel_print_compress_colors(si, chunks, y, active, &nactive);
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0)
contains[sl->data[x] - 1] = 1;
}
}
for (c = 0; c < ncolours; c++) { for (i = 0; i < nactive; i++) {
if (!contains[c]) c = active[i];
continue; chunk = &chunks[c];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c); tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
sixel_print_add(&buf, &len, &used, tmp, tmplen); sixel_print_add(&buf, &len, &used, tmp, tmplen);
sixel_print_add(&buf, &len, &used, chunk->data,
count = 0; chunk->used);
for (x = 0; x < si->x; x++) { sixel_print_repeat(&buf, &len, &used, chunk->count,
data = 0; chunk->pattern + 0x3f);
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] == c + 1)
data |= (1 << i);
}
data += 0x3f;
if (data != last) {
sixel_print_repeat(&buf, &len, &used,
count, last);
last = data;
count = 1;
} else
count++;
}
sixel_print_repeat(&buf, &len, &used, count, data);
sixel_print_add(&buf, &len, &used, "$", 1); sixel_print_add(&buf, &len, &used, "$", 1);
chunk->used = chunk->next_x = chunk->count = 0;
} }
if (buf[used - 1] == '$') if (buf[used - 1] == '$')
used--; used--;
sixel_print_add(&buf, &len, &used, "-", 1); sixel_print_add(&buf, &len, &used, "-", 1);
} }
if (buf[used - 1] == '$' || buf[used - 1] == '-') if (buf[used - 1] == '-')
used--; used--;
sixel_print_add(&buf, &len, &used, "\033\\", 2); sixel_print_add(&buf, &len, &used, "\033\\", 2);
@ -589,7 +636,11 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (size != NULL) if (size != NULL)
*size = used; *size = used;
free(contains); for (i = 0; i < ncolours; i++)
free(chunks[i].data);
free(active);
free(chunks);
return (buf); return (buf);
} }