[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.
This commit is contained in:
nick black 2021-03-28 14:16:27 -04:00 committed by Nick Black
parent 7127118d27
commit 58c1cdf055
5 changed files with 21 additions and 129 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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){

View File

@ -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;
}
}