Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sixel fixel #1480

Merged
merged 3 commits into from
Mar 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions src/demo/xray.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,14 @@ make_slider(struct notcurses* nc, int dimy, int dimx){
}

static int
perframecb(struct ncvisual* ncv __attribute__ ((unused)),
struct ncvisual_options* vopts,
perframecb(struct ncvisual* ncv, struct ncvisual_options* vopts,
const struct timespec* tspec, void* vnewplane){
(void)ncv;
// only need these two steps done once, but we can't do them in
// main() due to the plane being created in ncvisual_stream() =[
ncplane_set_resizecb(vopts->n, ncplane_resize_maximize);
ncplane_move_above(vnewplane, vopts->n);

struct notcurses* nc = ncplane_notcurses(vopts->n);
static int frameno = 0;
int x;
Expand All @@ -81,16 +86,6 @@ int xray_demo(struct notcurses* nc){
int dimx, dimy;
notcurses_term_dim_yx(nc, &dimy, &dimx);
ncplane_erase(notcurses_stdplane(nc));
struct ncplane_options nopts = {
.y = 1,
.rows = dimy - 2,
.cols = dimx,
.resizecb = ncplane_resize_maximize,
};
struct ncplane* n = ncplane_create(notcurses_stdplane(nc), &nopts);
if(n == NULL){
return -1;
}
char* path = find_data("notcursesIII.mkv");
struct ncvisual* ncv = ncvisual_from_file(path);
free(path);
Expand All @@ -100,11 +95,9 @@ int xray_demo(struct notcurses* nc){
struct ncplane* newpanel = make_slider(nc, dimy, dimx);
if(newpanel == NULL){
ncvisual_destroy(ncv);
ncplane_destroy(n);
return -1;
}
struct ncvisual_options vopts = {
.n = n,
.y = NCALIGN_CENTER,
.x = NCALIGN_CENTER,
.scaling = NCSCALE_SCALE_HIRES,
Expand All @@ -120,7 +113,6 @@ int xray_demo(struct notcurses* nc){
}
int ret = ncvisual_stream(nc, ncv, dm, perframecb, &vopts, newpanel);
ncvisual_destroy(ncv);
ncplane_destroy(n);
ncplane_destroy(newpanel);
return ret;
}
9 changes: 5 additions & 4 deletions src/lib/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,11 @@ 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);
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_hide(sprixel* s);
Expand All @@ -772,7 +777,6 @@ API int sprite_wipe_cell(const notcurses* nc, sprixel* s, int y, int x);
int sprite_kitty_annihilate(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s);
int sprite_kitty_init(int fd);
int sprite_sixel_init(int fd);
int sprite_sixel_annihilate(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s);
int sprite_init(const notcurses* nc);
void sprixel_invalidate(sprixel* s);
sprixel* sprixel_by_id(notcurses* nc, uint32_t id);
Expand Down Expand Up @@ -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);

Expand Down
29 changes: 10 additions & 19 deletions src/lib/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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){
Expand All @@ -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;
}
}
}
Expand Down Expand Up @@ -921,14 +925,6 @@ emit_bg_palindex(notcurses* nc, FILE* out, const nccell* srccell){
return 0;
}

int sprite_sixel_annihilate(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s){
(void)nc;
(void)p;
(void)out;
(void)s;
return 0;
}

// returns -1 on error, 0 on success, 1 on success + invalidations requiring
// a subsequent repass by the rasterizer.
static int
Expand All @@ -954,7 +950,7 @@ rasterize_sprixels(notcurses* nc, const ncpile* p, FILE* out){
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 = nc->tcache.pixel_destroy(nc, p, out, s);
int r = sprite_destroy(nc, p, out, s);
if(r < 0){
return -1;
}else if(r > 0){
Expand Down Expand Up @@ -1100,16 +1096,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;
Expand Down
113 changes: 2 additions & 111 deletions src/lib/sixel.c
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand All @@ -260,7 +151,7 @@ extract_color_table(const uint32_t* data, int linesize, int begy, int begx, int
}
int txyidx = (sy / cdimy) * cols + (visx / cdimx);
if(tacache[txyidx] == SPRIXCELL_ANNIHILATED){
//fprintf(stderr, "TRANS SKIP %d %d %d %d\n", visy, visx, sy, txyidx);
//fprintf(stderr, "TRANS SKIP %d %d %d %d (cell: %d %d)\n", visy, visx, sy, txyidx, sy / cdimy, visx / cdimx);
continue;
}
unsigned char comps[RGBSIZE];
Expand Down
19 changes: 14 additions & 5 deletions src/lib/sprite.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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){
Expand All @@ -101,3 +103,10 @@ int sprite_init(const notcurses* nc){
}
return nc->tcache.pixel_init(nc->ttyfd);
}

int sprite_destroy(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s){
if(!nc->tcache.pixel_destroy){
return 0;
}
return nc->tcache.pixel_destroy(nc, p, out, s);
}
2 changes: 0 additions & 2 deletions src/lib/terminfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,7 @@ query_sixel(tinfo* ti, int fd){
if(!ti->sixel_supported){
ti->sixel_supported = true;
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;
}
}
Expand Down