diff --git a/NEWS.md b/NEWS.md index 9d1a1055a..50a001b61 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,11 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. +* 2.2.6 (not yet released) + * `ncplane_rgba()` has been deprecated in favor of the new function + `ncplane_as_rgba()`, which the former now wraps. It will be removed + in ABI3. The new function can report the synthesized pixel geometry. + * 2.2.5 (2021-04-04) * Bugfix release, no user-visible changes. diff --git a/USAGE.md b/USAGE.md index f83782650..4d14e1738 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1017,9 +1017,11 @@ int ncplane_at_yx_cell(struct ncplane* n, int y, int x, nccell* c); // Start at the plane's 'begy'x'begx' coordinate (which must lie on the // plane), continuing for 'leny'x'lenx' cells. Either or both of 'leny' and // 'lenx' can be specified as -1 to go through the boundary of the plane. -// Only glyphs from the specified blitset may be present. -uint32_t* ncplane_rgba(const struct ncplane* nc, ncblitter_e blit, - int begy, int begx, int leny, int lenx); +// Only glyphs from the specified blitset may be present. If 'pxdimy' and/or +// 'pxdimx' are non-NULL, they will be filled in with the pixel geometry. +uint32_t* ncplane_as_rgba(const struct ncplane* n, ncblitter_e blit, + int begy, int begx, int leny, int lenx, + int *pxdimy, int *pxdimx); // return a nul-terminated, heap copy of the current (UTF-8) contents. char* ncplane_contents(const struct ncplane* nc, int begy, int begx, diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index e64400d24..40575c3b8 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -104,7 +104,7 @@ typedef struct ncplane_options { **int ncplane_at_yx_cell(struct ncplane* ***n***, int ***y***, int ***x***, nccell* ***c***);** -**uint32_t* ncplane_rgba(const struct ncplane* ***nc***, int ***begy***, int ***begx***, int ***leny***, int ***lenx***);** +**uint32_t* ncplane_as_rgba(const struct ncplane* ***nc***, int ***begy***, int ***begx***, int ***leny***, int ***lenx***, int* ***pxdimy***, int* ***pxdimx***);** **char* ncplane_contents(const struct ncplane* ***nc***, int ***begy***, int ***begx***, int ***leny***, int ***lenx***);** @@ -373,6 +373,9 @@ this result. **ncplane_at_yx_cell** and **ncplane_at_cursor_cell** instead load these values into an **nccell**, which is invalidated if the associated plane is destroyed. The caller should release this **nccell** with **cell_release**. +**ncplane_as_rgba** returns a heap-allocated array of **uint32_t** values, +each representing a single RGBA pixel, or **NULL** on failure. + Functions returning **int** return 0 on success, and non-zero on error. All other functions cannot fail (and return **void**). diff --git a/include/ncpp/Plane.hh b/include/ncpp/Plane.hh index 89889c734..f82204cbd 100644 --- a/include/ncpp/Plane.hh +++ b/include/ncpp/Plane.hh @@ -805,7 +805,7 @@ namespace ncpp uint32_t* rgba(ncblitter_e blit, int begy, int begx, int leny, int lenx) const noexcept { - return ncplane_rgba (plane, blit, begy, begx, leny, lenx); + return ncplane_as_rgba (plane, blit, begy, begx, leny, lenx, nullptr, nullptr); } char* content(int begy, int begx, int leny, int lenx) const noexcept diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index bcde3b7ed..4932b0ee5 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -2450,9 +2450,20 @@ struct ncvisual_options { // Start at the plane's 'begy'x'begx' coordinate (which must lie on the // plane), continuing for 'leny'x'lenx' cells. Either or both of 'leny' and // 'lenx' can be specified as -1 to go through the boundary of the plane. -// Only glyphs from the specified blitset may be present. -API ALLOC uint32_t* ncplane_rgba(const struct ncplane* n, ncblitter_e blit, - int begy, int begx, int leny, int lenx); +// Only glyphs from the specified blitset may be present. If 'pxdimy' and/or +// 'pxdimx' are non-NULL, they will be filled in with the pixel geometry. +API ALLOC uint32_t* ncplane_as_rgba(const struct ncplane* n, ncblitter_e blit, + int begy, int begx, int leny, int lenx, + int *pxdimy, int *pxdimx) + __attribute__ ((nonnull (1))); + +// Deprecated in favor of ncplane_as_rgba. This will be removed in ABI3. +ALLOC __attribute__ ((deprecated)) __attribute__ ((nonnull (1))) +static inline uint32_t* +ncplane_rgba(const struct ncplane* n, ncblitter_e blit, + int begy, int begx, int leny, int lenx){ + return ncplane_as_rgba(n, blit, begy, begx, leny, lenx, NULL, NULL); +} // Get the size and ratio of ncvisual pixels to output cells along the y // ('toy') and x ('tox') axes. A ncvisual of '*y'X'*x' pixels will require diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index dcdc211ba..168a4afbe 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -2424,12 +2424,16 @@ int ncdirect_inputready_fd(ncdirect* n){ return n->input.ttyinfd; } -uint32_t* ncplane_rgba(const ncplane* nc, ncblitter_e blit, - int begy, int begx, int leny, int lenx){ +uint32_t* ncplane_as_rgba(const ncplane* nc, ncblitter_e blit, + int begy, int begx, int leny, int lenx, + int *pxdimy, int *pxdimx){ + const notcurses* ncur = ncplane_notcurses_const(nc); if(begy < 0 || begx < 0){ + logerror(ncur, "Nil offset (%d,%d)\n", begy, begx); return NULL; } if(begx >= nc->lenx || begy >= nc->leny){ + logerror(ncur, "Invalid offset (%d,%d)\n", begy, begx); return NULL; } if(lenx == -1){ // -1 means "to the end"; use all space available @@ -2439,13 +2443,27 @@ uint32_t* ncplane_rgba(const ncplane* nc, ncblitter_e blit, leny = nc->leny - begy; } if(lenx < 0 || leny < 0){ // no need to draw zero-size object, exit + logerror(ncur, "Nil geometry (%dx%d)\n", leny, lenx); return NULL; } //fprintf(stderr, "sum: %d/%d avail: %d/%d\n", begy + leny, begx + lenx, nc->leny, nc->lenx); if(begx + lenx > nc->lenx || begy + leny > nc->leny){ + logerror(ncur, "Invalid specs %d + %d > %d or %d + %d > %d\n", + begx, lenx, nc->lenx, begy, leny, nc->leny); + return NULL; + } + if(blit > NCBLIT_2x1){ + logerror(ncur, "Blitter %d is not yet supported\n", blit); return NULL; } //fprintf(stderr, "ALLOCATING %zu\n", 4u * lenx * leny * 2); + // FIXME this all assumes NCBLIT_2x1, need blitter-specific scaling + if(pxdimy){ + *pxdimy = leny * 2; + } + if(pxdimx){ + *pxdimx = lenx; + } uint32_t* ret = malloc(sizeof(*ret) * lenx * leny * 2); if(ret){ for(int y = begy, targy = 0 ; y < begy + leny ; ++y, targy += 2){ @@ -2466,8 +2484,6 @@ uint32_t* ncplane_rgba(const ncplane* nc, ncblitter_e blit, // FIXME how do we deal with transparency? uint32_t frgba = (fr) + (fg << 16u) + (fb << 8u) + 0xff000000; uint32_t brgba = (br) + (bg << 16u) + (bb << 8u) + 0xff000000; - // FIXME integrate 'blit' - (void)blit; // FIXME need to be able to pick up quadrants! if((strcmp(c, " ") == 0) || (strcmp(c, "") == 0)){ *top = *bot = brgba; diff --git a/src/lib/visual.c b/src/lib/visual.c index a45c320c7..acfa1d861 100644 --- a/src/lib/visual.c +++ b/src/lib/visual.c @@ -698,7 +698,7 @@ ncplane* ncvisual_render(notcurses* nc, ncvisual* ncv, const struct ncvisual_opt ncvisual* ncvisual_from_plane(const ncplane* n, ncblitter_e blit, int begy, int begx, int leny, int lenx){ - uint32_t* rgba = ncplane_rgba(n, blit, begy, begx, leny, lenx); + uint32_t* rgba = ncplane_as_rgba(n, blit, begy, begx, leny, lenx, NULL, NULL); //fprintf(stderr, "snarg: %d/%d @ %d/%d (%p)\n", leny, lenx, begy, begx, rgba); //fprintf(stderr, "RGBA %p\n", rgba); if(rgba == NULL){ diff --git a/src/poc/rotator.c b/src/poc/rotator.c index 5a09cc947..9fa24b7e4 100644 --- a/src/poc/rotator.c +++ b/src/poc/rotator.c @@ -24,7 +24,8 @@ rotate_grad(struct notcurses* nc){ } notcurses_render(nc); clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); - uint32_t* rgba = ncplane_rgba(n, NCBLIT_DEFAULT, 0, 0, dimy, dimx); + uint32_t* rgba = ncplane_as_rgba(n, NCBLIT_DEFAULT, 0, 0, + dimy, dimx, NULL, NULL); if(rgba == NULL){ return -1; } @@ -150,7 +151,8 @@ rotate(struct notcurses* nc){ clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);; // we now have 2 rows of 20 cells each, with gradients. load 'em. - uint32_t* rgba = ncplane_rgba(n, NCBLIT_DEFAULT, dimy / 2, 0, 2, XSIZE); + uint32_t* rgba = ncplane_as_rgba(n, NCBLIT_DEFAULT, dimy / 2, 0, + 2, XSIZE, NULL, NULL); if(rgba == NULL){ return -1; } diff --git a/src/tests/rotate.cpp b/src/tests/rotate.cpp index 81e62e9d2..3adf29feb 100644 --- a/src/tests/rotate.cpp +++ b/src/tests/rotate.cpp @@ -173,8 +173,12 @@ TEST_CASE("Rotate") { ncvisual_options opts{}; auto rendered = ncvisual_render(nc_, ncv, &opts); REQUIRE(rendered); - uint32_t* rgbaret = ncplane_rgba(rendered, NCBLIT_DEFAULT, 0, 0, -1, -1); + int pxdimy, pxdimx; + uint32_t* rgbaret = ncplane_as_rgba(rendered, NCBLIT_DEFAULT, + 0, 0, -1, -1, &pxdimy, &pxdimx); REQUIRE(rgbaret); + CHECK(pxdimx == width); + CHECK(pxdimy == height); for(int i = 0 ; i < height * width / 2 ; ++i){ if(rgbaret[i] & CELL_BG_RGB_MASK){ CHECK(htole(rgbaret[i]) == rgba[i]); @@ -226,8 +230,12 @@ TEST_CASE("Rotate") { ncvisual_options opts{}; auto rendered = ncvisual_render(nc_, ncv, &opts); REQUIRE(rendered); - uint32_t* rgbaret = ncplane_rgba(rendered, NCBLIT_DEFAULT, 0, 0, -1, -1); + int pxdimy, pxdimx; + uint32_t* rgbaret = ncplane_as_rgba(rendered, NCBLIT_DEFAULT, + 0, 0, -1, -1, &pxdimy, &pxdimx); REQUIRE(rgbaret); + CHECK(pxdimy == height); + CHECK(pxdimx == width); for(int i = 0 ; i < height * width / 2 ; ++i){ if(rgbaret[i] & CELL_BG_RGB_MASK){ CHECK(htole(rgbaret[i]) == rgba[i]);