Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
fade.c
Go to the documentation of this file.
1#include <time.h>
2#include <sys/time.h>
3#include "internal.h"
4
5typedef struct ncfadectx {
6 unsigned rows; // number of rows when allocated
7 unsigned cols; // number of columns when allocated
8 int maxsteps; // maximum number of iterations
9 unsigned maxr, maxg, maxb; // maxima across foreground channels
10 unsigned maxbr, maxbg, maxbb; // maxima across background channels
11 uint64_t nanosecs_step; // nanoseconds per iteration
12 uint64_t startns; // time fade started
13 uint64_t* channels; // all channels from the framebuffer
15
17 return nctx->maxsteps;
18}
19
20// These arrays are too large to be safely placed on the stack. Get an atomic
21// snapshot of all channels on the plane. While copying the snapshot, determine
22// the maxima across each of the six components.
23static int
24alloc_ncplane_palette(ncplane* n, ncfadectx* pp, const struct timespec* ts){
25 ncplane_dim_yx(n, &pp->rows, &pp->cols);
26 // add an additional element for the background cell
27 int size = pp->rows * pp->cols + 1;
28 if((pp->channels = malloc(sizeof(*pp->channels) * size)) == NULL){
29 return -1;
30 }
31 pp->maxr = pp->maxg = pp->maxb = 0;
32 pp->maxbr = pp->maxbg = pp->maxbb = 0;
33 unsigned r, g, b, br, bg, bb;
34 uint64_t channels;
35 unsigned y;
36 for(y = 0 ; y < pp->rows ; ++y){
37 for(unsigned x = 0 ; x < pp->cols ; ++x){
38 channels = n->fb[nfbcellidx(n, y, x)].channels;
39 pp->channels[y * pp->cols + x] = channels;
40 ncchannels_fg_rgb8(channels, &r, &g, &b);
41 if(r > pp->maxr){
42 pp->maxr = r;
43 }
44 if(g > pp->maxg){
45 pp->maxg = g;
46 }
47 if(b > pp->maxb){
48 pp->maxb = b;
49 }
50 ncchannels_bg_rgb8(channels, &br, &bg, &bb);
51 if(br > pp->maxbr){
52 pp->maxbr = br;
53 }
54 if(bg > pp->maxbg){
55 pp->maxbg = bg;
56 }
57 if(bb > pp->maxbb){
58 pp->maxbb = bb;
59 }
60 }
61 }
62 channels = n->basecell.channels;
63 pp->channels[y * pp->cols] = channels;
64 ncchannels_fg_rgb8(channels, &r, &g, &b);
65 if(r > pp->maxr){
66 pp->maxr = r;
67 }
68 if(g > pp->maxg){
69 pp->maxg = g;
70 }
71 if(b > pp->maxb){
72 pp->maxb = b;
73 }
74 ncchannels_bg_rgb8(channels, &br, &bg, &bb);
75 if(br > pp->maxbr){
76 pp->maxbr = br;
77 }
78 if(bg > pp->maxbg){
79 pp->maxbg = bg;
80 }
81 if(bb > pp->maxbb){
82 pp->maxbb = bb;
83 }
84 int maxfsteps = pp->maxg > pp->maxr ? (pp->maxb > pp->maxg ? pp->maxb : pp->maxg) :
85 (pp->maxb > pp->maxr ? pp->maxb : pp->maxr);
86 int maxbsteps = pp->maxbg > pp->maxbr ? (pp->maxbb > pp->maxbg ? pp->maxbb : pp->maxbg) :
87 (pp->maxbb > pp->maxbr ? pp->maxbb : pp->maxbr);
88 pp->maxsteps = maxfsteps > maxbsteps ? maxfsteps : maxbsteps;
89 if(pp->maxsteps == 0){
90 pp->maxsteps = 1;
91 }
92 uint64_t nanosecs_total;
93 if(ts){
94 nanosecs_total = timespec_to_ns(ts);
95 pp->nanosecs_step = nanosecs_total / pp->maxsteps;
96 if(pp->nanosecs_step == 0){
97 pp->nanosecs_step = 1;
98 }
99 }else{
100 pp->nanosecs_step = 1;
101 }
102 struct timespec times;
103 clock_gettime(CLOCK_MONOTONIC, &times);
104 // Start time in absolute nanoseconds
105 pp->startns = timespec_to_ns(&times);
106 return 0;
107}
108
110 fadecb fader, void* curry){
111 // each time through, we need look each cell back up, due to the
112 // possibility of a resize event :/
113 unsigned dimy, dimx;
114 ncplane_dim_yx(n, &dimy, &dimx);
115 unsigned y;
116 for(y = 0 ; y < nctx->rows && y < dimy ; ++y){
117 for(unsigned x = 0 ; x < nctx->cols && x < dimx; ++x){
118 unsigned r, g, b;
119 ncchannels_fg_rgb8(nctx->channels[nctx->cols * y + x], &r, &g, &b);
120 unsigned br, bg, bb;
121 ncchannels_bg_rgb8(nctx->channels[nctx->cols * y + x], &br, &bg, &bb);
122 nccell* c = &n->fb[dimx * y + x];
123 if(!nccell_fg_default_p(c)){
124 r = r * iter / nctx->maxsteps;
125 g = g * iter / nctx->maxsteps;
126 b = b * iter / nctx->maxsteps;
127 nccell_set_fg_rgb8(c, r, g, b);
128 }
129 if(!nccell_bg_default_p(c)){
130 br = br * iter / nctx->maxsteps;
131 bg = bg * iter / nctx->maxsteps;
132 bb = bb * iter / nctx->maxsteps;
133 nccell_set_bg_rgb8(c, br, bg, bb);
134 }
135 }
136 }
137 uint64_t nextwake = (iter + 1) * nctx->nanosecs_step + nctx->startns;
138 struct timespec sleepspec;
139 sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC;
140 sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC;
141 int ret = 0;
142 if(fader){
143 ret |= fader(ncplane_notcurses(n), n, &sleepspec, curry);
144 }else{
145 ret |= notcurses_render(ncplane_notcurses(n));
146 // clock_nanosleep() has no love for CLOCK_MONOTONIC_RAW, at least as
147 // of Glibc 2.29 + Linux 5.3 (or FreeBSD 12) :/.
148 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepspec, NULL);
149 }
150 return ret;
151}
152
153static int
154ncplane_fadein_internal(ncplane* n, fadecb fader, ncfadectx* pp, void* curry){
155 // Current time, sampled each iteration
156 uint64_t curns;
157 do{
158 struct timespec times;
159 clock_gettime(CLOCK_MONOTONIC, &times);
160 curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
161 int iter = (curns - pp->startns) / pp->nanosecs_step + 1;
162 if(iter > pp->maxsteps){
163 break;
164 }
165 int r = ncplane_fadein_iteration(n, pp, iter, fader, curry);
166 if(r){
167 return r;
168 }
169 clock_gettime(CLOCK_MONOTONIC, &times);
170 }while(true);
171 return 0;
172}
173
175 fadecb fader, void* curry){
176 unsigned br, bg, bb;
177 unsigned r, g, b;
178 // each time through, we need look each cell back up, due to the
179 // possibility of a resize event :/
180 unsigned dimy, dimx;
181 ncplane_dim_yx(n, &dimy, &dimx);
182 unsigned y;
183 for(y = 0 ; y < nctx->rows && y < dimy ; ++y){
184 for(unsigned x = 0 ; x < nctx->cols && x < dimx; ++x){
185 nccell* c = &n->fb[dimx * y + x];
186 if(!nccell_fg_default_p(c)){
187 ncchannels_fg_rgb8(nctx->channels[nctx->cols * y + x], &r, &g, &b);
188 r = r * (nctx->maxsteps - iter) / nctx->maxsteps;
189 g = g * (nctx->maxsteps - iter) / nctx->maxsteps;
190 b = b * (nctx->maxsteps - iter) / nctx->maxsteps;
191 nccell_set_fg_rgb8(c, r, g, b);
192 }
193 if(!nccell_bg_default_p(c)){
194 ncchannels_bg_rgb8(nctx->channels[nctx->cols * y + x], &br, &bg, &bb);
195 br = br * (nctx->maxsteps - iter) / nctx->maxsteps;
196 bg = bg * (nctx->maxsteps - iter) / nctx->maxsteps;
197 bb = bb * (nctx->maxsteps - iter) / nctx->maxsteps;
198 nccell_set_bg_rgb8(c, br, bg, bb);
199 }
200 }
201 }
202 nccell* c = &n->basecell;
203 if(!nccell_fg_default_p(c)){
204 ncchannels_fg_rgb8(nctx->channels[nctx->cols * y], &r, &g, &b);
205 r = r * (nctx->maxsteps - iter) / nctx->maxsteps;
206 g = g * (nctx->maxsteps - iter) / nctx->maxsteps;
207 b = b * (nctx->maxsteps - iter) / nctx->maxsteps;
208 nccell_set_fg_rgb8(&n->basecell, r, g, b);
209 }
210 if(!nccell_bg_default_p(c)){
211 ncchannels_bg_rgb8(nctx->channels[nctx->cols * y], &br, &bg, &bb);
212 br = br * (nctx->maxsteps - iter) / nctx->maxsteps;
213 bg = bg * (nctx->maxsteps - iter) / nctx->maxsteps;
214 bb = bb * (nctx->maxsteps - iter) / nctx->maxsteps;
215 nccell_set_bg_rgb8(&n->basecell, br, bg, bb);
216 }
217 uint64_t nextwake = (iter + 1) * nctx->nanosecs_step + nctx->startns;
218 struct timespec sleepspec;
219 sleepspec.tv_sec = nextwake / NANOSECS_IN_SEC;
220 sleepspec.tv_nsec = nextwake % NANOSECS_IN_SEC;
221 int ret;
222 if(fader){
223 ret = fader(ncplane_notcurses(n), n, &sleepspec, curry);
224 }else{
225 ret = notcurses_render(ncplane_notcurses(n));
226 // clock_nanosleep() has no love for CLOCK_MONOTONIC_RAW, at least as
227 // of Glibc 2.29 + Linux 5.3 (or FreeBSD 12) :/.
228 clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleepspec, NULL);
229 }
230 return ret;
231}
232
233static ncfadectx*
234ncfadectx_setup_internal(ncplane* n, const struct timespec* ts){
235 if(!ncplane_notcurses(n)->tcache.caps.rgb &&
236 !ncplane_notcurses(n)->tcache.caps.can_change_colors){ // terminal can't fade
237 return NULL;
238 }
239 ncfadectx* nctx = malloc(sizeof(*nctx));
240 if(nctx){
241 if(alloc_ncplane_palette(n, nctx, ts) == 0){
242 return nctx;
243 }
244 free(nctx);
245 }
246 return NULL;
247}
248
250 return ncfadectx_setup_internal(n, NULL);
251}
252
254 if(nctx){
255 free(nctx->channels);
256 free(nctx);
257 }
258}
259
260int ncplane_fadeout(ncplane* n, const struct timespec* ts, fadecb fader, void* curry){
261 ncfadectx* pp = ncfadectx_setup_internal(n, ts);
262 if(!pp){
263 return -1;
264 }
265 struct timespec times;
266 ns_to_timespec(pp->startns, &times);
267 do{
268 uint64_t curns = times.tv_sec * NANOSECS_IN_SEC + times.tv_nsec;
269 int iter = (curns - pp->startns) / pp->nanosecs_step + 1;
270 if(iter > pp->maxsteps){
271 break;
272 }
273 int r = ncplane_fadeout_iteration(n, pp, iter, fader, curry);
274 if(r){
275 ncfadectx_free(pp);
276 return r;
277 }
278 clock_gettime(CLOCK_MONOTONIC, &times);
279 }while(true);
280 ncfadectx_free(pp);
281 return 0;
282}
283
284int ncplane_fadein(ncplane* n, const struct timespec* ts, fadecb fader, void* curry){
285 ncfadectx* nctx = ncfadectx_setup_internal(n, ts);
286 if(nctx == NULL){
287 struct timespec now;
288 clock_gettime(CLOCK_MONOTONIC, &now);
289 if(fader){
290 fader(ncplane_notcurses(n), n, &now, curry);
291 }else{
292 notcurses_render(ncplane_notcurses(n));
293 }
294 return -1;
295 }
296 int ret = ncplane_fadein_internal(n, fader, nctx, curry);
297 ncfadectx_free(nctx);
298 return ret;
299}
300
301int ncplane_pulse(ncplane* n, const struct timespec* ts, fadecb fader, void* curry){
302 ncfadectx pp;
303 int ret;
304 if(!notcurses_canfade(ncplane_notcurses(n))){
305 return -1;
306 }
307 if(alloc_ncplane_palette(n, &pp, ts)){
308 return -1;
309 }
310 for(;;){
311 ret = ncplane_fadein_internal(n, fader, &pp, curry);
312 if(ret){
313 break;
314 }
315 ret = ncplane_fadeout(n, ts, fader, curry);
316 if(ret){
317 break;
318 }
319 }
320 free(pp.channels);
321 return ret;
322}
const nccell * c
Definition egcpool.h:296
free(duplicated)
int ncplane_pulse(ncplane *n, const struct timespec *ts, fadecb fader, void *curry)
Definition fade.c:301
int ncplane_fadeout(ncplane *n, const struct timespec *ts, fadecb fader, void *curry)
Definition fade.c:260
int ncplane_fadein_iteration(ncplane *n, ncfadectx *nctx, int iter, fadecb fader, void *curry)
Definition fade.c:109
int ncplane_fadein(ncplane *n, const struct timespec *ts, fadecb fader, void *curry)
Definition fade.c:284
int ncplane_fadeout_iteration(ncplane *n, ncfadectx *nctx, int iter, fadecb fader, void *curry)
Definition fade.c:174
ncfadectx * ncfadectx_setup(ncplane *n)
Definition fade.c:249
void ncfadectx_free(ncfadectx *nctx)
Definition fade.c:253
int ncfadectx_iterations(const ncfadectx *nctx)
Definition fade.c:16
int r
Definition fbuf.h:226
notcurses * ncplane_notcurses(const ncplane *n)
Definition notcurses.c:2626
void ncplane_dim_yx(const ncplane *n, unsigned *rows, unsigned *cols)
Definition notcurses.c:301
int y
Definition notcurses.h:1905
int(* fadecb)(struct notcurses *nc, struct ncplane *n, const struct timespec *, void *curry)
Definition notcurses.h:3029
vopts n
Definition notcurses.h:3502
int int x
Definition notcurses.h:1905
uint64_t nanosecs_step
Definition fade.c:11
int maxsteps
Definition fade.c:8
unsigned maxb
Definition fade.c:9
uint64_t * channels
Definition fade.c:13
unsigned rows
Definition fade.c:6
unsigned maxbr
Definition fade.c:10
uint64_t startns
Definition fade.c:12
unsigned maxbg
Definition fade.c:10
unsigned maxr
Definition fade.c:9
unsigned cols
Definition fade.c:7
unsigned maxg
Definition fade.c:9
unsigned maxbb
Definition fade.c:10
return NULL
Definition termdesc.h:229