From 58c1cdf0556e50a92aef6973f94fe082f4c68309 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 28 Mar 2021 14:16:27 -0400 Subject: [PATCH] [bitmaps] ATL represent Fix Sixel mutation by printing the Sixel first, and only then printing text atop it. Come around a second time on sprixels only. All tests and demos now work under both the Sixel and the Kitty protocols. Huzzah! World leader, bitch! Ain't nobody else done this. --- src/lib/internal.h | 7 +-- src/lib/render.c | 19 ++++---- src/lib/sixel.c | 111 +-------------------------------------------- src/lib/sprite.c | 12 +++-- src/lib/terminfo.c | 1 - 5 files changed, 21 insertions(+), 129 deletions(-) diff --git a/src/lib/internal.h b/src/lib/internal.h index a7ced8e7e..947f29754 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -759,6 +759,10 @@ plane_debug(const ncplane* n, bool details){ } } +// nulls out a cell from a kitty bitmap via changing the alpha value +// throughout to 0. the same trick doesn't work on sixel, but there we +// can just print directly over the bitmap. +int sprite_kitty_cell_wipe(const notcurses* nc, sprixel* s, int y, int x); void sprixel_free(sprixel* s); void sprixel_invalidate(sprixel* s); void sprixel_hide(sprixel* s); @@ -1338,9 +1342,6 @@ ncdirect_bg_default_p(const struct ncdirect* nc){ return channels_bg_default_p(ncdirect_channels(nc)); } -int sprite_sixel_cell_wipe(const notcurses* nc, sprixel* s, int y, int x); -int sprite_kitty_cell_wipe(const notcurses* nc, sprixel* s, int y, int x); - int sixel_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx, const blitterargs* bargs); diff --git a/src/lib/render.c b/src/lib/render.c index 30b81825b..ce1368bc7 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -290,7 +290,11 @@ paint(const ncplane* p, struct crender* rvec, int dstleny, int dstlenx, // if we already have a glyph solved, and we run into a bitmap // cell, we need to null that cell out of the bitmap. if(crender->p || crender->s.bgblends){ - sprite_wipe_cell(ncplane_notcurses_const(p), p->sprite, y, x); + // if sprite_wipe_cell() fails, we presumably do not have the + // ability to wipe, and must reprint the character + if(sprite_wipe_cell(ncplane_notcurses_const(p), p->sprite, y, x)){ + crender->s.damaged = 1; + } }else if(!crender->p){ // if we are a bitmap, and above a cell that has changed (and // will thus be printed), we'll need redraw the sprixel. @@ -465,7 +469,7 @@ postpaint_cell(nccell* lastframe, int dimx, struct crender* crender, lock_in_highcontrast(targc, crender); nccell* prevcell = &lastframe[fbcellidx(y, dimx, *x)]; if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc) > 0){ - crender->s.damaged = true; + crender->s.damaged = 1; assert(!cell_wide_right_p(targc)); const int width = targc->width; for(int i = 1 ; i < width ; ++i){ @@ -479,7 +483,7 @@ postpaint_cell(nccell* lastframe, int dimx, struct crender* crender, targc->channels = crender[-i].c.channels; targc->stylemask = crender[-i].c.stylemask; if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc) > 0){ - crender->s.damaged = true; + crender->s.damaged = 1; } } } @@ -1100,16 +1104,11 @@ notcurses_rasterize_inner(notcurses* nc, const ncpile* p, FILE* out){ // need to home it expliticly. update_palette(nc, out); //fprintf(stderr, "pile %p ymax: %d xmax: %d\n", p, p->dimy + nc->stdplane->absy, p->dimx + nc->stdplane->absx); - if(rasterize_core(nc, p, out)){ + if(rasterize_sprixels(nc, p, out) < 0){ return -1; } - int r = rasterize_sprixels(nc, p, out); - if(r < 0){ + if(rasterize_core(nc, p, out)){ return -1; - }else if(r > 0){ - if(rasterize_core(nc, p, out)){ - return -1; - } } if(fflush(out)){ return -1; diff --git a/src/lib/sixel.c b/src/lib/sixel.c index 465e7484b..bff835ad5 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -1,114 +1,5 @@ #include "internal.h" -// sixel is in a sense simpler to edit in-place than kitty, as it has neither -// chunking nor base64 to worry about. in another sense, it's waaay suckier, -// because you effectively have to lex through a byte at a time (since the -// color bands have varying size). le sigh! we work geometrically here, -// blasting through each band and scrubbing the necessary cells therein. -// define a rectangle that will be scrubbed. -int sprite_sixel_cell_wipe(const notcurses* nc, sprixel* s, int ycell, int xcell){ - const int xpixels = nc->tcache.cellpixx; - const int ypixels = nc->tcache.cellpixy; - const int top = ypixels * ycell; // start scrubbing on this row - int bottom = ypixels * (ycell + 1); // do *not* scrub this row - const int left = xpixels * xcell; // start scrubbing on this column - int right = xpixels * (xcell + 1); // do *not* scrub this column - // if the cell is on the right or bottom borders, it might only be partially - // filled by actual graphic data, and we need to cap our target area. - if(right > s->pixx){ - right = s->pixx; - } - if(bottom > s->pixy){ - bottom = s->pixy; - } -//fprintf(stderr, "TARGET AREA: [ %dx%d -> %dx%d ] of %dx%d\n", top, left, bottom - 1, right - 1, s->pixy, s->pixx); - char* c = s->glyph; - // lines of sixels are broken by a hyphen. if we were guaranteed to already - // be in the meat of the sixel, it would be sufficient to count hyphens, but - // we must distinguish the introductory material from the sixmap, alas - // (after that, simply count hyphens). FIXME store loc in sprixel metadata? - // it seems sufficient to look for the first #d not followed by a semicolon. - // remember, these are sixels *we've* created internally, not random ones. - while(*c != '#'){ - ++c; - } - do{ - ++c; - while(isdigit(*c)){ - ++c; - } - while(*c == ';'){ - ++c; - while(isdigit(*c)){ - ++c; - } - } - }while(*c == '#'); - --c; - int row = 0; - while(row + 6 <= top){ - while(*c != '-'){ - ++c; - } - row += 6; - unsigned mask = 0; - if(row < top){ - for(int i = 0 ; i < top - row ; ++i){ - mask |= (1 << i); - } - } - // make masks containing only pixels which we will *not* be turning off - // (on the top or bottom), if any. go through each entry and if it - // occupies our target columns, scrub scrub scrub! - while(*c == '#' || isdigit(*c)){ - while(*c == '#' || isdigit(*c)){ - ++c; - } - int column = 0; - int rle = 0; - // here begins the substance, concluded by '-', '$', or '\e'. '!' indicates rle. - while(*c != '-' && *c != '$' && *c != '\e'){ - if(*c == '!'){ - rle = 0; - }else if(isdigit(*c)){ - rle *= 10; - rle += (*c - '0'); - }else{ - if(rle){ - // FIXME this can skip over the starting column - column += (rle - 1); - rle = 0; - } - if(column >= left && column < right){ // zorch it -//fprintf(stderr, "STARTED WITH %d %c\n", *c, *c); - *c = ((*c - 63) & mask) + 63; -//fprintf(stderr, "CHANGED TO %d %c\n", *c, *c); - } - ++column; - } - ++c; - } - if(*c == '-'){ - row += 6; - if(row >= bottom){ - return 0; - } - mask = 0; - if(bottom - row < 6){ - for(int i = 0 ; i < bottom - row ; ++i){ - mask |= (1 << (6 - i)); - } - } - }else if(*c == '\e'){ - return 0; - } - column = 0; - ++c; - } - } - return 0; -} - #define RGBSIZE 3 #define CENTSIZE (RGBSIZE + 1) // size of a color table entry @@ -250,7 +141,7 @@ extract_color_table(const uint32_t* data, int linesize, int begy, int begx, int int leny, int lenx, int cdimy, int cdimx, sixeltable* stab, sprixcell_e* tacache){ unsigned char mask = 0xc0; - int pos = 0; + int pos = 0; // pixel position for(int visy = begy ; visy < (begy + leny) ; visy += 6){ // pixel row for(int visx = begx ; visx < (begx + lenx) ; visx += 1){ // pixel column for(int sy = visy ; sy < (begy + leny) && sy < visy + 6 ; ++sy){ // offset within sprixel diff --git a/src/lib/sprite.c b/src/lib/sprite.c index e048092e0..8f5ed9f4f 100644 --- a/src/lib/sprite.c +++ b/src/lib/sprite.c @@ -71,9 +71,6 @@ int sprite_wipe_cell(const notcurses* nc, sprixel* s, int ycell, int xcell){ if(s->invalidated == SPRIXEL_HIDE){ // no need to do work if we're killing it return 0; } - if(!nc->tcache.pixel_cell_wipe){ - return 0; - } if(ycell >= s->dimy || ycell < 0){ logerror(nc, "Bad y coordinate %d (%d)\n", ycell, s->dimy); return -1; @@ -82,11 +79,16 @@ int sprite_wipe_cell(const notcurses* nc, sprixel* s, int ycell, int xcell){ logerror(nc, "Bad x coordinate %d (%d)\n", xcell, s->dimx); return -1; } - if(s->tacache[s->dimx * ycell + xcell] == 2){ + if(s->tacache[s->dimx * ycell + xcell] == SPRIXCELL_ANNIHILATED){ //fprintf(stderr, "CACHED WIPE %d %d/%d\n", s->id, ycell, xcell); return 0; // already annihilated } - s->tacache[s->dimx * ycell + xcell] = 2; + // mark the cell as annihilated whether we actually scrubbed it or not, + // so that we use this fact should we move to another frame + s->tacache[s->dimx * ycell + xcell] = SPRIXCELL_ANNIHILATED; + if(!nc->tcache.pixel_cell_wipe){ // sixel has no cell wiping + return -1; + } //fprintf(stderr, "WIPING %d %d/%d\n", s->id, ycell, xcell); int r = nc->tcache.pixel_cell_wipe(nc, s, ycell, xcell); if(r == 0){ diff --git a/src/lib/terminfo.c b/src/lib/terminfo.c index 855626f32..1ad9908e2 100644 --- a/src/lib/terminfo.c +++ b/src/lib/terminfo.c @@ -349,7 +349,6 @@ query_sixel(tinfo* ti, int fd){ ti->color_registers = 256; // assumed default [shrug] ti->pixel_destroy = sprite_sixel_annihilate; pixel_init = ti->pixel_init = sprite_sixel_init; - ti->pixel_cell_wipe = sprite_sixel_cell_wipe; ti->sixel_maxx = ti->sixel_maxy = 0; } }