Distinct fg/bg alpha channels #139 (#141)

* improved alpha macros
* demo: use new alpha macros
* add ncplane_set_*_alpha()
* explicitly set fg for uniblock
* outro: background is a space #139
* distinct alpha channels for fg/bg #139
* rename 'background' cell to 'default' #142
* doc palette fades
This commit is contained in:
Nick Black 2019-12-14 17:34:10 -05:00 committed by GitHub
parent 582017a16a
commit 8bd8055f72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 351 additions and 129 deletions

207
README.md
View File

@ -63,7 +63,7 @@ Why use this non-standard library?
* Thread safety, and efficient use in parallel programs, has been a design * Thread safety, and efficient use in parallel programs, has been a design
consideration from the beginning. consideration from the beginning.
* A svelter design than that codified by X/Open. * A svelter design than that codified by X/Open:
* Exported identifiers are prefixed to avoid common namespace collisions. * Exported identifiers are prefixed to avoid common namespace collisions.
* The library object exports a minimal set of symbols. Where reasonable, * The library object exports a minimal set of symbols. Where reasonable,
`static inline` header-only code is used. This facilitates compiler `static inline` header-only code is used. This facilitates compiler
@ -202,6 +202,13 @@ Utility functions operating on the toplevel `notcurses` object include:
// following a resize operation, but the cursor might have changed position. // following a resize operation, but the cursor might have changed position.
int notcurses_resize(struct notcurses* n, int* RESTRICT y, int* RESTRICT x); int notcurses_resize(struct notcurses* n, int* RESTRICT y, int* RESTRICT x);
// Return our current idea of the terminal dimensions in rows and cols.
static inline void
notcurses_term_dim_yx(const struct notcurses* n, int* RESTRICT rows,
int* RESTRICT cols){
ncplane_dim_yx(notcurses_stdplane_const(n), rows, cols);
}
// Refresh the physical screen to match what was last rendered (i.e., without // Refresh the physical screen to match what was last rendered (i.e., without
// reflecting any changes since the last call to notcurses_render()). This is // reflecting any changes since the last call to notcurses_render()). This is
// primarily useful if the screen is externally corrupted. // primarily useful if the screen is externally corrupted.
@ -376,14 +383,6 @@ int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
// the standard plane. // the standard plane.
int ncplane_destroy(struct ncplane* ncp); int ncplane_destroy(struct ncplane* ncp);
// Set the ncplane's background cell to this cell. It will be rendered anywhere
// that the ncplane's gcluster is 0. The default background is all zeroes.
// Erasing the ncplane does not eliminate the background.
int ncplane_set_background(struct ncplane* ncp, const cell* c);
// Extract the ncplane's background cell into 'c'.
int ncplane_background(struct ncplane* ncp, cell* c);
// Move this plane relative to the standard plane. It is an error to attempt to // Move this plane relative to the standard plane. It is an error to attempt to
// move the standard plane. // move the standard plane.
int ncplane_move_yx(struct ncplane* n, int y, int x); int ncplane_move_yx(struct ncplane* n, int y, int x);
@ -391,6 +390,52 @@ int ncplane_move_yx(struct ncplane* n, int y, int x);
// Get the origin of this plane relative to the standard plane. // Get the origin of this plane relative to the standard plane.
void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x); void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
// Returns the dimensions of this ncplane.
void ncplane_dim_yx(const struct ncplane* n, int* RESTRICT rows,
int* RESTRICT cols);
// 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,
// excluding the default cell.
void ncplane_erase(struct ncplane* n);
// Set the specified style bits for the ncplane 'n', whether they're actively
// supported or not.
void ncplane_styles_set(struct ncplane* n, unsigned stylebits);
// Add the specified styles to the ncplane's existing spec.
void ncplane_styles_on(struct ncplane* n, unsigned stylebits);
// Remove the specified styles from the ncplane's existing spec.
void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
// Return the current styling for this ncplane.
unsigned ncplane_styles(const struct ncplane* n);
```
If a given cell's glyph is zero, or its foreground channel is fully transparent,
it is considered to have no foreground. A _default_ cell can be chosen for the
`ncplane`, to be consulted in this case. If the default cell's glyph is likewise
zero (or its foreground channel fully transparent), the plane's foreground is
not rendered. Note that the default cell, like every other cell, has its own
foreground and background channels.
```c
// Set the ncplane's default cell to this cell. If defined, it will be rendered
// anywhere that the ncplane's gcluster is 0. Erasing the ncplane does not
// reset the default cell; this function must instead be called with a zero c.
int ncplane_set_default(struct ncplane* ncp, const cell* c);
// Extract the ncplane's default cell into 'c'.
int ncplane_default(struct ncplane* ncp, cell* c);
```
`ncplane`s are completely ordered along an imaginary z-axis. Newly-created
`ncplane`s are on the top of the stack. They can be freely reordered.
```c
// Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom. // Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom.
int ncplane_move_top(struct ncplane* n); int ncplane_move_top(struct ncplane* n);
int ncplane_move_bottom(struct ncplane* n); int ncplane_move_bottom(struct ncplane* n);
@ -400,7 +445,13 @@ int ncplane_move_below(struct ncplane* RESTRICT n, struct ncplane* RESTRICT belo
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'. // Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
int ncplane_move_above(struct ncplane* RESTRICT n, struct ncplane* RESTRICT above); int ncplane_move_above(struct ncplane* RESTRICT n, struct ncplane* RESTRICT above);
```
Each plane holds a user pointer which can be retrieved and set (or ignored). In
addition, the plane's virtual framebuffer can be accessed (note that this does
not necessarily reflect anything on the actual screen).
```c
// Retrieve the cell at the cursor location on the specified plane, returning // Retrieve the cell at the cursor location on the specified plane, returning
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased. // it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
int ncplane_at_cursor(struct ncplane* n, cell* c); int ncplane_at_cursor(struct ncplane* n, cell* c);
@ -411,18 +462,14 @@ int ncplane_at_cursor(struct ncplane* n, cell* c);
void* ncplane_set_userptr(struct ncplane* n, void* opaque); void* ncplane_set_userptr(struct ncplane* n, void* opaque);
void* ncplane_userptr(struct ncplane* n); void* ncplane_userptr(struct ncplane* n);
const void* ncplane_userptr_const(const struct ncplane* n); const void* ncplane_userptr_const(const struct ncplane* n);
```
// Returns the dimensions of this ncplane. All output is to `ncplane`s. There is no cost in moving the cursor around the
void ncplane_dim_yx(const struct ncplane* n, int* RESTRICT rows, virtual framebuffer. Output that's never rendered still has some memory transfer
int* RESTRICT cols); cost as the virtual framebuffer is prepared, but new data overwrites it in
memory.
// Return our current idea of the terminal dimensions in rows and cols.
static inline void
notcurses_term_dim_yx(const struct notcurses* n, int* RESTRICT rows,
int* RESTRICT cols){
ncplane_dim_yx(notcurses_stdplane_const(n), rows, cols);
}
```c
// Move the cursor to the specified position (the cursor needn't be visible). // Move the cursor to the specified position (the cursor needn't be visible).
// Returns -1 on error, including negative parameters, or ones exceeding the // Returns -1 on error, including negative parameters, or ones exceeding the
// plane's dimensions. // plane's dimensions.
@ -569,8 +616,13 @@ ncplane_vprintf_yx(struct ncplane* n, int y, int x, const char* format, va_list
} }
return ncplane_vprintf(n, format, ap); return ncplane_vprintf(n, format, ap);
} }
```
Lines and boxes can be drawn, interpolating their colors between their two
endpoints. For a line of a single color, be sure to specify the same channels
on both sides. Boxes allow fairly detailed specification of how they're drawn.
```c
// Draw horizontal or vertical lines using the specified cell, starting at the // Draw horizontal or vertical lines using the specified cell, starting at the
// current cursor position. The cursor will end at the cell following the last // current cursor position. The cursor will end at the cell following the last
// cell output (even, perhaps counter-intuitively, when drawing vertical // cell output (even, perhaps counter-intuitively, when drawing vertical
@ -696,25 +748,11 @@ ncplane_double_box_sized(struct ncplane* n, uint32_t attr, uint64_t channels,
return ncplane_double_box(n, attr, channels, y + ylen - 1, return ncplane_double_box(n, attr, channels, y + ylen - 1,
x + xlen - 1, ctlword); x + xlen - 1, ctlword);
} }
```
// Erase every cell in the ncplane, resetting all attributes to normal, all My 14 year-old self would never forgive me if we didn't have sweet palette fades.
// 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.
void ncplane_erase(struct ncplane* n);
// Set the specified style bits for the ncplane 'n', whether they're actively
// supported or not.
void ncplane_styles_set(struct ncplane* n, unsigned stylebits);
// Add the specified styles to the ncplane's existing spec.
void ncplane_styles_on(struct ncplane* n, unsigned stylebits);
// Remove the specified styles from the ncplane's existing spec.
void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
// Return the current styling for this ncplane.
unsigned ncplane_styles(const struct ncplane* n);
```c
// Fade the ncplane out over the provided time, calling the specified function // Fade the ncplane out over the provided time, calling the specified function
// when done. Requires a terminal which supports direct color, or at least // when done. Requires a terminal which supports direct color, or at least
// palette modification (if the terminal uses a palette, our ability to fade // palette modification (if the terminal uses a palette, our ability to fade
@ -791,6 +829,10 @@ ncplane_get_bg_alpha(const struct ncplane* nc){
return channels_get_bg_alpha(ncplane_get_channels(nc)); return channels_get_bg_alpha(ncplane_get_channels(nc));
} }
// Set the alpha parameters for ncplane 'n'.
int ncplane_set_fg_alpha(struct ncplane* n, int alpha);
int ncplane_set_bg_alpha(struct ncplane* n, int alpha);
// Extract 24 bits of foreground RGB from 'n', split into subcomponents. // Extract 24 bits of foreground RGB from 'n', split into subcomponents.
static inline unsigned static inline unsigned
ncplane_get_fg_rgb(const struct ncplane* n, unsigned* r, unsigned* g, unsigned* ncplane_get_fg_rgb(const struct ncplane* n, unsigned* r, unsigned* g, unsigned*
@ -873,6 +915,16 @@ typedef struct cell {
// "not default color" bit is set, any color you load will be ignored. // "not default color" bit is set, any color you load will be ignored.
uint64_t channels; // + 8b == 16b uint64_t channels; // + 8b == 16b
} cell; } cell;
#define CELL_WIDEASIAN_MASK 0x8000000080000000ull
#define CELL_FGDEFAULT_MASK 0x4000000000000000ull
#define CELL_FG_MASK 0x00ffffff00000000ull
#define CELL_BGDEFAULT_MASK 0x0000000040000000ull
#define CELL_BG_MASK 0x0000000000ffffffull
#define CELL_ALPHA_MASK 0x0000000030000000ull
#define CELL_ALPHA_SHIFT 28u
#define CELL_ALPHA_TRANS 3
#define CELL_ALPHA_OPAQUE 0
``` ```
`cell`s must be initialized with `CELL_TRIVIAL_INITIALIZER` or `cell_init()` `cell`s must be initialized with `CELL_TRIVIAL_INITIALIZER` or `cell_init()`
@ -1104,6 +1156,27 @@ cell_set_bg_rgb(cell* cl, int r, int g, int b){
return channels_set_bg_rgb(&cl->channels, r, g, b); return channels_set_bg_rgb(&cl->channels, r, g, b);
} }
// Same, but with rgb assembled into a channel (i.e. lower 24 bits).
static inline int
cell_set_fg(cell* c, uint32_t channel){
return channels_set_fg(&c->channels, channel);
}
static inline int
cell_set_bg(cell* c, uint32_t channel){
return channels_set_bg(&c->channels, channel);
}
static inline int
cell_set_fg_alpha(cell* c, int alpha){
return channels_set_fg_alpha(&c->channels, alpha);
}
static inline int
cell_set_bg_alpha(cell* c, int alpha){
return channels_set_bg_alpha(&c->channels, alpha);
}
// Is the foreground using the "default foreground color"? // Is the foreground using the "default foreground color"?
static inline bool static inline bool
cell_fg_default_p(const cell* cl){ cell_fg_default_p(const cell* cl){
@ -1117,6 +1190,19 @@ static inline bool
cell_bg_default_p(const cell* cl){ cell_bg_default_p(const cell* cl){
return channels_bg_default_p(cl->channels); return channels_bg_default_p(cl->channels);
} }
// Use the default color for the foreground.
static inline void
cell_set_fg_default(cell* c){
channels_set_fg_default(&c->channels);
}
// Use the default color for the background.
static inline void
cell_set_bg_default(cell* c){
channels_set_bg_default(&c->channels);
}
``` ```
### Multimedia ### Multimedia
@ -1217,19 +1303,29 @@ channel_set_rgb(unsigned* channel, int r, int g, int b){
return 0; return 0;
} }
// Same, but provide an assembled, packed 24 bits of rgb.
static inline int
channel_set(unsigned* channel, unsigned rgb){
if(rgb > 0xffffffu){
return -1;
}
*channel = (*channel & ~CELL_BG_MASK) | CELL_BGDEFAULT_MASK | rgb;
return 0;
}
// Extract the 2-bit alpha component from a 32-bit channel. // Extract the 2-bit alpha component from a 32-bit channel.
static inline unsigned static inline unsigned
channel_get_alpha(unsigned channel){ channel_get_alpha(unsigned channel){
return (channel & CELL_BGALPHA_MASK) >> 28u; return (channel & CELL_ALPHA_MASK) >> CELL_ALPHA_SHIFT;
} }
// Set the 2-bit alpha component of the 32-bit channel. // Set the 2-bit alpha component of the 32-bit channel.
static inline int static inline int
channel_set_alpha(unsigned* channel, int alpha){ channel_set_alpha(unsigned* channel, int alpha){
if(alpha < 0 || alpha > 3){ if(alpha < CELL_ALPHA_OPAQUE || alpha > CELL_ALPHA_TRANS){
return -1; return -1;
} }
*channel = (alpha << 28u) | (*channel & ~CELL_BGALPHA_MASK); *channel = (alpha << CELL_ALPHA_SHIFT) | (*channel & ~CELL_ALPHA_MASK);
return 0; return 0;
} }
@ -1317,6 +1413,27 @@ channels_set_bg_rgb(uint64_t* channels, int r, int g, int b){
return 0; return 0;
} }
// Same, but set an assembled 24 bits of rgb at once.
static inline int
channels_set_fg(uint64_t* channels, unsigned rgb){
unsigned channel = channels_get_fchannel(*channels);
if(channel_set(&channel, rgb) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
static inline int
channels_set_bg(uint64_t* channels, unsigned rgb){
unsigned channel = channels_get_bchannel(*channels);
if(channel_set(&channel, rgb) < 0){
return -1;
}
*channels = (*channels & 0xffffffff00000000llu) | channel;
return 0;
}
// Set the 2-bit alpha component of the foreground channel. // Set the 2-bit alpha component of the foreground channel.
static inline int static inline int
channels_set_fg_alpha(uint64_t* channels, int alpha){ channels_set_fg_alpha(uint64_t* channels, int alpha){
@ -1375,6 +1492,13 @@ channels_set_bg_default(uint64_t* channels){
### Perf ### Perf
Rendering performance can be very roughly categorized as inversely proportional
to the product of:
* color changes across the rendered screen,
* planar depth before an opaque glyph and background are locked in,
* number of UTF-8 bytes composing the rendered glyphs, and
* screen geometry
notcurses tracks statistics across its operation, and a snapshot can be notcurses tracks statistics across its operation, and a snapshot can be
acquired using the `notcurses_stats()` function. This function cannot fail. acquired using the `notcurses_stats()` function. This function cannot fail.
@ -1414,11 +1538,6 @@ cursor is updated based on the width of the output. Along the way, notcurses
attempts to minimize total amount of data written by eliding unnecessary color attempts to minimize total amount of data written by eliding unnecessary color
and style specifications, and moving the cursor over large unchanged areas. and style specifications, and moving the cursor over large unchanged areas.
The worst case input frame (in terms of output size) is one whose colors change
from coordinate to coordinate, uses multiple combining characters within each
grapheme cluster, and has a large geometry. Peculiarities of the terminal
make it impossible to comment more meaningfully regarding delay.
Using the "default color" as only one of the foreground or background requires Using the "default color" as only one of the foreground or background requires
emitting the `op` escape followed by the appropriate escape for changing the emitting the `op` escape followed by the appropriate escape for changing the
fore- or background (since `op` changes both at once). If you're printing full fore- or background (since `op` changes both at once). If you're printing full

View File

@ -311,13 +311,13 @@ API int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
// the standard plane. // the standard plane.
API int ncplane_destroy(struct ncplane* ncp); API int ncplane_destroy(struct ncplane* ncp);
// Set the ncplane's background cell to this cell. It will be rendered anywhere // Set the ncplane's default cell to this cell. If defined, it will be rendered
// that the ncplane's gcluster is 0. The default background is all zeroes. // anywhere that the ncplane's gcluster is 0. Erasing the ncplane does not
// Erasing the ncplane does not eliminate the background. // reset the default cell; this function must instead be called with a zero c.
API int ncplane_set_background(struct ncplane* ncp, const cell* c); API int ncplane_set_default(struct ncplane* ncp, const cell* c);
// Extract the ncplane's background cell into 'c'. // Extract the ncplane's default cell into 'c'.
API int ncplane_background(struct ncplane* ncp, cell* c); API int ncplane_default(struct ncplane* ncp, cell* c);
// Move this plane relative to the standard plane. It is an error to attempt to // Move this plane relative to the standard plane. It is an error to attempt to
// move the standard plane. // move the standard plane.
@ -588,16 +588,19 @@ ncplane_box_sized(struct ncplane* n, const cell* ul, const cell* ur,
// Erase every cell in the ncplane, resetting all attributes to normal, all // 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 // colors to the default color, and all cells to undrawn. All cells associated
// with this ncplane is invalidated, and must not be used after the call. // with this ncplane is invalidated, and must not be used after the call,
// excluding the default cell.
API void ncplane_erase(struct ncplane* n); API void ncplane_erase(struct ncplane* n);
#define CELL_WIDEASIAN_MASK 0x8000000000000000ull #define CELL_WIDEASIAN_MASK 0x8000000080000000ull
#define CELL_FGDEFAULT_MASK 0x4000000000000000ull #define CELL_FGDEFAULT_MASK 0x4000000000000000ull
#define CELL_FGALPHA_MASK 0x3000000000000000ull
#define CELL_FG_MASK 0x00ffffff00000000ull #define CELL_FG_MASK 0x00ffffff00000000ull
#define CELL_BGDEFAULT_MASK 0x0000000040000000ull #define CELL_BGDEFAULT_MASK 0x0000000040000000ull
#define CELL_BGALPHA_MASK 0x0000000030000000ull
#define CELL_BG_MASK 0x0000000000ffffffull #define CELL_BG_MASK 0x0000000000ffffffull
#define CELL_ALPHA_MASK 0x0000000030000000ull
#define CELL_ALPHA_SHIFT 28u
#define CELL_ALPHA_TRANS 3
#define CELL_ALPHA_OPAQUE 0
// These lowest-level functions manipulate a 64-bit channel encoding directly. // These lowest-level functions manipulate a 64-bit channel encoding directly.
// Users will typically manipulate ncplane and cell channels through those APIs, // Users will typically manipulate ncplane and cell channels through those APIs,
@ -647,19 +650,29 @@ channel_set_rgb(unsigned* channel, int r, int g, int b){
return 0; return 0;
} }
// Same, but provide an assembled, packed 24 bits of rgb.
static inline int
channel_set(unsigned* channel, unsigned rgb){
if(rgb > 0xffffffu){
return -1;
}
*channel = (*channel & ~CELL_BG_MASK) | CELL_BGDEFAULT_MASK | rgb;
return 0;
}
// Extract the 2-bit alpha component from a 32-bit channel. // Extract the 2-bit alpha component from a 32-bit channel.
static inline unsigned static inline unsigned
channel_get_alpha(unsigned channel){ channel_get_alpha(unsigned channel){
return (channel & CELL_BGALPHA_MASK) >> 28u; return (channel & CELL_ALPHA_MASK) >> CELL_ALPHA_SHIFT;
} }
// Set the 2-bit alpha component of the 32-bit channel. // Set the 2-bit alpha component of the 32-bit channel.
static inline int static inline int
channel_set_alpha(unsigned* channel, int alpha){ channel_set_alpha(unsigned* channel, int alpha){
if(alpha < 0 || alpha > 3){ if(alpha < CELL_ALPHA_OPAQUE || alpha > CELL_ALPHA_TRANS){
return -1; return -1;
} }
*channel = (alpha << 28u) | (*channel & ~CELL_BGALPHA_MASK); *channel = (alpha << CELL_ALPHA_SHIFT) | (*channel & ~CELL_ALPHA_MASK);
return 0; return 0;
} }
@ -747,6 +760,27 @@ channels_set_bg_rgb(uint64_t* channels, int r, int g, int b){
return 0; return 0;
} }
// Same, but set an assembled 24 bits of rgb at once.
static inline int
channels_set_fg(uint64_t* channels, unsigned rgb){
unsigned channel = channels_get_fchannel(*channels);
if(channel_set(&channel, rgb) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
static inline int
channels_set_bg(uint64_t* channels, unsigned rgb){
unsigned channel = channels_get_bchannel(*channels);
if(channel_set(&channel, rgb) < 0){
return -1;
}
*channels = (*channels & 0xffffffff00000000llu) | channel;
return 0;
}
// Set the 2-bit alpha component of the foreground channel. // Set the 2-bit alpha component of the foreground channel.
static inline int static inline int
channels_set_fg_alpha(uint64_t* channels, int alpha){ channels_set_fg_alpha(uint64_t* channels, int alpha){
@ -863,6 +897,17 @@ cell_set_bg_rgb(cell* cl, int r, int g, int b){
return channels_set_bg_rgb(&cl->channels, r, g, b); return channels_set_bg_rgb(&cl->channels, r, g, b);
} }
// Same, but with rgb assembled into a channel (i.e. lower 24 bits).
static inline int
cell_set_fg(cell* c, uint32_t channel){
return channels_set_fg(&c->channels, channel);
}
static inline int
cell_set_bg(cell* c, uint32_t channel){
return channels_set_bg(&c->channels, channel);
}
// Is the foreground using the "default foreground color"? // Is the foreground using the "default foreground color"?
static inline bool static inline bool
cell_fg_default_p(const cell* cl){ cell_fg_default_p(const cell* cl){
@ -938,7 +983,7 @@ ncplane_get_bg_rgb(const struct ncplane* n, unsigned* r, unsigned* g, unsigned*
API int ncplane_set_fg_rgb(struct ncplane* n, int r, int g, int b); API int ncplane_set_fg_rgb(struct ncplane* n, int r, int g, int b);
API int ncplane_set_bg_rgb(struct ncplane* n, int r, int g, int b); API int ncplane_set_bg_rgb(struct ncplane* n, int r, int g, int b);
// Same, but with rgb assembled into a channel (i.e. lower 32 bits). // Same, but with rgb assembled into a channel (i.e. lower 24 bits).
API void ncplane_set_fg(struct ncplane* n, uint32_t channel); API void ncplane_set_fg(struct ncplane* n, uint32_t channel);
API void ncplane_set_bg(struct ncplane* n, uint32_t channel); API void ncplane_set_bg(struct ncplane* n, uint32_t channel);
@ -946,6 +991,10 @@ API void ncplane_set_bg(struct ncplane* n, uint32_t channel);
API void ncplane_set_fg_default(struct ncplane* n); API void ncplane_set_fg_default(struct ncplane* n);
API void ncplane_set_bg_default(struct ncplane* n); API void ncplane_set_bg_default(struct ncplane* n);
// Set the alpha parameters for ncplane 'n'.
API int ncplane_set_fg_alpha(struct ncplane* n, int alpha);
API int ncplane_set_bg_alpha(struct ncplane* n, int alpha);
// Set the specified style bits for the ncplane 'n', whether they're actively // Set the specified style bits for the ncplane 'n', whether they're actively
// supported or not. // supported or not.
API void ncplane_styles_set(struct ncplane* n, unsigned stylebits); API void ncplane_styles_set(struct ncplane* n, unsigned stylebits);
@ -1043,13 +1092,13 @@ cell_styles_off(cell* c, unsigned stylebits){
c->attrword &= ~((stylebits & 0xffff) << CELL_STYLE_SHIFT); c->attrword &= ~((stylebits & 0xffff) << CELL_STYLE_SHIFT);
} }
// use the default color for the foreground // Use the default color for the foreground.
static inline void static inline void
cell_set_fg_default(cell* c){ cell_set_fg_default(cell* c){
channels_set_fg_default(&c->channels); channels_set_fg_default(&c->channels);
} }
// use the default color for the background // Use the default color for the background.
static inline void static inline void
cell_set_bg_default(cell* c){ cell_set_bg_default(cell* c){
channels_set_bg_default(&c->channels); channels_set_bg_default(&c->channels);
@ -1061,7 +1110,7 @@ cell_set_fg_alpha(cell* c, int alpha){
} }
static inline int static inline int
cell_set_bg_alpha(cell *c, int alpha){ cell_set_bg_alpha(cell* c, int alpha){
return channels_set_bg_alpha(&c->channels, alpha); return channels_set_bg_alpha(&c->channels, alpha);
} }

View File

@ -107,8 +107,8 @@ static const char luigi3[] = "0000001111100000"
static int static int
draw_luigi(struct ncplane* n, const char* sprite){ draw_luigi(struct ncplane* n, const char* sprite){
cell bgc = CELL_TRIVIAL_INITIALIZER; cell bgc = CELL_TRIVIAL_INITIALIZER;
cell_set_bg_alpha(&bgc, 3); cell_set_bg_alpha(&bgc, CELL_ALPHA_TRANS);
ncplane_set_background(n, &bgc); ncplane_set_default(n, &bgc);
cell_release(n, &bgc); cell_release(n, &bgc);
size_t s; size_t s;
int sbytes; int sbytes;

View File

@ -19,14 +19,15 @@ outro_message(struct notcurses* nc, int* rows, int* cols){
if(on == NULL){ if(on == NULL){
return NULL; return NULL;
} }
cell bgcell = CELL_TRIVIAL_INITIALIZER; cell bgcell = CELL_SIMPLE_INITIALIZER(' ');
channels_set_bg_rgb(&bgcell.channels, 0x58, 0x36, 0x58); channels_set_bg_rgb(&bgcell.channels, 0x58, 0x36, 0x58);
ncplane_set_background(on, &bgcell); if(ncplane_set_default(on, &bgcell) < 0){
return NULL;
}
ncplane_dim_yx(on, rows, cols); ncplane_dim_yx(on, rows, cols);
int ybase = 0; int ybase = 0;
// bevel the upper corners // bevel the upper corners
uint64_t channel = 0; if(ncplane_set_bg_alpha(on, CELL_ALPHA_TRANS)){
if(channels_set_bg_alpha(&channel, 3)){
return NULL; return NULL;
} }
if(ncplane_cursor_move_yx(on, ybase, 0)){ if(ncplane_cursor_move_yx(on, ybase, 0)){
@ -60,6 +61,9 @@ outro_message(struct notcurses* nc, int* rows, int* cols){
if(ncplane_set_bg_rgb(on, 0, 180, 180)){ if(ncplane_set_bg_rgb(on, 0, 180, 180)){
return NULL; return NULL;
} }
if(ncplane_set_bg_alpha(on, CELL_ALPHA_OPAQUE)){ // FIXME use intermediate
return NULL;
}
if(ncplane_putstr_aligned(on, ++ybase, str0, NCALIGN_CENTER) < 0){ if(ncplane_putstr_aligned(on, ++ybase, str0, NCALIGN_CENTER) < 0){
return NULL; return NULL;
} }

View File

@ -270,7 +270,7 @@ panelreel_demo_core(struct notcurses* nc, int efd, tabletctx** tctxs){
cell_set_fg_rgb(&popts.tabletattr, 19, 161, 14); cell_set_fg_rgb(&popts.tabletattr, 19, 161, 14);
cell_set_fg_rgb(&popts.borderattr, 136, 23, 152); cell_set_fg_rgb(&popts.borderattr, 136, 23, 152);
cell_set_bg_rgb(&popts.borderattr, 0, 0, 0); cell_set_bg_rgb(&popts.borderattr, 0, 0, 0);
if(channels_set_bg_alpha(&popts.bgchannel, 3)){ if(channels_set_bg_alpha(&popts.bgchannel, CELL_ALPHA_TRANS)){
return NULL; return NULL;
} }
struct ncplane* w = notcurses_stdplane(nc); struct ncplane* w = notcurses_stdplane(nc);

View File

@ -119,7 +119,7 @@ fill_chunk(struct ncplane* n, int idx){
cell_init(&style); cell_init(&style);
cell_set_fg_rgb(&style, r, g, b); cell_set_fg_rgb(&style, r, g, b);
cell_prime(n, &style, "", 0, channels); cell_prime(n, &style, "", 0, channels);
ncplane_set_background(n, &style); ncplane_set_default(n, &style);
cell_release(n, &style); cell_release(n, &style);
return 0; return 0;
} }

View File

@ -23,10 +23,16 @@ draw_block(struct ncplane* nn, uint32_t blockstart){
cell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER; cell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
cell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER; cell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
cells_rounded_box(nn, 0, 0, &ul, &ur, &ll, &lr, &hl, &vl); cells_rounded_box(nn, 0, 0, &ul, &ur, &ll, &lr, &hl, &vl);
cell_set_bg_alpha(&ul, 3); cell_set_bg_alpha(&ul, CELL_ALPHA_TRANS);
cell_set_bg_alpha(&ur, 3); cell_set_bg_alpha(&ur, CELL_ALPHA_TRANS);
cell_set_bg_alpha(&ll, 3); cell_set_bg_alpha(&ll, CELL_ALPHA_TRANS);
cell_set_bg_alpha(&lr, 3); cell_set_bg_alpha(&lr, CELL_ALPHA_TRANS);
cell_set_fg_rgb(&ll, 255, 255, 255);
cell_set_fg_rgb(&lr, 255, 255, 255);
cell_set_fg_rgb(&ul, 255, 255, 255);
cell_set_fg_rgb(&ur, 255, 255, 255);
cell_set_fg_rgb(&hl, 255, 255, 255);
cell_set_fg_rgb(&vl, 255, 255, 255);
cell_set_bg_rgb(&hl, 0, 0, 0); cell_set_bg_rgb(&hl, 0, 0, 0);
cell_set_bg_rgb(&vl, 0, 0, 0); cell_set_bg_rgb(&vl, 0, 0, 0);
if(ncplane_box_sized(nn, &ul, &ur, &ll, &lr, &hl, &vl, if(ncplane_box_sized(nn, &ul, &ur, &ll, &lr, &hl, &vl,

View File

@ -283,9 +283,8 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
int bytes_out, int egs_out, int cols_out){ int bytes_out, int egs_out, int cols_out){
cell c = CELL_TRIVIAL_INITIALIZER; cell c = CELL_TRIVIAL_INITIALIZER;
cell_load(n, &c, " "); cell_load(n, &c, " ");
cell_set_fg_alpha(&c, 3); cell_set_bg_alpha(&c, CELL_ALPHA_TRANS);
cell_set_bg_alpha(&c, 3); ncplane_set_default(n, &c);
ncplane_set_background(n, &c);
cell_release(n, &c); cell_release(n, &c);
uint64_t channels = 0; uint64_t channels = 0;
ncplane_set_fg_rgb(n, 64, 128, 240); ncplane_set_fg_rgb(n, 64, 128, 240);

View File

@ -50,7 +50,7 @@ typedef struct ncplane {
uint64_t channels; // works the same way as cells uint64_t channels; // works the same way as cells
uint32_t attrword; // same deal as in a cell uint32_t attrword; // same deal as in a cell
void* userptr; // slot for the user to stick some opaque pointer void* userptr; // slot for the user to stick some opaque pointer
cell background; // cell written anywhere that fb[i].gcluster == 0 cell defcell; // cell written anywhere that fb[i].gcluster == 0
struct notcurses* nc; // notcurses object of which we are a part struct notcurses* nc; // notcurses object of which we are a part
} ncplane; } ncplane;

View File

@ -347,7 +347,7 @@ ncplane_create(notcurses* nc, int rows, int cols, int yoff, int xoff){
p->z = nc->top; p->z = nc->top;
nc->top = p; nc->top = p;
p->nc = nc; p->nc = nc;
cell_init(&p->background); cell_init(&p->defcell);
nc->stats.fbbytes += fbsize; nc->stats.fbbytes += fbsize;
return p; return p;
} }
@ -773,6 +773,8 @@ int notcurses_stop(notcurses* nc){
int ret = 0; int ret = 0;
if(nc){ if(nc){
drop_signals(nc); drop_signals(nc);
// FIXME these can fail if we stop in the middle of a rendering operation.
// turn the fd back to blocking, perhaps?
if(nc->rmcup && term_emit("rmcup", nc->rmcup, nc->ttyfp, true)){ if(nc->rmcup && term_emit("rmcup", nc->rmcup, nc->ttyfp, true)){
ret = -1; ret = -1;
} }
@ -851,20 +853,28 @@ int ncplane_set_fg_rgb(ncplane* n, int r, int g, int b){
return channels_set_fg_rgb(&n->channels, r, g, b); return channels_set_fg_rgb(&n->channels, r, g, b);
} }
void ncplane_set_fg(ncplane* n, uint32_t halfchannel){ void ncplane_set_fg(ncplane* n, uint32_t channel){
n->channels = ((uint64_t)halfchannel << 32ul) | (n->channels & 0xffffffffull); n->channels = ((uint64_t)channel << 32ul) | (n->channels & 0xffffffffull);
} }
void ncplane_set_bg(ncplane* n, uint32_t halfchannel){ void ncplane_set_bg(ncplane* n, uint32_t channel){
n->channels = (n->channels & 0xffffffff00000000ull) | halfchannel; n->channels = (n->channels & 0xffffffff00000000ull) | channel;
} }
int ncplane_set_background(ncplane* ncp, const cell* c){ int ncplane_set_fg_alpha(ncplane* n, int alpha){
return cell_duplicate(ncp, &ncp->background, c); return channels_set_fg_alpha(&n->channels, alpha);
} }
int ncplane_background(ncplane* ncp, cell* c){ int ncplane_set_bg_alpha(ncplane *n, int alpha){
return cell_duplicate(ncp, c, &ncp->background); return channels_set_bg_alpha(&n->channels, alpha);
}
int ncplane_set_default(ncplane* ncp, const cell* c){
return cell_duplicate(ncp, &ncp->defcell, c);
}
int ncplane_default(ncplane* ncp, cell* c){
return cell_duplicate(ncp, c, &ncp->defcell);
} }
// 3 for foreground, 4 for background, ugh FIXME // 3 for foreground, 4 for background, ugh FIXME
@ -1052,10 +1062,26 @@ term_setstyles(const notcurses* nc, FILE* out, uint32_t* curattr, const cell* c,
return ret; return ret;
} }
// find the topmost cell for this coordinate // Find the topmost cell for this coordinate by walking down the z-buffer,
static const cell* // looking for an intersecting ncplane. Once we've found one, check it for
visible_cell(int y, int x, ncplane** retp){ // transparency in either the back- or foreground. If the alpha channel is
ncplane* p = *retp; // active, keep descending and blending until we hit opacity, or bedrock. We
// recurse to find opacity, and blend the result into what we have. The
// 'findfore' and 'findback' bools control our recursion--there's no point in
// going further down when a color is locked in, so don't (for instance) recurse
// further when we have a transparent foreground and opaque background atop an
// opaque foreground and transparent background. The cell we ultimately return
// (a const ref to 'c') is backed by '*retp' via rawdog copy; the caller must
// not call cell_release() upon it, nor use it beyond the scope of the render.
//
// So, as we go down, we find planes which can have impact on the result. Once
// we've locked the result in (base case), write the deep values we have to 'c'.
// Then, as we come back up, blend them as appropriate. The actual glyph is
// whichever one occurs at the top with a non-transparent α (α < 3). To effect
// tail recursion, though, we instead write first, and then recurse, blending
// as we descend. α <= 0 is opaque. α >= 3 is fully transparent.
static ncplane*
dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha){
while(p){ while(p){
// where in the plane this coordinate would be, based off absy/absx. the // where in the plane this coordinate would be, based off absy/absx. the
// true origin is 0,0, so abs=2,2 means coordinate 3,3 would be 1,1, while // true origin is 0,0, so abs=2,2 means coordinate 3,3 would be 1,1, while
@ -1064,33 +1090,52 @@ visible_cell(int y, int x, ncplane** retp){
poffy = y - p->absy; poffy = y - p->absy;
poffx = x - p->absx; poffx = x - p->absx;
if(poffy < p->leny && poffy >= 0){ if(poffy < p->leny && poffy >= 0){
if(poffx < p->lenx && poffx >= 0){ if(poffx < p->lenx && poffx >= 0){ // p is valid for this y, x
*retp = p;
const cell* vis = &p->fb[fbcellidx(p, poffy, poffx)]; const cell* vis = &p->fb[fbcellidx(p, poffy, poffx)];
// if we never loaded any content into the cell (or obliterated it by // if we never loaded any content into the cell (or obliterated it by
// writing in a zero), use the plane's background cell. // writing in a zero), use the plane's background cell.
if(vis->gcluster == 0){ if(vis->gcluster == 0){
vis = &p->background; vis = &p->defcell;
} }
// FIXME do this more rigorously, PoC bool lockedglyph = false;
if(cell_get_fg_alpha(vis) || cell_get_bg_alpha(vis)){ int nalpha;
*retp = p->z; if(falpha > 0 && (nalpha = cell_get_fg_alpha(vis)) < CELL_ALPHA_TRANS){
const cell* trans = visible_cell(y, x, retp); if(c->gcluster == 0){ // never write fully trans glyphs, never replace
if(trans){ if( (c->gcluster = vis->gcluster) ){ // index copy only
vis = trans; lockedglyph = true; // must return this ncplane for this glyph
}else{ c->attrword = vis->attrword;
*retp = p; cell_set_fg(c, cell_get_fg(vis)); // FIXME blend it in
falpha -= (CELL_ALPHA_TRANS - nalpha); // FIXME blend it in
}
} }
} }
return vis; if(balpha > 0 && (nalpha = cell_get_bg_alpha(vis)) < CELL_ALPHA_TRANS){
cell_set_bg(c, cell_get_bg(vis)); // FIXME blend it in
balpha -= (CELL_ALPHA_TRANS - nalpha);
}
if((falpha > 0 || balpha > 0) && p->z){ // we must go further!
ncplane* cand = dig_visible_cell(c, y, x, p->z, falpha, balpha);
if(!lockedglyph && cand){
p = cand;
}
}
return p;
} }
} }
p = p->z; p = p->z;
} }
// should never happen for valid y, x thanks to the stdscreen // should never happen for valid y, x thanks to the stdplane. you fucked up!
return NULL; return NULL;
} }
static inline ncplane*
visible_cell(cell* c, int y, int x, ncplane* n){
cell_init(c);
return dig_visible_cell(c, y, x, n, CELL_ALPHA_TRANS, CELL_ALPHA_TRANS);
}
// Call with c->gcluster == 3, falpha == 3, balpha == 0, *retp == topplane.
// 'n' ends up above 'above' // 'n' ends up above 'above'
int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){ int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){
ncplane** an = find_above_ncplane(n); ncplane** an = find_above_ncplane(n);
@ -1215,21 +1260,20 @@ notcurses_render_internal(notcurses* nc){
term_emit("cup", tiparm(nc->cup, y, 0), out, false); term_emit("cup", tiparm(nc->cup, y, 0), out, false);
for(x = 0 ; x < nc->stdscr->lenx ; ++x){ for(x = 0 ; x < nc->stdscr->lenx ; ++x){
unsigned r, g, b, br, bg, bb; unsigned r, g, b, br, bg, bb;
ncplane* p = nc->top; ncplane* p;
const cell* c = visible_cell(y, x, &p); cell c; // no need to initialize
if(c == NULL){ p = visible_cell(&c, y, x, nc->top);
continue; // shrug? assert(p);
}
// don't try to print a wide character on the last column; it'll instead // don't try to print a wide character on the last column; it'll instead
// be printed on the next line. they probably shouldn't be admitted, but // be printed on the next line. they probably shouldn't be admitted, but
// we can end up with one due to a resize. // we can end up with one due to a resize.
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(c))){ if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
continue; continue;
} }
// set the style. this can change the color back to the default; if it // set the style. this can change the color back to the default; if it
// does, we need update our elision possibilities. // does, we need update our elision possibilities.
bool normalized; bool normalized;
term_setstyles(nc, out, &curattr, c, &normalized); term_setstyles(nc, out, &curattr, &c, &normalized);
if(normalized){ if(normalized){
defaultelidable = true; defaultelidable = true;
bgelidable = false; bgelidable = false;
@ -1241,7 +1285,7 @@ notcurses_render_internal(notcurses* nc){
// then a turnon for whichever aren't default. // then a turnon for whichever aren't default.
// we can elide the default set iff the previous used both defaults // we can elide the default set iff the previous used both defaults
if(cell_fg_default_p(c) || cell_bg_default_p(c)){ if(cell_fg_default_p(&c) || cell_bg_default_p(&c)){
if(!defaultelidable){ if(!defaultelidable){
++nc->stats.defaultemissions; ++nc->stats.defaultemissions;
term_emit("op", nc->op, out, false); term_emit("op", nc->op, out, false);
@ -1255,8 +1299,8 @@ notcurses_render_internal(notcurses* nc){
} }
// we can elide the foreground set iff the previous used fg and matched // we can elide the foreground set iff the previous used fg and matched
if(!cell_fg_default_p(c)){ if(!cell_fg_default_p(&c)){
cell_get_fg_rgb(c, &r, &g, &b); cell_get_fg_rgb(&c, &r, &g, &b);
if(fgelidable && lastr == r && lastg == g && lastb == b){ if(fgelidable && lastr == r && lastg == g && lastb == b){
++nc->stats.fgelisions; ++nc->stats.fgelisions;
}else{ }else{
@ -1267,8 +1311,8 @@ notcurses_render_internal(notcurses* nc){
lastr = r; lastg = g; lastb = b; lastr = r; lastg = g; lastb = b;
defaultelidable = false; defaultelidable = false;
} }
if(!cell_bg_default_p(c)){ if(!cell_bg_default_p(&c)){
cell_get_bg_rgb(c, &br, &bg, &bb); cell_get_bg_rgb(&c, &br, &bg, &bb);
if(bgelidable && lastbr == br && lastbg == bg && lastbb == bb){ if(bgelidable && lastbr == br && lastbg == bg && lastbb == bb){
++nc->stats.bgelisions; ++nc->stats.bgelisions;
}else{ }else{
@ -1279,11 +1323,11 @@ notcurses_render_internal(notcurses* nc){
lastbr = br; lastbg = bg; lastbb = bb; lastbr = br; lastbg = bg; lastbb = bb;
defaultelidable = false; defaultelidable = false;
} }
term_putc(out, p, c); // fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x %p\n", y, x, r, g, b, p);
if(cell_double_wide_p(c)){ term_putc(out, p, &c);
if(cell_double_wide_p(&c)){
++x; ++x;
} }
//fprintf(stderr, "[%02d/%02d]\n", y, x);
} }
} }
ret |= fclose(out); ret |= fclose(out);
@ -1378,10 +1422,11 @@ int cell_duplicate(ncplane* n, cell* targ, const cell* c){
} }
int ncplane_putc(ncplane* n, const cell* c){ int ncplane_putc(ncplane* n, const cell* c){
ncplane_lock(n);
if(cursor_invalid_p(n)){ if(cursor_invalid_p(n)){
ncplane_unlock(n);
return -1; return -1;
} }
ncplane_lock(n);
cell* targ = &n->fb[fbcellidx(n, n->y, n->x)]; cell* targ = &n->fb[fbcellidx(n, n->y, n->x)];
if(cell_duplicate(n, targ, c) < 0){ if(cell_duplicate(n, targ, c) < 0){
ncplane_unlock(n); ncplane_unlock(n);
@ -1830,11 +1875,11 @@ void ncplane_erase(ncplane* n){
// we must preserve the background, but a pure cell_duplicate() would be // we must preserve the background, but a pure cell_duplicate() would be
// wiped out by the egcpool_dump(). do a duplication (to get the attrword // wiped out by the egcpool_dump(). do a duplication (to get the attrword
// and channels), and then reload. // and channels), and then reload.
char* egc = cell_egc_copy(n, &n->background); char* egc = cell_egc_copy(n, &n->defcell);
memset(n->fb, 0, sizeof(*n->fb) * n->lenx * n->leny); memset(n->fb, 0, sizeof(*n->fb) * n->lenx * n->leny);
egcpool_dump(&n->pool); egcpool_dump(&n->pool);
egcpool_init(&n->pool); egcpool_init(&n->pool);
cell_load(n, &n->background, egc); cell_load(n, &n->defcell, egc);
free(egc); free(egc);
ncplane_unlock(n); ncplane_unlock(n);
} }
@ -1948,7 +1993,7 @@ alloc_ncplane_palette(ncplane* n, planepalette* pp){
} }
} }
// FIXME factor this duplication out // FIXME factor this duplication out
channels = n->background.channels; channels = n->defcell.channels;
pp->channels[y * pp->cols] = channels; pp->channels[y * pp->cols] = channels;
channels_get_fg_rgb(channels, &r, &g, &b); channels_get_fg_rgb(channels, &r, &g, &b);
if(r > pp->maxr){ if(r > pp->maxr){
@ -2098,20 +2143,20 @@ int ncplane_fadeout(ncplane* n, const struct timespec* ts){
} }
} }
} }
cell* c = &n->background; cell* c = &n->defcell;
if(!cell_fg_default_p(c)){ if(!cell_fg_default_p(c)){
channels_get_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b); channels_get_fg_rgb(pp.channels[pp.cols * y], &r, &g, &b);
r = r * (maxsteps - iter) / maxsteps; r = r * (maxsteps - iter) / maxsteps;
g = g * (maxsteps - iter) / maxsteps; g = g * (maxsteps - iter) / maxsteps;
b = b * (maxsteps - iter) / maxsteps; b = b * (maxsteps - iter) / maxsteps;
cell_set_fg_rgb(&n->background, r, g, b); cell_set_fg_rgb(&n->defcell, r, g, b);
} }
if(!cell_bg_default_p(c)){ if(!cell_bg_default_p(c)){
channels_get_bg_rgb(pp.channels[pp.cols * y], &br, &bg, &bb); channels_get_bg_rgb(pp.channels[pp.cols * y], &br, &bg, &bb);
br = br * (maxsteps - iter) / maxsteps; br = br * (maxsteps - iter) / maxsteps;
bg = bg * (maxsteps - iter) / maxsteps; bg = bg * (maxsteps - iter) / maxsteps;
bb = bb * (maxsteps - iter) / maxsteps; bb = bb * (maxsteps - iter) / maxsteps;
cell_set_bg_rgb(&n->background, br, bg, bb); cell_set_bg_rgb(&n->defcell, br, bg, bb);
} }
notcurses_render(n->nc); notcurses_render(n->nc);
uint64_t nextwake = (iter + 1) * nanosecs_step + startns; uint64_t nextwake = (iter + 1) * nanosecs_step + startns;

View File

@ -598,7 +598,7 @@ panelreel* panelreel_create(ncplane* w, const panelreel_options* popts, int efd)
} }
cell bgc = CELL_TRIVIAL_INITIALIZER; cell bgc = CELL_TRIVIAL_INITIALIZER;
bgc.channels = popts->bgchannel; bgc.channels = popts->bgchannel;
ncplane_set_background(pr->p, &bgc); ncplane_set_default(pr->p, &bgc);
cell_release(pr->p, &bgc); cell_release(pr->p, &bgc);
if(panelreel_redraw(pr)){ if(panelreel_redraw(pr)){
ncplane_destroy(pr->p); ncplane_destroy(pr->p);

View File

@ -41,7 +41,7 @@ TEST_F(CellTest, SetItalic) {
cell_styles_set(&c, CELL_STYLE_ITALIC); cell_styles_set(&c, CELL_STYLE_ITALIC);
ASSERT_EQ(1, cell_load(n_, &c, "i")); ASSERT_EQ(1, cell_load(n_, &c, "i"));
cell_set_fg_rgb(&c, 255, 255, 255); cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_background(n_, &c); ncplane_set_default(n_, &c);
cell_release(n_, &c); cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_)); EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_ITALIC); cell_styles_off(&c, CELL_STYLE_ITALIC);
@ -55,7 +55,7 @@ TEST_F(CellTest, SetBold) {
cell_styles_set(&c, CELL_STYLE_BOLD); cell_styles_set(&c, CELL_STYLE_BOLD);
ASSERT_EQ(1, cell_load(n_, &c, "b")); ASSERT_EQ(1, cell_load(n_, &c, "b"));
cell_set_fg_rgb(&c, 255, 255, 255); cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_background(n_, &c); ncplane_set_default(n_, &c);
cell_release(n_, &c); cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_)); EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_BOLD); cell_styles_off(&c, CELL_STYLE_BOLD);
@ -69,7 +69,7 @@ TEST_F(CellTest, SetUnderline) {
cell_styles_set(&c, CELL_STYLE_UNDERLINE); cell_styles_set(&c, CELL_STYLE_UNDERLINE);
ASSERT_EQ(1, cell_load(n_, &c, "u")); ASSERT_EQ(1, cell_load(n_, &c, "u"));
cell_set_fg_rgb(&c, 255, 255, 255); cell_set_fg_rgb(&c, 255, 255, 255);
ncplane_set_background(n_, &c); ncplane_set_default(n_, &c);
cell_release(n_, &c); cell_release(n_, &c);
EXPECT_EQ(0, notcurses_render(nc_)); EXPECT_EQ(0, notcurses_render(nc_));
cell_styles_off(&c, CELL_STYLE_UNDERLINE); cell_styles_off(&c, CELL_STYLE_UNDERLINE);