17#define TRANS_PALETTE_ENTRY 65535
20#define AUXVECELEMSIZE 2
138 pthread_mutex_lock(&eng->
lock);
151 pthread_mutex_unlock(&eng->
lock);
153 pthread_cond_broadcast(&eng->
cond);
163 pthread_mutex_lock(&eng->
lock);
165 pthread_cond_wait(&eng->
cond, &eng->
lock);
167 pthread_mutex_unlock(&eng->
lock);
175sixelcount(
int dimy,
int dimx){
176 return (dimy + 5) / 6 * dimx;
182sixelbandcount(
int dimy){
183 return sixelcount(dimy, 1);
189sixelmap_create(
int dimy){
190 sixelmap* ret = malloc(
sizeof(*ret));
210 for(
int j = 0 ; j < s->
size ; ++j){
219 sixelband_free(&s->
bands[i]);
227static inline unsigned
229 unsigned r = round(
c * 100.0 / 255);
230 return r > 99 ? 99:
r;
236static inline unsigned
237qnode_keys(
unsigned r,
unsigned g,
unsigned b,
unsigned *skey){
238 unsigned ssr = ss(
r);
239 unsigned ssg = ss(g);
240 unsigned ssb = ss(b);
241 unsigned ret = ssr / 10 * 100 + ssg / 10 * 10 + ssb / 10;
242 *skey = (((ssr % 10) / 5) << 2u) +
243 (((ssg % 10) / 5) << 1u) +
251chosen_p(
const qnode* q){
252 return q->
cidx & 0x8000u;
255static inline unsigned
256make_chosen(
unsigned cidx){
257 return cidx | 0x8000u;
261static inline unsigned
263 return q->
cidx & ~0x8000u;
266#define QNODECOUNT 1000
277alloc_qstate(
unsigned colorregs){
278 qstate* qs = malloc(
sizeof(*qs));
317insert_color(
qstate* qs, uint32_t pixel){
318 const unsigned r = ncpixel_r(pixel);
319 const unsigned g = ncpixel_g(pixel);
320 const unsigned b = ncpixel_b(pixel);
322 const unsigned key = qnode_keys(
r, g, b, &skey);
356 memset(o, 0,
sizeof(*o));
361 memcpy(o->
q[skeynat], q,
sizeof(*q));
386 o->
q[skey]->
q.
pop = 1;
391 o->
q[skey]->
cidx = 0;
400find_color(
const qstate* qs, uint32_t pixel){
401 const unsigned r = ncpixel_r(pixel);
402 const unsigned g = ncpixel_g(pixel);
403 const unsigned b = ncpixel_b(pixel);
405 const unsigned key = qnode_keys(
r, g, b, &skey);
411 logpanic(
"internal error: no color for 0x%016x", pixel);
426 sixel[4] = value +
'0';
430write_rle(
char* vec,
int* voff,
int rle,
int rep){
432 *voff += sprintf(vec + *voff,
"!%d", rle);
434 vec[(*voff)++] = rep;
437 vec[(*voff)++] = rep;
459sixelband_extend(
char* vec,
struct band_extender* bes,
int dimx,
int curx){
468 if((vec = malloc(dimx + 1)) ==
NULL){
475 int clearlen = curx - (bes->
rle + bes->
wrote);
476 write_rle(vec, &bes->
length, clearlen,
'?');
505write_auxvec(uint8_t* auxvec, uint16_t color,
int endy,
int y,
int x,
int len,
506 char rep,
char masked,
int cellpxy,
int cellpxx){
509 const char diff = rep ^ masked;
511 const int xoff =
x % cellpxx;
512 const int yoff =
y % cellpxy;
514 for(
char bitselector = 1 ; bitselector < 0x40 ; bitselector <<= 1u, ++dy){
515 if((diff & bitselector) == 0){
519 if(yoff + dy == endy){
526 for(
int i = 0 ; i <
len ; ++i){
537wipe_color(
sixelband* b,
int color,
int y,
int endy,
int startx,
int endx,
538 char mask,
int dimx, uint8_t* auxvec,
539 int cellpxy,
int cellpxx){
540 const char* vec = b->
vecs[color];
545 char* newvec = malloc(dimx + 1);
564 }
else if(*vec ==
'!'){
571 char masked = ((rep - 63) & mask) + 63;
573 if(
x + rle <= startx){
574 write_rle(newvec, &voff, rle, rep);
576 }
else if(masked == rep){
577 write_rle(newvec, &voff, rle, rep);
582 write_rle(newvec, &voff, startx -
x, rep);
589 write_rle(newvec, &voff, endx -
x, masked);
590 write_auxvec(auxvec, color, endy,
y,
x, endx -
x, rep, masked, cellpxy, cellpxx);
594 write_rle(newvec, &voff, rle, masked);
595 write_auxvec(auxvec, color, endy,
y,
x, rle, rep, masked, cellpxy, cellpxx);
600 write_rle(newvec, &voff, rle, rep);
608 strcpy(newvec + voff, vec);
619 b->
vecs[color] = newvec;
626wipe_band(
sixelmap* smap,
int band,
int startx,
int endx,
627 int starty,
int endy,
int dimx,
int cellpxy,
int cellpxx,
631 const int sy = band * 6 < starty ? starty - band * 6 : 0;
632 const int ey = (band + 1) * 6 > endy ? 6 - ((band + 1) * 6 - endy) : 6;
635 unsigned char mask = 63;
637 for(
int i = 0 ; i < 6 ; ++i){
638 if(i >= sy && i < ey){
645 for(
int i = 0 ; i < b->
size ; ++i){
646 wiped += wipe_color(b, i, band * 6, endy, startx, endx, mask,
647 dimx, auxvec, cellpxy, cellpxx);
661 const int cellpxy = ncplane_pile(s->
n)->cellpxy;
662 const int cellpxx = ncplane_pile(s->
n)->cellpxx;
664 const int startx = xcell * cellpxx;
665 const int starty = ycell * cellpxy;
666 int endx = ((xcell + 1) * cellpxx);
670 int endy = ((ycell + 1) * cellpxy);
674 const int startband = starty / 6;
675 const int endband = (endy - 1) / 6;
679 for(
int b = startband ; b <= endband ; ++b){
680 w += wipe_band(smap, b, startx, endx, starty, endy, s->
pixx,
681 cellpxy, cellpxx, auxvec);
707 if(s->
n && s->
n->
tam){
710 for(
unsigned y = 0 ;
y < s->
dimy ; ++
y){
711 for(
unsigned x = 0 ;
x < s->
dimx ; ++
x){
712 unsigned txyidx =
y * s->
dimx +
x;
731 for(
unsigned y = 0 ;
y < s->
dimy ; ++
y){
732 const unsigned yy = absy +
y;
733 for(
unsigned x = 0 ;
x < s->
dimx ; ++
x){
736 const unsigned xx = absx +
x;
737 if(xx < p->dimx && yy < p->dimy){
738 unsigned ridx = yy * p->
dimx + xx;
745 free(
s->needs_refresh);
746 s->needs_refresh =
NULL;
753update_rmatrix(
unsigned char* rmatrix,
int txyidx,
const tament* tam){
766qnodecmp(
const void* q0,
const void* q1){
767 const qnode* qa = q0;
768 const qnode* qb = q1;
776get_active_set(
qstate* qs, uint32_t colors){
777 qnode* act = malloc(
sizeof(*act) * colors);
778 unsigned targidx = 0;
782 for(
unsigned z = 0 ; z < total && targidx < colors ; ++z){
785 memcpy(&act[targidx], &qs->
qnodes[z],
sizeof(*act));
788 act[targidx].
qlink = z;
793 for(
unsigned s = 0 ; s < 8 && targidx < colors ; ++s){
796 memcpy(&act[targidx], o->
q[s],
sizeof(*act));
805 assert(targidx == colors);
806 qsort(act, colors,
sizeof(*act), qnodecmp);
811find_next_lowest_chosen(
const qstate* qs,
int z,
int i,
const qnode** hq){
820 if(h && chosen_p(h)){
844choose(
qstate* qs,
qnode*
q,
int z,
int i,
int* hi,
int* lo,
849 *hi = find_next_lowest_chosen(qs, z, i, hq);
851 int cur = z * 8 + (i >= 0 ? i : 4);
854 }
else if(*hi == -1 || cur - *lo < *hi - cur){
868merge_color_table(
qstate* qs){
881 for(
int z = qs->
smap->
colors - 1 ; z >= 0 ; --z){
913 for(
int i = 0 ; i < 8 ; ++i){
915 choose(qs, o->
q[i], z, i, &hi, &lo, &hq, &lq);
919 choose(qs, &qs->
qnodes[z], z, -1, &hi, &lo, &hq, &lq);
928load_color_table(
const qstate* qs){
931 for(
int z = 0 ; z < total && loaded < qs->
smap->
colors ; ++z){
946build_sixel_band(
qstate* qs,
int bnum){
950 size_t bsize =
sizeof(*b->
vecs) * b->
size;
956 b->
vecs = malloc(bsize);
961 memset(b->
vecs, 0, bsize);
962 memset(meta, 0, mlen);
963 const int ystart = qs->
bargs->
begy + bnum * 6;
983 for(
int y = ystart ;
y < endy ; ++
y){
988 int cidx = find_color(qs, *rgb);
994 for(act = 0 ; act < activepos ; ++act){
995 if(active[act].color == cidx){
996 active[act].rep |= (1u << (
y - ystart));
1000 if(act == activepos){
1001 active[activepos].color = cidx;
1002 active[activepos].rep = (1u << (
y - ystart));
1008 for(
int i = 0 ; i < activepos ; ++i){
1009 const int c = active[i].color;
1010 if(meta[
c].rep == active[i].rep && meta[
c].rle + meta[
c].wrote ==
x){
1020 meta[
c].
rep = active[i].rep;
1026 b->
vecs[i] = sixelband_extend(b->
vecs[i], &meta[i], qs->
lenx,
x);
1043 if(build_sixel_band(qs, b) < 0){
1060 enqueue_to_workers(sengine, qs);
1062 qs->
table = malloc(tsize);
1066 load_color_table(qs);
1068 block_on_workers(sengine, qs);
1073extract_cell_color_table(
qstate* qs,
long cellid){
1075 const long x = cellid % ccols;
1076 const long y = cellid / ccols;
1081 const int leny = qs->
leny;
1082 const int lenx = qs->
lenx;
1083 const int cstartx = begx +
x * cdimx;
1084 const int cstarty = begy +
y * cdimy;
1086 tament* tam = qs->bargs->u.pixel.spx->
n->tam;
1087 int cendy = cstarty + cdimy;
1088 if(cendy > begy + leny){
1089 cendy = begy + leny;
1091 int cendx = cstartx + cdimx;
1092 if(cendx > begx + lenx){
1093 cendx = begx + lenx;
1099 if(cstarty >= cendy){
1105 const uint32_t* rgb = (qs->
data + (qs->
linesize / 4 * cstarty) + cstartx);
1108 update_rmatrix(rmatrix, cellid, tam);
1110 free(tam[cellid].auxvector);
1113 update_rmatrix(rmatrix, cellid, tam);
1114 free(tam[cellid].auxvector);
1119 update_rmatrix(rmatrix, cellid, tam);
1122 update_rmatrix(rmatrix, cellid, tam);
1126 for(
int visy = cstarty ; visy < cendy ; ++visy){
1127 for(
int visx = cstartx ; visx < cendx ; ++visx){
1154 if(insert_color(qs, *rgb)){
1161 rmatrix[cellid] = 0;
1181 rmatrix = malloc(sizeof(*rmatrix) * crows * ccols);
1187 for(
int y = 0 ;
y < crows ; ++
y){
1188 for(
int x = 0 ;
x < ccols ; ++
x){
1189 if(extract_cell_color_table(qs, cellid)){
1196 if(merge_color_table(qs)){
1199 if(build_data_table(sengine, qs)){
1208 int rr,
r = fbuf_puts(f,
"\x1bP0;");
1212 if((rr = fbuf_putint(f, p2)) < 0){
1216 if((rr = fbuf_puts(f,
";0q\"1;1;")) < 0){
1220 if((rr = fbuf_putint(f, lenx)) < 0){
1224 if(fbuf_putc(f,
';') != 1){
1228 if((rr = fbuf_putint(f, leny)) < 0){
1237write_sixel_creg(
fbuf* f,
int idx,
int rc,
int gc,
int bc){
1239 if(fbuf_putc(f,
'#') != 1){
1243 if((rr = fbuf_putint(f,
idx)) < 0){
1247 if((rr = fbuf_puts(f,
";2;")) < 0){
1251 if((rr = fbuf_putint(f, rc)) < 0){
1255 if(fbuf_putc(f,
';') != 1){
1259 if((rr = fbuf_putint(f, gc)) < 0){
1263 if(fbuf_putc(f,
';') != 1){
1267 if((rr = fbuf_putint(f, bc)) < 0){
1279write_sixel_header(
qstate* qs,
fbuf* f,
int leny){
1284 int r = write_sixel_intro(f, qs->
smap->
p2, leny, qs->
lenx);
1291 int rr = write_sixel_creg(f, i, rgb[0], rgb[1], rgb[2]);
1303 int needclosure = 0;
1305 for(
int i = 0 ; i < band->
size ; ++i){
1308 if(fbuf_putc(f,
'$') != 1){
1314 if(fbuf_putc(f,
'#') != 1){
1317 if(fbuf_putint(f, i) < 0){
1320 if(fbuf_puts(f, band->
vecs[i]) < 0){
1325 if(fbuf_putc(f,
'-') != 1){
1329 if(fbuf_puts(f,
"\e\\") < 0){
1346 if(write_sixel_payload(&s->
glyph, s->
smap) < 0){
1363 int outy = qs->
leny;
1365 outy += 6 - (qs->
leny % 6);
1368 int parse_start = write_sixel_header(qs, &f, outy);
1369 if(parse_start < 0){
1377 scrub_tam_boundaries(tam, outy, qs->
lenx, cellpxy, cellpxx);
1402 logerror(
"couldn't allocate qstate");
1413 if(extract_color_table(sengine, qs)){
1421 int r = sixel_blit_inner(qs, smap, bargs,
n->tam);
1443 for(
int yy = starty ; yy < starty + (int)s->
dimy && yy < (
int)p->
dimy ; ++yy){
1444 for(
int xx = startx ; xx < startx + (int)s->
dimx && xx < (
int)p->
dimx ; ++xx){
1445 int ridx = yy * p->
dimx + xx;
1452 sprixel* trues =
r->sprixel ?
r->sprixel :
s;
1453 if(yy >= (
int)trues->
n->
leny || yy - trues->
n->
absy < 0){
1457 if(xx >= (
int)trues->
n->
lenx || xx - trues->
n->
absx < 0){
1478 int yoff,
int xoff){
1482 if(
s->wipes_outstanding){
1483 if(sixel_reblit(
s)){
1486 s->wipes_outstanding =
false;
1489 const int targy =
s->n->absy + yoff;
1490 const int targx =
s->n->absx + xoff;
1491 if(goto_location(
p->nc, f, targy, targx,
NULL)){
1495 for(
int yy =
s->movedfromy ; yy <
s->movedfromy + (int)
s->dimy && yy < (
int)
p->dimy ; ++yy){
1499 for(
int xx =
s->movedfromx ; xx <
s->movedfromx + (int)
s->dimx && xx < (
int)
p->dimx ; ++xx){
1503 struct crender *
r = &
p->crender[yy *
p->dimx + xx];
1511 if(fbuf_putn(f,
s->glyph.
buf,
s->glyph.used) < 0){
1515 return s->glyph.used;
1520sixel_worker(
void*
v){
1525 unsigned bufpos = 0;
1527 pthread_mutex_lock(&sengine->
lock);
1528 while(wq->
used == 0 && !sengine->
done){
1529 pthread_cond_wait(&sengine->
cond, &sengine->
lock);
1536 pthread_mutex_unlock(&sengine->
lock);
1541 bool sendsignal =
false;
1542 pthread_mutex_lock(&sengine->
lock);
1547 pthread_mutex_unlock(&sengine->
lock);
1549 pthread_cond_broadcast(&sengine->
cond);
1559sixel_init_core(
tinfo* ti,
const char* initstr,
int fd){
1564 pthread_mutex_init(&sengine->
lock,
NULL);
1565 pthread_cond_init(&sengine->
cond,
NULL);
1566 sengine->
done =
false;
1567 const int workers_wanted =
sizeof(sengine->
tids) /
sizeof(*sengine->
tids);
1568 for(
int w = 0 ; w < workers_wanted ; ++w){
1572 if(pthread_create(&sengine->
tids[w],
NULL, sixel_worker, &sengine->
queues[w])){
1573 logerror(
"couldn't spin up sixel worker %d/%d", w, workers_wanted);
1578 return tty_emit(initstr, fd);
1589 return sixel_init_core(ti,
"\e[?80l\e[?8452h", fd);
1597 return sixel_init_core(ti,
"\e[?80h\e[?8452h", fd);
1603 return sixel_init_core(ti,
"\e[?8452h", fd);
1609restore_vec(
sixelband* b,
int color,
int bit,
int xoff,
int dimx){
1610 if(color >= b->
size){
1615 const char* vec = b->
vecs[color];
1621 if((
v = sixelband_extend(
v, &bes, dimx, xoff)) ==
NULL){
1628 if((
v = malloc(dimx + 1)) ==
NULL){
1634 rle += (*vec -
'0');
1635 }
else if(*vec ==
'!'){
1643 if(
x +
rle <= xoff){
1644 write_rle(
v, &voff,
rle,
rep);
1647 write_rle(
v, &voff,
rle,
rep);
1651 write_rle(
v, &voff, xoff -
x,
rep);
1655 write_rle(
v, &voff, 1, ((
rep - 63) | bit) + 63);
1659 write_rle(
v, &voff,
rle,
rep);
1667 strcpy(
v + voff, vec);
1689restore_band(
sixelmap* smap,
int band,
int startx,
int endx,
1690 int starty,
int endy,
int dimx,
int cellpxy,
int cellpxx,
1693 const int sy = band * 6 < starty ? starty - band * 6 : 0;
1694 const int ey = (band + 1) * 6 > endy ? 6 - ((band + 1) * 6 - endy) : 6;
1695 const int width = endx - startx;
1696 const int height = ey - sy;
1697 const int totalpixels = width * height;
1700 int yoff = ((band * 6) + sy - starty) % cellpxy;
1701 int xoff = startx % cellpxx;
1702 for(
int dy = sy ; dy < ey ; ++dy, ++yoff){
1704 const int bit = 1 << dy;
1706 for(
int dx = 0 ; startx + dx < endx ; ++dx){
1711 restore_vec(b, color, bit, startx + dx, dimx);
1717 return totalpixels - restored;
1729 const int cellpxy = ncplane_pile(s->
n)->cellpxy;
1730 const int cellpxx = ncplane_pile(s->
n)->cellpxx;
1732 const int startx = xcell * cellpxx;
1733 const int starty = ycell * cellpxy;
1734 int endx = ((xcell + 1) * cellpxx);
1735 if(endx >= s->
pixx){
1738 int endy = ((ycell + 1) * cellpxy);
1739 if(endy >= s->
pixy){
1742 const int startband = starty / 6;
1743 const int endband = (endy - 1) / 6;
1747 for(
int b = startband ; b <= endband ; ++b){
1748 w += restore_band(smap, b, startx, endx, starty, endy, s->
pixx,
1749 cellpxy, cellpxx, auxvec);
1753 if(w == cellpxx * cellpxy){
1767 pthread_mutex_lock(&sengine->
lock);
1769 pthread_mutex_unlock(&sengine->
lock);
1770 pthread_cond_broadcast(&sengine->
cond);
1771 loginfo(
"joining %u sixel thread%s", tids, tids == 1 ?
"" :
"s");
1772 for(
unsigned t = 0 ; t < tids ; ++t){
1773 pthread_join(sengine->
tids[t],
NULL);
1775 pthread_mutex_destroy(&sengine->
lock);
1776 pthread_cond_destroy(&sengine->
cond);
1778 loginfo(
"reaped sixel engine");
1788 uint8_t* a = malloc(slen);
1790 memset(a, 0xff, slen);
void sprixel_invalidate(sprixel *s, int y, int x)
#define logerror(fmt,...)
#define logpanic(fmt,...)
void ncplane_abs_yx(const ncplane *n, int *RESTRICT y, int *RESTRICT x)
notcurses * ncplane_notcurses(const ncplane *n)
struct ncvisual_options v
API int API int const nccell unsigned len
uint8_t * sixel_trans_auxvec(const ncpile *p)
int sixel_scrub(const ncpile *p, sprixel *s)
void sixel_refresh(const ncpile *p, sprixel *s)
int sixel_init(tinfo *ti, int fd)
int sixel_init_inverted(tinfo *ti, int fd)
int sixel_rebuild(sprixel *s, int ycell, int xcell, uint8_t *auxvec)
void sixel_cleanup(tinfo *ti)
int sixel_draw(const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
int sixel_wipe(sprixel *s, int ycell, int xcell)
#define TRANS_PALETTE_ENTRY
int sixel_blit(ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
void sixelmap_free(sixelmap *s)
int sixel_init_forcesdm(tinfo *ti, int fd)
@ SPRIXCELL_ANNIHILATED_TRANS
struct blitterargs::@3::@5 pixel
unsigned char comps[RGBSIZE]
const struct blitterargs * bargs
pthread_t tids[POPULATION]
work_queue queues[POPULATION]
unsigned char * needs_refresh
qstate * qstates[WORKERDEPTH]
struct sixel_engine * sengine