diff --git a/image-sixel.c b/image-sixel.c index 8fa02a82..a03c8619 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -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) { @@ -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; @@ -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); @@ -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); @@ -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); }