implement ncplane_mergedown() #361

This commit is contained in:
nick black 2020-03-21 01:52:28 -04:00 committed by Nick Black
parent 38f4255dc0
commit 7e71ad8e11
3 changed files with 123 additions and 40 deletions

View File

@ -1749,6 +1749,28 @@ cell_simple_p(const cell* c){
// is invalidated by any further operation on the plane 'n', so...watch out!
API const char* cell_extended_gcluster(const struct ncplane* n, const cell* c);
// Returns true if the two cells are distinct EGCs, attributes, or channels.
// The actual egcpool index needn't be the same--indeed, the planes needn't even
// be the same. Only the expanded EGC must be equal. The EGC must be bit-equal;
// it would probably be better to test whether they're Unicode-equal FIXME.
static inline bool
cellcmp(const struct ncplane* n1, const cell* RESTRICT c1,
const struct ncplane* n2, const cell* RESTRICT c2){
if(c1->attrword != c2->attrword){
return true;
}
if(c1->channels != c2->channels){
return true;
}
if(cell_simple_p(c1) && cell_simple_p(c2)){
return c1->gcluster != c2->gcluster;
}
if(cell_simple_p(c1) || cell_simple_p(c2)){
return true;
}
return strcmp(cell_extended_gcluster(n1, c1), cell_extended_gcluster(n2, c2));
}
// True if the cell does not generate foreground pixels (i.e., the cell is
// entirely whitespace or special characters).
// FIXME do this at cell prep time and set a bit in the channels

View File

@ -205,9 +205,10 @@ lock_in_highcontrast(cell* targc, struct crender* crender){
// Paints a single ncplane into the provided framebuffer 'fb'. Whenever a cell
// is locked in, it is compared against the last frame. If it is different, the
// 'damagevec' bitmap is updated with a 1.
// 'damagevec' bitmap is updated with a 1. 'pool' is typically nc->pool, but can
// be whatever's backing fb.
static int
paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){
paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec, cell* fb, egcpool* pool){
int y, x, dimy, dimx, offy, offx;
// don't use ncplane_dim_yx()/ncplane_yx() here, lest we deadlock
dimy = p->leny;
@ -312,14 +313,14 @@ paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){
// which were already locked in were skipped at the top of the loop)?
if(cell_locked_p(targc)){
lock_in_highcontrast(targc, crender);
cell* prevcell = &nc->lastframe[fbcellidx(absy, nc->lfdimx, absx)];
cell* prevcell = &lastframe[fbcellidx(absy, nc->lfdimx, absx)];
/*if(cell_simple_p(targc)){
fprintf(stderr, "WROTE %u [%c] to %d/%d (%d/%d)\n", targc->gcluster, prevcell->gcluster, y, x, absy, absx);
}else{
fprintf(stderr, "WROTE %u [%s] to %d/%d (%d/%d)\n", targc->gcluster, extended_gcluster(crender->p, targc), y, x, absy, absx);
}
fprintf(stderr, "POOL: %p NC: %p SRC: %p\n", nc->pool.pool, nc, crender->p);*/
if(cellcmp_and_dupfar(&nc->pool, prevcell, crender->p, targc)){
if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc)){
crender->damaged = true;
if(cell_double_wide_p(targc)){
ncplane* tmpp = crender->p;
@ -331,7 +332,7 @@ fprintf(stderr, "POOL: %p NC: %p SRC: %p\n", nc->pool.pool, nc, crender->p);*/
targc->gcluster = 0;
targc->channels = targc[-1].channels;
targc->attrword = targc[-1].attrword;
if(cellcmp_and_dupfar(&nc->pool, prevcell, crender->p, targc)){
if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc)){
crender->damaged = true;
}
}
@ -342,12 +343,62 @@ fprintf(stderr, "POOL: %p NC: %p SRC: %p\n", nc->pool.pool, nc, crender->p);*/
return 0;
}
static int
ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst){
if(paint(nc, src, rvec, fb)){
static void
init_fb(cell* fb, int dimy, int dimx){
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
cell* c = &fb[fbcellidx(y, dimx, x)];
c->gcluster = 0;
c->channels = 0;
c->attrword = 0;
cell_set_fg_alpha(c, CELL_ALPHA_TRANSPARENT);
cell_set_bg_alpha(c, CELL_ALPHA_TRANSPARENT);
}
}
}
static void
postpaint(cell* fb, cell* lastframe, int dimy, int dimx,
struct crender* rvec, egcpool* pool){
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
cell* targc = &fb[fbcellidx(y, dimx, x)];
if(!cell_locked_p(targc)){
struct crender* crender = &rvec[fbcellidx(y, dimx, x)];
lock_in_highcontrast(targc, crender);
cell* prevcell = &lastframe[fbcellidx(y, dimx, x)];
if(targc->gcluster == 0){
targc->gcluster = ' ';
}
if(cellcmp_and_dupfar(pool, prevcell, crender->p, targc)){
crender->damaged = true;
}
}
}
}
}
// FIXME need handle a dst that isn't the standard plane! paint() will only
// paint within the real viewport currently.
int ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst){
notcurses* nc = src->nc;
int dimy, dimx;
ncplane_dim_yx(dst, &dimy, &dimx);
cell* fb = malloc(sizeof(*fb) * dimy * dimx);
const size_t crenderlen = sizeof(struct crender) * dimy * dimx;
struct crender* rvec = malloc(crenderlen);
memset(rvec, 0, crenderlen);
init_fb(fb, dimy, dimx);
if(paint(nc, src, dst->fb, rvec, fb, &dst->pool) || paint(nc, dst, dst->fb, rvec, fb, &dst->pool)){
free(rvec);
free(fb);
return -1;
}
// if src is below dst, dst
postpaint(fb, dst->fb, dimy, dimx, rvec, &dst->pool);
free(dst->fb);
dst->fb = fb;
free(rvec);
return 0;
}
// We execute the painter's algorithm, starting from our topmost plane. The
@ -360,43 +411,19 @@ notcurses_render_internal(notcurses* nc, struct crender* rvec){
if(reshape_shadow_fb(nc)){
return -1;
}
// don't use ncplane_dim_yx()/ncplane_yx() here, lest we deadlock
int dimy = nc->stdscr->leny;
int dimx = nc->stdscr->lenx;
int dimy, dimx;
ncplane_dim_yx(nc->stdscr, &dimy, &dimx);
cell* fb = malloc(sizeof(*fb) * dimy * dimx);
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
cell* c = &fb[fbcellidx(y, dimx, x)];
c->gcluster = 0;
c->channels = 0;
c->attrword = 0;
cell_set_fg_alpha(c, CELL_ALPHA_TRANSPARENT);
cell_set_bg_alpha(c, CELL_ALPHA_TRANSPARENT);
}
}
init_fb(fb, dimy, dimx);
ncplane* p = nc->top;
while(p){
if(paint(nc, p, rvec, fb)){
if(paint(nc, p, nc->lastframe, rvec, fb, &nc->pool)){
free(fb);
return -1;
}
p = p->z;
}
for(int y = 0 ; y < dimy ; ++y){
for(int x = 0 ; x < dimx ; ++x){
cell* targc = &fb[fbcellidx(y, dimx, x)];
if(!cell_locked_p(targc)){
struct crender* crender = &rvec[fbcellidx(y, dimx, x)];
lock_in_highcontrast(targc, crender);
cell* prevcell = &nc->lastframe[fbcellidx(y, dimx, x)];
if(targc->gcluster == 0){
targc->gcluster = ' ';
}
if(cellcmp_and_dupfar(&nc->pool, prevcell, crender->p, targc)){
crender->damaged = true;
}
}
}
}
postpaint(fb, nc->lastframe, dimy, dimx, rvec, &nc->pool);
free(fb);
return 0;
}
@ -979,7 +1006,7 @@ int notcurses_render(notcurses* nc){
int ret;
clock_gettime(CLOCK_MONOTONIC, &start);
int bytes = -1;
size_t crenderlen = sizeof(struct crender) * nc->stdscr->leny * nc->stdscr->lenx;
const size_t crenderlen = sizeof(struct crender) * nc->stdscr->leny * nc->stdscr->lenx;
struct crender* crender = malloc(crenderlen);
memset(crender, 0, crenderlen);
if(notcurses_render_internal(nc, crender) == 0){

View File

@ -311,6 +311,40 @@ TEST_CASE("Fills") {
CHECK(0 == notcurses_render(nc_));
}
SUBCASE("MergeDown") {
auto p1 = ncplane_new(nc_, 1, 10, 0, 0, nullptr);
REQUIRE(p1);
// make sure glyphs replace nulls
CHECK(0 < ncplane_putstr(p1, "0123456789"));
CHECK(0 == ncplane_mergedown(p1, n_));
cell cbase = CELL_TRIVIAL_INITIALIZER;
cell cp = CELL_TRIVIAL_INITIALIZER;
for(int i = 0 ; i < 10 ; ++i){
CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase));
CHECK(0 < ncplane_at_yx(p1, 0, i, &cp));
CHECK(0 == cellcmp(n_, &cbase, p1, &cp));
}
CHECK(0 == ncplane_cursor_move_yx(p1, 0, 0));
// make sure glyphs replace glyps
CHECK(0 < ncplane_putstr(p1, "9876543210"));
CHECK(0 == ncplane_mergedown(p1, n_));
for(int i = 0 ; i < 10 ; ++i){
CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase));
CHECK(0 < ncplane_at_yx(p1, 0, i, &cp));
CHECK(0 == cellcmp(n_, &cbase, p1, &cp));
}
// make sure nulls do not replace glyphs
auto p2 = ncplane_new(nc_, 1, 10, 0, 0, nullptr);
CHECK(0 == ncplane_mergedown(p2, n_));
ncplane_destroy(p2);
for(int i = 0 ; i < 10 ; ++i){
CHECK(0 < ncplane_at_yx(n_, 0, i, &cbase));
CHECK(0 < ncplane_at_yx(p1, 0, i, &cp));
CHECK(0 == cellcmp(n_, &cbase, p1, &cp));
}
ncplane_destroy(p1);
}
CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));