Skip to content

Commit

Permalink
Optimize sixel_print. Previously, the algorithm scanned each pixel se…
Browse files Browse the repository at this point in the history
…veral

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.
  • Loading branch information
nicm committed Oct 14, 2024
1 parent cb00e86 commit 8ff6523
Showing 1 changed file with 91 additions and 40 deletions.
131 changes: 91 additions & 40 deletions image-sixel.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ struct sixel_image {
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
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
{
Expand Down Expand Up @@ -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 *
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;
u_int *colours, ncolours, i, c, x, y, count;
struct sixel_line *sl;
u_int *colours, ncolours, i, c, y, *active, nactive;
struct sixel_chunk *chunks, *chunk;

if (map != NULL) {
colours = map->colours;
Expand All @@ -514,7 +579,6 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)

if (ncolours == 0)
return (NULL);
contains = xcalloc(1, ncolours);

len = 8192;
buf = xmalloc(len);
Expand All @@ -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);
}

chunks = xcalloc(ncolours, sizeof *chunks);
active = xcalloc(ncolours, sizeof *active);

for (i = 0; i < ncolours; i++) {
c = colours[i];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
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) {
memset(contains, 0, ncolours);
for (x = 0; x < si->x; x++) {
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;
}
}
nactive = 0;
sixel_print_compress_colors(si, chunks, y, active, &nactive);

for (c = 0; c < ncolours; c++) {
if (!contains[c])
continue;
for (i = 0; i < nactive; i++) {
c = active[i];
chunk = &chunks[c];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
sixel_print_add(&buf, &len, &used, tmp, tmplen);

count = 0;
for (x = 0; x < si->x; x++) {
data = 0;
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, chunk->data,
chunk->used);
sixel_print_repeat(&buf, &len, &used, chunk->count,
chunk->pattern + 0x3f);
sixel_print_add(&buf, &len, &used, "$", 1);
chunk->used = chunk->next_x = chunk->count = 0;
}

if (buf[used - 1] == '$')
used--;
sixel_print_add(&buf, &len, &used, "-", 1);
}
if (buf[used - 1] == '$' || buf[used - 1] == '-')
if (buf[used - 1] == '-')
used--;

sixel_print_add(&buf, &len, &used, "\033\\", 2);
Expand All @@ -589,7 +636,11 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
if (size != NULL)
*size = used;

free(contains);
for (i = 0; i < ncolours; i++)
free(chunks[i].data);
free(active);
free(chunks);

return (buf);
}

Expand Down

0 comments on commit 8ff6523

Please sign in to comment.