mirror of
https://github.com/dankamongmen/notcurses.git
synced 2024-10-31 15:20:13 +00:00
Graceful fallback among blitters #637
If we're in ASCII mode, no blitter except for NCBLIT_1x1 is going to work. Whenever NCBLIT_DEFAULT is provided, select NCBLIT_1x1 if we're in ASCII mode. Add NCVISUAL_OPTIONS_MAYDEGRADE and NCPLOT_OPTIONS_MAYDEGRADE. Both serve to allow smooth degradation when a blitter other than NCBLIT_DEFAULT has been provided. Closes #637. Make calc_gradient_cell() static inline so our templated ncppplot implementation can use it (ugh). When using NCBLIT_1x1 for plots in ASCII mode, use space rather than full block, and invert colors. Use NCBLIT_DEFAULT in the demo for the FPS plot.
This commit is contained in:
parent
33c0e7fa2c
commit
f47bde1c4e
@ -28,7 +28,21 @@ typedef enum {
|
||||
NCBLIT_SIXEL, // six rows, 1 column (RGB)
|
||||
} ncblitter_e;
|
||||
|
||||
typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
|
||||
#define NCVISUAL_OPTIONS_MAYDEGRADE 0x0001
|
||||
|
||||
struct ncvisual_options {
|
||||
struct ncplane* n;
|
||||
ncscale_e scaling;
|
||||
int y, x;
|
||||
int begy, begx; // origin of rendered section
|
||||
int leny, lenx; // size of rendered section
|
||||
ncblitter_e blitter; // glyph set to use (maps input to output cells)
|
||||
uint64_t flags; // bitmask over NCVISUAL_OPTIONS_*
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
|
||||
```
|
||||
|
||||
**bool notcurses_canopen_images(const struct notcurses* nc);**
|
||||
|
@ -2142,6 +2142,8 @@ API nc_err_e ncvisual_rotate(struct ncvisual* n, double rads);
|
||||
// transformation, unless the size is unchanged.
|
||||
API nc_err_e ncvisual_resize(struct ncvisual* n, int rows, int cols);
|
||||
|
||||
#define NCVISUAL_OPTIONS_MAYDEGRADE 0x0001 // blitter can be worse than requested
|
||||
|
||||
struct ncvisual_options {
|
||||
// if no ncplane is provided, one will be created using the exact size
|
||||
// necessary to render the source with perfect fidelity (this might be
|
||||
@ -2161,8 +2163,10 @@ struct ncvisual_options {
|
||||
// these numbers are all in terms of ncvisual pixels.
|
||||
int begy, begx; // origin of rendered section
|
||||
int leny, lenx; // size of rendered section
|
||||
// use NCBLIT_DEFAULT if you don't care, to use NCBLIT_2x2 (assuming
|
||||
// UTF8) or NCBLIT_1x1 (in an ASCII environment)
|
||||
ncblitter_e blitter; // glyph set to use (maps input to output cells)
|
||||
uint64_t flags; // currently all zero
|
||||
uint64_t flags; // bitmask over NCVISUAL_OPTIONS_*
|
||||
};
|
||||
|
||||
// Render the decoded frame to the specified ncplane (if one is not provided,
|
||||
@ -2737,12 +2741,15 @@ API int ncmenu_destroy(struct ncmenu* n);
|
||||
#define NCPLOT_OPTIONS_LABELTICKSD 0x0001 // show labels for dependent axis
|
||||
#define NCPLOT_OPTIONS_EXPONENTIALD 0x0002 // exponential dependent axis
|
||||
#define NCPLOT_OPTIONS_VERTICALI 0x0004 // independent axis is vertical
|
||||
#define NCPLOT_OPTIONS_MAYDEGRADE 0x0008 // blitter can be worse than requested
|
||||
|
||||
typedef struct ncplot_options {
|
||||
// channels for the maximum and minimum levels. linear interpolation will be
|
||||
// applied across the domain between these two.
|
||||
uint64_t maxchannel;
|
||||
uint64_t minchannel;
|
||||
// if you don't care, pass NCBLIT_DEFAULT and get NCBLIT_8x1 (assuming
|
||||
// UTF8) or NCBLIT_1x1 (in an ASCII environment)
|
||||
ncblitter_e gridtype; // number of "pixels" per row x column
|
||||
// independent variable can either be a contiguous range, or a finite set
|
||||
// of keys. for a time range, say the previous hour sampled with second
|
||||
@ -2756,7 +2763,7 @@ typedef struct ncplot_options {
|
||||
// The plot will make free use of the entirety of the plane.
|
||||
// for domain autodiscovery, set miny == maxy == 0.
|
||||
API struct ncuplot* ncuplot_create(struct ncplane* n, const ncplot_options* opts,
|
||||
uint64_t miny, uint64_t maxy);
|
||||
uint64_t miny, uint64_t maxy);
|
||||
API struct ncdplot* ncdplot_create(struct ncplane* n, const ncplot_options* opts,
|
||||
double miny, double maxy);
|
||||
|
||||
|
@ -549,7 +549,6 @@ int fpsgraph_init(struct notcurses* nc){
|
||||
ncplot_options opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.flags = NCPLOT_OPTIONS_LABELTICKSD | NCPLOT_OPTIONS_EXPONENTIALD;
|
||||
opts.gridtype = NCBLIT_8x1;
|
||||
channels_set_fg_rgb(&opts.minchannel, 0xff, 0x00, 0xff);
|
||||
channels_set_bg(&opts.minchannel, 0x201020);
|
||||
channels_set_bg_alpha(&opts.minchannel, CELL_ALPHA_BLEND);
|
||||
|
@ -218,7 +218,7 @@ int main(void){
|
||||
ncpp::Plane pplane{PLOTHEIGHT, dimx, dimy - PLOTHEIGHT, 0, nullptr};
|
||||
struct ncplot_options popts{};
|
||||
// FIXME would be nice to switch over to exponential at some level
|
||||
popts.flags = NCPLOT_OPTIONS_LABELTICKSD;
|
||||
popts.flags = NCPLOT_OPTIONS_LABELTICKSD | NCPLOT_OPTIONS_MAYDEGRADE;
|
||||
popts.minchannel = popts.maxchannel = 0;
|
||||
channels_set_fg_rgb(&popts.minchannel, 0x40, 0x50, 0xb0);
|
||||
channels_set_fg_rgb(&popts.maxchannel, 0x40, 0xff, 0xd0);
|
||||
|
@ -283,7 +283,7 @@ braille_blit(ncplane* nc, int placey, int placex, int linesize,
|
||||
}
|
||||
|
||||
// NCBLIT_DEFAULT is not included, as it has no defined properties. It ought
|
||||
// be replaced with some real blitter implementation.
|
||||
// be replaced with some real blitter implementation by the calling widget.
|
||||
const struct blitset geomdata[] = {
|
||||
{ .geom = NCBLIT_8x1, .width = 1, .height = 8, .egcs = L" ▁▂▃▄▅▆▇█",
|
||||
.blit = NULL, .fill = false, },
|
||||
|
@ -1,8 +1,18 @@
|
||||
#ifndef NOTCURSES_BLITSET
|
||||
#define NOTCURSES_BLITSET
|
||||
|
||||
#include "notcurses/notcurses.h"
|
||||
|
||||
static inline const struct blitset*
|
||||
lookup_blitset(ncblitter_e setid) {
|
||||
lookup_blitset(const struct notcurses* nc, ncblitter_e setid, bool may_degrade) {
|
||||
// the only viable blitter in ASCII is NCBLIT_1x1
|
||||
if(!notcurses_canutf8(nc) && setid != NCBLIT_1x1){
|
||||
if(may_degrade){
|
||||
setid = NCBLIT_1x1;
|
||||
}else{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
const struct blitset* bset = geomdata;
|
||||
while(bset->egcs){
|
||||
if(bset->geom == setid){
|
||||
|
@ -67,99 +67,6 @@ int ncplane_polyfill_yx(ncplane* n, int y, int x, const cell* c){
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Our gradient is a 2d lerp among the four corners of the region. We start
|
||||
// with the observation that each corner ought be its exact specified corner,
|
||||
// and the middle ought be the exact average of all four corners' components.
|
||||
// Another observation is that if all four corners are the same, every cell
|
||||
// ought be the exact same color. From this arises the observation that a
|
||||
// perimeter element is not affected by the other three sides:
|
||||
//
|
||||
// a corner element is defined by itself
|
||||
// a perimeter element is defined by the two points on its side
|
||||
// an internal element is defined by all four points
|
||||
//
|
||||
// 2D equation of state: solve for each quadrant's contribution (min 2x2):
|
||||
//
|
||||
// X' = (xlen - 1) - X
|
||||
// Y' = (ylen - 1) - Y
|
||||
// TLC: X' * Y' * TL
|
||||
// TRC: X * Y' * TR
|
||||
// BLC: X' * Y * BL
|
||||
// BRC: X * Y * BR
|
||||
// steps: (xlen - 1) * (ylen - 1) [maximum steps away from origin]
|
||||
//
|
||||
// Then add TLC + TRC + BLC + BRC + steps / 2, and divide by steps (the
|
||||
// steps / 2 is to work around truncate-towards-zero).
|
||||
static int
|
||||
calc_gradient_component(unsigned tl, unsigned tr, unsigned bl, unsigned br,
|
||||
int y, int x, int ylen, int xlen){
|
||||
assert(y >= 0);
|
||||
assert(y < ylen);
|
||||
assert(x >= 0);
|
||||
assert(x < xlen);
|
||||
const int avm = (ylen - 1) - y;
|
||||
const int ahm = (xlen - 1) - x;
|
||||
if(xlen < 2){
|
||||
if(ylen < 2){
|
||||
return tl;
|
||||
}
|
||||
return (tl * avm + bl * y) / (ylen - 1);
|
||||
}
|
||||
if(ylen < 2){
|
||||
return (tl * ahm + tr * x) / (xlen - 1);
|
||||
}
|
||||
const int tlc = ahm * avm * tl;
|
||||
const int blc = ahm * y * bl;
|
||||
const int trc = x * avm * tr;
|
||||
const int brc = y * x * br;
|
||||
const int divisor = (ylen - 1) * (xlen - 1);
|
||||
return ((tlc + blc + trc + brc) + divisor / 2) / divisor;
|
||||
}
|
||||
|
||||
// calculate one of the channels of a gradient at a particular point.
|
||||
static inline uint32_t
|
||||
calc_gradient_channel(uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr,
|
||||
int y, int x, int ylen, int xlen){
|
||||
uint32_t chan = 0;
|
||||
channel_set_rgb_clipped(&chan,
|
||||
calc_gradient_component(channel_r(ul), channel_r(ur),
|
||||
channel_r(ll), channel_r(lr),
|
||||
y, x, ylen, xlen),
|
||||
calc_gradient_component(channel_g(ul), channel_g(ur),
|
||||
channel_g(ll), channel_g(lr),
|
||||
y, x, ylen, xlen),
|
||||
calc_gradient_component(channel_b(ul), channel_b(ur),
|
||||
channel_b(ll), channel_b(lr),
|
||||
y, x, ylen, xlen));
|
||||
channel_set_alpha(&chan, channel_alpha(ul)); // precondition: all αs are equal
|
||||
return chan;
|
||||
}
|
||||
|
||||
// calculate both channels of a gradient at a particular point, storing them
|
||||
// into `c`->channels. x and y ought be the location within the gradient.
|
||||
static inline void
|
||||
calc_gradient_channels(cell* c, uint64_t ul, uint64_t ur, uint64_t ll,
|
||||
uint64_t lr, int y, int x, int ylen, int xlen){
|
||||
if(!channels_fg_default_p(ul)){
|
||||
cell_set_fchannel(c, calc_gradient_channel(channels_fchannel(ul),
|
||||
channels_fchannel(ur),
|
||||
channels_fchannel(ll),
|
||||
channels_fchannel(lr),
|
||||
y, x, ylen, xlen));
|
||||
}else{
|
||||
cell_set_fg_default(c);
|
||||
}
|
||||
if(!channels_bg_default_p(ul)){
|
||||
cell_set_bchannel(c, calc_gradient_channel(channels_bchannel(ul),
|
||||
channels_bchannel(ur),
|
||||
channels_bchannel(ll),
|
||||
channels_bchannel(lr),
|
||||
y, x, ylen, xlen));
|
||||
}else{
|
||||
cell_set_bg_default(c);
|
||||
}
|
||||
}
|
||||
|
||||
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) ||
|
||||
@ -307,7 +214,8 @@ int ncplane_gradient(ncplane* n, const char* egc, uint32_t attrword,
|
||||
return -1;
|
||||
}
|
||||
targc->attrword = attrword;
|
||||
calc_gradient_channels(targc, ul, ur, bl, br, y - yoff, x - xoff, ylen, xlen);
|
||||
calc_gradient_channels(&targc->channels, ul, ur, bl, br,
|
||||
y - yoff, x - xoff, ylen, xlen);
|
||||
++total;
|
||||
}
|
||||
}
|
||||
@ -340,7 +248,8 @@ int ncplane_stain(struct ncplane* n, int ystop, int xstop,
|
||||
for(int y = yoff ; y <= ystop ; ++y){
|
||||
for(int x = xoff ; x <= xstop ; ++x){
|
||||
cell* targc = ncplane_cell_ref_yx(n, y, x);
|
||||
calc_gradient_channels(targc, tl, tr, bl, br, y - yoff, x - xoff, ylen, xlen);
|
||||
calc_gradient_channels(&targc->channels, tl, tr, bl, br,
|
||||
y - yoff, x - xoff, ylen, xlen);
|
||||
++total;
|
||||
}
|
||||
}
|
||||
|
@ -663,6 +663,102 @@ ncplane_center(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
|
||||
int ncvisual_bounding_box(const struct ncvisual* ncv, int* leny, int* lenx,
|
||||
int* offy, int* offx);
|
||||
|
||||
// Our gradient is a 2d lerp among the four corners of the region. We start
|
||||
// with the observation that each corner ought be its exact specified corner,
|
||||
// and the middle ought be the exact average of all four corners' components.
|
||||
// Another observation is that if all four corners are the same, every cell
|
||||
// ought be the exact same color. From this arises the observation that a
|
||||
// perimeter element is not affected by the other three sides:
|
||||
//
|
||||
// a corner element is defined by itself
|
||||
// a perimeter element is defined by the two points on its side
|
||||
// an internal element is defined by all four points
|
||||
//
|
||||
// 2D equation of state: solve for each quadrant's contribution (min 2x2):
|
||||
//
|
||||
// X' = (xlen - 1) - X
|
||||
// Y' = (ylen - 1) - Y
|
||||
// TLC: X' * Y' * TL
|
||||
// TRC: X * Y' * TR
|
||||
// BLC: X' * Y * BL
|
||||
// BRC: X * Y * BR
|
||||
// steps: (xlen - 1) * (ylen - 1) [maximum steps away from origin]
|
||||
//
|
||||
// Then add TLC + TRC + BLC + BRC + steps / 2, and divide by steps (the
|
||||
// steps / 2 is to work around truncate-towards-zero).
|
||||
static int
|
||||
calc_gradient_component(unsigned tl, unsigned tr, unsigned bl, unsigned br,
|
||||
int y, int x, int ylen, int xlen){
|
||||
assert(y >= 0);
|
||||
assert(y < ylen);
|
||||
assert(x >= 0);
|
||||
assert(x < xlen);
|
||||
const int avm = (ylen - 1) - y;
|
||||
const int ahm = (xlen - 1) - x;
|
||||
if(xlen < 2){
|
||||
if(ylen < 2){
|
||||
return tl;
|
||||
}
|
||||
return (tl * avm + bl * y) / (ylen - 1);
|
||||
}
|
||||
if(ylen < 2){
|
||||
return (tl * ahm + tr * x) / (xlen - 1);
|
||||
}
|
||||
const int tlc = ahm * avm * tl;
|
||||
const int blc = ahm * y * bl;
|
||||
const int trc = x * avm * tr;
|
||||
const int brc = y * x * br;
|
||||
const int divisor = (ylen - 1) * (xlen - 1);
|
||||
return ((tlc + blc + trc + brc) + divisor / 2) / divisor;
|
||||
}
|
||||
|
||||
// calculate one of the channels of a gradient at a particular point.
|
||||
static inline uint32_t
|
||||
calc_gradient_channel(uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr,
|
||||
int y, int x, int ylen, int xlen){
|
||||
uint32_t chan = 0;
|
||||
channel_set_rgb_clipped(&chan,
|
||||
calc_gradient_component(channel_r(ul), channel_r(ur),
|
||||
channel_r(ll), channel_r(lr),
|
||||
y, x, ylen, xlen),
|
||||
calc_gradient_component(channel_g(ul), channel_g(ur),
|
||||
channel_g(ll), channel_g(lr),
|
||||
y, x, ylen, xlen),
|
||||
calc_gradient_component(channel_b(ul), channel_b(ur),
|
||||
channel_b(ll), channel_b(lr),
|
||||
y, x, ylen, xlen));
|
||||
channel_set_alpha(&chan, channel_alpha(ul)); // precondition: all αs are equal
|
||||
return chan;
|
||||
}
|
||||
|
||||
// calculate both channels of a gradient at a particular point, storing them
|
||||
// into `channels'. x and y ought be the location within the gradient.
|
||||
static inline void
|
||||
calc_gradient_channels(uint64_t* channels, uint64_t ul, uint64_t ur,
|
||||
uint64_t ll, uint64_t lr, int y, int x,
|
||||
int ylen, int xlen){
|
||||
if(!channels_fg_default_p(ul)){
|
||||
channels_set_fchannel(channels,
|
||||
calc_gradient_channel(channels_fchannel(ul),
|
||||
channels_fchannel(ur),
|
||||
channels_fchannel(ll),
|
||||
channels_fchannel(lr),
|
||||
y, x, ylen, xlen));
|
||||
}else{
|
||||
channels_set_fg_default(channels);
|
||||
}
|
||||
if(!channels_bg_default_p(ul)){
|
||||
channels_set_bchannel(channels,
|
||||
calc_gradient_channel(channels_bchannel(ul),
|
||||
channels_bchannel(ur),
|
||||
channels_bchannel(ll),
|
||||
channels_bchannel(lr),
|
||||
y, x, ylen, xlen));
|
||||
}else{
|
||||
channels_set_bg_default(channels);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -24,7 +24,19 @@ class ncppplot {
|
||||
if(maxy < miny){
|
||||
return false;
|
||||
}
|
||||
auto bset = lookup_blitset(opts && opts->gridtype ? opts->gridtype : NCBLIT_8x1);
|
||||
ncblitter_e blitter = opts ? opts->gridtype : NCBLIT_DEFAULT;
|
||||
if(blitter == NCBLIT_DEFAULT){
|
||||
if(notcurses_canutf8(ncplane_notcurses(n))){
|
||||
blitter = NCBLIT_8x1;
|
||||
}else{
|
||||
blitter = NCBLIT_1x1;
|
||||
}
|
||||
}
|
||||
bool degrade_blitter = true;
|
||||
if(opts && !(opts->flags & NCPLOT_OPTIONS_MAYDEGRADE)){
|
||||
degrade_blitter = false;
|
||||
}
|
||||
auto bset = lookup_blitset(ncplane_notcurses(n), blitter, degrade_blitter);
|
||||
if(bset == nullptr){
|
||||
return false;
|
||||
}
|
||||
@ -76,38 +88,11 @@ class ncppplot {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add to or set the value corresponding to this x. If x is beyond the current
|
||||
// x window, the x window is advanced to include x, and values passing beyond
|
||||
// the window are lost. The first call will place the initial window. The plot
|
||||
// will be redrawn, but notcurses_render() is not called.
|
||||
int add_sample(uint64_t x, T y){
|
||||
if(window_slide(x)){
|
||||
return -1;
|
||||
}
|
||||
update_sample(x, y, false);
|
||||
if(update_domain(x)){
|
||||
return -1;
|
||||
}
|
||||
return redraw_plot();
|
||||
}
|
||||
|
||||
int set_sample(uint64_t x, T y){
|
||||
if(window_slide(x)){
|
||||
return -1;
|
||||
}
|
||||
update_sample(x, y, true);
|
||||
if(update_domain(x)){
|
||||
return -1;
|
||||
}
|
||||
return redraw_plot();
|
||||
}
|
||||
|
||||
void destroy(){
|
||||
free(slots);
|
||||
}
|
||||
|
||||
// FIXME everything below here ought be private, but it busts unit tests
|
||||
int redraw_plot(){
|
||||
int redraw_plot() {
|
||||
ncplane_erase(ncp);
|
||||
const int scale = bset->width;
|
||||
int dimy, dimx;
|
||||
@ -122,7 +107,7 @@ class ncppplot {
|
||||
if(exponentiali){
|
||||
if(maxy > miny){
|
||||
interval = pow(maxy - miny, (double)1 / (dimy * states));
|
||||
//fprintf(stderr, "miny: %ju maxy: %ju dimy: %d states: %zu\n", miny, maxy, dimy, states);
|
||||
//fprintf(stderr, "miny: %ju maxy: %ju dimy: %d states: %zu\n", miny, maxy, dimy, states);
|
||||
}else{
|
||||
interval = 0;
|
||||
}
|
||||
@ -158,6 +143,8 @@ class ncppplot {
|
||||
#define MAXWIDTH 2
|
||||
int idx = slotstart; // idx holds the real slot index; we move backwards
|
||||
for(int x = finalx ; x >= startx ; --x){
|
||||
// a single column might correspond to more than 1 ('scale', up to
|
||||
// MAXWIDTH) slot's worth of samples. prepare the working gval set.
|
||||
T gvals[MAXWIDTH];
|
||||
// load it retaining the same ordering we have in the actual array
|
||||
for(int i = scale - 1 ; i >= 0 ; --i){
|
||||
@ -179,6 +166,10 @@ class ncppplot {
|
||||
double intervalbase = miny;
|
||||
const wchar_t* egc = bset->egcs;
|
||||
for(int y = 0 ; y < dimy ; ++y){
|
||||
uint64_t channels = 0;
|
||||
calc_gradient_channels(&channels, maxchannel, maxchannel,
|
||||
minchannel, minchannel, y, x, dimy, dimx);
|
||||
ncplane_set_channels(ncp, channels);
|
||||
size_t egcidx = 0, sumidx = 0;
|
||||
// if we've got at least one interval's worth on the number of positions
|
||||
// times the number of intervals per position plus the starting offset,
|
||||
@ -206,10 +197,26 @@ class ncppplot {
|
||||
egcidx = 0;
|
||||
}
|
||||
}
|
||||
if(sumidx){
|
||||
// if we're not UTF8, we can only arrive here via NCBLIT_1x1 (otherwise
|
||||
// we would have errored out during construction). even then, however,
|
||||
// we need handle ASCII differently, since it can't print full block.
|
||||
// in ASCII mode, egcidx != means swap colors and use space.
|
||||
if(notcurses_canutf8(ncplane_notcurses(ncp)) || !sumidx){
|
||||
if(ncplane_putwc_yx(ncp, dimy - y - 1, x, egc[sumidx]) <= 0){
|
||||
return -1;
|
||||
}
|
||||
}else{
|
||||
const uint64_t swapbg = channels_bchannel(channels);
|
||||
const uint64_t swapfg = channels_fchannel(channels);
|
||||
channels_set_bchannel(&channels, swapfg);
|
||||
channels_set_fchannel(&channels, swapbg);
|
||||
ncplane_set_channels(ncp, channels);
|
||||
if(ncplane_putsimple_yx(ncp, dimy - y - 1, x, ' ') <= 0){
|
||||
return -1;
|
||||
}
|
||||
channels_set_bchannel(&channels, swapbg);
|
||||
channels_set_fchannel(&channels, swapfg);
|
||||
ncplane_set_channels(ncp, channels);
|
||||
}
|
||||
if(done){
|
||||
break;
|
||||
@ -224,11 +231,33 @@ class ncppplot {
|
||||
if(ncplane_cursor_move_yx(ncp, 0, 0)){
|
||||
return -1;
|
||||
}
|
||||
if(ncplane_stain(ncp, dimy - 1, dimx - 1, maxchannel, maxchannel,
|
||||
minchannel, minchannel) <= 0){
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add to or set the value corresponding to this x. If x is beyond the current
|
||||
// x window, the x window is advanced to include x, and values passing beyond
|
||||
// the window are lost. The first call will place the initial window. The plot
|
||||
// will be redrawn, but notcurses_render() is not called.
|
||||
int add_sample(uint64_t x, T y) {
|
||||
if(window_slide(x)){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
update_sample(x, y, false);
|
||||
if(update_domain(x)){
|
||||
return -1;
|
||||
}
|
||||
return redraw_plot();
|
||||
}
|
||||
|
||||
int set_sample(uint64_t x, T y) {
|
||||
if(window_slide(x)){
|
||||
return -1;
|
||||
}
|
||||
update_sample(x, y, true);
|
||||
if(update_domain(x)){
|
||||
return -1;
|
||||
}
|
||||
return redraw_plot();
|
||||
}
|
||||
|
||||
// if we're doing domain detection, update the domain to reflect the value we
|
||||
|
@ -41,7 +41,7 @@ auto ncvisual_geom(const notcurses* nc, const ncvisual* n, ncblitter_e blitter,
|
||||
if(blitter == NCBLIT_DEFAULT){
|
||||
blitter = ncvisual_default_blitter(nc);
|
||||
}
|
||||
const struct blitset* bset = lookup_blitset(blitter);
|
||||
const struct blitset* bset = lookup_blitset(nc, blitter, false);
|
||||
if(!bset){
|
||||
return -1;
|
||||
}
|
||||
@ -65,10 +65,11 @@ auto ncvisual_geom(const notcurses* nc, const ncvisual* n, ncblitter_e blitter,
|
||||
static const struct blitset*
|
||||
rgba_blitter(const notcurses* nc, const struct ncvisual_options* opts){
|
||||
const struct blitset* bset;
|
||||
if(opts && opts->blitter){
|
||||
bset = lookup_blitset(opts->blitter);
|
||||
const bool maydegrade = !opts || (opts->flags & NCVISUAL_OPTIONS_MAYDEGRADE);
|
||||
if(opts && opts->blitter != NCBLIT_DEFAULT){
|
||||
bset = lookup_blitset(nc, opts->blitter, maydegrade);
|
||||
}else{
|
||||
bset = lookup_blitset(ncvisual_default_blitter(nc));
|
||||
bset = lookup_blitset(nc, ncvisual_default_blitter(nc), maydegrade);
|
||||
}
|
||||
if(bset && !bset->blit){ // FIXME remove this once all blitters are enabled
|
||||
bset = nullptr;
|
||||
@ -376,7 +377,7 @@ auto ncvisual_from_bgra(const void* bgra, int rows, int rowstride,
|
||||
|
||||
auto ncvisual_render(notcurses* nc, ncvisual* ncv,
|
||||
const struct ncvisual_options* vopts) -> ncplane* {
|
||||
if(vopts && vopts->flags){
|
||||
if(vopts && vopts->flags > NCVISUAL_OPTIONS_MAYDEGRADE){
|
||||
return nullptr;
|
||||
}
|
||||
int lenx = vopts ? vopts->lenx : 0;
|
||||
|
Loading…
Reference in New Issue
Block a user