[sixel] clean up sprixels on move #1449

pull/1494/head
nick black 4 years ago committed by Nick Black
parent 4a884a009e
commit fbe16935f3

@ -49,6 +49,7 @@ typedef enum {
SPRIXEL_QUIESCENT, // sprixel has been drawn
SPRIXEL_INVALIDATED, // sprixel needs to be redrawn
SPRIXEL_HIDE, // sprixel queued for destruction
SPRIXEL_MOVED, // sprixel needs be moved
} sprixel_e;
// elements of the T-A matrix
@ -77,6 +78,8 @@ typedef struct sprixel {
// each tacache entry is one of 0 (standard opaque cell), 1 (cell with
// some transparency), 2 (annihilated, excised)
int parse_start; // where to start parsing for cell wipes
int movedfromy; // for SPRIXEL_MOVED, the starting absolute position,
int movedfromx; // so that we can damage old cells when redrawn
} sprixel;
// A plane is memory for some rectilinear virtual window, plus current cursor
@ -360,6 +363,7 @@ typedef struct tinfo {
// this means dialing down their alpha to 0 (in equivalent space).
int (*pixel_cell_wipe)(const struct notcurses* nc, sprixel* s, int y, int x);
int (*pixel_init)(int fd);
int (*pixel_draw)(const struct notcurses* n, const struct ncpile* p, sprixel* s, FILE* out);
bool pixel_query_done; // have we yet performed pixel query?
bool sextants; // do we have (good, vetted) Unicode 13 sextant support?
bool braille; // do we have Braille support? (linux console does not)
@ -399,6 +403,40 @@ typedef struct ncdirect {
uint64_t flags; // copied in ncdirect_init() from param
} ncdirect;
// Extracellular state for a cell during the render process. There is one
// crender per rendered cell, and they are initialized to all zeroes.
struct crender {
const ncplane *p; // source of glyph for this cell
nccell c;
uint32_t hcfg; // fg channel prior to HIGHCONTRAST (need full channel)
sprixel* sprixel; // bitmap encountered during traversal
struct {
// If the glyph we render is from an ncvisual, and has a transparent or
// blended background, blitter stacking is in effect. This is a complicated
// issue, but essentially, imagine a bottom block is rendered with a green
// bottom and transparent top. on a lower plane, a top block is rendered
// with a red foreground and blue background. Normally, this would result
// in a blue top and green bottom, but that's not what we ever wanted --
// what makes sense is a red top and green bottom. So ncvisual rendering
// sets bits from CELL_BLITTERSTACK_MASK when rendering a cell with a
// transparent background. When paint() selects a glyph, it checks for these
// bits. If they are set, any lower planes with CELL_BLITTERSTACK_MASK set
// take this into account when solving the background color.
unsigned blittedquads: 4;
unsigned damaged: 1; // only used in rasterization
// if CELL_ALPHA_HIGHCONTRAST is in play, we apply the HSV flip once the
// background is locked in. set highcontrast to indicate this.
unsigned highcontrast: 1;
unsigned fgblends: 8;
unsigned bgblends: 8;
// we'll need recalculate the foreground relative to the solved background,
// and then reapply any foreground shading from above the highcontrast
// declaration. save the foreground state when we go highcontrast.
unsigned hcfgblends: 8; // number of foreground blends prior to HIGHCONTRAST
unsigned sprixeled: 1; // have we passed through a sprixel?
} s;
};
typedef struct ncpile {
ncplane* top; // topmost plane, never NULL
ncplane* bottom; // bottommost plane, never NULL
@ -767,7 +805,11 @@ int sprite_kitty_cell_wipe(const notcurses* nc, sprixel* s, int y, int x);
int sprite_destroy(const struct notcurses* nc, const struct ncpile* p, FILE* out, sprixel* s);
void sprixel_free(sprixel* s);
void sprixel_invalidate(sprixel* s);
void sprixel_movefrom(sprixel* s, int y, int x);
void sprixel_hide(sprixel* s);
int sprite_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
int kitty_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
int sixel_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
// dimy and dimx are cell geometry, not pixel. takes ownership of s on success.
sprixel* sprixel_create(ncplane* n, char* s, int bytes, int placey, int placex,
int sprixelid, int dimy, int dimx, int pixy, int pixx,

@ -242,7 +242,7 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx,
//fprintf(stderr, "total: %d chunks = %d, s=%d,v=%d\n", total, chunks, lenx, leny);
while(chunks--){
if(totalout == 0){
*parse_start = fprintf(fp, "\e_Gf=32,s=%d,v=%d,i=%d,a=T,%c=1;",
*parse_start = fprintf(fp, "\e_Gf=32,p=1,s=%d,v=%d,i=%d,a=T,%c=1;",
lenx, leny, sprixelid, chunks ? 'm' : 'q');
}else{
fprintf(fp, "\e_G%sm=%d;", chunks ? "" : "q=1,", chunks ? 1 : 0);
@ -348,11 +348,6 @@ int kitty_blit(ncplane* n, int linesize, const void* data,
return 1;
}
// clears all kitty bitmaps
int sprite_kitty_init(int fd){
return tty_emit("\e_Ga=d\e\\", fd);
}
// removes the kitty bitmap graphic identified by s->id
int sprite_kitty_annihilate(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s){
(void)p;
@ -362,3 +357,20 @@ int sprite_kitty_annihilate(const notcurses* nc, const ncpile* p, FILE* out, spr
}
return 0;
}
int kitty_draw(const notcurses* nc, const ncpile* p, sprixel* s, FILE* out){
(void)nc;
(void)p;
(void)out;
int ret = 0;
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
ret = -1;
}
s->invalidated = SPRIXEL_QUIESCENT;
return ret;
}
// clears all kitty bitmaps
int sprite_kitty_init(int fd){
return tty_emit("\e_Ga=d\e\\", fd);
}

@ -1933,12 +1933,11 @@ int ncplane_box(ncplane* n, const nccell* ul, const nccell* ur,
static void
move_bound_planes(ncplane* n, int dy, int dx){
while(n){
n->absy += dy;
n->absx += dx;
// FIXME do these only if we actually changed location
if(n->sprite){
sprixel_invalidate(n->sprite);
sprixel_movefrom(n->sprite, n->absy, n->absx);
}
n->absy += dy;
n->absx += dx;
move_bound_planes(n->blist, dy, dx);
n = n->bnext;
}
@ -1951,12 +1950,11 @@ int ncplane_move_yx(ncplane* n, int y, int x){
int dy, dx; // amount moved
dy = (n->boundto->absy + y) - n->absy;
dx = (n->boundto->absx + x) - n->absx;
n->absx += dx;
n->absy += dy;
// FIXME do these only if we actually changed location
if(n->sprite){
sprixel_invalidate(n->sprite);
sprixel_movefrom(n->sprite, n->absy, n->absx);
}
n->absx += dx;
n->absy += dy;
move_bound_planes(n->blist, dy, dx);
return 0;
}

@ -184,40 +184,6 @@ int cell_duplicate(ncplane* n, nccell* targ, const nccell* c){
return 0;
}
// Extracellular state for a cell during the render process. There is one
// crender per rendered cell, and they are initialized to all zeroes.
struct crender {
const ncplane *p; // source of glyph for this cell
nccell c;
uint32_t hcfg; // fg channel prior to HIGHCONTRAST (need full channel)
sprixel* sprixel; // bitmap encountered during traversal
struct {
// If the glyph we render is from an ncvisual, and has a transparent or
// blended background, blitter stacking is in effect. This is a complicated
// issue, but essentially, imagine a bottom block is rendered with a green
// bottom and transparent top. on a lower plane, a top block is rendered
// with a red foreground and blue background. Normally, this would result
// in a blue top and green bottom, but that's not what we ever wanted --
// what makes sense is a red top and green bottom. So ncvisual rendering
// sets bits from CELL_BLITTERSTACK_MASK when rendering a cell with a
// transparent background. When paint() selects a glyph, it checks for these
// bits. If they are set, any lower planes with CELL_BLITTERSTACK_MASK set
// take this into account when solving the background color.
unsigned blittedquads: 4;
unsigned damaged: 1; // only used in rasterization
// if CELL_ALPHA_HIGHCONTRAST is in play, we apply the HSV flip once the
// background is locked in. set highcontrast to indicate this.
unsigned highcontrast: 1;
unsigned fgblends: 8;
unsigned bgblends: 8;
// we'll need recalculate the foreground relative to the solved background,
// and then reapply any foreground shading from above the highcontrast
// declaration. save the foreground state when we go highcontrast.
unsigned hcfgblends: 8; // number of foreground blends prior to HIGHCONTRAST
unsigned sprixeled: 1; // have we passed through a sprixel?
} s;
};
// Emit fchannel with RGB changed to contrast effectively against bchannel.
static uint32_t
highcontrast(uint32_t bchannel){
@ -936,36 +902,34 @@ rasterize_sprixels(notcurses* nc, const ncpile* p, FILE* out){
sprixel** parent = &nc->sprixelcache;
int ret = 0;
while( (s = *parent) ){
if(s->invalidated == SPRIXEL_INVALIDATED){
if(s->invalidated == SPRIXEL_INVALIDATED || s->invalidated == SPRIXEL_MOVED){
int y, x;
ncplane_yx(s->n, &y, &x);
y += s->y;
x += s->x;
//fprintf(stderr, "DRAWING BITMAP %d AT %d/%d for %p\n", s->id, y + nc->stdplane->absy, x + nc->stdplane->absx, s->n);
if(goto_location(nc, out, y + nc->stdplane->absy, x + nc->stdplane->absx)){
return -1;
}
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
return -1;
if(goto_location(nc, out, y + nc->stdplane->absy, x + nc->stdplane->absx) == 0){
if(sprite_draw(nc, p, s, out)){
return -1;
}
nc->rstate.hardcursorpos = true;
parent = &s->next;
}else{
ret = -1;
}
s->invalidated = SPRIXEL_QUIESCENT;
nc->rstate.hardcursorpos = true;
parent = &s->next;
}else if(s->invalidated == SPRIXEL_HIDE){
//fprintf(stderr, "OUGHT HIDE %d [%dx%d @ %d/%d] %p\n", s->id, s->dimy, s->dimx, s->y, s->x, s);
int r = sprite_destroy(nc, p, out, s);
if(r < 0){
return -1;
}else if(r > 0){
ret = 1;
if(sprite_destroy(nc, p, out, s) == 0){
*parent = s->next;
sprixel_free(s);
}else{
ret = -1;
}
*parent = s->next;
sprixel_free(s);
}else{
parent = &s->next;
}
}
// FIXME what effect does emission have on rasterizing style state?
// FIXME what effect (if any) does emission have on rasterizing style state?
return ret;
}

@ -478,6 +478,24 @@ int sixel_blit(ncplane* n, int linesize, const void* data,
return r;
}
int sixel_draw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){
if(s->invalidated == SPRIXEL_MOVED){
for(int yy = s->movedfromy ; yy < s->movedfromy + s->dimy ; ++yy){
for(int xx = s->movedfromx ; xx < s->movedfromx + s->dimx ; ++xx){
if(yy < n->rstate.y || yy >= n->rstate.y + s->dimy ||
xx < n->rstate.x || xx >= n->rstate.x + s->dimx){
}
p->crender[yy * p->dimx + xx].s.damaged = 1;
}
}
}
if(fwrite(s->glyph, s->glyphlen, 1, out) != 1){
return -1;
}
s->invalidated = SPRIXEL_QUIESCENT;
return 0;
}
int sprite_sixel_init(int fd){
// \e[?8452: DECSDM private "sixel scrolling" mode keeps the sixel from
// scrolling, but puts it at the current cursor location (as opposed to

@ -7,10 +7,25 @@ void sprixel_free(sprixel* s){
}
}
// store the original (absolute) coordinates from which we moved, so that
// we can invalidate them in sprite_draw().
void sprixel_movefrom(sprixel* s, int y, int x){
if(s->invalidated != SPRIXEL_HIDE){
if(s->invalidated != SPRIXEL_MOVED){
s->invalidated = SPRIXEL_MOVED;
s->movedfromy = y;
s->movedfromx = x;
}
}
}
void sprixel_hide(sprixel* s){
s->invalidated = SPRIXEL_HIDE;
s->n->sprite = NULL;
s->n = NULL;
// guard so that a double call doesn't drop core on s->n->sprite
if(s->invalidated != SPRIXEL_HIDE){
s->invalidated = SPRIXEL_HIDE;
s->n->sprite = NULL;
s->n = NULL;
}
}
void sprixel_invalidate(sprixel* s){
@ -86,6 +101,12 @@ int sprite_wipe_cell(const notcurses* nc, sprixel* s, int ycell, int xcell){
return r;
}
// precondition: s->invalidated is SPRIXEL_INVALIDATED or SPRIXEL_MOVED.
int sprite_draw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){
int r = n->tcache.pixel_draw(n, p, s, out);
return r;
}
int sprite_init(const notcurses* nc){
if(!nc->tcache.pixel_init){
return 0;

@ -63,6 +63,7 @@ apply_term_heuristics(tinfo* ti, const char* termname){
ti->pixel_cell_wipe = sprite_kitty_cell_wipe;
ti->pixel_destroy = sprite_kitty_annihilate;
ti->pixel_init = sprite_kitty_init;
ti->pixel_draw = kitty_draw;
set_pixel_blitter(kitty_blit);
/*}else if(strstr(termname, "alacritty")){
ti->sextants = true; // alacritty https://github.com/alacritty/alacritty/issues/4409 */
@ -348,6 +349,7 @@ query_sixel(tinfo* ti, int fd){
ti->sixel_supported = true;
ti->color_registers = 256; // assumed default [shrug]
pixel_init = ti->pixel_init = sprite_sixel_init;
ti->pixel_draw = sixel_draw;
ti->sixel_maxx = ti->sixel_maxy = 0;
}
}

Loading…
Cancel
Save