Skip to content

Commit

Permalink
notcurses_highgradient() added #398
Browse files Browse the repository at this point in the history
  • Loading branch information
dankamongmen committed Mar 8, 2020
1 parent d7ea87e commit eead4c1
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 44 deletions.
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -1157,6 +1157,23 @@ ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword,
return ncplane_gradient(n, egc, attrword, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1);
}

// Do a high-resolution gradient using upper blocks and synced backgrounds.
// This doubles the number of vertical gradations, but restricts you to
// half blocks (appearing to be full blocks).
int ncplane_highgradient(struct ncplane* n, uint32_t ul, uint32_t ur,
uint32_t ll, uint32_t lr, int ystop, int xstop);

static inline int
ncplane_highgradient_sized(struct ncplane* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr, int ylen, int xlen){
if(ylen < 1 || xlen < 1){
return -1;
}
int y, x;
ncplane_cursor_yx(n, &y, &x);
return ncplane_highgradient(n, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1);
}

// Set the given style throughout the specified region, keepying content and
// channels otherwise unchanged.
int ncplane_format(struct ncplane* n, int ystop, int xstop, uint32_t attrword);
Expand Down
4 changes: 4 additions & 0 deletions doc/man/man3/notcurses_lines.3.md
Expand Up @@ -63,6 +63,10 @@ ncplane_box_sized(struct ncplane* n, const cell* ul, const cell* ur,
**static inline int ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);**
**int ncplane_highgradient(struct ncplane* n, uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr, int ystop, int xstop);**
**static inline int ncplane_highgradient_sized(struct ncplane* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);**
**int ncplane_format(struct ncplane* n, int ystop, int xstop, uint32_t attrword);**
**int ncplane_stain(struct ncplane* n, int ystop, int xstop, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr);**
Expand Down
17 changes: 17 additions & 0 deletions include/notcurses/notcurses.h
Expand Up @@ -907,6 +907,12 @@ API int ncplane_gradient(struct ncplane* n, const char* egc, uint32_t attrword,
uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr,
int ystop, int xstop);

// Do a high-resolution gradient using upper blocks and synced backgrounds.
// This doubles the number of vertical gradations, but restricts you to
// half blocks (appearing to be full blocks).
API int ncplane_highgradient(struct ncplane* n, uint32_t ul, uint32_t ur,
uint32_t ll, uint32_t lr, int ystop, int xstop);

// Draw a gradient with its upper-left corner at the current cursor position,
// having dimensions 'ylen'x'xlen'. See ncplane_gradient for more information.
static inline int
Expand All @@ -921,6 +927,17 @@ ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword,
return ncplane_gradient(n, egc, attrword, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1);
}

static inline int
ncplane_highgradient_sized(struct ncplane* n, uint64_t ul, uint64_t ur,
uint64_t ll, uint64_t lr, int ylen, int xlen){
if(ylen < 1 || xlen < 1){
return -1;
}
int y, x;
ncplane_cursor_yx(n, &y, &x);
return ncplane_highgradient(n, ul, ur, ll, lr, y + ylen - 1, x + xlen - 1);
}

// Set the given style throughout the specified region, keepying content and
// channels otherwise unchanged.
API int ncplane_format(struct ncplane* n, int ystop, int xstop, uint32_t attrword);
Expand Down
2 changes: 2 additions & 0 deletions python/src/notcurses/build_notcurses.py
Expand Up @@ -363,6 +363,8 @@
int ncplane_polyfill_yx(struct ncplane* n, int y, int x, const cell* c);
int ncplane_gradient(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ystop, int xstop);
int ncplane_gradient_sized(struct ncplane* n, const char* egc, uint32_t attrword, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);
int ncplane_highgradient(struct ncplane* n, uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr, int ystop, int xstop);
int ncplane_highgradient_sized(struct ncplane* n, uint64_t ul, uint64_t ur, uint64_t ll, uint64_t lr, int ylen, int xlen);
int ncplane_putsimple_stainable(struct ncplane* n, char c);
int ncplane_putegc_stainable(struct ncplane* n, const char* gclust, int* sbytes);
int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* sbytes);
Expand Down
15 changes: 8 additions & 7 deletions src/demo/intro.c
Expand Up @@ -22,17 +22,17 @@ fader(struct notcurses* nc, struct ncplane* ncp, void* curry){
int intro(struct notcurses* nc){
int rows, cols;
struct ncplane* ncp = notcurses_stddim_yx(nc, &rows, &cols);
uint64_t cul, cur, cll, clr;
cul = cur = cll = clr = 0;
channels_set_fg_rgb(&cul, 0, 0xd0, 0); channels_set_bg(&cul, 0);
channels_set_fg_rgb(&cur, 0xff, 0, 0); channels_set_bg(&cur, 0);
channels_set_fg_rgb(&cll, 0x88, 0, 0xcc); channels_set_bg(&cll, 0);
channels_set_fg_rgb(&clr, 0, 0, 0); channels_set_bg(&clr, 0);
uint32_t ccul, ccur, ccll, cclr;
ccul = ccur = ccll = cclr = 0;
channel_set_rgb(&ccul, 0, 0xd0, 0);
channel_set_rgb(&ccur, 0xff, 0, 0);
channel_set_rgb(&ccll, 0x88, 0, 0xcc);
channel_set_rgb(&cclr, 0, 0, 0);
// we use full block rather+fg than space+bg to conflict less with the menu
if(ncplane_cursor_move_yx(ncp, 0, 0)){
return -1;
}
if(ncplane_gradient_sized(ncp, "█", 0, cul, cur, cll, clr, rows, cols)){
if(ncplane_highgradient_sized(ncp, ccul, ccur, ccll, cclr, rows, cols)){
return -1;
}
cell c = CELL_TRIVIAL_INITIALIZER;
Expand All @@ -56,6 +56,7 @@ int intro(struct notcurses* nc){
NCBOXGRAD_RIGHT | NCBOXGRAD_LEFT)){
return -1;
}
uint64_t cul, cur, cll, clr;
cul = cur = cll = clr = 0;
channels_set_fg_rgb(&cul, 200, 0, 200); channels_set_bg_rgb(&cul, 0, 64, 0);
channels_set_fg_rgb(&cur, 200, 0, 200); channels_set_bg_rgb(&cur, 0, 64, 0);
Expand Down
20 changes: 10 additions & 10 deletions src/demo/sliding.c
Expand Up @@ -97,25 +97,25 @@ fill_chunk(struct ncplane* n, int idx){
char buf[4];
int maxy, maxx;
ncplane_dim_yx(n, &maxy, &maxx);
snprintf(buf, sizeof(buf), "%02d", idx + 1); // don't zero-index to viewer
uint64_t channels = 0;
int r = 64 + hidx * 10;
int b = 64 + vidx * 30;
int g = 225 - ((hidx + vidx) * 12);
channels_set_fg_rgb(&channels, r, g, b);
uint64_t ul, ur, ll, lr;
ul = ur = ll = lr = 0;
channels_set_bg_rgb(&ul, r, g, b); channels_set_fg(&ul, 0);
channels_set_bg_rgb(&lr, r, g, b); channels_set_fg(&lr, 0);
channels_set_bg_rgb(&ur, g, b, r); channels_set_fg(&ur, 0);
channels_set_bg_rgb(&ll, b, r, g); channels_set_fg(&ll, 0);
uint32_t ul = 0, ur = 0, ll = 0, lr = 0;
channel_set_rgb(&ul, r, g, b);
channel_set_rgb(&lr, r, g, b);
channel_set_rgb(&ur, g, b, r);
channel_set_rgb(&ll, b, r, g);
int ret = 0;
ret |= ncplane_gradient_sized(n, " ", 0, ul, ur, ll, lr, maxy, maxx);
ret |= ncplane_highgradient_sized(n, ul, ur, ll, lr, maxy, maxx);
ret |= ncplane_double_box(n, 0, channels, maxy - 1, maxx - 1, 0);
if(maxx >= 4 && maxy >= 3){
ret |= ncplane_cursor_move_yx(n, (maxy - 1) / 2, (maxx - 1) / 2);
ret |= (ncplane_putegc_stainable(n, buf, NULL) < 0);
ret |= (ncplane_putegc_stainable(n, buf + 1, NULL) < 0);
snprintf(buf, sizeof(buf), "%d", (idx + 1) / 10); // don't zero-index to viewer
ret |= (ncplane_putegc(n, buf, NULL) < 0);
snprintf(buf, sizeof(buf), "%d", (idx + 1) % 10);
ret |= (ncplane_putegc(n, buf, NULL) < 0);
}
return ret;
}
Expand Down
107 changes: 80 additions & 27 deletions src/lib/fill.c
Expand Up @@ -158,6 +158,27 @@ calc_gradient_channels(cell* c, uint64_t ul, uint64_t ur, uint64_t ll,
}
}

static bool
check_gradient_channel_args(uint32_t ul, uint32_t ur, uint32_t bl, uint32_t br){
if(channel_default_p(ul) || channel_default_p(ur) ||
channel_default_p(bl) || channel_default_p(br)){
if(!(channel_default_p(ul) && channel_default_p(ur) &&
channel_default_p(bl) && channel_default_p(br))){
return true;
}
}
if(channel_alpha(ul) != channel_alpha(ur) ||
channel_alpha(ur) != channel_alpha(bl) ||
channel_alpha(bl) != channel_alpha(br)){
return true;
}
if(channel_palindex_p(ul) || channel_palindex_p(bl) ||
channel_palindex_p(br) || channel_palindex_p(ur)){
return true;
}
return false;
}

// Given the four channels arguments, verify that:
//
// - if any is default foreground, all are default foreground
Expand All @@ -167,39 +188,71 @@ calc_gradient_channels(cell* c, uint64_t ul, uint64_t ur, uint64_t ll,
// - palette-indexed color must not be used
static bool
check_gradient_args(uint64_t ul, uint64_t ur, uint64_t bl, uint64_t br){
if(channels_fg_default_p(ul) || channels_fg_default_p(ur) ||
channels_fg_default_p(bl) || channels_fg_default_p(br)){
if(!(channels_fg_default_p(ul) && channels_fg_default_p(ur) &&
channels_fg_default_p(bl) && channels_fg_default_p(br))){
return true;
}
}
if(channels_bg_default_p(ul) || channels_bg_default_p(ur) ||
channels_bg_default_p(bl) || channels_bg_default_p(br)){
if(!(channels_bg_default_p(ul) && channels_bg_default_p(ur) &&
channels_bg_default_p(bl) && channels_bg_default_p(br))){
return true;
}
}
if(channels_fg_alpha(ul) != channels_fg_alpha(ur) ||
channels_fg_alpha(ur) != channels_fg_alpha(bl) ||
channels_fg_alpha(bl) != channels_fg_alpha(br)){
if(check_gradient_channel_args(channels_fchannel(ul), channels_fchannel(ur),
channels_fchannel(bl), channels_fchannel(br))){
return true;
}
if(channels_bg_alpha(ul) != channels_bg_alpha(ur) ||
channels_bg_alpha(ur) != channels_bg_alpha(bl) ||
channels_bg_alpha(bl) != channels_bg_alpha(br)){
if(check_gradient_channel_args(channels_bchannel(ul), channels_bchannel(ur),
channels_bchannel(bl), channels_bchannel(br))){
return true;
}
if(channels_fg_palindex_p(ul) || channels_fg_palindex_p(bl) ||
channels_fg_palindex_p(br) || channels_fg_palindex_p(ur)){
return true;
return false;
}

// calculate both channels of a gradient at a particular point, knowing that
// we're using double halfblocks, into `c`->channels.
static inline void
calc_highgradient(cell* c, uint64_t ul, uint64_t ur, uint64_t ll,
uint64_t lr, int y, int x, int ylen, int xlen){
if(!channel_default_p(ul)){
cell_set_fchannel(c, calc_gradient_channel(ul, ur, ll, lr,
y * 2, x, ylen, xlen));
cell_set_bchannel(c, calc_gradient_channel(ul, ur, ll, lr,
y * 2 + 1, x, ylen, xlen));
}else{
cell_set_fg_default(c);
cell_set_bg_default(c);
}
if(channels_bg_palindex_p(ul) || channels_bg_palindex_p(bl) ||
channels_bg_palindex_p(br) || channels_bg_palindex_p(ur)){
return true;
}

int ncplane_highgradient(ncplane* n, uint32_t ul, uint32_t ur,
uint32_t ll, uint32_t lr, int ystop, int xstop){
if(check_gradient_channel_args(ul, ur, ll, lr)){
return -1;
}
return false;
int yoff, xoff, ymax, xmax;
ncplane_cursor_yx(n, &yoff, &xoff);
// must be at least 1x1, with its upper-left corner at the current cursor
if(ystop < yoff){
return -1;
}
if(xstop < xoff){
return -1;
}
ncplane_dim_yx(n, &ymax, &xmax);
// must be within the ncplane
if(xstop >= xmax || ystop >= ymax){
return -1;
}
const int xlen = xstop - xoff + 1;
const int ylen = (ystop - yoff + 1) * 2;
if(xlen == 1){
if(ul != ur || ll != lr){
return -1;
}
}
for(int y = yoff ; y <= ystop ; ++y){
for(int x = xoff ; x <= xstop ; ++x){
cell* targc = ncplane_cell_ref_yx(n, y, x);
targc->channels = 0;
if(cell_load(n, targc, "▀") < 0){
return -1;
}
// FIXME do the loop
calc_highgradient(targc, ul, ur, ll, lr, y - yoff, x - xoff, ylen, xlen);
}
}
return 0;
}

int ncplane_gradient(ncplane* n, const char* egc, uint32_t attrword,
Expand Down
28 changes: 28 additions & 0 deletions tests/fills.cpp
Expand Up @@ -283,6 +283,34 @@ TEST_CASE("Fills") {
CHECK(chan2 == d.channels);
}

// Unlike a typical gradient, a high gradient ought be able to do a vertical
// change in a single row.
SUBCASE("HighGradient2Colors1Row") {
uint32_t ul, ur, ll, lr;
ul = ur = ll = lr = 0;
channel_set(&ul, 0xffffff);
channel_set(&lr, 0x000000);
channel_set(&ll, 0x00ffff);
channel_set(&ur, 0xff00ff);
int dimy, dimx;
ncplane_dim_yx(n_, &dimy, &dimx);
REQUIRE(0 == ncplane_highgradient_sized(n_, ul, ur, ll, lr, dimy, dimx));
CHECK(0 == notcurses_render(nc_));
}

SUBCASE("HighGradient") {
uint32_t ul, ur, ll, lr;
ul = ur = ll = lr = 0;
channel_set(&ul, 0xffffff);
channel_set(&lr, 0x000000);
channel_set(&ll, 0x00ffff);
channel_set(&ur, 0xff00ff);
int dimy, dimx;
ncplane_dim_yx(n_, &dimy, &dimx);
REQUIRE(0 == ncplane_highgradient_sized(n_, ul, ur, ll, lr, dimy, dimx));
CHECK(0 == notcurses_render(nc_));
}

CHECK(0 == notcurses_stop(nc_));
CHECK(0 == fclose(outfp_));

Expand Down

0 comments on commit eead4c1

Please sign in to comment.