[core] introduce ncvisual_geom(), replacing ncvisual_blitter_geom() #1684

pull/2320/head
nick black 3 years ago committed by nick black
parent c906d2cf8a
commit a0b34f7062

@ -12,6 +12,12 @@ rearrangements of Notcurses.
deprecated functionality, ABI3 ought require small changes, if any.
* 2.4.9 (not yet released)
* `ncvisual_geom()` has been introduced, using the `ncvgeom` struct
introduced for direct mode. This allows complete statement of geometry
for an `ncvisual`. It replaces `ncvisual_blitter_geom()`, which has been
deprecated, and will be removed in ABI3. It furthermore exposes some of
the information previously available only from `ncplane_pixelgeom()`,
though that function continues to be supported.
* On transition between `ncplane`s (on terminals implementing complex wide
glyphs), Notcurses now always issues an `hpa` sequence to force horizontal
positioning. This fixes a number of longstanding bugs in e.g. the

@ -3299,17 +3299,38 @@ Various transformations can be applied to an `ncvisual`, regardless of how
it was built up:
```c
// Get the size and ratio of ncvisual pixels to output cells along the y
// and x axes. The input size (in pixels) will be written to 'y' and 'x'.
// The scaling will be written to 'scaley' and 'scalex'. With these:
// rows = (y / scaley) + !!(y % scaley) or (y + scaley - 1) / scaley
// cols = (x / scalex) + !!(x % scalex) or (x + scalex - 1) / scalex
// Returns non-zero for an invalid 'vopts'. The blitter that will be used
// is returned in '*blitter'.
// These results are invalidated upon a terminal resize.
int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts, int* y, int* x,
int* scaley, int* scalex, ncblitter_e* blitter);
// describes all geometries of an ncvisual: those which are inherent, and those
// dependent upon a given rendering regime. pixy and pixx are the true internal
// pixel geometry, taken directly from the load (and updated by
// ncvisual_resize()). cdimy/cdimx are the cell-pixel geometry *at the time
// of this call* (it can change with a font change, in which case all values
// other than pixy/pixx are invalidated). rpixy/rpixx are the pixel geometry as
// handed to the blitter, following any scaling. scaley/scalex are the number
// of input pixels drawn to a single cell; when using NCBLIT_PIXEL, they are
// equivalent to cdimy/cdimx. rcelly/rcellx are the cell geometry as written by
// the blitter, following any padding (there is padding whenever rpix{y, x} is
// not evenly divided by scale{y, x}, and also sometimes for Sixel).
// maxpixely/maxpixelx are defined only when NCBLIT_PIXEL is used, and specify
// the largest bitmap that the terminal is willing to accept. blitter is the
// blitter which will be used, a function of the requested blitter and the
// blitters actually supported by this environment. if no ncvisual was
// supplied, only cdimy/cdimx are filled in.
typedef struct ncvgeom {
int pixy, pixx; // true pixel geometry of ncvisual data
int cdimy, cdimx; // terminal cell geometry when this was calculated
int rpixy, rpixx; // rendered pixel geometry (per visual_options)
int rcelly, rcellx; // rendered cell geometry (per visual_options)
int scaley, scalex; // pixels per filled cell (scale == c for bitmaps)
int maxpixely, maxpixelx; // only defined for NCBLIT_PIXEL
ncblitter_e blitter;// blitter that will be used
} ncvgeom;
// all-purpose ncvisual geometry solver. one or both of 'nc' and 'n' must be
// non-NULL. if 'nc' is NULL, only pixy/pixx will be filled in, with the true
// pixel geometry of 'n'. if 'n' is NULL, only cdimy/cdimx, blitter, and (if
// applicable) maxpixely/maxpixelx are filled in.
int ncvisual_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts, ncvgeom* geom);
// Scale the visual to 'rows' X 'columns' pixels, using the best scheme
// available. This is a lossy transformation, unless the size is unchanged.

@ -51,6 +51,16 @@ struct ncvisual_options {
};
typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
typedef struct ncvgeom {
int pixy, pixx; // true pixel geometry of ncvisual data
int cdimy, cdimx; // terminal cell geometry when this was calculated
int rpixy, rpixx; // rendered pixel geometry (per visual_options)
int rcelly, rcellx; // rendered cell geometry (per visual_options)
int scaley, scalex; // pixels per filled cell (scale == c for bitmaps)
int maxpixely, maxpixelx; // only defined for NCBLIT_PIXEL
ncblitter_e blitter;// blitter that will be used
} ncvgeom;
```
**struct ncvisual* ncvisual_from_file(const char* ***file***);**
@ -67,7 +77,7 @@ typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
**struct ncvisual* ncvisual_from_plane(struct ncplane* ***n***, ncblitter_e ***blit***, int ***begy***, int ***begx***, int ***leny***, int ***lenx***);**
**int ncvisual_blitter_geom(const struct notcurses* ***nc***, const struct ncvisual* ***n***, const struct ncvisual_options* ***vopts***, int* ***y***, int* ***x***, int* ***scaley***, int* ***scalex***, ncblitter_e* ***blitter***);**
**int ncvisual_geom(const struct notcurses* ***nc***, const struct ncvisual* ***n***, const struct ncvisual_options* ***vopts***, ncvgeom* ***geom***);**
**void ncvisual_destroy(struct ncvisual* ***ncv***);**
@ -177,9 +187,14 @@ geometry of same. ***flags*** is a bitfield over:
as a transparent color.
* **NCVISUAL_OPTION_CHILDPLANE**: Make a new plane, as a child of ***n***.
**ncvisual_blitter_geom** allows the caller to determine any or all of the
visual's pixel geometry, the blitter to be used, and that blitter's scaling
in both dimensions. Any but the first argument may be **NULL**.
**ncvisual_geom** allows the caller to determine any or all of the visual's
pixel geometry, the blitter to be used, and that blitter's scaling in both
dimensions. Any but the final argument may be **NULL**, though at least one
of ***nc*** and ***n*** must be non-**NULL**. If ***nc*** is **NULL**,
only properties intrinsic to the visual are returned (i.e. its original
pixel geometry). If ***n*** is **NULL**, only properties independent of the
visual are returned (i.e. cell-pixel geometry and maximum bitmap geometry).
If both are supplied, all fields of the **ncvgeom** structure are filled in.
**ncplane_qrcode** draws an ISO/IEC 18004:2015 QR Code for the **len** bytes of
**data** using **NCBLIT_2x1** (this is the only blitter that will work with QR
@ -317,7 +332,8 @@ which the visual was rendered. If **opts->n** is provided, this will be
**opts->n**. Otherwise, a plane will be created, perfectly sized for the
visual and the specified blitter.
**ncvisual_blitter_geom** returns non-zero if the specified blitter is invalid.
**ncvisual_geom** returns non-zero if the specified configuration is invalid,
or if both ***nc*** and ***n*** are **NULL**.
**ncvisual_media_defblitter** returns the blitter selected by **NCBLIT_DEFAULT**
in the specified configuration. If UTF8 is not enabled, this will always be
@ -358,7 +374,7 @@ When using non-interpolative blitting together with scaling, unless your goal
includes minimizing the total area required, lower-resolution blitters will
generally look just as good as higher resolution blitters, and be faster.
The results of **ncvisual_blitter_geom** are invalidated by a terminal resize.
The results of **ncvisual_geom** are invalidated by a terminal resize.
# BUGS

@ -96,9 +96,9 @@ namespace ncpp
return error_guard<int> (ncvisual_polyfill_yx (visual, y, x, rgba), -1);
}
bool geom (const struct ncvisual_options *vopts, int *y, int *x, int *toy, int *tox, ncblitter_e* blitter) const NOEXCEPT_MAYBE
bool geom (const struct ncvisual_options *vopts, ncvgeom* geom) const NOEXCEPT_MAYBE
{
return error_guard (ncvisual_blitter_geom (get_notcurses (), visual, vopts, y, x, toy, tox, blitter), -1);
return error_guard (ncvisual_geom (get_notcurses (), visual, vopts, geom), -1);
}
bool at (int y, int x, uint32_t* pixel) const

@ -2895,13 +2895,14 @@ struct ncvisual_options {
// maxpixely/maxpixelx are defined only when NCBLIT_PIXEL is used, and specify
// the largest bitmap that the terminal is willing to accept. blitter is the
// blitter which will be used, a function of the requested blitter and the
// blitters actually supported by this environment.
// blitters actually supported by this environment. if no ncvisual was
// supplied, only cdimy/cdimx are filled in.
typedef struct ncvgeom {
int pixy, pixx; // true pixel geometry of ncvisual data
int cdimy, cdimx; // terminal cell geometry when this was calculated
int rpixy, rpixx; // rendered pixel geometry
int rcelly, rcellx; // rendered cell geometry
int scaley, scalex; // pixels per filled cell
int rpixy, rpixx; // rendered pixel geometry (per visual_options)
int rcelly, rcellx; // rendered cell geometry (per visual_options)
int scaley, scalex; // pixels per filled cell (scale == c for bitmaps)
int maxpixely, maxpixelx; // only defined for NCBLIT_PIXEL
ncblitter_e blitter;// blitter that will be used
} ncvgeom;
@ -2916,7 +2917,15 @@ typedef struct ncvgeom {
API int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts, int* y, int* x,
int* scaley, int* scalex, ncblitter_e* blitter)
__attribute__ ((nonnull (1)));
__attribute__ ((nonnull (1))) __attribute__ ((deprecated));
// all-purpose ncvisual geometry solver. one or both of 'nc' and 'n' must be
// non-NULL. if 'nc' is NULL, only pixy/pixx will be filled in, with the true
// pixel geometry of 'n'. if 'n' is NULL, only cdimy/cdimx, blitter, and (if
// applicable) maxpixely/maxpixelx are filled in.
API int ncvisual_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts, ncvgeom* geom)
__attribute__ ((nonnull (4)));
// Destroy an ncvisual. Rendered elements will not be disrupted, but the visual
// can be neither decoded nor rendered any further.

@ -36,8 +36,6 @@ zoom_map(struct notcurses* nc, const char* map, int* ret){
if(ncv == NULL){
return NULL;
}
// true height and width of visual, and blitter scaling parameters
int vheight, yscale, vwidth, xscale;
// first we want to get the true size, so don't supply NCSSCALE_STRETCH yet,
// but *do* explicitly supply NCBLIT_2x2 since we're not scaling.
struct ncvisual_options vopts = {
@ -45,11 +43,16 @@ zoom_map(struct notcurses* nc, const char* map, int* ret){
.blitter = NCBLIT_2x2,
.flags = NCVISUAL_OPTION_NOINTERPOLATE,
};
if(ncvisual_blitter_geom(nc, ncv, &vopts, &vheight, &vwidth,
&yscale, &xscale, NULL)){
ncvgeom geom;
if(ncvisual_geom(nc, ncv, &vopts, &geom)){
ncvisual_destroy(ncv);
return NULL;
}
// true height and width of visual, and blitter scaling parameters
int vheight = geom.pixy;
int vwidth = geom.pixx;
int yscale = geom.scaley;
int xscale = geom.scalex;
//fprintf(stderr, "VHEIGHT: %d VWIDTH: %d scale: %d/%d\n", vheight, vwidth, yscale, xscale);
// we start at the lower left corner of the outzoomed map
int placey, placex; // dimensions of true display

@ -78,20 +78,11 @@ orcashow(struct notcurses* nc, int dimy, int dimx){
.blitter = NCBLIT_PIXEL,
.scaling = NCSCALE_STRETCH,
};
int cellpxy, cellpxx;
ncplane_pixelgeom(notcurses_stdplane_const(nc), NULL, NULL,
&cellpxy, &cellpxx, NULL, NULL);
int odimy, odimx, scaley, scalex;
ncvisual_blitter_geom(nc, ncv, &vopts, &odimy, &odimx, &scaley, &scalex, NULL);
// even if we can't do pixel output, we want her the same size as if weu
// *could* do pixel output. if we have no idea as to the geom, use scale.
if(cellpxy == 0){
cellpxy = scaley;
cellpxx = scalex;
}
ncvgeom geom;
ncvisual_geom(nc, ncv, &vopts, &geom);
struct ncplane_options nopts = {
.rows = (odimy / cellpxy) + !!(odimy % cellpxy),
.cols = (odimx / cellpxx) + !!(odimx % cellpxx),
.rows = geom.rcelly,
.cols = geom.rcellx,
.name = "orca",
};
if(nopts.cols > dimx - 1){

@ -36,18 +36,18 @@ visualize(struct notcurses* nc, struct ncvisual* ncv){
| NCVISUAL_OPTION_VERALIGNED
| NCVISUAL_OPTION_CHILDPLANE,
};
int scalex, scaley, truey, truex;
vopts.x = NCALIGN_CENTER;
vopts.y = NCALIGN_CENTER;
ncplane_erase(stdn); // to clear out old text
struct ncplane* n = NULL;
if(ncvisual_blitter_geom(nc, ncv, &vopts, &truey, &truex, &scaley, &scalex, NULL) == 0){
ncvgeom geom;
if(ncvisual_geom(nc, ncv, &vopts, &geom) == 0){
if( (n = ncvisual_blit(nc, ncv, &vopts)) ){
ncplane_move_below(n, stdn);
ncplane_printf_aligned(stdn, ncplane_dim_y(stdn) / 2 - 1, NCALIGN_CENTER,
"%03dx%03d", truex, truey);
"%03dx%03d", geom.pixx, geom.pixy);
ncplane_printf_aligned(stdn, ncplane_dim_y(stdn) / 2 + 1, NCALIGN_CENTER,
"%d:%d pixels -> cell", scalex, scaley);
"%d:%d pixels -> cell", geom.scalex, geom.scaley);
}
}
//fprintf(stderr, "X: %d truex: %d scalex: %d\n", vopts.x, truex, scalex);

@ -219,10 +219,10 @@ int luigi_demo(struct notcurses* nc){
ncplane_move_top(lastseen);
}
ncplane_move_yx(lastseen, yoff, i);
int dimy, scaley;
ncvisual_blitter_geom(nc, wmncv, NULL, &dimy, NULL, &scaley, NULL, NULL);
dimy /= scaley;
ncplane_move_yx(wmplane, rows * 4 / 5 - dimy + 1 + (i % 2), i - 60);
ncvgeom geom;
ncvisual_geom(nc, wmncv, NULL, &geom);
geom.pixy /= geom.scaley;
ncplane_move_yx(wmplane, rows * 4 / 5 - geom.pixy + 1 + (i % 2), i - 60);
DEMO_RENDER(nc);
demo_nanosleep(nc, &stepdelay);
}

@ -167,10 +167,10 @@ int normal_demo(struct notcurses* nc){
struct ncvisual_options vopts = {
.n = nstd,
};
int yscale, xscale;
ncvisual_blitter_geom(nc, NULL, &vopts, NULL, NULL, &yscale, &xscale, NULL);
dy *= yscale;
dx *= xscale;
ncvgeom geom;
ncvisual_geom(nc, NULL, &vopts, &geom);
dy *= geom.scaley;
dx *= geom.scalex;
uint32_t* rgba = malloc(sizeof(*rgba) * dy * dx);
if(!rgba){
goto err;
@ -179,10 +179,10 @@ int normal_demo(struct notcurses* nc){
rgba[off] = 0xff000000;
}
int y;
if(dy / yscale % 2){
y = dy / yscale + 1;
if(dy / geom.scaley % 2){
y = dy / geom.scaley + 1;
for(int x = 0 ; x < dx ; ++x){
if(mcell(offset(rgba, y, x, dx), y, x, dy / yscale, dx)){
if(mcell(offset(rgba, y, x, dx), y, x, dy / geom.scaley, dx)){
goto err;
}
}

@ -237,7 +237,8 @@ int yield_demo(struct notcurses* nc){
ncplane_set_fg_rgb8(label, 0xff, 0xff, 0xff);
ncplane_set_styles(label, NCSTYLE_BOLD);
ncplane_printf_aligned(label, 0, NCALIGN_CENTER, "Yield: %03.1f%%", 0.0);
if(ncvisual_blitter_geom(nc, v1, &m1.vopts, &maxy, &maxx, NULL, NULL, NULL)){
ncvgeom geom;
if(ncvisual_geom(nc, v1, &m1.vopts, &geom)){
ncplane_destroy(label);
ncvisual_destroy(v1);
ncvisual_destroy(v2);
@ -253,7 +254,7 @@ int yield_demo(struct notcurses* nc){
ncplane_destroy(m1.vopts.n);
return -1;
}
total = maxx * maxy;
total = geom.pixy * geom.pixx;
DEMO_RENDER(nc);

@ -132,7 +132,6 @@ ncvisual_origin(const struct ncvisual_options* vopts, int* restrict begy, int* r
// FIXME we ought also do the output calculations here (how many rows x cols,
// given the input plane vopts->n and scaling vopts->scaling)--but do not
// perform any actual scaling, nor create any planes!
// takes tcache distinctly; nc is used only for logging, and can be NULL
int ncvisual_blitset_geom(const notcurses* nc, const tinfo* tcache,
const ncvisual* n, const struct ncvisual_options* vopts,
int* y, int* x, int* scaley, int* scalex,
@ -287,6 +286,16 @@ int ncvisual_blitter_geom(const notcurses* nc, const ncvisual* n,
return ret;
}
int ncvisual_geom(const notcurses* nc, const ncvisual* n,
const struct ncvisual_options* vopts, ncvgeom* geom){
if(nc == NULL && n == NULL){
logerror("got NULL for both sources\n");
return -1;
}
// FIXME
return 0;
}
void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){
if(*rowstride % 4){ // must be a multiple of 4 bytes
return NULL;

@ -498,9 +498,11 @@ int ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale,
ncplane_erase(activevopts.n); // new frame could be partially transparent
}
// decay the blitter explicitly, so that the callback knows the blitter it
// was actually rendered with
ncvisual_blitter_geom(nc, ncv, &activevopts, NULL, NULL, NULL, NULL,
&activevopts.blitter);
// was actually rendered with. basically just need rgba_blitter(), but
// that's not exported.
ncvgeom geom;
ncvisual_geom(nc, ncv, &activevopts, &geom);
activevopts.blitter = geom.blitter;
if((newn = ncvisual_blit(nc, ncv, &activevopts)) == NULL){
if(activevopts.n != vopts->n){
ncplane_destroy(activevopts.n);

@ -41,7 +41,6 @@ int main(int argc, char** argv){
if(!ncv){
goto err;
}
int scaley, scalex;
vopts.n = n;
if((ncvisual_blit(nc, ncv, &vopts)) == NULL){
goto err;
@ -52,8 +51,9 @@ int main(int argc, char** argv){
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
ncplane_erase(n);
ncvisual_blitter_geom(nc, ncv, &vopts, NULL, NULL, &scaley, &scalex, NULL);
if(ncvisual_resize(ncv, dimy * scaley, dimx * scalex)){
ncvgeom geom;
ncvisual_geom(nc, ncv, &vopts, &geom);
if(ncvisual_resize(ncv, dimy * geom.scaley, dimx * geom.scalex)){
goto err;
}
if(ncvisual_blit(nc, ncv, &vopts) == NULL){

@ -92,14 +92,18 @@ TEST_CASE("Media") {
ncplane_dim_yx(ncp_, &dimy, &dimx);
auto ncv = ncvisual_from_file(find_data("changes.jpg").get());
REQUIRE(ncv);
int odimy, odimx, ndimy, ndimx;
struct ncvisual_options opts{};
opts.n = ncp_;
CHECK(0 == ncvisual_blitter_geom(nc_, ncv, &opts, &odimy, &odimx, nullptr, nullptr, nullptr));
ncvgeom geom{};
CHECK(0 == ncvisual_geom(nc_, ncv, &opts, &geom));
int odimy = geom.pixy;
int odimx = geom.pixx;
CHECK(ncvisual_blit(nc_, ncv, &opts));
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncvisual_resize_noninterpolative(ncv, ncv->pixy * 2, ncv->pixx * 2));
CHECK(0 == ncvisual_blitter_geom(nc_, ncv, &opts, &ndimy, &ndimx, nullptr, nullptr, nullptr));
CHECK(0 == ncvisual_geom(nc_, ncv, &opts, &geom));
int ndimy = geom.pixy;
int ndimx = geom.pixx;
CHECK(ndimy == odimy * 2);
CHECK(ndimx == odimx * 2);
CHECK(ncvisual_blit(nc_, ncv, &opts));

Loading…
Cancel
Save