From 270b1b20ee73a7d4a15602ea0a2f42840327a0f0 Mon Sep 17 00:00:00 2001 From: nick black Date: Sun, 4 Oct 2020 11:34:15 -0400 Subject: [PATCH] implement notcurses_render_to_buffer() #214 --- USAGE.md | 2 +- doc/man/man3/notcurses_render.3.md | 2 +- include/ncpp/NotCurses.hh | 5 ++++ include/notcurses/notcurses.h | 2 +- python/src/notcurses/build_notcurses.py | 2 +- src/lib/render.c | 37 +++++++++++++++++++++++-- 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/USAGE.md b/USAGE.md index 77cd99059..2941e2e26 100644 --- a/USAGE.md +++ b/USAGE.md @@ -175,7 +175,7 @@ int notcurses_render(struct notcurses* nc); // 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 // writing another. The returned buffer must be freed by the caller. -int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t buflen); +int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen); // Write the last rendered frame, in its entirety, to 'fp'. If // notcurses_render() has not yet been called, nothing will be written. diff --git a/doc/man/man3/notcurses_render.3.md b/doc/man/man3/notcurses_render.3.md index cf0c7e383..daf967315 100644 --- a/doc/man/man3/notcurses_render.3.md +++ b/doc/man/man3/notcurses_render.3.md @@ -16,7 +16,7 @@ notcurses_render - sync the physical display to the virtual ncplanes **int notcurses_render_to_file(struct notcurses* nc, FILE* fp);** -**int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t buflen);** +**int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen);** # DESCRIPTION diff --git a/include/ncpp/NotCurses.hh b/include/ncpp/NotCurses.hh index c9b0212ea..c6c5b7e41 100644 --- a/include/ncpp/NotCurses.hh +++ b/include/ncpp/NotCurses.hh @@ -195,6 +195,11 @@ namespace ncpp 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 { return error_guard (notcurses_render_to_file (nc, fp), -1); diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 6f3bcef1b..3fbdca7c7 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -851,7 +851,7 @@ API int notcurses_render(struct notcurses* nc); // 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 // 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); // Write the last rendered frame, in its entirety, to 'fp'. If // notcurses_render() has not yet been called, nothing will be written. diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index 707118975..60bf35543 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -53,7 +53,7 @@ void notcurses_version_components(int* major, int* minor, int* patch, int* tweak int notcurses_lex_margins(const char* op, notcurses_options* opts); int notcurses_stop(struct notcurses*); int notcurses_render(struct notcurses* nc); -int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t buflen); +int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen); int notcurses_render_to_file(struct notcurses* nc, FILE* fp); struct ncplane* notcurses_stdplane(struct notcurses*); const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc); diff --git a/src/lib/render.c b/src/lib/render.c index 73acdda02..0661a7fdc 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -909,7 +909,7 @@ notcurses_rasterize_inner(notcurses* nc, const struct crender* rvec, FILE* out){ //fprintf(stderr, "damageidx: %ld\n", damageidx); } } - return 0; + return nc->rstate.mstrsize; } // rasterize the rendered frame, and blockingly write it out to the terminal. @@ -935,7 +935,8 @@ raster_and_write(notcurses* nc, const struct crender* rvec, FILE* out){ // if the cursor is enabled, store its location and disable it. then, once done // rasterizing, enable it afresh, moving it to the stored location. if left on -// during rasterization, we'll get grotesque flicker. +// during rasterization, we'll get grotesque flicker. 'out' is a memstream +// used to collect a buffer. static inline int notcurses_rasterize(notcurses* nc, const struct crender* rvec, FILE* out){ const int cursory = nc->cursory; @@ -1054,7 +1055,7 @@ notcurses_render_internal(notcurses* nc, struct crender* rvec){ } int notcurses_render(notcurses* nc){ - struct timespec start, rasterdone, writedone; + struct timespec start, rasterdone; clock_gettime(CLOCK_MONOTONIC, &start); int dimy, dimx; notcurses_resize(nc, &dimy, &dimx); @@ -1072,11 +1073,41 @@ int notcurses_render(notcurses* nc){ if(bytes < 0){ return -1; } + struct timespec writedone; clock_gettime(CLOCK_MONOTONIC, &writedone); update_write_stats(&writedone, &rasterdone, &nc->stats); return 0; } +// 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 +// 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){ + struct timespec start, rasterdone; + clock_gettime(CLOCK_MONOTONIC, &start); + int dimy, dimx; + notcurses_resize(nc, &dimy, &dimx); + int bytes = -1; + const size_t crenderlen = sizeof(struct crender) * nc->stdplane->leny * nc->stdplane->lenx; + struct crender* crender = malloc(crenderlen); + init_rvec(crender, crenderlen / sizeof(struct crender)); + if(notcurses_render_internal(nc, crender) == 0){ + clock_gettime(CLOCK_MONOTONIC, &rasterdone); + update_render_stats(&rasterdone, &start, &nc->stats); + bytes = notcurses_rasterize_inner(nc, crender, nc->rstate.mstreamfp); + } + update_render_bytes(&nc->stats, bytes); + if(bytes < 0){ + return -1; + } + *buf = memdup(nc->rstate.mstreamfp, nc->rstate.mstrsize); + if(buf == NULL){ + return -1; + } + *buflen = nc->rstate.mstrsize; + return 0; +} + // copy the UTF8-encoded EGC out of the cell, whether simple or complex. the // result is not tied to the ncplane, and persists across erases / destruction. static inline char*