fade: move more reproduced code into common area #659

This commit is contained in:
nick black 2020-06-01 02:33:33 -04:00 committed by Nick Black
parent 0766dd08e7
commit 74e8e9c3d9
4 changed files with 94 additions and 92 deletions

View File

@ -9,12 +9,18 @@ rearrangements of Notcurses.
`ncblitter_e` field, allowing visuals to be mapped to various plotting `ncblitter_e` field, allowing visuals to be mapped to various plotting
paradigms including Sixel, Braille and quadrants. Not all backends have paradigms including Sixel, Braille and quadrants. Not all backends have
been implemented, and not all implementations are in their final form. been implemented, and not all implementations are in their final form.
`CELL_ALPHA_BLEND` can now be used for translucent visuals.
* Added `ncvisual_geom()`, providing access to an `ncvisual` size and * Added `ncvisual_geom()`, providing access to an `ncvisual` size and
its pixel-to-cell blitting ratios. its pixel-to-cell blitting ratios.
* Deprecated functions `ncvisual_open_plane()` and `ncplane_visual_open()` * Deprecated functions `ncvisual_open_plane()` and `ncplane_visual_open()`
have been removed. Their functionality is present in have been removed. Their functionality is present in
`ncvisual_from_file()`. The function `ncvisual_plane()` no longer has `ncvisual_from_file()`. The function `ncvisual_plane()` no longer has
any meaning, and has been removed. any meaning, and has been removed.
* The `fadecb` typedef now accepts as its third argument a `const struct
timespec`. This is the absolute deadline through which the frame ought
be displayed. Like the changes to `ncvisual_stream()`, this gives more
flexibility to the callback, and allows more precise timing. There will
be further changes to the Fade API before API freeze (see #659).
* 1.4.3 (2020-05-22) * 1.4.3 (2020-05-22)
* Plot: make 8x1 the default, instead of 1x1. * Plot: make 8x1 the default, instead of 1x1.

View File

@ -1200,8 +1200,7 @@ typedef int (*fadecb)(struct notcurses* nc, struct ncplane* ncp, void* curry);
// Fade the ncplane out over the provided time, calling the specified function // Fade the ncplane out over the provided time, calling the specified function
// when done. Requires a terminal which supports truecolor, or at least palette // when done. Requires a terminal which supports truecolor, or at least palette
// modification (if the terminal uses a palette, our ability to fade planes is // modification (if the terminal uses a palette, our ability to fade planes is
// limited, and affected by the complexity of the rest of the screen). It is // limited, and affected by the complexity of the rest of the screen).
// not safe to resize or destroy the plane during the fadeout FIXME.
int ncplane_fadeout(struct ncplane* n, const struct timespec* ts, fadecb fader, void* curry); int ncplane_fadeout(struct ncplane* n, const struct timespec* ts, fadecb fader, void* curry);
// Fade the ncplane in over the specified time. Load the ncplane with the // Fade the ncplane in over the specified time. Load the ncplane with the

View File

@ -1974,8 +1974,7 @@ typedef int (*fadecb)(struct notcurses* nc, struct ncplane* ncp,
// Fade the ncplane out over the provided time, calling the specified function // Fade the ncplane out over the provided time, calling the specified function
// when done. Requires a terminal which supports truecolor, or at least palette // when done. Requires a terminal which supports truecolor, or at least palette
// modification (if the terminal uses a palette, our ability to fade planes is // modification (if the terminal uses a palette, our ability to fade planes is
// limited, and affected by the complexity of the rest of the screen). It is // limited, and affected by the complexity of the rest of the screen).
// not safe to resize or destroy the plane during the fadeout FIXME.
API int ncplane_fadeout(struct ncplane* n, const struct timespec* ts, API int ncplane_fadeout(struct ncplane* n, const struct timespec* ts,
fadecb fader, void* curry); fadecb fader, void* curry);

View File

@ -6,6 +6,8 @@ typedef struct ncfadectx {
int maxsteps; // maximum number of iterations int maxsteps; // maximum number of iterations
unsigned maxr, maxg, maxb; // maxima across foreground channels unsigned maxr, maxg, maxb; // maxima across foreground channels
unsigned maxbr, maxbg, maxbb; // maxima across background channels unsigned maxbr, maxbg, maxbb; // maxima across background channels
uint64_t nanosecs_step; // nanoseconds per iteration
uint64_t startns; // time fade started
uint64_t* channels; // all channels from the framebuffer uint64_t* channels; // all channels from the framebuffer
} ncfadectx; } ncfadectx;
@ -13,7 +15,7 @@ typedef struct ncfadectx {
// snapshot of all channels on the plane. While copying the snapshot, determine // snapshot of all channels on the plane. While copying the snapshot, determine
// the maxima across each of the six components. // the maxima across each of the six components.
static int static int
alloc_ncplane_palette(ncplane* n, ncfadectx* pp){ alloc_ncplane_palette(ncplane* n, ncfadectx* pp, const struct timespec* ts){
ncplane_dim_yx(n, &pp->rows, &pp->cols); ncplane_dim_yx(n, &pp->rows, &pp->cols);
// add an additional element for the background cell // add an additional element for the background cell
int size = pp->rows * pp->cols + 1; int size = pp->rows * pp->cols + 1;
@ -82,28 +84,28 @@ alloc_ncplane_palette(ncplane* n, ncfadectx* pp){
if(pp->maxsteps == 0){ if(pp->maxsteps == 0){
pp->maxsteps = 1; pp->maxsteps = 1;
} }
return 0; uint64_t nanosecs_total = timespec_to_ns(ts);
} pp->nanosecs_step = nanosecs_total / pp->maxsteps;
if(pp->nanosecs_step == 0){
static int pp->nanosecs_step = 1;
ncplane_fadein_internal(ncplane* n, const struct timespec* ts,
fadecb fader, ncfadectx* pp, void* curry){
uint64_t nanosecs_total = ts->tv_sec * NANOSECS_IN_SEC + ts->tv_nsec;
uint64_t nanosecs_step = nanosecs_total / pp->maxsteps;
if(nanosecs_step == 0){
nanosecs_step = 1;
} }
struct timespec times; struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times); clock_gettime(CLOCK_MONOTONIC, &times);
// Start time in absolute nanoseconds // Start time in absolute nanoseconds
uint64_t startns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec; pp->startns = timespec_to_ns(&times);
return 0;
}
static int
ncplane_fadein_internal(ncplane* n, fadecb fader, ncfadectx* pp, void* curry){
// Current time, sampled each iteration // Current time, sampled each iteration
uint64_t curns; uint64_t curns;
int ret = 0; int ret = 0;
do{ do{
struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times); clock_gettime(CLOCK_MONOTONIC, &times);
curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec; curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
int iter = (curns - startns) / nanosecs_step + 1; int iter = (curns - pp->startns) / pp->nanosecs_step + 1;
if(iter > pp->maxsteps){ if(iter > pp->maxsteps){
break; break;
} }
@ -133,7 +135,7 @@ ncplane_fadein_internal(ncplane* n, const struct timespec* ts,
} }
} }
} }
uint64_t nextwake = (iter + 1) * nanosecs_step + startns; uint64_t nextwake = (iter + 1) * pp->nanosecs_step + pp->startns;
struct timespec sleepspec; struct timespec sleepspec;
sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC; sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC;
sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC; sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC;
@ -152,89 +154,85 @@ ncplane_fadein_internal(ncplane* n, const struct timespec* ts,
return ret; return ret;
} }
int ncplane_fadeout_iteration(ncplane* n, ncfadectx* nctx, int iter,
fadecb fader, void* curry){
unsigned br, bg, bb;
unsigned r, g, b;
int y, x;
// each time through, we need look each cell back up, due to the
// possibility of a resize event :/
int dimy, dimx;
ncplane_dim_yx(n, &dimy, &dimx);
for(y = 0 ; y < nctx->rows && y < dimy ; ++y){
for(x = 0 ; x < nctx->cols && x < dimx; ++x){
cell* c = &n->fb[dimx * y + x];
if(!cell_fg_default_p(c)){
channels_fg_rgb(nctx->channels[nctx->cols * y + x], &r, &g, &b);
r = r * (nctx->maxsteps - iter) / nctx->maxsteps;
g = g * (nctx->maxsteps - iter) / nctx->maxsteps;
b = b * (nctx->maxsteps - iter) / nctx->maxsteps;
cell_set_fg_rgb(c, r, g, b);
}
if(!cell_bg_default_p(c)){
channels_bg_rgb(nctx->channels[nctx->cols * y + x], &br, &bg, &bb);
br = br * (nctx->maxsteps - iter) / nctx->maxsteps;
bg = bg * (nctx->maxsteps - iter) / nctx->maxsteps;
bb = bb * (nctx->maxsteps - iter) / nctx->maxsteps;
cell_set_bg_rgb(c, br, bg, bb);
}
}
}
cell* c = &n->basecell;
if(!cell_fg_default_p(c)){
channels_fg_rgb(nctx->channels[nctx->cols * y], &r, &g, &b);
r = r * (nctx->maxsteps - iter) / nctx->maxsteps;
g = g * (nctx->maxsteps - iter) / nctx->maxsteps;
b = b * (nctx->maxsteps - iter) / nctx->maxsteps;
cell_set_fg_rgb(&n->basecell, r, g, b);
}
if(!cell_bg_default_p(c)){
channels_bg_rgb(nctx->channels[nctx->cols * y], &br, &bg, &bb);
br = br * (nctx->maxsteps - iter) / nctx->maxsteps;
bg = bg * (nctx->maxsteps - iter) / nctx->maxsteps;
bb = bb * (nctx->maxsteps - iter) / nctx->maxsteps;
cell_set_bg_rgb(&n->basecell, br, bg, bb);
}
uint64_t nextwake = (iter + 1) * nctx->nanosecs_step + nctx->startns;
struct timespec sleepspec;
sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC;
sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC;
int ret;
if(fader){
ret = fader(n->nc, n, &sleepspec, curry);
}else{
ret = notcurses_render(n->nc);
// clock_nanosleep() has no love for CLOCK_MONOTONIC_RAW, at least as
// of Glibc 2.29 + Linux 5.3 (or FreeBSD 12) :/.
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepspec, NULL);
}
return ret;
}
int ncplane_fadeout(ncplane* n, const struct timespec* ts, fadecb fader, void* curry){ int ncplane_fadeout(ncplane* n, const struct timespec* ts, fadecb fader, void* curry){
ncfadectx pp; ncfadectx pp;
if(!n->nc->tcache.RGBflag && !n->nc->tcache.CCCflag){ // terminal can't fade if(!n->nc->tcache.RGBflag && !n->nc->tcache.CCCflag){ // terminal can't fade
return -1; return -1;
} }
if(alloc_ncplane_palette(n, &pp)){ if(alloc_ncplane_palette(n, &pp, ts)){
return -1; return -1;
} }
uint64_t nanosecs_total = timespec_to_ns(ts);
uint64_t nanosecs_step = nanosecs_total / pp.maxsteps;
if(nanosecs_step == 0){
nanosecs_step = 1;
}
struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times);
// Start time in absolute nanoseconds
uint64_t startns = timespec_to_ns(&times);
int ret = 0; int ret = 0;
struct timespec times;
ns_to_timespec(pp.startns, &times);
do{ do{
unsigned br, bg, bb;
unsigned r, g, b;
uint64_t curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec; uint64_t curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
int iter = (curns - startns) / nanosecs_step + 1; int iter = (curns - pp.startns) / pp.nanosecs_step + 1;
if(iter > pp.maxsteps){ if(iter > pp.maxsteps){
break; break;
} }
int y, x; int r = ncplane_fadeout_iteration(n, &pp, iter, fader, curry);
// each time through, we need look each cell back up, due to the if(r){
// possibility of a resize event :/ return r;
int dimy, dimx;
ncplane_dim_yx(n, &dimy, &dimx);
for(y = 0 ; y < pp.rows && y < dimy ; ++y){
for(x = 0 ; x < pp.cols && x < dimx; ++x){
cell* c = &n->fb[dimx * y + x];
if(!cell_fg_default_p(c)){
channels_fg_rgb(pp.channels[pp.cols * y + x], &r, &g, &b);
r = r * (pp.maxsteps - iter) / pp.maxsteps;
g = g * (pp.maxsteps - iter) / pp.maxsteps;
b = b * (pp.maxsteps - iter) / pp.maxsteps;
cell_set_fg_rgb(c, r, g, b);
}
if(!cell_bg_default_p(c)){
channels_bg_rgb(pp.channels[pp.cols * y + x], &br, &bg, &bb);
br = br * (pp.maxsteps - iter) / pp.maxsteps;
bg = bg * (pp.maxsteps - iter) / pp.maxsteps;
bb = bb * (pp.maxsteps - iter) / pp.maxsteps;
cell_set_bg_rgb(c, br, bg, bb);
}
}
}
cell* c = &n->basecell;
if(!cell_fg_default_p(c)){
channels_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b);
r = r * (pp.maxsteps - iter) / pp.maxsteps;
g = g * (pp.maxsteps - iter) / pp.maxsteps;
b = b * (pp.maxsteps - iter) / pp.maxsteps;
cell_set_fg_rgb(&n->basecell, r, g, b);
}
if(!cell_bg_default_p(c)){
channels_bg_rgb(pp.channels[pp.cols * y], &br, &bg, &bb);
br = br * (pp.maxsteps - iter) / pp.maxsteps;
bg = bg * (pp.maxsteps - iter) / pp.maxsteps;
bb = bb * (pp.maxsteps - iter) / pp.maxsteps;
cell_set_bg_rgb(&n->basecell, br, bg, bb);
}
uint64_t nextwake = (iter + 1) * nanosecs_step + startns;
struct timespec sleepspec;
sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC;
sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC;
int rsleep;
if(fader){
ret = fader(n->nc, n, &sleepspec, curry);
}else{
ret = notcurses_render(n->nc);
}
if(ret){
break;
}
// clock_nanosleep() has no love for CLOCK_MONOTONIC_RAW, at least as
// of Glibc 2.29 + Linux 5.3 (or FreeBSD 12) :/.
rsleep = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepspec, NULL);
if(rsleep){
break;
} }
clock_gettime(CLOCK_MONOTONIC, &times); clock_gettime(CLOCK_MONOTONIC, &times);
}while(true); }while(true);
@ -254,10 +252,10 @@ int ncplane_fadein(ncplane* n, const struct timespec* ts, fadecb fader, void* cu
} }
return -1; return -1;
} }
if(alloc_ncplane_palette(n, &pp)){ if(alloc_ncplane_palette(n, &pp, ts)){
return -1; return -1;
} }
int ret = ncplane_fadein_internal(n, ts, fader, &pp, curry); int ret = ncplane_fadein_internal(n, fader, &pp, curry);
free(pp.channels); free(pp.channels);
return ret; return ret;
} }
@ -268,11 +266,11 @@ int ncplane_pulse(ncplane* n, const struct timespec* ts, fadecb fader, void* cur
if(!n->nc->tcache.RGBflag && !n->nc->tcache.CCCflag){ // terminal can't fade if(!n->nc->tcache.RGBflag && !n->nc->tcache.CCCflag){ // terminal can't fade
return -1; return -1;
} }
if(alloc_ncplane_palette(n, &pp)){ if(alloc_ncplane_palette(n, &pp, ts)){
return -1; return -1;
} }
for(;;){ for(;;){
ret = ncplane_fadein_internal(n, ts, fader, &pp, curry); ret = ncplane_fadein_internal(n, fader, &pp, curry);
if(ret){ if(ret){
break; break;
} }