Paint sprixels bottom-to-top (#1589)

* Paint sprixels in order, bottom-to-top

We don't want to have to track sprixel order whenever someone
moves an ncplane, so just keep a list growing backwards as we
pass top-to-bottom in notcurses_render_internal(). Each time
we hit a sprixel plane, splice it out of the sprixel list, and
add it to the front of our temporary list. When we hit the
bottom, stick this temporary list on the end of our existing
list (any such planes are to be deleted, which comes before
drawing). Closes #1575.

* reorder collected sprixellist; solves kitty but breaks sixel =/ #1575

* remove debugging cruft

* [rust] fix up mergedown mutability
pull/1591/head
Nick Black 3 years ago committed by GitHub
parent 0c2749707c
commit 2c5d938cbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -811,6 +811,10 @@ struct ncplane* ncplane_dup(struct ncplane* n, void* opaque);
// this operation. Do not supply the same plane for both 'src' and 'dst'.
int ncplane_mergedown(struct ncplane* restrict src, struct ncplane* restrict dst);
// If 'src' does not intersect with 'dst', 'dst' will not be changed, but it is
// not an error. If 'dst' is NULL, the operation will target the standard plane.
int ncplane_mergedown_simple(const ncplane* restrict src, ncplane* restrict dst);
// Erase every cell in the ncplane, resetting all attributes to normal, all
// colors to the default color, and all cells to undrawn. All cells associated
// with this ncplane are invalidated, and must not be used after the call,

@ -97,8 +97,8 @@ bool notcurses_canutf8(const struct notcurses* nc);
int notcurses_mouse_enable(struct notcurses* n);
int notcurses_mouse_disable(struct notcurses* n);
int ncplane_destroy(struct ncplane* ncp);
int ncplane_mergedown(const struct ncplane* src, struct ncplane* dst, int begsrcy, int begsrcx, int leny, int lenx, int dsty, int dstx);
int ncplane_mergedown_simple(const struct ncplane* restrict src, struct ncplane* restrict dst);
int ncplane_mergedown(struct ncplane* src, struct ncplane* dst, int begsrcy, int begsrcx, int leny, int lenx, int dsty, int dstx);
int ncplane_mergedown_simple(struct ncplane* restrict src, struct ncplane* restrict dst);
void ncplane_erase(struct ncplane* n);
int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);
void ncplane_cursor_yx(struct ncplane* n, int* y, int* x);

@ -186,9 +186,9 @@ typedef struct ncplane_options {
**void notcurses_drop_planes(struct notcurses* ***nc***);**
**int ncplane_mergedown(const struct ncplane* ***src***, struct ncplane* ***dst***, int ***begsrcy***, int ***begsrcx***, int ***leny***, int ***lenx***, int ***dsty***, int ***dstx***);**
**int ncplane_mergedown(struct ncplane* ***src***, struct ncplane* ***dst***, int ***begsrcy***, int ***begsrcx***, int ***leny***, int ***lenx***, int ***dsty***, int ***dstx***);**
**int ncplane_mergedown_simple(const struct ncplane* restrict ***src***, struct ncplane* restrict ***dst***);**
**int ncplane_mergedown_simple(struct ncplane* restrict ***src***, struct ncplane* restrict ***dst***);**
**void ncplane_erase(struct ncplane* ***n***);**

@ -1965,6 +1965,7 @@ API int ncplane_mergedown_simple(struct ncplane* RESTRICT src,
// is an error to define a target origin such that the projected subregion is
// not entirely contained within 'dst'. Behavior is undefined if 'src' and
// 'dst' are equivalent. 'dst' is modified, but 'src' remains unchanged.
// neither 'src' nor 'dst' may have sprixels.
API int ncplane_mergedown(struct ncplane* RESTRICT src,
struct ncplane* RESTRICT dst,
int begsrcy, int begsrcx, int leny, int lenx,

@ -998,7 +998,7 @@ impl NcPlane {
/// *C style function: [ncplane_mergedown()][crate::ncplane_mergedown].*
pub fn mergedown(
&mut self,
source: &NcPlane,
source: &mut NcPlane,
source_y: NcDim,
source_x: NcDim,
len_y: NcDim,
@ -1038,7 +1038,7 @@ impl NcPlane {
//
// TODO: maybe create a reversed method, and/or an associated function,
// for `mergedown` too.
pub fn mergedown_simple(&mut self, source: &NcPlane) -> NcResult<()> {
pub fn mergedown_simple(&mut self, source: &mut NcPlane) -> NcResult<()> {
error![
unsafe { crate::ncplane_mergedown_simple(source, self) },
"NcPlane.mergedown_simple(NcPlane)"

@ -141,11 +141,12 @@ typedef enum {
SPRIXCELL_ANNIHILATED, // this cell has been wiped (all trans)
} sprixcell_e;
// there is a context-wide set of displayed pixel glyphs ("sprixels"); i.e.
// these are independent of particular piles. there should never be very many
// a sprixel represents a bitmap, using whatever local protocol is available.
// there is a list of sprixels per ncpile. there ought never be very many
// associated with a context (a dozen or so at max). with the kitty protocol,
// we can register them, and then manipulate them by id. with the sixel
// protocol, we just have to rewrite them.
// protocol, we just have to rewrite them. there's a doubly-linked list of
// sprixels per ncpile, to which the pile keeps a head link.
typedef struct sprixel {
char* glyph; // glyph; can be quite large
int glyphlen; // length of the glyph in bytes
@ -155,6 +156,7 @@ typedef struct sprixel {
struct ncplane* n; // associated ncplane
sprixel_e invalidated;// sprixel invalidation state
struct sprixel* next;
struct sprixel* prev;
int y, x;
int dimy, dimx; // cell geometry
int pixy, pixx; // pixel geometry (might be smaller than cell geo)

@ -194,9 +194,16 @@ paint_sprixel(ncplane* p, struct crender* rvec, int starty, int startx,
// only those cells where 'p' intersects with the target rendering area are
// rendered.
//
// the sprixelstack orders sprixels of the plane (so we needn't keep them
// ordered between renders). each time we meet a sprixel, extract it from
// the pile's sprixel list, and update the sprixelstack.
//
// FIXME lift the cell_sprixel_p() variant out and run it its own way
// (unless we want to let sprixels live off-origin in ncplanes), eliminating
// per-cell sprixel_by_id() check
static void
paint(ncplane* p, struct crender* rvec, int dstleny, int dstlenx,
int dstabsy, int dstabsx){
int dstabsy, int dstabsx, sprixel** sprixelstack){
int y, x, dimy, dimx, offy, offx;
ncplane_dim_yx(p, &dimy, &dimx);
offy = p->absy - dstabsy;
@ -220,6 +227,22 @@ paint(ncplane* p, struct crender* rvec, int dstleny, int dstlenx,
if(p->sprite){
paint_sprixel(p, rvec, starty, startx, dimy, dimx, offy, offx,
dstleny, dstlenx);
// decouple from the pile's sixel list
if(p->sprite->next){
p->sprite->next->prev = p->sprite->prev;
}
if(p->sprite->prev){
p->sprite->prev->next = p->sprite->next;
}else{
ncplane_pile(p)->sprixelcache = p->sprite->next;
}
// stick on the head of the running list: top sprixel is at end
if(*sprixelstack){
(*sprixelstack)->prev = p->sprite;
}
p->sprite->next = *sprixelstack;
p->sprite->prev = NULL;
*sprixelstack = p->sprite;
return;
}
for(y = starty ; y < dimy ; ++y){
@ -482,6 +505,10 @@ int ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst,
leny, lenx, src->leny, src->lenx);
return -1;
}
if(src->sprite || dst->sprite){
logerror(ncplane_notcurses_const(dst), "Can't merge sprixel planes\n");
return -1;
}
const int totalcells = dst->leny * dst->lenx;
nccell* rendfb = calloc(sizeof(*rendfb), totalcells);
const size_t crenderlen = sizeof(struct crender) * totalcells;
@ -493,8 +520,8 @@ int ncplane_mergedown(ncplane* restrict src, ncplane* restrict dst,
return -1;
}
init_rvec(rvec, totalcells);
paint(src, rvec, dst->leny, dst->lenx, dst->absy, dst->absx);
paint(dst, rvec, dst->leny, dst->lenx, dst->absy, dst->absx);
paint(src, rvec, dst->leny, dst->lenx, dst->absy, dst->absx, NULL);
paint(dst, rvec, dst->leny, dst->lenx, dst->absy, dst->absx, NULL);
//fprintf(stderr, "Postpaint start (%dx%d)\n", dst->leny, dst->lenx);
postpaint(rendfb, dst->leny, dst->lenx, rvec, &dst->pool);
//fprintf(stderr, "Postpaint done (%dx%d)\n", dst->leny, dst->lenx);
@ -883,7 +910,9 @@ clean_sprixels(notcurses* nc, ncpile* p, FILE* out){
if(s->invalidated == SPRIXEL_HIDE){
//fprintf(stderr, "OUGHT HIDE %d [%dx%d @ %d/%d] %p\n", s->id, s->dimy, s->dimx, s->y, s->x, s);
if(sprite_destroy(nc, p, out, s) == 0){
*parent = s->next;
if( (*parent = s->next) ){
s->next->prev = s->prev;
}
sprixel_free(s);
}else{
ret = -1;
@ -1242,11 +1271,25 @@ int notcurses_render_to_file(notcurses* nc, FILE* fp){
static void
ncpile_render_internal(ncplane* n, struct crender* rvec, int leny, int lenx,
int absy, int absx){
ncplane* p = ncplane_pile(n)->top;
ncpile* np = ncplane_pile(n);
ncplane* p = np->top;
sprixel* sprixel_list = NULL;
while(p){
paint(p, rvec, leny, lenx, absy, absx);
paint(p, rvec, leny, lenx, absy, absx, &sprixel_list);
p = p->below;
}
if(sprixel_list){
if(np->sprixelcache){
sprixel* s = sprixel_list;
while(s->next){
s = s->next;
}
if( (s->next = np->sprixelcache) ){
np->sprixelcache->prev = s;
}
}
np->sprixelcache = sprixel_list;
}
}
int ncpile_rasterize(ncplane* n){

@ -22,6 +22,7 @@ sprixel_debug(FILE* out, const sprixel* s){
}
}
// doesn't splice us out of any lists, just frees
void sprixel_free(sprixel* s){
if(s){
if(s->n){
@ -115,14 +116,17 @@ sprixel* sprixel_alloc(ncplane* n, int dimy, int dimx, int placey, int placex){
//fprintf(stderr, "LOOKING AT %p (p->n = %p)\n", ret, ret->n);
if(ncplane_pile(ret->n)){
ncpile* np = ncplane_pile(ret->n);
ret->next = np->sprixelcache;
if( (ret->next = np->sprixelcache) ){
ret->next->prev = ret;
}
np->sprixelcache = ret;
ret->prev = NULL;
const notcurses* nc = ncplane_notcurses_const(ret->n);
ret->cellpxy = nc->tcache.cellpixy;
ret->cellpxx = nc->tcache.cellpixx;
//fprintf(stderr, "%p %p %p\n", nc->sprixelcache, ret, nc->sprixelcache->next);
}else{
ret->next = NULL;
}else{ // ncdirect case
ret->next = ret->prev = NULL;
ret->cellpxy = ret->cellpxx = -1;
}
}

@ -105,6 +105,7 @@ TEST_CASE("Bitmaps") {
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
CHECK(0 == ncplane_destroy(n));
CHECK(0 == notcurses_render(nc_));
}
// should not be able to emit glyphs to a sprixelated plane
@ -137,6 +138,7 @@ TEST_CASE("Bitmaps") {
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
CHECK(0 == ncplane_destroy(n));
CHECK(0 == notcurses_render(nc_));
}
SUBCASE("BitmapStack") {
@ -175,9 +177,13 @@ TEST_CASE("Bitmaps") {
// should see only the red one now
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncplane_destroy(botn));
CHECK(0 == ncplane_destroy(topn));
ncvisual_destroy(ncv);
// now we see only yellow
CHECK(0 == notcurses_render(nc_));
CHECK(0 == ncplane_destroy(topn));
ncvisual_destroy(ncv2);
// and now we see none
CHECK(0 == notcurses_render(nc_));
}
#ifdef NOTCURSES_USE_MULTIMEDIA
@ -193,6 +199,7 @@ TEST_CASE("Bitmaps") {
CHECK(0 == ncplane_destroy(newn));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
CHECK(0 == notcurses_render(nc_));
}
#endif
@ -262,6 +269,7 @@ TEST_CASE("Bitmaps") {
CHECK(0 == ncplane_destroy(infn));
CHECK(0 == ncplane_destroy(n));
ncvisual_destroy(ncv);
CHECK(0 == notcurses_render(nc_));
}
SUBCASE("PixelCellWipe") {
@ -426,6 +434,7 @@ TEST_CASE("Bitmaps") {
}
}
CHECK(0 == ncplane_destroy(n));
CHECK(0 == notcurses_render(nc_));
}
#ifdef NOTCURSES_USE_MULTIMEDIA
@ -454,6 +463,7 @@ TEST_CASE("Bitmaps") {
CHECK(0 == ncplane_destroy(newn));
CHECK(0 == notcurses_render(nc_));
ncvisual_destroy(ncv);
CHECK(0 == notcurses_render(nc_));
}
#endif

Loading…
Cancel
Save