first go at ncplane_fadeout() #40

This commit is contained in:
nick black 2019-12-02 02:49:22 -05:00 committed by Nick Black
parent 055f49b5fd
commit 4426ef0574
3 changed files with 138 additions and 3 deletions

View File

@ -1,6 +1,7 @@
#ifndef NOTCURSES_NOTCURSES
#define NOTCURSES_NOTCURSES
#include <time.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
@ -345,6 +346,13 @@ API void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
// Return the current styling for this ncplane.
API unsigned ncplane_styles(const struct ncplane* n);
// Fade the ncplane out over the provided time, calling the specified function
// when done. Requires a terminal which supports direct color, or at least
// palette 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 not safe to resize or destroy the plane during the fadeout FIXME.
API int ncplane_fadeout(struct ncplane* n, const struct timespec* ts);
// Working with cells
#define CELL_TRIVIAL_INITIALIZER { .gcluster = '\0', .attrword = 0, .channels = 0, }
@ -439,6 +447,22 @@ cell_rgb_blue(uint32_t rgb){
#define CELL_BGDEFAULT_MASK 0x0000000040000000ull
#define CELL_BG_MASK 0x0000000000ffffffull
static inline void
cell_rgb_get_fg(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
uint32_t fg = ((channels & CELL_FG_MASK));
*r = cell_rgb_red(fg);
*g = cell_rgb_green(fg);
*b = cell_rgb_blue(fg);
}
static inline void
cell_rgb_get_bg(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
uint32_t bg = ((channels & CELL_BG_MASK) >> 32u);
*r = cell_rgb_red(bg);
*g = cell_rgb_green(bg);
*b = cell_rgb_blue(bg);
}
static inline int
cell_rgb_set_fg(uint64_t* channels, int r, int g, int b){
if(r >= 256 || g >= 256 || b >= 256){

View File

@ -127,6 +127,8 @@ intro(struct notcurses* nc){
return -1;
}
nanosleep(&demodelay, NULL);
struct timespec fade = demodelay;
ncplane_fadeout(ncp, &fade);
return 0;
}

View File

@ -25,6 +25,7 @@
#include "egcpool.h"
#define ESC "\x1b"
#define NANOSECS_IN_SEC 1000000000
typedef struct ncstats {
uint64_t renders; // number of notcurses_render() runs
@ -158,8 +159,8 @@ static void
update_render_stats(const struct timespec* time1, const struct timespec* time0,
ncstats* stats){
int64_t elapsed = timespec_subtract_ns(time1, time0);
//fprintf(stderr, "Rendering took %ld.%03lds\n", elapsed / 1000000000,
// (elapsed % 1000000000) / 1000000);
//fprintf(stderr, "Rendering took %ld.%03lds\n", elapsed / NANOSECS_IN_SEC,
// (elapsed % NANOSECS_IN_SEC) / 1000000);
if(elapsed > 0){ // don't count clearly incorrect information, egads
++stats->renders;
stats->renders_ns += elapsed;
@ -722,7 +723,7 @@ int notcurses_stop(notcurses* nc){
nc->stats.renders_ns / 1000000000.0,
nc->stats.render_min_ns / 1000000000.0,
nc->stats.render_max_ns / 1000000000.0,
avg / 1000000000);
avg / NANOSECS_IN_SEC);
while(nc->top){
ncplane* p = nc->top;
nc->top = p->z;
@ -1597,3 +1598,111 @@ int ncvisual_render(const ncvisual* ncv){
}
return 0;
}
typedef struct planepalette {
int size; // number of channel entries
unsigned maxr, maxg, maxb; // maxima across foreground channels
unsigned maxbr, maxbg, maxbb; // maxima across background channels
uint64_t* channels; // all channels from the framebuffer
} planepalette;
// These arrays are too large to be safely placed on the stack.
static int
alloc_ncplane_palette(ncplane* n, planepalette* pp){
int dimy, dimx;
ncplane_dim_yx(n, &dimy, &dimx);
pp->size = dimy * dimx;
if((pp->channels = malloc(sizeof(*pp->channels) * pp->size)) == NULL){
return -1;
}
pp->maxr = pp->maxg = pp->maxb = 0;
pp->maxbr = pp->maxbg = pp->maxbb = 0;
int y, x;
for(y = 0 ; y < dimy ; ++y){
for(x = 0 ; x < dimx ; ++x){
uint64_t channels = n->fb[fbcellidx(n, y, x)].channels;
pp->channels[y * dimx + x] = channels;
unsigned r, g, b, br, bg, bb;
cell_rgb_get_fg(channels, &r, &g, &b);
if(r > pp->maxr){
pp->maxr = r;
}
if(g > pp->maxg){
pp->maxg = g;
}
if(b > pp->maxb){
pp->maxb = b;
}
cell_rgb_get_bg(channels, &br, &bg, &bb);
if(br > pp->maxbr){
pp->maxbr = br;
}
if(bg > pp->maxbg){
pp->maxbg = bg;
}
if(bb > pp->maxbb){
pp->maxbb = bb;
}
}
}
return 0;
}
int ncplane_fadeout(ncplane* n, const struct timespec* ts){
planepalette pp;
if(!n->nc->RGBflag && !n->nc->CCCflag){ // terminal can't fade
return -1;
}
if(alloc_ncplane_palette(n, &pp)){
return -1;
}
int maxfsteps = pp.maxg > pp.maxr ? (pp.maxb > pp.maxg ? pp.maxb : pp.maxg) :
(pp.maxb > pp.maxr ? pp.maxb : pp.maxr);
int maxbsteps = pp.maxbg > pp.maxbr ? (pp.maxbb > pp.maxbg ? pp.maxbb : pp.maxbg) :
(pp.maxbb > pp.maxbr ? pp.maxbb : pp.maxbr);
int maxsteps = maxfsteps > maxbsteps ? maxfsteps : maxbsteps;
uint64_t nanosecs_total = ts->tv_sec * NANOSECS_IN_SEC + ts->tv_nsec;
uint64_t nanosecs_step = nanosecs_total / maxsteps;
struct timespec times;
clock_gettime(CLOCK_MONOTONIC, &times);
// Start time in absolute nanoseconds
uint64_t startns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
do{
clock_gettime(CLOCK_MONOTONIC, &times);
uint64_t curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
int iter = (curns - startns) / nanosecs_step + 1;
if(iter > maxsteps){
break;
}
int p;
for(p = 0 ; p < pp.size ; ++p){
cell* c = &n->fb[p];
unsigned r, g, b;
cell_rgb_get_fg(pp.channels[p], &r, &g, &b);
unsigned br, bg, bb;
cell_rgb_get_bg(pp.channels[p], &br, &bg, &bb);
r = r * (maxsteps - iter) / maxsteps;
g = g * (maxsteps - iter) / maxsteps;
b = b * (maxsteps - iter) / maxsteps;
br = br * (maxsteps - iter) / maxsteps;
bg = bg * (maxsteps - iter) / maxsteps;
bb = bb * (maxsteps - iter) / maxsteps;
cell_set_fg(c, r, g, b);
cell_set_bg(c, br, bg, bb);
}
notcurses_render(n->nc);
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 r;
// clock_nanosleep() has no love for CLOCK_MONOTONIC_RAW, at least as
// of Glibc 2.29 + Linux 5.3 :/.
r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepspec, NULL);
if(r){
break;
}
}while(true);
free(pp.channels);
return 0;
}