reject sprixels larger than plane, add unit test #1572

This commit is contained in:
nick black 2021-04-24 04:41:23 -04:00
parent cc73811dd4
commit 5a72383cb0
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
4 changed files with 53 additions and 12 deletions

View File

@ -352,6 +352,8 @@ last row is cleared, and output begins at the beginning of the last row. This
does not take place until output is generated (i.e. it is possible to fill a does not take place until output is generated (i.e. it is possible to fill a
plane when scrolling is enabled). plane when scrolling is enabled).
## Bitmaps
**ncplane_pixelgeom** retrieves pixel geometry details. **pxy** and **pxx** **ncplane_pixelgeom** retrieves pixel geometry details. **pxy** and **pxx**
return the size of the plane in pixels. **celldimy** and **celldimx** return return the size of the plane in pixels. **celldimy** and **celldimx** return
the size of a cell in pixels (these ought be the same across planes). the size of a cell in pixels (these ought be the same across planes).
@ -364,9 +366,9 @@ parameter (save **n**) may be **NULL**.
When a plane is blitted to using **ncvisual_render** and **NCBLIT_PIXEL** (see When a plane is blitted to using **ncvisual_render** and **NCBLIT_PIXEL** (see
**notcurses_visual(3)**), it ceases to accept cell-based output. The sprixel **notcurses_visual(3)**), it ceases to accept cell-based output. The sprixel
will remain associated until a new sprixel is blitted to the plane, the plane will remain associated until a new sprixel is blitted to the plane, the plane
is resized, or the plane is destroyed. The base cell of a sprixelated plane is resized, the plane is erased, or the plane is destroyed. The base cell of a
has no effect; if the sprixel is not even multiples of the cell geometry, the sprixelated plane has no effect; if the sprixel is not even multiples of the
"excess plane" is ignored during rendering. cell geometry, the "excess plane" is ignored during rendering.
# RETURN VALUES # RETURN VALUES

View File

@ -583,10 +583,6 @@ int resize_callbacks_children(ncplane* n){
// can be used on stdplane, unlike ncplane_resize() which prohibits it. // can be used on stdplane, unlike ncplane_resize() which prohibits it.
int ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny, int ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
int keeplenx, int yoff, int xoff, int ylen, int xlen){ int keeplenx, int yoff, int xoff, int ylen, int xlen){
if(n->sprite){
logerror(ncplane_notcurses_const(n), "Can't resize sprixelated (id %d) plane\n", n->sprite->id);
return -1;
}
if(keepleny < 0 || keeplenx < 0){ // can't retain negative size if(keepleny < 0 || keeplenx < 0){ // can't retain negative size
logerror(ncplane_notcurses_const(n), "Can't retain negative size %dx%d\n", keepleny, keeplenx); logerror(ncplane_notcurses_const(n), "Can't retain negative size %dx%d\n", keepleny, keeplenx);
return -1; return -1;
@ -626,6 +622,9 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny,
} }
loginfo(ncplane_notcurses_const(n), "%dx%d @ %d/%d → %d/%d @ %d/%d (keeping %dx%d from %d/%d)\n", rows, cols, n->absy, n->absx, ylen, xlen, n->absy + keepy + yoff, n->absx + keepx + xoff, keepleny, keeplenx, keepy, keepx); loginfo(ncplane_notcurses_const(n), "%dx%d @ %d/%d → %d/%d @ %d/%d (keeping %dx%d from %d/%d)\n", rows, cols, n->absy, n->absx, ylen, xlen, n->absy + keepy + yoff, n->absx + keepx + xoff, keepleny, keeplenx, keepy, keepx);
notcurses* nc = ncplane_notcurses(n); notcurses* nc = ncplane_notcurses(n);
if(n->sprite){
sprixel_hide(n->sprite);
}
// we're good to resize. we'll need alloc up a new framebuffer, and copy in // we're good to resize. we'll need alloc up a new framebuffer, and copy in
// those elements we're retaining, zeroing out the rest. alternatively, if // those elements we're retaining, zeroing out the rest. alternatively, if
// we've shrunk, we will be filling the new structure. // we've shrunk, we will be filling the new structure.

View File

@ -158,6 +158,16 @@ ncvisual_blitset_geom(const notcurses* nc, const ncvisual* n,
logerror(nc, "Non-origin x placement %d for sprixel\n", vopts->x); logerror(nc, "Non-origin x placement %d for sprixel\n", vopts->x);
return -1; return -1;
} }
int rows = (*leny + nc->tcache.cellpixy - 1) / nc->tcache.cellpixy;
if(rows > ncplane_dim_y(vopts->n)){
logerror(nc, "Sprixel too tall %d for plane %d\n", *leny, ncplane_dim_y(vopts->n) * nc->tcache.cellpixy);
return -1;
}
int cols = (*lenx + nc->tcache.cellpixx - 1) / nc->tcache.cellpixx;
if(cols > ncplane_dim_x(vopts->n)){
logerror(nc, "Sprixel too wide %d for plane %d\n", *lenx, ncplane_dim_x(vopts->n) * nc->tcache.cellpixx);
return -1;
}
} }
} }
if(n){ if(n){

View File

@ -38,7 +38,7 @@ TEST_CASE("Bitmaps") {
.flags = NCVISUAL_OPTION_NODEGRADE, .flags = NCVISUAL_OPTION_NODEGRADE,
.transcolor = 0, .transcolor = 0,
}; };
CHECK(0 == ncvisual_resize(ncv, 6, 1)); // FIXME get down to 1, 1 CHECK(0 == ncvisual_resize(ncv, 6, 1)); // FIXME get down to 1, 1 (sixel needs handle)
auto n = ncvisual_render(nc_, ncv, &vopts); auto n = ncvisual_render(nc_, ncv, &vopts);
REQUIRE(nullptr != n); REQUIRE(nullptr != n);
auto s = n->sprite; auto s = n->sprite;
@ -46,6 +46,38 @@ TEST_CASE("Bitmaps") {
ncvisual_destroy(ncv); ncvisual_destroy(ncv);
} }
// a sprixel requires a plane large enough to hold it
SUBCASE("SprixelTooBig") {
auto y = nc_->tcache.cellpixy + 1;
auto x = nc_->tcache.cellpixx + 1;
std::vector<uint32_t> v(x * y, htole(0xe61c28ff));
auto ncv = ncvisual_from_rgba(v.data(), y, sizeof(decltype(v)::value_type) * x, x);
REQUIRE(nullptr != ncv);
struct ncplane_options nopts = {
.y = 0, .x = 0,
.rows = 1, .cols = 1,
.userptr = nullptr, .name = "small", .resizecb = nullptr,
.flags = 0, .margin_b = 0, .margin_r = 0,
};
auto n = ncplane_create(n_, &nopts);
struct ncvisual_options vopts = {
.n = n,
.scaling = NCSCALE_NONE,
.y = 0,
.x = 0,
.begy = 0, .begx = 0,
.leny = 0, .lenx = 0,
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_NODEGRADE,
.transcolor = 0,
};
CHECK(nullptr == ncvisual_render(nc_, ncv, &vopts));
CHECK(0 == notcurses_render(nc_));
sleep(2);
ncvisual_destroy(ncv);
CHECK(0 == ncplane_destroy(n));
}
#ifdef NOTCURSES_USE_MULTIMEDIA #ifdef NOTCURSES_USE_MULTIMEDIA
SUBCASE("PixelRender") { SUBCASE("PixelRender") {
auto ncv = ncvisual_from_file(find_data("worldmap.png")); auto ncv = ncvisual_from_file(find_data("worldmap.png"));
@ -294,8 +326,6 @@ TEST_CASE("Bitmaps") {
CHECK(0 == ncplane_destroy(n)); CHECK(0 == ncplane_destroy(n));
} }
// too much output -- OOMs ctest FIXME
/*
#ifdef NOTCURSES_USE_MULTIMEDIA #ifdef NOTCURSES_USE_MULTIMEDIA
SUBCASE("PixelWipeImage") { SUBCASE("PixelWipeImage") {
uint64_t channels = 0; uint64_t channels = 0;
@ -315,7 +345,8 @@ TEST_CASE("Bitmaps") {
for(int y = 0 ; y < s->dimy ; ++y){ for(int y = 0 ; y < s->dimy ; ++y){
for(int x = 0 ; x < s->dimx ; ++x){ for(int x = 0 ; x < s->dimx ; ++x){
CHECK(1 == ncplane_putchar_yx(n_, y, x, 'x')); CHECK(1 == ncplane_putchar_yx(n_, y, x, 'x'));
CHECK(0 == notcurses_render(nc_)); // FIXME generates too much output, OOMing ctest
// CHECK(0 == notcurses_render(nc_));
} }
} }
CHECK(0 == ncplane_destroy(newn)); CHECK(0 == ncplane_destroy(newn));
@ -323,7 +354,6 @@ TEST_CASE("Bitmaps") {
ncvisual_destroy(ncv); ncvisual_destroy(ncv);
} }
#endif #endif
*/
CHECK(!notcurses_stop(nc_)); CHECK(!notcurses_stop(nc_));
} }