Further work on opacity optimizations (#1549)

* [bitmap] on move, clear old OPAQUE cells #1527
* update ncvisual_blitter_geom() documentation #1547
* replace some ncvisual_blitter_geom with NCVISUAL_OPTION_VERALIGNED
* [normal] replace ncvisual_blitter_geom with NCVISUAL_OPTION_VERALIGNED
* [intro] use ncvisual_blitter_geom() correctly #1547
* ncvisual_blitter_geom: only set *blitter on success
This commit is contained in:
Nick Black 2021-04-17 23:11:11 -04:00 committed by GitHub
parent eaee89c99f
commit 8c6cd6a630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 128 additions and 73 deletions

View File

@ -3058,14 +3058,15 @@ it was built up:
```c
// 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
// ('*y' * '*toy')X('*x' * '*tox') cells for full output. Returns non-zero
// for an invalid 'blitter' in 'vopts'. Scaling is taken into account. The
// blitter that will be used is returned in 'blitter'.
// 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)
// cols = (x / scalex) + !!(x % scalex)
// Returns non-zero for an invalid 'vopts'. The blitter that will be used
// is returned in '*blitter'.
int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts,
int* y, int* x, int* toy, int* tox,
ncblitter_e* blitter);
const struct ncvisual_options* vopts, int* y, int* x,
int* scaley, int* scalex, ncblitter_e* blitter);
// Rotate the visual 'rads' radians. Only M_PI/2 and -M_PI/2 are
// supported at the moment, but this will change FIXME.

View File

@ -58,7 +58,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_geom(const struct notcurses* ***nc***, const struct ncvisual* ***n***, const struct ncvisual_options* ***vopts***, int* ***y***, int* ***x***, int* ***toy***, int* ***tox***);**
**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***);**
**void ncvisual_destroy(struct ncvisual* ***ncv***);**
@ -143,6 +143,12 @@ geometry of same. **flags** is a bitfield over:
* **NCVISUAL_OPTION_NODEGRADE** If the specified blitter is not available, fail rather than degrading.
* **NCVISUAL_OPTION_BLEND**: Render with **CELL_ALPHA_BLEND**.
* **NCVISUAL_OPTION_HORALIGNED**: Interpret ***x*** as an **ncalign_e**.
* **NCVISUAL_OPTION_VERALIGNED**: Interpret ***y*** as an **ncalign_e**.
**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**.
**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
@ -267,7 +273,7 @@ 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_geom** returns non-zero if the **blitter** is invalid.
**ncvisual_blitter_geom** returns non-zero if the specified blitter is invalid.
**ncvisual_media_defblitter** returns the blitter selected by **NCBLIT_DEFAULT**
in the specified configuration. If UTF8 is not enabled, this will always be

View File

@ -2696,21 +2696,22 @@ ncplane_rgba(const struct ncplane* n, ncblitter_e blit,
}
// 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
// ('*y' * '*toy')X('*x' * '*tox') cells for full output. Returns non-zero
// for an invalid 'vopts->blitter'. Scaling is taken into consideration.
// The blitter that will be used is returned in '*blitter'.
// 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)
// cols = (x / scalex) + !!(x % scalex)
// Returns non-zero for an invalid 'vopts'. The blitter that will be used
// is returned in '*blitter'.
API int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts,
int* y, int* x, int* toy, int* tox,
ncblitter_e* blitter)
const struct ncvisual_options* vopts, int* y, int* x,
int* scaley, int* scalex, ncblitter_e* blitter)
__attribute__ ((nonnull (1)));
__attribute__ ((deprecated)) static inline int
ncvisual_geom(const struct notcurses* nc, const struct ncvisual* n,
const struct ncvisual_options* vopts,
int* y, int* x, int* toy, int* tox){
return ncvisual_blitter_geom(nc, n, vopts, y, x, toy, tox, NULL);
int* y, int* x, int* scaley, int* scalex){
return ncvisual_blitter_geom(nc, n, vopts, y, x, scaley, scalex, NULL);
}
// Destroy an ncvisual. Rendered elements will not be disrupted, but the visual

View File

@ -36,15 +36,16 @@ zoom_map(struct notcurses* nc, const char* map, int* ret){
if(ncv == NULL){
return NULL;
}
int vheight, yscale;
int vwidth, xscale;
// 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 = {
.y = 1,
.blitter = NCBLIT_2x2,
};
if(ncvisual_blitter_geom(nc, ncv, &vopts, &vheight, &vwidth, &yscale, &xscale, NULL)){
if(ncvisual_blitter_geom(nc, ncv, &vopts, &vheight, &vwidth,
&yscale, &xscale, NULL)){
ncvisual_destroy(ncv);
return NULL;
}

View File

@ -69,14 +69,16 @@ orcashow(struct notcurses* nc, int dimy, int dimx){
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_NODEGRADE,
};
int odimy, odimx;
ncvisual_blitter_geom(nc, ncv, &vopts, NULL, NULL, &odimy, &odimx, NULL);
vopts.y = dimy - odimy - 1;
vopts.x = dimx - odimx;
if(odimx > dimx || odimy > dimy - 1){
int odimy, odimx, scaley, scalex;
ncvisual_blitter_geom(nc, ncv, &vopts, &odimy, &odimx, &scaley, &scalex, NULL);
int rows = (odimy / scaley) + !!(odimy % scaley);
int cols = (odimx / scalex) + !!(odimx % scalex);
if(cols > dimx || rows > dimy - 1){
ncvisual_destroy(ncv);
return NULL;
}
vopts.y = dimy - rows - 1;
vopts.x = dimx - cols;
struct ncplane* n = ncvisual_render(nc, ncv, &vopts);
ncvisual_destroy(ncv);
return n;

View File

@ -110,11 +110,9 @@ rotate_visual(struct notcurses* nc, struct ncplane* n, int dy, int dx){
r = -1;
break;
}
int vy, vx, vyscale, vxscale;
ncvisual_blitter_geom(nc, ncv, &vopts, &vy, &vx, &vyscale, &vxscale, NULL);
vopts.x = NCALIGN_CENTER;
vopts.y = (dimy - (vy / vyscale)) / 2;
vopts.flags |= NCVISUAL_OPTION_HORALIGNED;
vopts.y = NCALIGN_CENTER;
vopts.flags |= NCVISUAL_OPTION_HORALIGNED | NCVISUAL_OPTION_VERALIGNED;
struct ncplane* newn;
if((newn = ncvisual_render(nc, ncv, &vopts)) == NULL){
r = -1;

View File

@ -112,7 +112,7 @@ int xray_demo(struct notcurses* nc){
| NCVISUAL_OPTION_ADDALPHA,
};
float dm = 0;
// returns 0 if the selected blitter isn't available
// returns non-zero if the selected blitter isn't available
if(ncvisual_blitter_geom(nc, ncv, &vopts, NULL, NULL, NULL, NULL, NULL)){
vopts.flags &= ~NCVISUAL_OPTION_NODEGRADE;
dm = 0.5 * delaymultiplier;

View File

@ -547,7 +547,7 @@ ncdirect_render_visual(ncdirect* n, ncvisual* ncv, ncblitter_e blitfxn,
bargs.u.pixel.colorregs = n->tcache.color_registers;
int cols = lenx / bargs.u.pixel.celldimx + !!(lenx % bargs.u.pixel.celldimx);
int rows = leny / bargs.u.pixel.celldimy + !!(leny % bargs.u.pixel.celldimy);
if((bargs.u.pixel.spx = sprixel_alloc(ncdv, ncv, rows, cols)) == NULL){
if((bargs.u.pixel.spx = sprixel_alloc(ncdv, ncv, rows, cols, 0, 0)) == NULL){
free_plane(ncdv);
return NULL;
}

View File

@ -649,7 +649,8 @@ int ncplane_qrcode(ncplane* n, int* ymax, int* xmax, const void* data, size_t le
if(ncvisual_render(ncplane_notcurses(n), ncv, &vopts) == n){
ret = square;
}
ncvisual_blitter_geom(ncplane_notcurses(n), ncv, &vopts, NULL, NULL, &yscale, &xscale, NULL);
ncvisual_blitter_geom(ncplane_notcurses(n), ncv, &vopts, NULL, NULL,
&yscale, &xscale, NULL);
}
ncvisual_destroy(ncv);
}

View File

@ -111,7 +111,8 @@ typedef enum {
//
// when a sprixel is removed from the rendering pile, in Sixel all cells it
// covered must be marked damaged, so that they are rendered, obliterating
// the bitmap. in Kitty the bitmap can simply be deleted.
// the bitmap. in Kitty the bitmap can simply be deleted, except for those
// cells which were SPRIXCELL_OPAQUE (they must be damaged).
//
// when a sprixel is moved, its TAM must be updated. OPAQUE, MIXED, and
// TRANSPARENT cells retain their entries. ANNIHILATED cells remain
@ -922,7 +923,8 @@ int sprite_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
int kitty_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
int sixel_draw(const notcurses* n, const ncpile *p, sprixel* s, FILE* out);
// dimy and dimx are cell geometry, not pixel. takes ownership of s on success.
sprixel* sprixel_alloc(ncplane* n, struct ncvisual* ncv, int dimy, int dimx);
sprixel* sprixel_alloc(ncplane* n, struct ncvisual* ncv, int dimy, int dimx,
int placey, int placex);
sprixel* sprixel_recycle(ncplane* n, struct ncvisual* ncv);
int sprixel_load(sprixel* spx, char* s, int bytes, int placey, int placex,
int pixy, int pixx, int parse_start);
@ -945,14 +947,18 @@ static inline bool sprixel_kitty_p(const tinfo* t){
}
// get the TAM entry for these (absolute) coordinates
static inline sprixcell_e sprixel_state(sprixel* s, int y, int x){
static inline sprixcell_e
sprixel_state(sprixel* s, int y, int x){
int localy = y - s->n->absy;
int localx = x - s->n->absx;
assert(localy >= 0);
assert(localy < s->dimy);
assert(localx >= 0);
assert(localx < s->dimx);
//fprintf(stderr, "TAM %d at %d/%d (%d/%d)\n", s->n->tacache[localy * s->dimx + localx], localy, localx, y, x);
return s->n->tacache[localy * s->dimx + localx];
}
static inline void
pool_release(egcpool* pool, nccell* c){
if(cell_extended_p(c)){

View File

@ -209,7 +209,7 @@ int sprite_kitty_cell_wipe(const notcurses* nc, sprixel* s, int ycell, int xcell
if(thisrow == 0){
//fprintf(stderr, "CLEARED ROW, TARGY: %d\n", targy - 1);
if(--targy == 0){
s->invalidated = SPRIXEL_INVALIDATED;
//s->invalidated = SPRIXEL_INVALIDATED;
return 0;
}
thisrow = targx;
@ -367,13 +367,27 @@ int kitty_blit(ncplane* n, int linesize, const void* data,
return 1;
}
// removes the kitty bitmap graphic identified by s->id
// removes the kitty bitmap graphic identified by s->id, and damages those
// cells which were SPRIXCEL_OPAQUE
int sprite_kitty_annihilate(const notcurses* nc, const ncpile* p, FILE* out, sprixel* s){
(void)p;
(void)nc;
if(fprintf(out, "\e_Ga=d,d=i,i=%d\e\\", s->id) < 0){
return 0;
}
//fprintf(stderr, "MOVED FROM: %d/%d\n", s->movedfromy, s->movedfromx);
for(int yy = s->movedfromy ; yy < s->movedfromy + s->dimy && yy < p->dimy ; ++yy){
for(int xx = s->movedfromx ; xx < s->movedfromx + s->dimx && xx < p->dimx ; ++xx){
struct crender *r = &p->crender[yy * p->dimx + xx];
if(s->n){
//fprintf(stderr, "CHECKING %d/%d\n", yy - s->movedfromy, xx - s->movedfromx);
if(s->n->tacache[(yy - s->movedfromy) * s->dimx + (xx - s->movedfromx)] == SPRIXCELL_OPAQUE){
//fprintf(stderr, "DAMAGING %d/%d!\n", yy, xx);
r->s.damaged = 1;
}
}
}
}
return 0;
}

View File

@ -866,7 +866,10 @@ clean_sprixels(notcurses* nc, const ncpile* p, FILE* out){
ret = -1;
}
}else if(s->invalidated == SPRIXEL_MOVED || s->invalidated == SPRIXEL_INVALIDATED){
// FIXME clean this up, don't use sprite_draw, don't always move, etc.
// FIXME clean this up, don't use sprite_draw, etc.
if(s->invalidated == SPRIXEL_MOVED){
sprite_destroy(nc, p, out, s);
}
int y, x;
ncplane_yx(s->n, &y, &x);
y += s->y;

View File

@ -527,7 +527,10 @@ int sixel_draw(const notcurses* n, const ncpile* p, sprixel* s, FILE* out){
if(s->invalidated == SPRIXEL_MOVED){
for(int yy = s->movedfromy ; yy < s->movedfromy + s->dimy ; ++yy){
for(int xx = s->movedfromx ; xx < s->movedfromx + s->dimx ; ++xx){
p->crender[yy * p->dimx + xx].s.damaged = 1;
//fprintf(stderr, "DAMAGING DUE TO MOVE: %d/%d (%d)\n", yy, xx, yy * p->dimx + xx);
if(xx < p->dimx && yy < p->dimy){
p->crender[yy * p->dimx + xx].s.damaged = 1;
}
}
}
s->invalidated = SPRIXEL_INVALIDATED;

View File

@ -24,8 +24,10 @@ sprixel* sprixel_recycle(ncplane* n, ncvisual* ncv){
assert(hides);
int dimy = hides->dimy;
int dimx = hides->dimx;
int y = hides->y;
int x = hides->x;
sprixel_hide(hides);
return sprixel_alloc(n, ncv, dimy, dimx);
return sprixel_alloc(n, ncv, dimy, dimx, y, x);
}
return n->sprite;
}
@ -35,6 +37,7 @@ sprixel* sprixel_recycle(ncplane* n, ncvisual* ncv){
void sprixel_movefrom(sprixel* s, int y, int x){
if(s->invalidated != SPRIXEL_HIDE){
if(s->invalidated != SPRIXEL_MOVED){
//fprintf(stderr, "SETTING TO MOVE: %d/%d was: %d\n", y, x, s->invalidated);
s->invalidated = SPRIXEL_MOVED;
s->movedfromy = y;
s->movedfromx = x;
@ -83,7 +86,8 @@ sprixel* sprixel_by_id(const notcurses* nc, uint32_t id){
return NULL;
}
sprixel* sprixel_alloc(ncplane* n, ncvisual* ncv, int dimy, int dimx){
sprixel* sprixel_alloc(ncplane* n, ncvisual* ncv, int dimy, int dimx,
int placey, int placex){
sprixel* ret = malloc(sizeof(sprixel));
if(ret){
memset(ret, 0, sizeof(*ret));
@ -91,6 +95,8 @@ sprixel* sprixel_alloc(ncplane* n, ncvisual* ncv, int dimy, int dimx){
ret->ncv = ncv;
ret->dimy = dimy;
ret->dimx = dimx;
ret->y = placey;
ret->x = placex;
ret->id = ++sprixelid_nonce;
//fprintf(stderr, "LOOKING AT %p (p->n = %p)\n", ret, ret->n);
if(ncplane_pile(ret->n)){

View File

@ -15,13 +15,17 @@ struct ncplane;
struct sprixel;
struct ncvisual_details;
// an ncvisual is essentially just an unpacked RGBA bitmap, created by
// reading media from disk, supplying RGBA pixels directly in memory, or
// synthesizing pixels from a plane.
typedef struct ncvisual {
struct ncvisual_details* details;// implementation-specific details
uint32_t* data; // (scaled) RGBA image data, rowstride bytes per row
int cols, rows;
int cols, rows; // pixel geometry, *not* cell geometry
// lines are sometimes padded. this many true bytes per row in data.
int rowstride;
bool owndata; // we own data iff owndata == true
// FIXME this cannot live here! it breaks wiping. associate with plane.
struct sprixel* spx; // non-NULL if this is NCBLIT_PIXEL
} ncvisual;

View File

@ -75,11 +75,24 @@ ncvisual_origin(const struct ncvisual_options* vopts, int* restrict begy, int* r
*begx = vopts ? vopts->begx : 0;
}
// 'leny' and 'lenx' get the number of pixels to actually be rendered, 'y' and
// 'x' get the original size of the visual in pixels, and 'scaley' and 'scalex'
// get the number of pixels per cell with the selected 'blitter'.
// 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!
static int
ncvisual_blitset_geom(const notcurses* nc, const ncvisual* n,
const struct ncvisual_options* vopts,
int* y, int* x, int* toy, int* tox,
int* y, int* x, int* scaley, int* scalex,
int* leny, int* lenx, const struct blitset** blitter){
int fakeleny, fakelenx;
if(leny == NULL){
leny = &fakeleny;
}
if(lenx == NULL){
lenx = &fakelenx;
}
if(vopts && vopts->flags >= (NCVISUAL_OPTION_ADDALPHA << 1u)){
logwarn(nc, "Warning: unknown ncvisual options %016jx\n", (uintmax_t)vopts->flags);
}
@ -140,8 +153,10 @@ ncvisual_blitset_geom(const notcurses* nc, const ncvisual* n,
*y = n->rows;
*x = n->cols;
}else{
int rows = vopts->n ? ncplane_dim_y(vopts->n) : ncplane_dim_y(nc->stdplane);
int cols = vopts->n ? ncplane_dim_x(vopts->n) : ncplane_dim_x(nc->stdplane);
int rows = (vopts && vopts->n) ? ncplane_dim_y(vopts->n) :
ncplane_dim_y(nc->stdplane);
int cols = (vopts && vopts->n) ? ncplane_dim_x(vopts->n) :
ncplane_dim_x(nc->stdplane);
*y = rows * encoding_y_scale(&nc->tcache, bset);
*x = cols * encoding_x_scale(&nc->tcache, bset);
}
@ -149,11 +164,11 @@ ncvisual_blitset_geom(const notcurses* nc, const ncvisual* n,
scale_visual(n, y, x);
}
}
if(toy){
*toy = encoding_y_scale(&nc->tcache, bset);
if(scaley){
*scaley = encoding_y_scale(&nc->tcache, bset);
}
if(tox){
*tox = encoding_x_scale(&nc->tcache, bset);
if(scalex){
*scalex = encoding_x_scale(&nc->tcache, bset);
}
if(vopts && vopts->flags & NCVISUAL_OPTION_HORALIGNED){
if(vopts->x < NCALIGN_UNALIGNED || vopts->x > NCALIGN_RIGHT){
@ -166,12 +181,12 @@ ncvisual_blitset_geom(const notcurses* nc, const ncvisual* n,
int ncvisual_blitter_geom(const notcurses* nc, const ncvisual* n,
const struct ncvisual_options* vopts,
int* y, int* x, int* toy, int* tox,
int* y, int* x, int* scaley, int* scalex,
ncblitter_e* blitter){
const struct blitset* bset;
int leny, lenx;
int ret = ncvisual_blitset_geom(nc, n, vopts, y, x, toy, tox, &leny, &lenx, &bset);
if(blitter){
int ret = ncvisual_blitset_geom(nc, n, vopts, y, x, scaley, scalex,
NULL, NULL, &bset);
if(ret == 0 && blitter){
*blitter = bset->geom;
}
return ret;
@ -694,7 +709,7 @@ ncplane* ncvisual_render_pixels(notcurses* nc, ncvisual* ncv, const struct blits
if(n->sprite){
sprixel_hide(n->sprite);
}
if((ncv->spx = sprixel_alloc(n, ncv, rows, cols)) == NULL){
if((ncv->spx = sprixel_alloc(n, ncv, rows, cols, placey, placex)) == NULL){
goto err;
}
}else{
@ -722,8 +737,9 @@ err:
ncplane* ncvisual_render(notcurses* nc, ncvisual* ncv, const struct ncvisual_options* vopts){
const struct blitset* bset;
int srcy, srcx, toy, tox, leny, lenx;
if(ncvisual_blitset_geom(nc, ncv, vopts, &srcy, &srcx, &toy, &tox, &leny, &lenx, &bset) < 0){
int leny, lenx;
if(ncvisual_blitset_geom(nc, ncv, vopts, NULL, NULL, NULL, NULL,
&leny, &lenx, &bset) < 0){
// ncvisual_blitset_geom() emits its own diagnostics, no need for an error here
return NULL;
}

View File

@ -402,7 +402,8 @@ int ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale,
}
// 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);
ncvisual_blitter_geom(nc, ncv, &activevopts, NULL, NULL, NULL, NULL,
&activevopts.blitter);
if((newn = ncvisual_render(nc, ncv, &activevopts)) == NULL){
if(activevopts.n != vopts->n){
ncplane_destroy(activevopts.n);

View File

@ -64,15 +64,14 @@ rotate_grad(struct notcurses* nc){
notcurses_render(nc);
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);;
vopts.n = NULL;
vopts.x = NCALIGN_CENTER;
vopts.y = NCALIGN_CENTER;
vopts.flags |= NCVISUAL_OPTION_HORALIGNED | NCVISUAL_OPTION_VERALIGNED;
ncplane_erase(n);
for(int i = 0 ; i < 4 ; ++i){
int vy, vx, scaley, scalex;
if(ncvisual_rotate(v, M_PI / 2)){
return -1;
}
ncvisual_blitter_geom(nc, v, &vopts, &vy, &vx, &scaley, &scalex, NULL);
vopts.x = (dimx - (vx / scalex)) / 2;
vopts.y = (dimy - (vy / scaley)) / 2;
struct ncplane* newn = ncvisual_render(nc, v, &vopts);
if(newn == NULL){
return -1;
@ -83,10 +82,6 @@ rotate_grad(struct notcurses* nc){
}
for(int i = 0 ; i < 8 ; ++i){
int vy, vx, scaley, scalex;
ncvisual_blitter_geom(nc, v, &vopts, &vy, &vx, &scaley, &scalex, NULL);
vopts.x = (dimx - (vx / scalex)) / 2;
vopts.y = (dimy - (vy / scaley)) / 2;
if(ncvisual_rotate(v, M_PI / 4)){
return -1;
}

View File

@ -24,14 +24,12 @@ int main(int argc, char** argv){
struct ncvisual_options vopts{};
bool failed = false;
int dimy, dimx;
int scaley, scalex;
int top = 0;
int bot;
auto ncv = ncvisual_from_file(file);
if(!ncv){
goto err;
}
ncvisual_blitter_geom(nc, ncv, &vopts, nullptr, nullptr, &scaley, &scalex, nullptr);
vopts.scaling = NCSCALE_STRETCH;
struct ncplane* ntarg;
if((ntarg = ncvisual_render(nc, ncv, &vopts)) == nullptr){

View File

@ -62,6 +62,9 @@ int main(int argc, char** argv){
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
vopts.n = NULL;
vopts.x = NCALIGN_CENTER;
vopts.y = NCALIGN_CENTER;
vopts.flags |= NCVISUAL_OPTION_HORALIGNED | NCVISUAL_OPTION_VERALIGNED;
ncplane_destroy(n);
for(double i = 0 ; i < 256 ; ++i){
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
@ -69,10 +72,6 @@ int main(int argc, char** argv){
failed = true;
break;
}
int vy, vx;
ncvisual_blitter_geom(nc, ncv, &vopts, &vy, &vx, &scaley, &scalex, nullptr);
vopts.x = (dimx - (vx / scalex)) / 2;
vopts.y = (dimy - (vy / scaley)) / 2;
struct ncplane* newn;
if((newn = ncvisual_render(nc, ncv, &vopts)) == nullptr){
failed = true;