Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
sixel.c File Reference
#include <math.h>
#include <stdatomic.h>
#include "internal.h"
#include "fbuf.h"
Include dependency graph for sixel.c:

Go to the source code of this file.

Data Structures

struct  qsample
 
struct  qnode
 
struct  onode
 
struct  sixelband
 
struct  sixelmap
 
struct  qstate
 
struct  work_queue
 
struct  sixel_engine
 
struct  band_extender
 

Macros

#define RGBSIZE   3
 
#define POPULATION   3
 
#define WORKERDEPTH   3
 
#define TRANS_PALETTE_ENTRY   65535
 
#define AUXVECELEMSIZE   2
 
#define QNODECOUNT   1000
 

Typedefs

typedef struct qsample qsample
 
typedef struct qnode qnode
 
typedef struct onode onode
 
typedef struct sixelband sixelband
 
typedef struct sixelmap sixelmap
 
typedef struct qstate qstate
 
typedef struct work_queue work_queue
 
typedef struct sixel_engine sixel_engine
 

Enumerations

enum  sixel_p2_e { SIXEL_P2_ALLOPAQUE = 0 , SIXEL_P2_TRANS = 1 }
 

Functions

void sixelmap_free (sixelmap *s)
 
int sixel_wipe (sprixel *s, int ycell, int xcell)
 
void sixel_refresh (const ncpile *p, sprixel *s)
 
int sixel_blit (ncplane *n, int linesize, const void *data, int leny, int lenx, const blitterargs *bargs)
 
int sixel_scrub (const ncpile *p, sprixel *s)
 
int sixel_draw (const tinfo *ti, const ncpile *p, sprixel *s, fbuf *f, int yoff, int xoff)
 
int sixel_init_forcesdm (tinfo *ti, int fd)
 
int sixel_init_inverted (tinfo *ti, int fd)
 
int sixel_init (tinfo *ti, int fd)
 
int sixel_rebuild (sprixel *s, int ycell, int xcell, uint8_t *auxvec)
 
void sixel_cleanup (tinfo *ti)
 
uint8_t * sixel_trans_auxvec (const ncpile *p)
 

Macro Definition Documentation

◆ AUXVECELEMSIZE

#define AUXVECELEMSIZE   2

Definition at line 20 of file sixel.c.

◆ POPULATION

#define POPULATION   3

Definition at line 10 of file sixel.c.

◆ QNODECOUNT

#define QNODECOUNT   1000

Definition at line 266 of file sixel.c.

◆ RGBSIZE

#define RGBSIZE   3

Definition at line 6 of file sixel.c.

◆ TRANS_PALETTE_ENTRY

#define TRANS_PALETTE_ENTRY   65535

Definition at line 17 of file sixel.c.

◆ WORKERDEPTH

#define WORKERDEPTH   3

Definition at line 13 of file sixel.c.

Typedef Documentation

◆ onode

typedef struct onode onode

◆ qnode

typedef struct qnode qnode

◆ qsample

typedef struct qsample qsample

◆ qstate

typedef struct qstate qstate

◆ sixel_engine

typedef struct sixel_engine sixel_engine

◆ sixelband

typedef struct sixelband sixelband

◆ sixelmap

typedef struct sixelmap sixelmap

◆ work_queue

typedef struct work_queue work_queue

Enumeration Type Documentation

◆ sixel_p2_e

enum sixel_p2_e
Enumerator
SIXEL_P2_ALLOPAQUE 
SIXEL_P2_TRANS 

Definition at line 55 of file sixel.c.

55 {
sixel_p2_e
Definition sixel.c:55
@ SIXEL_P2_TRANS
Definition sixel.c:57
@ SIXEL_P2_ALLOPAQUE
Definition sixel.c:56

Function Documentation

◆ sixel_blit()

int sixel_blit ( ncplane n,
int  linesize,
const void *  data,
int  leny,
int  lenx,
const blitterargs bargs 
)

Definition at line 1389 of file sixel.c.

1390 {
1391 if(bargs->u.pixel.colorregs >= TRANS_PALETTE_ENTRY){
1392 logerror("palette too large %d", bargs->u.pixel.colorregs);
1393 return -1;
1394 }
1395 sixelmap* smap = sixelmap_create(leny - bargs->begy);
1396 if(smap == NULL){
1397 return -1;
1398 }
1399 assert(n->tam);
1400 qstate* qs;
1401 if((qs = alloc_qstate(bargs->u.pixel.colorregs)) == NULL){
1402 logerror("couldn't allocate qstate");
1403 sixelmap_free(smap);
1404 return -1;
1405 }
1406 qs->bargs = bargs;
1407 qs->data = data;
1408 qs->linesize = linesize;
1409 qs->smap = smap;
1410 qs->leny = leny;
1411 qs->lenx = lenx;
1412 sixel_engine* sengine = ncplane_pile(n) ? ncplane_notcurses(n)->tcache.sixelengine : NULL;
1413 if(extract_color_table(sengine, qs)){
1414 free(bargs->u.pixel.spx->needs_refresh);
1415 bargs->u.pixel.spx->needs_refresh = NULL;
1416 sixelmap_free(smap);
1417 free_qstate(qs);
1418 return -1;
1419 }
1420 // takes ownership of sixelmap on success
1421 int r = sixel_blit_inner(qs, smap, bargs, n->tam);
1422 free_qstate(qs);
1423 if(r < 0){
1424 sixelmap_free(smap);
1425 // FIXME free refresh table?
1426 }
1427 scrub_color_table(bargs->u.pixel.spx);
1428 // we haven't actually emitted the body of the sixel yet. instead, we'll emit
1429 // it at sixel_redraw(), thus avoiding a double emission in the case of wipes
1430 // taking place before it's visible.
1431 bargs->u.pixel.spx->wipes_outstanding = 1;
1432 return r;
1433}
assert(false)
free(duplicated)
int r
Definition fbuf.h:226
#define logerror(fmt,...)
Definition logging.h:32
notcurses * ncplane_notcurses(const ncplane *n)
Definition notcurses.c:2626
vopts n
Definition notcurses.h:3502
#define TRANS_PALETTE_ENTRY
Definition sixel.c:17
void sixelmap_free(sixelmap *s)
Definition sixel.c:216
struct blitterargs::@3::@5 pixel
sprixel * spx
Definition internal.h:388
union blitterargs::@3 u
int colorregs
Definition internal.h:387
tinfo tcache
Definition internal.h:360
Definition sixel.c:88
const uint32_t * data
Definition sixel.c:103
sixelmap * smap
Definition sixel.c:105
int linesize
Definition sixel.c:104
const struct blitterargs * bargs
Definition sixel.c:102
int leny
Definition sixel.c:108
int lenx
Definition sixel.c:108
bool wipes_outstanding
Definition sprite.h:158
unsigned char * needs_refresh
Definition sprite.h:156
void * sixelengine
Definition termdesc.h:175
return NULL
Definition termdesc.h:229
Here is the call graph for this function:

◆ sixel_cleanup()

void sixel_cleanup ( tinfo ti)

Definition at line 1764 of file sixel.c.

1764 {
1765 sixel_engine* sengine = ti->sixelengine;
1766 const unsigned tids = POPULATION;
1767 pthread_mutex_lock(&sengine->lock);
1768 sengine->done = 1;
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);
1774 }
1775 pthread_mutex_destroy(&sengine->lock);
1776 pthread_cond_destroy(&sengine->cond);
1777 free(sengine);
1778 loginfo("reaped sixel engine");
1779 ti->sixelengine = NULL;
1780 // no way to know what the state was before; we ought use XTSAVE/XTRESTORE
1781}
#define loginfo(fmt,...)
Definition logging.h:42
#define POPULATION
Definition sixel.c:10
pthread_cond_t cond
Definition sixel.c:124
pthread_mutex_t lock
Definition sixel.c:123
bool done
Definition sixel.c:127
pthread_t tids[POPULATION]
Definition sixel.c:126
Here is the call graph for this function:

◆ sixel_draw()

int sixel_draw ( const tinfo ti,
const ncpile p,
sprixel s,
fbuf f,
int  yoff,
int  xoff 
)

Definition at line 1477 of file sixel.c.

1478 {
1479 (void)ti;
1480 // if we've wiped or rebuilt any cells, effect those changes now, or else
1481 // we'll get flicker when we move to the new location.
1482 if(s->wipes_outstanding){
1483 if(sixel_reblit(s)){
1484 return -1;
1485 }
1486 s->wipes_outstanding = false;
1487 }
1488 if(p){
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)){
1492 return -1;
1493 }
1494 if(s->invalidated == SPRIXEL_MOVED){
1495 for(int yy = s->movedfromy ; yy < s->movedfromy + (int)s->dimy && yy < (int)p->dimy ; ++yy){
1496 if(yy < 0){
1497 continue;
1498 }
1499 for(int xx = s->movedfromx ; xx < s->movedfromx + (int)s->dimx && xx < (int)p->dimx ; ++xx){
1500 if(xx < 0){
1501 continue;
1502 }
1503 struct crender *r = &p->crender[yy * p->dimx + xx];
1504 if(!r->sprixel || sprixel_state(r->sprixel, yy, xx) != SPRIXCELL_OPAQUE_SIXEL){
1505 r->s.damaged = 1;
1506 }
1507 }
1508 }
1509 }
1510 }
1511 if(fbuf_putn(f, s->glyph.buf, s->glyph.used) < 0){
1512 return -1;
1513 }
1514 s->invalidated = SPRIXEL_QUIESCENT;
1515 return s->glyph.used;
1516}
@ SPRIXCELL_OPAQUE_SIXEL
Definition sprite.h:116
@ SPRIXEL_QUIESCENT
Definition sprite.h:21
@ SPRIXEL_MOVED
Definition sprite.h:26
struct crender::@2 s
const ncplane * p
Definition internal.h:267
unsigned dimx
Definition internal.h:325
struct notcurses * nc
Definition internal.h:322
unsigned dimy
Definition internal.h:325
int absy
Definition internal.h:83
int absx
Definition internal.h:83
unsigned dimx
Definition sprite.h:146
int movedfromx
Definition sprite.h:151
int movedfromy
Definition sprite.h:150
unsigned dimy
Definition sprite.h:146
struct ncplane * n
Definition sprite.h:142
sprixel_e invalidated
Definition sprite.h:143

◆ sixel_init()

int sixel_init ( tinfo ti,
int  fd 
)

Definition at line 1602 of file sixel.c.

1602 {
1603 return sixel_init_core(ti, "\e[?8452h", fd);
1604}

◆ sixel_init_forcesdm()

int sixel_init_forcesdm ( tinfo ti,
int  fd 
)

Definition at line 1588 of file sixel.c.

1588 {
1589 return sixel_init_core(ti, "\e[?80l\e[?8452h", fd);
1590}

◆ sixel_init_inverted()

int sixel_init_inverted ( tinfo ti,
int  fd 
)

Definition at line 1592 of file sixel.c.

1592 {
1593 // some terminals, at some versions, invert the sense of DECSDM. for those,
1594 // we must use 80h rather than the correct 80l. this grows out of a
1595 // misunderstanding in XTerm through patchlevel 368, which was widely
1596 // copied into other terminals.
1597 return sixel_init_core(ti, "\e[?80h\e[?8452h", fd);
1598}

◆ sixel_rebuild()

int sixel_rebuild ( sprixel s,
int  ycell,
int  xcell,
uint8_t *  auxvec 
)

Definition at line 1724 of file sixel.c.

1724 {
1725//fprintf(stderr, "REBUILDING %d/%d\n", ycell, xcell);
1726 if(auxvec == NULL){
1727 return -1;
1728 }
1729 const int cellpxy = ncplane_pile(s->n)->cellpxy;
1730 const int cellpxx = ncplane_pile(s->n)->cellpxx;
1731 sixelmap* smap = s->smap;
1732 const int startx = xcell * cellpxx;
1733 const int starty = ycell * cellpxy;
1734 int endx = ((xcell + 1) * cellpxx);
1735 if(endx >= s->pixx){
1736 endx = s->pixx;
1737 }
1738 int endy = ((ycell + 1) * cellpxy);
1739 if(endy >= s->pixy){
1740 endy = s->pixy;
1741 }
1742 const int startband = starty / 6;
1743 const int endband = (endy - 1) / 6;
1744//fprintf(stderr, "%d/%d start: %d/%d end: %d/%d bands: %d-%d\n", ycell, xcell, starty, startx, endy, endx, starty / 6, endy / 6);
1745 // walk through each color, and wipe the necessary sixels from each band
1746 int w = 0;
1747 for(int b = startband ; b <= endband ; ++b){
1748 w += restore_band(smap, b, startx, endx, starty, endy, s->pixx,
1749 cellpxy, cellpxx, auxvec);
1750 }
1751 s->wipes_outstanding = true;
1752 sprixcell_e newstate;
1753 if(w == cellpxx * cellpxy){
1754 newstate = SPRIXCELL_TRANSPARENT;
1755 }else if(w){
1756 newstate = SPRIXCELL_MIXED_SIXEL;
1757 }else{
1758 newstate = SPRIXCELL_OPAQUE_SIXEL;
1759 }
1760 s->n->tam[s->dimx * ycell + xcell].state = newstate;
1761 return 1;
1762}
sprixcell_e
Definition sprite.h:114
@ SPRIXCELL_TRANSPARENT
Definition sprite.h:115
@ SPRIXCELL_MIXED_SIXEL
Definition sprite.h:118
tament * tam
Definition internal.h:106
int pixx
Definition sprite.h:147
int pixy
Definition sprite.h:147
struct sixelmap * smap
Definition sprite.h:157
sprixcell_e state
Definition sprite.h:127

◆ sixel_refresh()

void sixel_refresh ( const ncpile p,
sprixel s 
)

Definition at line 725 of file sixel.c.

725 {
726 if(s->needs_refresh == NULL){
727 return;
728 }
729 int absy, absx;
730 ncplane_abs_yx(s->n, &absy, &absx);
731 for(unsigned y = 0 ; y < s->dimy ; ++y){
732 const unsigned yy = absy + y;
733 for(unsigned x = 0 ; x < s->dimx ; ++x){
734 unsigned idx = y * s->dimx + x;
735 if(s->needs_refresh[idx]){
736 const unsigned xx = absx + x;
737 if(xx < p->dimx && yy < p->dimy){
738 unsigned ridx = yy * p->dimx + xx;
739 struct crender *r = &p->crender[ridx];
740 r->s.damaged = 1;
741 }
742 }
743 }
744 }
745 free(s->needs_refresh);
746 s->needs_refresh = NULL;
747}
uint32_t idx
Definition egcpool.h:298
void ncplane_abs_yx(const ncplane *n, int *RESTRICT y, int *RESTRICT x)
Definition notcurses.c:2642
int y
Definition notcurses.h:1905
int int x
Definition notcurses.h:1905
Here is the call graph for this function:

◆ sixel_scrub()

int sixel_scrub ( const ncpile p,
sprixel s 
)

Definition at line 1439 of file sixel.c.

1439 {
1440 loginfo("%d state %d at %d/%d (%d/%d)", s->id, s->invalidated, s->movedfromy, s->movedfromx, s->dimy, s->dimx);
1441 int starty = s->movedfromy;
1442 int startx = s->movedfromx;
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;
1446 struct crender *r = &p->crender[ridx];
1447 if(!s->n){
1448 // need this to damage cells underneath a sprixel we're removing
1449 r->s.damaged = 1;
1450 continue;
1451 }
1452 sprixel* trues = r->sprixel ? r->sprixel : s;
1453 if(yy >= (int)trues->n->leny || yy - trues->n->absy < 0){
1454 r->s.damaged = 1;
1455 continue;
1456 }
1457 if(xx >= (int)trues->n->lenx || xx - trues->n->absx < 0){
1458 r->s.damaged = 1;
1459 continue;
1460 }
1461 sprixcell_e state = sprixel_state(trues, yy, xx);
1462//fprintf(stderr, "CHECKING %d/%d state: %d %d/%d\n", yy - s->movedfromy - s->n->absy, xx - s->movedfromx - s->n->absx, state, yy, xx);
1463 if(state == SPRIXCELL_TRANSPARENT || state == SPRIXCELL_MIXED_SIXEL){
1464 r->s.damaged = 1;
1465 }else if(s->invalidated == SPRIXEL_MOVED){
1466 // ideally, we wouldn't damage our annihilated sprixcells, but if
1467 // we're being annihilated only during this cycle, we need to go
1468 // ahead and damage it.
1469 r->s.damaged = 1;
1470 }
1471 }
1472 }
1473 return 1;
1474}
unsigned lenx
Definition internal.h:86
unsigned leny
Definition internal.h:86
uint32_t id
Definition sprite.h:139
Here is the caller graph for this function:

◆ sixel_trans_auxvec()

uint8_t * sixel_trans_auxvec ( const ncpile p)

Definition at line 1786 of file sixel.c.

1786 {
1787 const size_t slen = AUXVECELEMSIZE * p->cellpxy * p->cellpxx;
1788 uint8_t* a = malloc(slen);
1789 if(a){
1790 memset(a, 0xff, slen);
1791 }
1792 return a;
1793}
#define AUXVECELEMSIZE
Definition sixel.c:20
unsigned cellpxx
Definition internal.h:326
unsigned cellpxy
Definition internal.h:326
Here is the caller graph for this function:

◆ sixel_wipe()

int sixel_wipe ( sprixel s,
int  ycell,
int  xcell 
)

Definition at line 655 of file sixel.c.

655 {
656//fprintf(stderr, "WIPING %d/%d\n", ycell, xcell);
657 uint8_t* auxvec = sixel_trans_auxvec(ncplane_pile(s->n));
658 if(auxvec == NULL){
659 return -1;
660 }
661 const int cellpxy = ncplane_pile(s->n)->cellpxy;
662 const int cellpxx = ncplane_pile(s->n)->cellpxx;
663 sixelmap* smap = s->smap;
664 const int startx = xcell * cellpxx;
665 const int starty = ycell * cellpxy;
666 int endx = ((xcell + 1) * cellpxx);
667 if(endx >= s->pixx){
668 endx = s->pixx;
669 }
670 int endy = ((ycell + 1) * cellpxy);
671 if(endy >= s->pixy){
672 endy = s->pixy;
673 }
674 const int startband = starty / 6;
675 const int endband = (endy - 1) / 6;
676//fprintf(stderr, "y/x: %d/%d bands: %d-%d start: %d/%d end: %d/%d\n", ycell, xcell, startband, endband - 1, starty, startx, endy, endx);
677 // walk through each color, and wipe the necessary sixels from each band
678 int w = 0;
679 for(int b = startband ; b <= endband ; ++b){
680 w += wipe_band(smap, b, startx, endx, starty, endy, s->pixx,
681 cellpxy, cellpxx, auxvec);
682 }
683 if(w){
684 s->wipes_outstanding = true;
685 }
686 change_p2(s->glyph.buf, SIXEL_P2_TRANS);
687 assert(NULL == s->n->tam[s->dimx * ycell + xcell].auxvector);
688 s->n->tam[s->dimx * ycell + xcell].auxvector = auxvec;
689 // FIXME this invalidation ought not be necessary, since we're simply
690 // wiping, and thus a glyph is going to be printed over whatever we've
691 // just destroyed. in alacritty, however, this isn't sufficient to knock
692 // out a graphic; we need repaint with the transparency.
693 // see https://github.com/dankamongmen/notcurses/issues/2142
694 int absx, absy;
695 ncplane_abs_yx(s->n, &absy, &absx);
696 sprixel_invalidate(s, absy, absx);
697 return 0;
698}
void sprixel_invalidate(sprixel *s, int y, int x)
Definition sprite.c:103
uint8_t * sixel_trans_auxvec(const ncpile *p)
Definition sixel.c:1786
char * buf
Definition fbuf.h:28
fbuf glyph
Definition sprite.h:138
void * auxvector
Definition sprite.h:128
Here is the call graph for this function:

◆ sixelmap_free()

void sixelmap_free ( sixelmap s)

Definition at line 216 of file sixel.c.

216 {
217 if(s){
218 for(int i = 0 ; i < s->sixelbands ; ++i){
219 sixelband_free(&s->bands[i]);
220 }
221 free(s->bands);
222 free(s);
223 }
224}
int sixelbands
Definition sixel.c:83
sixelband * bands
Definition sixel.c:84
Here is the call graph for this function:
Here is the caller graph for this function: