mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-10 01:29:05 -04:00
implement ncpile_{render, rasterize}() 🦀🦀 #1135
This commit is contained in:
parent
7a21574798
commit
fd97aa844c
1
NEWS.md
1
NEWS.md
@ -23,7 +23,6 @@ rearrangements of Notcurses.
|
|||||||
* Add new function `ncpile_render()`, which renders the pile containing the
|
* Add new function `ncpile_render()`, which renders the pile containing the
|
||||||
specified plane to the specified buffer. Add new function
|
specified plane to the specified buffer. Add new function
|
||||||
`ncpile_rasterize()` to rasterize the specified buffer to output.
|
`ncpile_rasterize()` to rasterize the specified buffer to output.
|
||||||
* `notcurses_render_to_buffer()` has been deprecated. Use `ncpile_render()`.
|
|
||||||
|
|
||||||
* 2.0.7 (2020-11-21)
|
* 2.0.7 (2020-11-21)
|
||||||
* The `horiz` union of `ncplane_options` has been discarded; the `int x`
|
* The `horiz` union of `ncplane_options` has been discarded; the `int x`
|
||||||
|
@ -195,6 +195,11 @@ namespace ncpp
|
|||||||
return error_guard (notcurses_render (nc), -1);
|
return error_guard (notcurses_render (nc), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool render_to_buffer (char** buf, size_t* buflen) const NOEXCEPT_MAYBE
|
||||||
|
{
|
||||||
|
return error_guard (notcurses_render_to_buffer (nc, buf, buflen), -1);
|
||||||
|
}
|
||||||
|
|
||||||
bool render_to_file (FILE* fp) const NOEXCEPT_MAYBE
|
bool render_to_file (FILE* fp) const NOEXCEPT_MAYBE
|
||||||
{
|
{
|
||||||
return error_guard (notcurses_render_to_file (nc, fp), -1);
|
return error_guard (notcurses_render_to_file (nc, fp), -1);
|
||||||
|
@ -857,8 +857,7 @@ API int notcurses_render(struct notcurses* nc);
|
|||||||
// do not write the resulting buffer out to the terminal. Using this function,
|
// do not write the resulting buffer out to the terminal. Using this function,
|
||||||
// the user can control the writeout process, and render a second frame while
|
// the user can control the writeout process, and render a second frame while
|
||||||
// writing another. The returned buffer must be freed by the caller.
|
// writing another. The returned buffer must be freed by the caller.
|
||||||
API int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen)
|
API int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen);
|
||||||
__attribute__ ((deprecated));
|
|
||||||
|
|
||||||
// Write the last rendered frame, in its entirety, to 'fp'. If
|
// Write the last rendered frame, in its entirety, to 'fp'. If
|
||||||
// notcurses_render() has not yet been called, nothing will be written.
|
// notcurses_render() has not yet been called, nothing will be written.
|
||||||
|
@ -96,11 +96,11 @@ typedef struct ncplane {
|
|||||||
// current presentation state of the terminal. it is carried across render
|
// current presentation state of the terminal. it is carried across render
|
||||||
// instances. initialize everything to 0 on a terminal reset / startup.
|
// instances. initialize everything to 0 on a terminal reset / startup.
|
||||||
typedef struct renderstate {
|
typedef struct renderstate {
|
||||||
// we assemble the encoded output in a POSIX memstream, and keep it around
|
// we assemble the encoded (rasterized) output in a POSIX memstream, and keep
|
||||||
// between uses. this could be a problem if it ever tremendously spiked, but
|
// it around between uses. this could be a problem if it ever tremendously
|
||||||
// that's a highly unlikely situation.
|
// spiked, but that's a highly unlikely situation.
|
||||||
char* mstream; // buffer for rendering memstream, see open_memstream(3)
|
char* mstream; // buffer for rasterizing memstream, see open_memstream(3)
|
||||||
FILE* mstreamfp;// FILE* for rendering memstream
|
FILE* mstreamfp;// FILE* for rasterizing memstream
|
||||||
size_t mstrsize;// size of rendering memstream
|
size_t mstrsize;// size of rendering memstream
|
||||||
|
|
||||||
// the current cursor position. this is independent of whether the cursor is
|
// the current cursor position. this is independent of whether the cursor is
|
||||||
@ -298,20 +298,13 @@ typedef struct ncdirect {
|
|||||||
uint64_t flags; // copied in ncdirect_init() from param
|
uint64_t flags; // copied in ncdirect_init() from param
|
||||||
} ncdirect;
|
} ncdirect;
|
||||||
|
|
||||||
// A rendering context/result, suitable for reuse across many rendering
|
|
||||||
// operations. A pile's planes are rendered down into a single virtual plane,
|
|
||||||
// which can be (relative to the last rendered plane, common across the
|
|
||||||
// notcurses context) rasterized to the actual terminal.
|
|
||||||
typedef struct ncrender {
|
|
||||||
char* buf;
|
|
||||||
size_t buflen;
|
|
||||||
} ncrender;
|
|
||||||
|
|
||||||
typedef struct ncpile {
|
typedef struct ncpile {
|
||||||
ncplane* top; // topmost plane, never NULL
|
ncplane* top; // topmost plane, never NULL
|
||||||
ncplane* bottom; // bottommost plane, never NULL
|
ncplane* bottom; // bottommost plane, never NULL
|
||||||
struct notcurses* nc; // notcurses context
|
struct notcurses* nc; // notcurses context
|
||||||
struct ncpile *prev, *next; // circular list
|
struct ncpile *prev, *next; // circular list
|
||||||
|
size_t crenderlen; // size of crender array in bytes
|
||||||
|
struct crender* crender; // crender array
|
||||||
} ncpile;
|
} ncpile;
|
||||||
|
|
||||||
// the standard pile can be reached through ->stdplane.
|
// the standard pile can be reached through ->stdplane.
|
||||||
|
@ -271,6 +271,7 @@ ncpile_destroy(ncpile* pile){
|
|||||||
if(pile){
|
if(pile){
|
||||||
pile->prev->next = pile->next;
|
pile->prev->next = pile->next;
|
||||||
pile->next->prev = pile->prev;
|
pile->next->prev = pile->prev;
|
||||||
|
free(pile->crender);
|
||||||
free(pile);
|
free(pile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,6 +316,8 @@ make_ncpile(notcurses* nc, ncplane* n){
|
|||||||
n->pile = ret;
|
n->pile = ret;
|
||||||
n->above = NULL;
|
n->above = NULL;
|
||||||
n->below = NULL;
|
n->below = NULL;
|
||||||
|
ret->crenderlen = 0;
|
||||||
|
ret->crender = NULL;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
112
src/lib/render.c
112
src/lib/render.c
@ -152,11 +152,10 @@ int cell_duplicate(ncplane* n, cell* targ, const cell* c){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extracellular state for a cell during the render process. This array is
|
// Extracellular state for a cell during the render process. There is one
|
||||||
// passed along to rasterization, which uses only the 'damaged' bools. There
|
// crender per rendered cell, and they are initialized to all zeroes.
|
||||||
// is one crender per rendered cell, and they are initialized to all zeroes.
|
|
||||||
struct crender {
|
struct crender {
|
||||||
const ncplane *p;
|
const ncplane *p; // source of glyph for this cell
|
||||||
cell c;
|
cell c;
|
||||||
unsigned fgblends;
|
unsigned fgblends;
|
||||||
unsigned bgblends;
|
unsigned bgblends;
|
||||||
@ -366,7 +365,7 @@ paint(const ncplane* p, struct crender* rvec, int dstleny, int dstlenx,
|
|||||||
|
|
||||||
// it's not a pure memset(), because CELL_ALPHA_OPAQUE is the zero value, and
|
// it's not a pure memset(), because CELL_ALPHA_OPAQUE is the zero value, and
|
||||||
// we need CELL_ALPHA_TRANSPARENT
|
// we need CELL_ALPHA_TRANSPARENT
|
||||||
static void
|
static inline void
|
||||||
init_rvec(struct crender* rvec, int totalcells){
|
init_rvec(struct crender* rvec, int totalcells){
|
||||||
memset(rvec, 0, sizeof(*rvec) * totalcells);
|
memset(rvec, 0, sizeof(*rvec) * totalcells);
|
||||||
for(int t = 0 ; t < totalcells ; ++t){
|
for(int t = 0 ; t < totalcells ; ++t){
|
||||||
@ -709,12 +708,12 @@ update_palette(notcurses* nc, FILE* out){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync the cursor to the specified location with as little overhead as
|
// sync the drawing position to the specified location with as little overhead
|
||||||
// possible (with nothing, if already at the right location).
|
// as possible (with nothing, if already at the right location).
|
||||||
// FIXME fall back to synthesized moves in the absence of capabilities (i.e.
|
// FIXME fall back to synthesized moves in the absence of capabilities (i.e.
|
||||||
// textronix lacks cup; fake it with horiz+vert moves)
|
// textronix lacks cup; fake it with horiz+vert moves)
|
||||||
static inline int
|
static inline int
|
||||||
stage_cursor(notcurses* nc, FILE* out, int y, int x){
|
goto_location(notcurses* nc, FILE* out, int y, int x){
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if(nc->rstate.y == y){ // only need move x
|
if(nc->rstate.y == y){ // only need move x
|
||||||
const int xdiff = x - nc->rstate.x;
|
const int xdiff = x - nc->rstate.x;
|
||||||
@ -779,7 +778,7 @@ notcurses_rasterize_inner(notcurses* nc, const struct crender* rvec, FILE* out){
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
++nc->stats.cellemissions;
|
++nc->stats.cellemissions;
|
||||||
if(stage_cursor(nc, out, y, x)){
|
if(goto_location(nc, out, y, x)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// set the style. this can change the color back to the default; if it
|
// set the style. this can change the color back to the default; if it
|
||||||
@ -1038,7 +1037,7 @@ int notcurses_render_to_file(notcurses* nc, FILE* fp){
|
|||||||
// which cells were changed. We solve for each coordinate's cell by walking
|
// which cells were changed. We solve for each coordinate's cell by walking
|
||||||
// down the z-buffer, looking at intersections with ncplanes. This implies
|
// down the z-buffer, looking at intersections with ncplanes. This implies
|
||||||
// locking down the EGC, the attributes, and the channels for each cell.
|
// locking down the EGC, the attributes, and the channels for each cell.
|
||||||
static int
|
static void
|
||||||
ncpile_render_internal(ncplane* n, struct crender* rvec, int leny, int lenx,
|
ncpile_render_internal(ncplane* n, struct crender* rvec, int leny, int lenx,
|
||||||
int absy, int absx){
|
int absy, int absx){
|
||||||
ncplane* p = ncplane_pile(n)->top;
|
ncplane* p = ncplane_pile(n)->top;
|
||||||
@ -1046,63 +1045,78 @@ ncpile_render_internal(ncplane* n, struct crender* rvec, int leny, int lenx,
|
|||||||
paint(p, rvec, leny, lenx, absy, absx);
|
paint(p, rvec, leny, lenx, absy, absx);
|
||||||
p = p->below;
|
p = p->below;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ncpile_rasterize(ncplane* n){
|
||||||
|
struct timespec start, writedone;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
|
int dimy, dimx;
|
||||||
|
const struct ncpile* pile = ncplane_pile(n);
|
||||||
|
notcurses_resize(ncplane_notcurses(n), &dimy, &dimx);
|
||||||
|
postpaint(ncplane_notcurses(n)->lastframe, dimy, dimx, pile->crender,
|
||||||
|
&ncplane_notcurses(n)->pool);
|
||||||
|
int bytes = notcurses_rasterize(ncplane_notcurses(n), pile->crender,
|
||||||
|
ncplane_notcurses(n)->rstate.mstreamfp);
|
||||||
|
// accepts -1 as an indication of failure
|
||||||
|
update_render_bytes(&ncplane_notcurses(n)->stats, bytes);
|
||||||
|
if(bytes < 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &writedone);
|
||||||
|
update_write_stats(&writedone, &start, &ncplane_notcurses(n)->stats);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure the crender vector of 'n' is sufficiently large for 'dimy'x'dimx'
|
||||||
|
static int
|
||||||
|
engorge_crender_vector(ncpile* n, int dimy, int dimx){
|
||||||
|
const size_t crenderlen = sizeof(struct crender) * dimy * dimx;
|
||||||
|
if(crenderlen > n->crenderlen){
|
||||||
|
struct crender* tmp = realloc(n->crender, crenderlen);
|
||||||
|
if(tmp == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n->crender = tmp;
|
||||||
|
n->crenderlen = crenderlen;
|
||||||
|
}
|
||||||
|
init_rvec(n->crender, crenderlen / sizeof(struct crender));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ncpile_render(ncplane* n){
|
int ncpile_render(ncplane* n){
|
||||||
struct timespec start, rasterdone;
|
struct timespec start, renderdone;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||||
int dimy, dimx;
|
int dimy, dimx;
|
||||||
notcurses_resize(ncplane_notcurses(n), &dimy, &dimx);
|
// render against our current notion of screen geometry
|
||||||
int bytes = -1;
|
ncplane_dim_yx(notcurses_stdplane(ncplane_notcurses(n)), &dimy, &dimx);
|
||||||
const size_t crenderlen = sizeof(struct crender) * dimy * dimx;
|
if(engorge_crender_vector(ncplane_pile(n), dimy, dimx)){
|
||||||
struct crender* crender = malloc(crenderlen);
|
|
||||||
init_rvec(crender, crenderlen / sizeof(struct crender));
|
|
||||||
if(ncpile_render_internal(n, crender, dimy, dimx,
|
|
||||||
notcurses_stdplane(ncplane_notcurses(n))->absy,
|
|
||||||
notcurses_stdplane(ncplane_notcurses(n))->absx) == 0){
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &rasterdone);
|
|
||||||
update_render_stats(&rasterdone, &start, &ncplane_notcurses(n)->stats);
|
|
||||||
// FIXME extract and move to ncpile_rasterize
|
|
||||||
postpaint(ncplane_notcurses(n)->lastframe, dimy, dimx, crender,
|
|
||||||
&ncplane_notcurses(n)->pool);
|
|
||||||
bytes = notcurses_rasterize(ncplane_notcurses(n), crender,
|
|
||||||
ncplane_notcurses(n)->rstate.mstreamfp);
|
|
||||||
}
|
|
||||||
// accepts -1 as an indication of failure
|
|
||||||
update_render_bytes(&ncplane_notcurses(n)->stats, bytes);
|
|
||||||
free(crender);
|
|
||||||
if(bytes < 0){
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
struct timespec writedone;
|
ncpile_render_internal(n, ncplane_pile(n)->crender, dimy, dimx,
|
||||||
clock_gettime(CLOCK_MONOTONIC, &writedone);
|
notcurses_stdplane(ncplane_notcurses(n))->absy,
|
||||||
update_write_stats(&writedone, &rasterdone, &ncplane_notcurses(n)->stats);
|
notcurses_stdplane(ncplane_notcurses(n))->absx);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &renderdone);
|
||||||
|
update_render_stats(&renderdone, &start, &ncplane_notcurses(n)->stats);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int notcurses_render(notcurses* nc){
|
int notcurses_render(notcurses* nc){
|
||||||
return ncpile_render(notcurses_stdplane(nc));
|
struct ncplane* stdn = notcurses_stdplane(nc);
|
||||||
|
if(ncpile_render(stdn)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return(ncpile_rasterize(stdn));
|
||||||
}
|
}
|
||||||
|
|
||||||
// for now, we just run the top half of notcurses_render(), and copy out the
|
// for now, we just run the top half of notcurses_render(), and copy out the
|
||||||
// memstream from within rstate. we want to allocate our own here, and return
|
// memstream from within rstate. we want to allocate our own here, and return
|
||||||
// it, to avoid the copy, but we need feed the params through to do so FIXME.
|
// it, to avoid the copy, but we need feed the params through to do so FIXME.
|
||||||
int notcurses_render_to_buffer(notcurses* nc, char** buf, size_t* buflen){
|
int notcurses_render_to_buffer(notcurses* nc, char** buf, size_t* buflen){
|
||||||
struct timespec start, rasterdone;
|
struct ncplane* stdn = notcurses_stdplane(nc);
|
||||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
if(ncpile_render(stdn)){
|
||||||
int dimy, dimx;
|
return -1;
|
||||||
notcurses_resize(nc, &dimy, &dimx);
|
|
||||||
int bytes = -1;
|
|
||||||
const size_t crenderlen = sizeof(struct crender) * dimy * dimx;
|
|
||||||
struct crender* crender = malloc(crenderlen);
|
|
||||||
init_rvec(crender, crenderlen / sizeof(struct crender));
|
|
||||||
if(ncpile_render_internal(nc->stdplane, crender, dimy, dimx, nc->stdplane->absy, nc->stdplane->absx) == 0){
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &rasterdone);
|
|
||||||
update_render_stats(&rasterdone, &start, &nc->stats);
|
|
||||||
postpaint(nc->lastframe, dimy, dimx, crender, &nc->pool);
|
|
||||||
bytes = notcurses_rasterize_inner(nc, crender, nc->rstate.mstreamfp);
|
|
||||||
}
|
}
|
||||||
|
int bytes = notcurses_rasterize_inner(nc, ncplane_pile(stdn)->crender, nc->rstate.mstreamfp);
|
||||||
update_render_bytes(&nc->stats, bytes);
|
update_render_bytes(&nc->stats, bytes);
|
||||||
if(bytes < 0){
|
if(bytes < 0){
|
||||||
return -1;
|
return -1;
|
||||||
@ -1188,7 +1202,7 @@ int notcurses_cursor_enable(notcurses* nc, int y, int x){
|
|||||||
if(nc->ttyfd < 0 || !nc->tcache.cnorm){
|
if(nc->ttyfd < 0 || !nc->tcache.cnorm){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(stage_cursor(nc, nc->ttyfp, y + nc->stdplane->absy, x + nc->stdplane->absx)){
|
if(goto_location(nc, nc->ttyfp, y + nc->stdplane->absy, x + nc->stdplane->absx)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
// if we were already positive, we're already visible, no need to write cnorm
|
// if we were already positive, we're already visible, no need to write cnorm
|
||||||
|
Loading…
x
Reference in New Issue
Block a user