sixel: combine ctable/dtable extraction passes, cuts 80% of runtime #1380

This commit is contained in:
nick black 2021-03-03 22:34:14 -05:00
parent 43b3724f07
commit e3ede498b6
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

View File

@ -8,81 +8,101 @@ break_sixel_comps(unsigned char comps[static 3], uint32_t rgba, unsigned char ma
//fprintf(stderr, "%u %u %u\n", comps[0], comps[1], comps[2]);
}
// FIXME you can have more (or fewer) than 256 registers...detect?
#define MAXCOLORS 256
// first pass: extract up to 256 sixelspace colors over arbitrarily many sixels
// sixelspace is 0..100 corresponding to 0..255, lame =[
// FIXME you can have more (or fewer) than 256 registers...detect?
typedef struct colortable {
int colors;
int sixelcount;
unsigned char table[3 * 256];
unsigned char table[5 * MAXCOLORS]; // components + dtable index
} colortable;
// second pass: construct data for extracted colors over the sixels
typedef struct sixeltable {
const colortable* ctab;
colortable* ctab;
unsigned char* data; // |colors|x|sixelcount|-byte arrays
} sixeltable;
// returns the index at which the provided color can be found, possibly
// inserting it into the table. returns -1 if the color is not in the
// table and the table is full.
static inline int
ctable_to_dtable(const unsigned char* ctable){
return ctable[3] * 256 + ctable[4];
}
static inline void
dtable_to_ctable(int dtable, unsigned char* ctable){
ctable[3] = dtable / 256;
ctable[4] = dtable % 256;
}
// returns the index at which the provided color can be found *in the
// dtable*, possibly inserting it into the ctable. returns -1 if the
// color is not in the table and the table is full.
// FIXME switch to binary search, duh
// FIXME replace all these 3s with sizeof(comps)
// FIXME replace all these 3s and 5s
static int
find_color(colortable* ctab, unsigned char comps[static 3]){
int i;
for(i = 0 ; i < ctab->colors ; ++i){
int cmp = memcmp(ctab->table + i * 3, comps, 3);
int cmp = memcmp(ctab->table + i * 5, comps, 3);
if(cmp == 0){
return i;
return ctable_to_dtable(ctab->table + i * 5);
}else if(cmp > 0){
break;
}
}
if(ctab->colors == sizeof(ctab->table) / 3){
if(ctab->colors == MAXCOLORS){
return -1;
}
if(i < ctab->colors){
memmove(ctab->table + (i + 1) * 3, ctab->table + i * 3, (ctab->colors - i) * 3);
memmove(ctab->table + (i + 1) * 5, ctab->table + i * 5, (ctab->colors - i) * 5);
}
memcpy(ctab->table + i * 3, comps, 3);
memcpy(ctab->table + i * 5, comps, 3);
dtable_to_ctable(ctab->colors, ctab->table + i * 5);
++ctab->colors;
return i;
return ctab->colors - 1;
//return ctable_to_dtable(ctab->table + i * 5);
}
// rather inelegant preprocess of the entire image. colors are converted to the
// 100x100x100 sixel colorspace, and built into a table. if there are more than
// 256 converted colors, we (currently) reject it FIXME. ideally we'd do the
// image piecemeal, allowing us to get complete color fidelity; barring that,
// we'd have something sensibly quantize us down first FIXME. we ought do
// MAXCOLORS converted colors, we (currently) reject it FIXME. ideally we'd do
// the image piecemeal, allowing us to get complete color fidelity; barring
// that, we'd have something sensibly quantize us down first FIXME. we ought do
// everything in a single pass FIXME.
// what do we do if every pixel is transparent (0 colors)? FIXME
static int
extract_ctable_inner(const uint32_t* data, int linesize, int begy, int begx,
int leny, int lenx, colortable* ctab, unsigned char mask){
int leny, int lenx, sixeltable* stab, unsigned char mask){
int pos = 0;
for(int visy = begy ; visy < (begy + leny) ; visy += 6){
for(int visx = begx ; visx < (begx + lenx) ; visx += 1){
for(int sy = visy ; sy < (begy + leny) && sy < visy + 6 ; ++sy){
const uint32_t* rgb = (const uint32_t*)(data + (linesize / 4 * sy) + (visx));
const uint32_t* rgb = (const uint32_t*)(data + (linesize / 4 * sy) + visx);
if(rgba_trans_p(ncpixel_a(*rgb))){
continue;
}
unsigned char comps[3];
break_sixel_comps(comps, *rgb, mask);
if(find_color(ctab, comps) < 0){
int c = find_color(stab->ctab, comps);
if(c < 0){
//fprintf(stderr, "FAILED FINDING COLOR AUGH 0x%02x\n", mask);
return -1;
}
stab->data[c * stab->ctab->sixelcount + pos] |= (1u << (sy - visy));
//fprintf(stderr, "color %d pos %d: 0x%x\n", c, pos, stab->data[c * stab->ctab->sixelcount + pos]);
}
++ctab->sixelcount;
++pos;
}
}
return 0;
}
static inline void
initialize_ctable(colortable* ctab){
ctab->colors = 0;
ctab->sixelcount = 0;
initialize_stable(sixeltable* stab){
stab->ctab->colors = 0;
memset(stab->data, 0, stab->ctab->sixelcount * MAXCOLORS);
}
// Use as many of the original colors as we can, but not more than will fit
@ -91,11 +111,11 @@ initialize_ctable(colortable* ctab){
// progressively mask more out until they all fit.
static int
extract_color_table(const uint32_t* data, int linesize, int begy, int begx,
int leny, int lenx, colortable* ctab, unsigned char* mask){
int leny, int lenx, sixeltable* stab, unsigned char* mask){
*mask = 0xff;
while(mask){
initialize_ctable(ctab);
if(extract_ctable_inner(data, linesize, begy, begx, leny, lenx, ctab, *mask) == 0){
initialize_stable(stab);
if(extract_ctable_inner(data, linesize, begy, begx, leny, lenx, stab, *mask) == 0){
return 0;
}
*mask <<= 1;
@ -104,41 +124,6 @@ extract_color_table(const uint32_t* data, int linesize, int begy, int begx,
return -1;
}
// Having assembled the color table, build the raw sixels.
static int
extract_data_table(const uint32_t* data, int linesize, int begy, int begx,
int leny, int lenx, sixeltable* stab, unsigned char mask){
//fprintf(stderr, "colors: %d sixelcount: %d\n", stab->ctab->colors, stab->ctab->sixelcount);
for(int c = 0 ; c < stab->ctab->colors ; ++c){
int pos = 0;
//fprintf(stderr, "dimy/x: %d/%d placey/x: %d/%d begyx: %d/%d lenyx: %d/%d\n", dimy, dimx, placey, placex, begy, begx, leny, lenx);
for(int visy = begy ; visy < (begy + leny) ; visy += 6){
for(int visx = begx ; visx < (begx + lenx) ; visx += 1){
//fprintf(stderr, "handling sixel %d for color %d visy: %d\n", pos, c, visy);
for(int sy = visy ; sy < (begy + leny) && sy < visy + 6 ; ++sy){
const uint32_t* rgb = (const uint32_t*)(data + (linesize / 4 * sy) + visx);
//fprintf(stderr, "%p: %08x\n", rgb, *rgb);
if(rgba_trans_p(ncpixel_a(*rgb))){
//fprintf(stderr, "transparent\n");
continue;
}
unsigned char comps[3];
break_sixel_comps(comps, *rgb, mask);
//fprintf(stderr, "%d/%d/%d\n", comps[0], comps[1], comps[2]);
if(memcmp(comps, stab->ctab->table + c * 3, 3) == 0){
stab->data[c * stab->ctab->sixelcount + pos] |= (1u << (sy - visy));
//fprintf(stderr, "%d ", c * stab->ctab->sixelcount + pos);
//fputc(stab->data[c * stab->ctab->sixelcount + pos] + 63, stderr);
}
}
//fprintf(stderr, "color %d pos %d: %u\n", c, pos, stab->data[c * stab->ctab->sixelcount + pos]);
++pos;
}
}
}
return 0;
}
// Emit some number of equivalent, subsequent sixels, using sixel RLE. We've
// seen the sixel |crle| for |seenrle| columns in a row. |seenrle| must > 0.
static int
@ -165,7 +150,7 @@ static int
write_sixel_data(FILE* fp, int lenx, sixeltable* stab){
fprintf(fp, "\e[?80h\ePq"); // FIXME pixelon
for(int i = 0 ; i < stab->ctab->colors ; ++i){
const unsigned char* rgb = stab->ctab->table + i * 3;
const unsigned char* rgb = stab->ctab->table + i * 5;
fprintf(fp, "#%d;2;%u;%u;%u", i, rgb[0], rgb[1], rgb[2]);
}
int p = 0;
@ -174,20 +159,21 @@ write_sixel_data(FILE* fp, int lenx, sixeltable* stab){
fprintf(fp, "#%d", i);
int seenrle = 0;
unsigned char crle = 0;
int idx = ctable_to_dtable(stab->ctab->table + i * 5);
for(int m = p ; m < stab->ctab->sixelcount && m < p + lenx ; ++m){
//fprintf(stderr, "%d ", i * stab->ctab->sixelcount + m);
//fputc(stab->data[i * stab->ctab->sixelcount + m] + 63, stderr);
//fprintf(stderr, "%d ", idx * stab->ctab->sixelcount + m);
//fputc(stab->data[idx * stab->ctab->sixelcount + m] + 63, stderr);
if(seenrle){
if(stab->data[i * stab->ctab->sixelcount + m] == crle){
if(stab->data[idx * stab->ctab->sixelcount + m] == crle){
++seenrle;
}else{
write_rle(fp, seenrle, crle);
seenrle = 1;
crle = stab->data[i * stab->ctab->sixelcount + m];
crle = stab->data[idx * stab->ctab->sixelcount + m];
}
}else{
seenrle = 1;
crle = stab->data[i * stab->ctab->sixelcount + m];
crle = stab->data[idx * stab->ctab->sixelcount + m];
}
}
write_rle(fp, seenrle, crle);
@ -240,23 +226,19 @@ int sixel_blit(ncplane* nc, int placey, int placex, int linesize,
if(ctab == NULL){
return -1;
}
unsigned char mask;
if(extract_color_table(data, linesize, begy, begx, leny, lenx, ctab, &mask)){
free(ctab);
return -1;
}
ctab->sixelcount = (lenx - begx) * ((leny - begy + 5) / 6);
sixeltable stable = {
.ctab = ctab,
.data = malloc(ctab->colors * ctab->sixelcount),
.data = malloc(MAXCOLORS * ctab->sixelcount),
};
if(stable.data == NULL){
free(ctab);
return -1;
}
memset(stable.data, 0, ctab->colors * ctab->sixelcount);
if(extract_data_table(data, linesize, begy, begx, leny, lenx, &stable, mask)){
free(stable.data);
unsigned char mask;
if(extract_color_table(data, linesize, begy, begx, leny, lenx, &stable, &mask)){
free(ctab);
free(stable.data);
return -1;
}
int r = sixel_blit_inner(nc, placey, placex, lenx, &stable, cellpixx);