[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.
pull/1485/head
nick black 3 years ago committed by Nick Black
parent 7127118d27
commit 58c1cdf055

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

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

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

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

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

Loading…
Cancel
Save