Repository: dankamongmen/notcurses (language: python)

Issue:
emit space+bg whenever fg==bg in rasterizer
While commenting `CELL_NOBACKGROUND_MASK`, I realized anytime the foreground is equal to the background in rasterization, we can just emit a space and a background color (or a full block and a foreground color, if that's better given the context). The only problem is that this would break copy-and-paste of the affected selection. I think this is most likely to happen when two halfblocks stack on one another, though, in which case it really ought be fine(?). Think about this a bit more; I don't love the breaking of copying.
emit space+bg whenever fg==bg in rasterizer
While commenting `CELL_NOBACKGROUND_MASK`, I realized anytime the foreground is equal to the background in rasterization, we can just emit a space and a background color (or a full block and a foreground color, if that's better given the context). The only problem is that this would break copy-and-paste of the affected selection. I think this is most likely to happen when two halfblocks stack on one another, though, in which case it really ought be fine(?). Think about this a bit more; I don't love the breaking of copying.
emit space+bg whenever fg==bg in rasterizer
While commenting `CELL_NOBACKGROUND_MASK`, I realized anytime the foreground is equal to the background in rasterization, we can just emit a space and a background color (or a full block and a foreground color, if that's better given the context). The only problem is that this would break copy-and-paste of the affected selection. I think this is most likely to happen when two halfblocks stack on one another, though, in which case it really ought be fine(?). Think about this a bit more; I don't love the breaking of copying.
emit space+bg whenever fg==bg in rasterizer
While commenting `CELL_NOBACKGROUND_MASK`, I realized anytime the foreground is equal to the background in rasterization, we can just emit a space and a background color (or a full block and a foreground color, if that's better given the context). The only problem is that this would break copy-and-paste of the affected selection. I think this is most likely to happen when two halfblocks stack on one another, though, in which case it really ought be fine(?). Think about this a bit more; I don't love the breaking of copying.


Discussion / hints:
technical note: this optimization can only be applied when neither the fg nor bg is using default colors (as they could otherwise be different, and indeed almost certainly are).
I think the way to do this is

* lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
* put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
* set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
* set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
* if `noforeground` is set, don't emit foreground color
* if `noforeground` is set, use `calcbg` rather than our bg
* at `term_puts()`, if `noforeground` is set, emit a space

does that work?
> I think the way to do this is
> 
> * lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
> * put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
> * set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
> * set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
> * if `noforeground` is set, don't emit foreground color
> * if `noforeground` is set, use `calcbg` rather than our bg
> * at `term_puts()`, if `noforeground` is set, emit a space
> 
> does that work?

also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
if it's palettes, who cares, just emit it, it's short as hell. save the 12b rgb escape.
> also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?

ahhh, nope, we're extracting the first byte and then using `iscntrl()`, *not* `iswcntrl()`. so it is indeed broken, but in a different way, heh.
> > also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
> 
> ahhh, nope, we're extracting the first byte and then using `iscntrl()`, _not_ `iswcntrl()`. so it is indeed broken, but in a different way, heh.

i think we want to do this in the output layer (`ncplane_putc()` essentially), not the rasterizer. one, we don't want to recheck every render. two, we don't want to check e.g. directly-blitted cells at all.
i'm reading over my notes from February and can't follow a goddamn thing
so let's keep this simple:

if both are RGB,
and both are the *same* RGB,
then ignore the foreground,
ensure the background is what we want,
and emit a space.
technical note: this optimization can only be applied when neither the fg nor bg is using default colors (as they could otherwise be different, and indeed almost certainly are).
I think the way to do this is

* lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
* put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
* set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
* set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
* if `noforeground` is set, don't emit foreground color
* if `noforeground` is set, use `calcbg` rather than our bg
* at `term_puts()`, if `noforeground` is set, emit a space

does that work?
> I think the way to do this is
> 
> * lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
> * put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
> * set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
> * set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
> * if `noforeground` is set, don't emit foreground color
> * if `noforeground` is set, use `calcbg` rather than our bg
> * at `term_puts()`, if `noforeground` is set, emit a space
> 
> does that work?

also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
if it's palettes, who cares, just emit it, it's short as hell. save the 12b rgb escape.
> also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?

ahhh, nope, we're extracting the first byte and then using `iscntrl()`, *not* `iswcntrl()`. so it is indeed broken, but in a different way, heh.
> > also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
> 
> ahhh, nope, we're extracting the first byte and then using `iscntrl()`, _not_ `iswcntrl()`. so it is indeed broken, but in a different way, heh.

i think we want to do this in the output layer (`ncplane_putc()` essentially), not the rasterizer. one, we don't want to recheck every render. two, we don't want to check e.g. directly-blitted cells at all.
i'm reading over my notes from February and can't follow a goddamn thing
so let's keep this simple:

if both are RGB,
and both are the *same* RGB,
then ignore the foreground,
ensure the background is what we want,
and emit a space.
technical note: this optimization can only be applied when neither the fg nor bg is using default colors (as they could otherwise be different, and indeed almost certainly are).
I think the way to do this is

* lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
* put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
* set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
* set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
* if `noforeground` is set, don't emit foreground color
* if `noforeground` is set, use `calcbg` rather than our bg
* at `term_puts()`, if `noforeground` is set, emit a space

does that work?
> I think the way to do this is
> 
> * lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
> * put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
> * set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
> * set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
> * if `noforeground` is set, don't emit foreground color
> * if `noforeground` is set, use `calcbg` rather than our bg
> * at `term_puts()`, if `noforeground` is set, emit a space
> 
> does that work?

also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
if it's palettes, who cares, just emit it, it's short as hell. save the 12b rgb escape.
> also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?

ahhh, nope, we're extracting the first byte and then using `iscntrl()`, *not* `iswcntrl()`. so it is indeed broken, but in a different way, heh.
> > also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
> 
> ahhh, nope, we're extracting the first byte and then using `iscntrl()`, _not_ `iswcntrl()`. so it is indeed broken, but in a different way, heh.

i think we want to do this in the output layer (`ncplane_putc()` essentially), not the rasterizer. one, we don't want to recheck every render. two, we don't want to check e.g. directly-blitted cells at all.
i'm reading over my notes from February and can't follow a goddamn thing
so let's keep this simple:

if both are RGB,
and both are the *same* RGB,
then ignore the foreground,
ensure the background is what we want,
and emit a space.
technical note: this optimization can only be applied when neither the fg nor bg is using default colors (as they could otherwise be different, and indeed almost certainly are).
I think the way to do this is

* lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
* put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
* set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
* set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
* if `noforeground` is set, don't emit foreground color
* if `noforeground` is set, use `calcbg` rather than our bg
* at `term_puts()`, if `noforeground` is set, emit a space

does that work?
> I think the way to do this is
> 
> * lift the `c->gcluster == 0 || iswcntrl(c->gcluster)` check out of `term_puts()`
> * put it into the top of `notcurses_rasterize_inner()`'s innermost loop, and add a whitespace check
> * set new `noforeground` bool if any of the above are true, set new `calcbg` to their background
> * set `noforeground` if fgrgb equals bgrgb or fgpal equals bgpal, set `calcbg` to our background
> * if `noforeground` is set, don't emit foreground color
> * if `noforeground` is set, use `calcbg` rather than our bg
> * at `term_puts()`, if `noforeground` is set, emit a space
> 
> does that work?

also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
if it's palettes, who cares, just emit it, it's short as hell. save the 12b rgb escape.
> also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?

ahhh, nope, we're extracting the first byte and then using `iscntrl()`, *not* `iswcntrl()`. so it is indeed broken, but in a different way, heh.
> > also, it's important to note that `iswspace()` and `iswctrl()` want a `wint_t`, and thus presumably native byte-endian UTF-32, whereas we've got little-endian UTF-8 in `c->gcluster`. so i think the `iswctrl()` test has always been broken?
> 
> ahhh, nope, we're extracting the first byte and then using `iscntrl()`, _not_ `iswcntrl()`. so it is indeed broken, but in a different way, heh.

i think we want to do this in the output layer (`ncplane_putc()` essentially), not the rasterizer. one, we don't want to recheck every render. two, we don't want to check e.g. directly-blitted cells at all.
i'm reading over my notes from February and can't follow a goddamn thing
so let's keep this simple:

if both are RGB,
and both are the *same* RGB,
then ignore the foreground,
ensure the background is what we want,
and emit a space.

Fix patch:
diff --git a/NEWS.md b/NEWS.md
index 10d424ecb0..e286e0fba2 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -32,6 +32,8 @@ rearrangements of Notcurses.
   * `ncplayer` now defaults to pixel blitting.
   * `NCKEY_SIGNAL` is no longer a synonym for `NCKEY_RESIZE`, but instead
     indicates receipt of `SIGCONT`.
+  * `CELL_TRIVIAL_INITIALIZER`, `CELL_CHAR_INITIALIZER`, and
+    `CELL_INITIALIZER` are all now prefixed with `NC`.
   * A new resize callback, `ncplane_resize_placewithin()`, has been added.
 
 * 2.4.9 (2021-11-11)
diff --git a/include/ncpp/Cell.hh b/include/ncpp/Cell.hh
index bc3165f782..121fc6a00b 100644
--- a/include/ncpp/Cell.hh
+++ b/include/ncpp/Cell.hh
@@ -30,13 +30,13 @@ namespace ncpp
 		explicit Cell (uint32_t c, NotCurses *ncinst = nullptr) noexcept
 			: Root (ncinst)
 		{
-			_cell = CELL_CHAR_INITIALIZER (c);
+			_cell = NCCELL_CHAR_INITIALIZER (c);
 		}
 
 		explicit Cell (uint32_t c, uint16_t a, uint64_t chan, NotCurses *ncinst = nullptr) noexcept
 			: Root (ncinst)
 		{
-			_cell = CELL_INITIALIZER (c, a, chan);
+			_cell = NCCELL_INITIALIZER (c, a, chan);
 		}
 
 		operator nccell* () noexcept
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 5b78035214..ba8159c666 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -652,14 +652,14 @@ typedef struct nccell {
 // protect against such misuse here. problems *will* ensue. similarly, do not
 // set channel flags other than colors/alpha. we assign non-printing glyphs
 // a width of 1 to match utf8_egc_len()'s behavior for whitespace/NUL.
-#define CELL_INITIALIZER(c, s, chan) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
+#define NCCELL_INITIALIZER(c, s, chan) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
   .width = (uint8_t)((wcwidth(c) < 0 || !c) ? 1 : wcwidth(c)), .stylemask = (s), .channels = (chan), }
 // python fails on #define CELL_CHAR_INITIALIZER(c) CELL_INITIALIZER(c, 0, 0)
-#define CELL_CHAR_INITIALIZER(c) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
+#define NCCELL_CHAR_INITIALIZER(c) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
   .width = (uint8_t)((wcwidth(c) < 0 || !c) ? 1 : wcwidth(c)), .stylemask = 0, .channels = 0, }
 // python fails on #define CELL_TRIVIAL_INITIALIZER CELL_CHAR_INITIALIZER(0)
-#define CELL_TRIVIAL_INITIALIZER { .gcluster = 0, .gcluster_backstop = 0,\
-                                   .width = 1, .stylemask = 0, .channels = 0, }
+#define NCCELL_TRIVIAL_INITIALIZER { .gcluster = 0, .gcluster_backstop = 0,\
+                                     .width = 1, .stylemask = 0, .channels = 0, }
 
 static inline void
 nccell_init(nccell* c){
@@ -1929,7 +1929,7 @@ ncplane_putc(struct ncplane* n, const nccell* c){
 // This works whether the underlying char is signed or unsigned.
 static inline int
 ncplane_putchar_yx(struct ncplane* n, int y, int x, char c){
-  nccell ce = CELL_INITIALIZER((uint32_t)c, ncplane_styles(n), ncplane_channels(n));
+  nccell ce = NCCELL_INITIALIZER((uint32_t)c, ncplane_styles(n), ncplane_channels(n));
   return ncplane_putc_yx(n, y, x, &ce);
 }
 
@@ -2889,9 +2889,9 @@ static inline int
 ncplane_rounded_box(struct ncplane* n, uint16_t styles, uint64_t channels,
                     unsigned ystop, unsigned xstop, unsigned ctlword){
   int ret = 0;
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if((ret = nccells_rounded_box(n, styles, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
     ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
   }
@@ -2909,12 +2909,12 @@ ncplane_perimeter_rounded(struct ncplane* n, uint16_t stylemask,
   }
   unsigned dimy, dimx;
   ncplane_dim_yx(n, &dimy, &dimx);
-  nccell ul = CELL_TRIVIAL_INITIALIZER;
-  nccell ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER;
-  nccell lr = CELL_TRIVIAL_INITIALIZER;
-  nccell vl = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER;
+  nccell lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell vl = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER;
   if(nccells_rounded_box(n, stylemask, channels, &ul, &ur, &ll, &lr, &hl, &vl)){
     return -1;
   }
@@ -2938,9 +2938,9 @@ static inline int
 ncplane_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
                    unsigned ylen, unsigned xlen, unsigned ctlword){
   int ret = 0;
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if((ret = nccells_double_box(n, styles, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
     ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ylen, xlen, ctlword);
   }
@@ -2958,12 +2958,12 @@ ncplane_perimeter_double(struct ncplane* n, uint16_t stylemask,
   }
   unsigned dimy, dimx;
   ncplane_dim_yx(n, &dimy, &dimx);
-  nccell ul = CELL_TRIVIAL_INITIALIZER;
-  nccell ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER;
-  nccell lr = CELL_TRIVIAL_INITIALIZER;
-  nccell vl = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER;
+  nccell lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell vl = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER;
   if(nccells_double_box(n, stylemask, channels, &ul, &ur, &ll, &lr, &hl, &vl)){
     return -1;
   }
diff --git a/src/demo/box.c b/src/demo/box.c
index b018f25b29..856092faaf 100644
--- a/src/demo/box.c
+++ b/src/demo/box.c
@@ -171,9 +171,9 @@ int box_demo(struct notcurses* nc, uint64_t startns){
   ncchannels_set_bg_alpha(&transchan, NCALPHA_TRANSPARENT);
   ncchannels_set_fg_alpha(&transchan, NCALPHA_TRANSPARENT);
   ncplane_set_base(n, "", 0, transchan);
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ll = CELL_TRIVIAL_INITIALIZER;
-  nccell lr = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ll = NCCELL_TRIVIAL_INITIALIZER;
+  nccell lr = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if(nccells_double_box(n, 0, 0, &ul, &ur, &ll, &lr, &hl, &vl)){
     return -1;
   }
diff --git a/src/demo/chunli.c b/src/demo/chunli.c
index a916aab4fa..5c01091496 100644
--- a/src/demo/chunli.c
+++ b/src/demo/chunli.c
@@ -57,7 +57,7 @@ int chunli_demo(struct notcurses* nc, uint64_t startns){
   int ret;
   unsigned dimx, dimy;
   ncplane_dim_yx(notcurses_stdplane_const(nc), &dimy, &dimx);
-  nccell b = CELL_TRIVIAL_INITIALIZER;
+  nccell b = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_alpha(&b, NCALPHA_TRANSPARENT);
   nccell_set_bg_alpha(&b, NCALPHA_TRANSPARENT);
   if( (ret = chunli_draw(nc, "bmp", CHUNS, &b)) ){
diff --git a/src/demo/eagle.c b/src/demo/eagle.c
index b0488997f4..3c19bd3d21 100644
--- a/src/demo/eagle.c
+++ b/src/demo/eagle.c
@@ -119,7 +119,7 @@ zoom_map(struct notcurses* nc, const char* map, int* ret){
 
 static int
 draw_eagle(struct ncplane* n, const char* sprite){
-  nccell bgc = CELL_TRIVIAL_INITIALIZER;
+  nccell bgc = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_alpha(&bgc, NCALPHA_TRANSPARENT);
   nccell_set_bg_alpha(&bgc, NCALPHA_TRANSPARENT);
   ncplane_set_base_cell(n, &bgc);
diff --git a/src/demo/fission.c b/src/demo/fission.c
index d2c35a82e1..d7392f7354 100644
--- a/src/demo/fission.c
+++ b/src/demo/fission.c
@@ -161,7 +161,7 @@ int fission_demo(struct notcurses* nc, uint64_t startns){
             ncplane_resize_simple(n, newy, newx);
             continue;
           }
-          nccell c = CELL_TRIVIAL_INITIALIZER;
+          nccell c = NCCELL_TRIVIAL_INITIALIZER;
           uint16_t smask;
           uint64_t channels;
           char* egc = ncplane_at_yx(stdn, usey, usex, &smask, &channels);
diff --git a/src/demo/highcon.c b/src/demo/highcon.c
index 81a5bf06db..5c7ca6e088 100644
--- a/src/demo/highcon.c
+++ b/src/demo/highcon.c
@@ -75,7 +75,7 @@ int highcon_demo(struct notcurses* nc, uint64_t startns){
     return -1;
   }
   const char motto[] = " high contrast text is evaluated relative to the solved background";
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   unsigned total = 0, r = 0, g = 0, b = 0;
   for(int out = 0 ; out < totcells ; ++out){ // build up the initial screen
     scrcolors[out] = generate_next_color(&total, &r, &g, &b, STEP);
diff --git a/src/demo/hud.c b/src/demo/hud.c
index 00558ac05c..f3b9652951 100644
--- a/src/demo/hud.c
+++ b/src/demo/hud.c
@@ -124,7 +124,7 @@ debug_toggle(struct notcurses* nc){
   }
   fbuf_free(&f);
   for(unsigned y = 0 ; y < ncplane_dim_y(n) ; ++y){
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     nccell_set_fg_alpha(&c, NCALPHA_TRANSPARENT);
     nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT);
     ncplane_putc_yx(n, y, ncplane_dim_x(n) - 1, &c);
@@ -171,9 +171,9 @@ about_toggle(struct notcurses* nc){
     ncplane_printf_aligned(n, 4, NCALIGN_RIGHT, "restart Ctrl+R  ");
     ncplane_printf_aligned(n, 5, NCALIGN_CENTER, "q quit");
     ncplane_putstr_aligned(n, 7, NCALIGN_CENTER, "\u00a9 nick black <nickblack@linux.com>");
-    nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-    nccell lr = CELL_TRIVIAL_INITIALIZER, ll = CELL_TRIVIAL_INITIALIZER;
-    nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+    nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+    nccell lr = NCCELL_TRIVIAL_INITIALIZER, ll = NCCELL_TRIVIAL_INITIALIZER;
+    nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
     channels = 0;
     ncchannels_set_fg_rgb(&channels, 0xc020c0);
     ncchannels_set_bg_rgb(&channels, 0);
@@ -337,9 +337,9 @@ struct ncmenu* menu_create(struct notcurses* nc){
 static int
 hud_refresh(struct ncplane* n){
   ncplane_erase(n);
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell lr = CELL_TRIVIAL_INITIALIZER, ll = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell lr = NCCELL_TRIVIAL_INITIALIZER, ll = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if(nccells_rounded_box(n, NCSTYLE_NONE, 0, &ul, &ur, &ll, &lr, &hl, &vl)){
     return -1;
   }
@@ -382,7 +382,7 @@ hud_print_finished(elem* list){
       break;
     }
     if(hud){
-      nccell c = CELL_TRIVIAL_INITIALIZER;
+      nccell c = NCCELL_TRIVIAL_INITIALIZER;
       ncplane_base(hud, &c);
       ncplane_set_bg_rgb(hud, nccell_bg_rgb(&c));
       ncplane_set_bg_alpha(hud, NCALPHA_BLEND);
@@ -395,7 +395,7 @@ hud_print_finished(elem* list){
       char buf[NCPREFIXCOLUMNS + 2];
       ncnmetric(e->totalns, sizeof(buf), NANOSECS_IN_SEC, buf, 0, 1000, '\0');
       for(int x = 6 ; x < 14 - ncstrwidth(buf, NULL, NULL) ; ++x){
-        nccell ci = CELL_TRIVIAL_INITIALIZER;
+        nccell ci = NCCELL_TRIVIAL_INITIALIZER;
         ncplane_putc_yx(hud, 1, x, &ci);
       }
       if(ncplane_printf_yx(hud, line, 14 - ncstrwidth(buf, NULL, NULL), "%ss", buf) < 0){
@@ -599,7 +599,7 @@ int demo_render(struct notcurses* nc){
     }
     uint64_t ns = timespec_to_ns(&ts) - elems->startns;
     ++elems->frames;
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_base(hud, &c);
     ncplane_set_bg_rgb(hud, nccell_bg_rgb(&c));
     ncplane_set_bg_alpha(hud, NCALPHA_BLEND);
@@ -613,7 +613,7 @@ int demo_render(struct notcurses* nc){
     char buf[NCPREFIXCOLUMNS + 2];
     ncnmetric(ns, sizeof(buf), NANOSECS_IN_SEC, buf, 0, 1000, '\0');
     for(int x = 6 ; x < 14 - ncstrwidth(buf, NULL, NULL) ; ++x){
-      nccell ci = CELL_TRIVIAL_INITIALIZER;
+      nccell ci = NCCELL_TRIVIAL_INITIALIZER;
       ncplane_putc_yx(hud, 1, x, &ci);
     }
 //fprintf(stderr, "[%s] %zu %d\n", buf, strlen(buf), ncstrwidth(buf, NULL, NULL));
diff --git a/src/demo/intro.c b/src/demo/intro.c
index cbd19fc29a..af523d783d 100644
--- a/src/demo/intro.c
+++ b/src/demo/intro.c
@@ -104,12 +104,12 @@ int intro_demo(struct notcurses* nc, uint64_t startns){
   if(ncplane_gradient2x1(ncp, -1, -1, rows - 3, cols - 2, ccul, ccur, ccll, cclr) <= 0){
     return -1;
   }
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_bg_rgb8(&c, 0x20, 0x20, 0x20);
   ncplane_set_base_cell(ncp, &c);
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if(ncplane_cursor_move_yx(ncp, 1, 0)){
     return -1;
   }
diff --git a/src/demo/jungle.c b/src/demo/jungle.c
index bd3d8af276..e338c1c797 100644
--- a/src/demo/jungle.c
+++ b/src/demo/jungle.c
@@ -26609,8 +26609,8 @@ int jungle_demo(struct notcurses* nc, uint64_t startns){
   const int yiter = ORIGHEIGHT / (dimy - yoff) + !!(ORIGHEIGHT % dimy);
   const int xoff = (dimx - ORIGWIDTH / xiter) / 2;
   ncplane_erase(n);
-  nccell c = CELL_TRIVIAL_INITIALIZER;
-  nccell_load(n, &c, "\xe2\x96\x80"); // upper half block
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
+  nccell_load(n, &c, u8"\u2580"); // upper half block
   for(size_t y = 0 ; y < ORIGHEIGHT ; y += (yiter * 2)){
     unsigned targy = yoff + y / (yiter * 2);
     if(targy < dimy / 2){
@@ -26622,7 +26622,7 @@ int jungle_demo(struct notcurses* nc, uint64_t startns){
         int idx = y * ORIGWIDTH + x;
         int idx2 = (y + yiter) * ORIGWIDTH + x;
         if(nccell_set_fg_palindex(&c, buf[idx])){
-                return -1;
+          return -1;
         }
         if(y + yiter < ORIGHEIGHT){
           if(nccell_set_bg_palindex(&c, buf[idx2])){
@@ -26634,7 +26634,7 @@ int jungle_demo(struct notcurses* nc, uint64_t startns){
           }
         }
         if(ncplane_putc(n, &c) < 0){
-                return -1;
+          return -1;
         }
       }
     }
diff --git a/src/demo/mojibake.c b/src/demo/mojibake.c
index 82df98ab73..8e4c1bec55 100644
--- a/src/demo/mojibake.c
+++ b/src/demo/mojibake.c
@@ -3621,7 +3621,7 @@ makegroup(struct ncplane* title, int y, const char* emoji, const char* name){
   if(n == NULL){
     return NULL;
   }
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   y = 1;
   int x = 1;
   while(*emoji){
diff --git a/src/demo/normal.c b/src/demo/normal.c
index c792c32525..551103fe47 100644
--- a/src/demo/normal.c
+++ b/src/demo/normal.c
@@ -159,7 +159,7 @@ int normal_demo(struct notcurses* nc, uint64_t startns){
   int r = -1;
   struct ncplane* nstd = notcurses_stddim_yx(nc, &dy, &dx);
   ncplane_erase(nstd);
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_rgb8(&c, 0x0, 0x0, 0x0);
   nccell_set_bg_rgb8(&c, 0x0, 0x0, 0x0);
   ncplane_set_base_cell(nstd, &c);
diff --git a/src/demo/reel.c b/src/demo/reel.c
index 4cb561b690..f9cf4f6be6 100644
--- a/src/demo/reel.c
+++ b/src/demo/reel.c
@@ -62,7 +62,7 @@ kill_active_tablet(struct ncreel* pr, tabletctx** tctx){
 static int
 tabletdraw(struct ncplane* w, int maxy, tabletctx* tctx, unsigned rgb){
   char cchbuf[2];
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   int y;
   int maxx = ncplane_dim_x(w) - 1;
   if(maxy > tctx->lines){
diff --git a/src/demo/trans.c b/src/demo/trans.c
index ac1e41d660..cf15416ac4 100644
--- a/src/demo/trans.c
+++ b/src/demo/trans.c
@@ -39,7 +39,7 @@ legend(struct notcurses* nc, const char* msg){
   if(n == NULL){
     return NULL;
   }
-  nccell c = CELL_TRIVIAL_INITIALIZER;
+  nccell c = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_rgb8(&c, 0, 0, 0); // darken surrounding characters by half
   nccell_set_fg_alpha(&c, NCALPHA_BLEND);
   nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT); // don't touch background
@@ -120,7 +120,7 @@ slidepanel(struct notcurses* nc, struct ncplane* stdn){
   // no glyph, we should show underlying glyphs in the default colors. The
   // background default might be transparent, at the window level (i.e. a copy
   // of the underlying desktop).
-  nccell c = CELL_CHAR_INITIALIZER(' ');
+  nccell c = NCCELL_CHAR_INITIALIZER(' ');
   struct timespec cur;
   ncplane_set_base_cell(n, &c);
   clock_gettime(CLOCK_MONOTONIC, &cur);
diff --git a/src/demo/uniblock.c b/src/demo/uniblock.c
index 03c94ba3b3..848828bbfa 100644
--- a/src/demo/uniblock.c
+++ b/src/demo/uniblock.c
@@ -69,9 +69,9 @@ static int
 draw_block(struct ncplane* nn, uint32_t blockstart){
   unsigned dimx, dimy;
   ncplane_dim_yx(nn, &dimy, &dimx);
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   nccells_rounded_box(nn, 0, 0, &ul, &ur, &ll, &lr, &hl, &vl);
   nccell_set_bg_alpha(&ul, NCALPHA_TRANSPARENT);
   nccell_set_bg_alpha(&ur, NCALPHA_TRANSPARENT);
diff --git a/src/demo/whiteout.c b/src/demo/whiteout.c
index 93c3186cf5..755c9448d6 100644
--- a/src/demo/whiteout.c
+++ b/src/demo/whiteout.c
@@ -184,7 +184,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
   ncplane_putegc_yx(n, 4, 17, "┬", NULL);
   ncplane_putegc_yx(n, 5, 17, "│", NULL);
   ncplane_putegc_yx(n, 6, 17, "╰", NULL);
-  nccell hl = CELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER;
   nccell_load(n, &hl, "─");
   nccell_set_fg_rgb8(&hl, 255, 255, 255);
   nccell_set_bg_rgb8(&hl, 32, 64, 32);
diff --git a/src/fetch/main.c b/src/fetch/main.c
index d50aef25f6..f2d532faf1 100644
--- a/src/fetch/main.c
+++ b/src/fetch/main.c
@@ -535,7 +535,7 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi,
   ncplane_printf_aligned(infop, 3, NCALIGN_RIGHT, "Shell: %s ", fi->shell ? fi->shell : "n/a");
   if(notcurses_cantruecolor(nc)){
     ncplane_printf_aligned(infop, 4, NCALIGN_LEFT, " RGB TERM: %s", fi->term);
-    nccell c = CELL_CHAR_INITIALIZER('R');
+    nccell c = NCCELL_CHAR_INITIALIZER('R');
     nccell_set_styles(&c, NCSTYLE_BOLD);
     nccell_set_fg_rgb8(&c, 0xf0, 0xa0, 0xa0);
     ncplane_putc_yx(infop, 4, 1, &c);
@@ -554,9 +554,9 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi,
   ncplane_printf_aligned(infop, 5, NCALIGN_CENTER, "%s (%d cores)",
                          fi->cpu_model ? fi->cpu_model : fallback_cpuinfo(),
                          fi->core_count);
-  nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-  nccell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
-  nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
   if(nccells_rounded_box(infop, 0, 0, &ul, &ur, &ll, &lr, &hl, &vl)){
     return -1;
   }
diff --git a/src/lib/fill.c b/src/lib/fill.c
index a07a18ec70..9cc8eeae7b 100644
--- a/src/lib/fill.c
+++ b/src/lib/fill.c
@@ -365,8 +365,8 @@ rotate_output(ncplane* dst, uint32_t tchan, uint32_t bchan){
 //   lower?) having the two channels as fore- and background.
 static int
 rotate_2x1_cw(ncplane* src, ncplane* dst, int srcy, int srcx, int dsty, int dstx){
-  nccell c1 = CELL_TRIVIAL_INITIALIZER;
-  nccell c2 = CELL_TRIVIAL_INITIALIZER;
+  nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
+  nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
   if(ncplane_at_yx_cell(src, srcy, srcx, &c1) < 0){
     return -1;
   }
@@ -406,8 +406,8 @@ rotate_2x1_cw(ncplane* src, ncplane* dst, int srcy, int srcx, int dsty, int dstx
 
 static int
 rotate_2x1_ccw(ncplane* src, ncplane* dst, int srcy, int srcx, int dsty, int dstx){
-  nccell c1 = CELL_TRIVIAL_INITIALIZER;
-  nccell c2 = CELL_TRIVIAL_INITIALIZER;
+  nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
+  nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
   if(ncplane_at_yx_cell(src, srcy, srcx, &c1) < 0){
     return -1;
   }
diff --git a/src/lib/internal.h b/src/lib/internal.h
index bacb898adb..0dbed48231 100644
--- a/src/lib/internal.h
+++ b/src/lib/internal.h
@@ -330,6 +330,7 @@ typedef struct notcurses {
 
   // we keep a copy of the last rendered frame. this facilitates O(1)
   // notcurses_at_yx() and O(1) damage detection (at the cost of some memory).
+  // FIXME why isn't this just an ncplane rather than ~10 different members?
   nccell* lastframe;// last rasterized framebuffer, NULL until first raster
   // the last pile we rasterized. NULL until we've rasterized once. might
   // be invalid due to the pile being destroyed; you are only allowed to
@@ -1238,10 +1239,23 @@ box_corner_needs(unsigned ctlword){
 // True if the cell does not generate background pixels (i.e., the cell is a
 // solid or shaded block, or certain emoji).
 static inline bool
-cell_nobackground_p(const nccell* c){
+nccell_nobackground_p(const nccell* c){
+  // needs to match all four bits of NC_NOBACKGROUND_MASK, not just one
   return (c->channels & NC_NOBACKGROUND_MASK) == NC_NOBACKGROUND_MASK;
 }
 
+// True iff the foreground and background color are both RGB, and equal.
+static inline bool
+nccell_rgbequal_p(const nccell* c){
+  if(nccell_fg_default_p(c) || nccell_fg_palindex_p(c)){
+    return false;
+  }
+  if(nccell_bg_default_p(c) || nccell_bg_palindex_p(c)){
+    return false;
+  }
+  return nccell_fg_rgb(c) == nccell_bg_rgb(c);
+}
+
 // Returns a number 0 <= n <= 15 representing the four quadrants, and which (if
 // any) are occupied due to blitting with a transparent background. The mapping
 // is {tl, tr, bl, br}.
diff --git a/src/lib/menu.c b/src/lib/menu.c
index 78d2cf8eb4..3ce8215903 100644
--- a/src/lib/menu.c
+++ b/src/lib/menu.c
@@ -302,7 +302,7 @@ write_header(ncmenu* ncm){
   if(ncplane_cursor_move_yx(ncm->ncp, ypos, 0)){
     return -1;
   }
-  nccell c = CELL_INITIALIZER(' ', 0, ncm->headerchannels);
+  nccell c = NCCELL_INITIALIZER(' ', 0, ncm->headerchannels);
   ncplane_set_styles(ncm->ncp, 0);
   if(ncplane_putc(ncm->ncp, &c) < 0){
     return -1;
@@ -335,7 +335,7 @@ write_header(ncmenu* ncm){
         return -1;
       }
       if(ncm->sections[i].shortcut_offset >= 0){
-        nccell cl = CELL_TRIVIAL_INITIALIZER;
+        nccell cl = NCCELL_TRIVIAL_INITIALIZER;
         if(ncplane_at_yx_cell(ncm->ncp, ypos, xoff + ncm->sections[i].shortcut_offset, &cl) < 0){
           return -1;
         }
@@ -420,7 +420,7 @@ ncmenu* ncmenu_create(ncplane* n, const ncmenu_options* opts){
           ret->sectionchannels = opts->sectionchannels;
           ret->disablechannels = ret->sectionchannels;
           ncchannels_set_fg_rgb(&ret->disablechannels, 0xdddddd);
-          nccell c = CELL_TRIVIAL_INITIALIZER;
+          nccell c = NCCELL_TRIVIAL_INITIALIZER;
           nccell_set_fg_alpha(&c, NCALPHA_TRANSPARENT);
           nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT);
           ncplane_set_base_cell(ret->ncp, &c);
@@ -519,7 +519,7 @@ int ncmenu_unroll(ncmenu* n, int sectionidx){
         }
       }
       if(sec->items[i].shortcut_offset >= 0){
-        nccell cl = CELL_TRIVIAL_INITIALIZER;
+        nccell cl = NCCELL_TRIVIAL_INITIALIZER;
         if(ncplane_at_yx_cell(n->ncp, ypos, xpos + 1 + sec->items[i].shortcut_offset, &cl) < 0){
           return -1;
         }
diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c
index c981ad45dc..a65741a311 100644
--- a/src/lib/notcurses.c
+++ b/src/lib/notcurses.c
@@ -1966,7 +1966,7 @@ int ncplane_hline_interp(ncplane* n, const nccell* c, unsigned len,
   int deltbg = bg2 - bg1;
   int deltbb = bb2 - bb1;
   unsigned ret;
-  nccell dupc = CELL_TRIVIAL_INITIALIZER;
+  nccell dupc = NCCELL_TRIVIAL_INITIALIZER;
   if(nccell_duplicate(n, &dupc, c) < 0){
     return -1;
   }
@@ -2024,7 +2024,7 @@ int ncplane_vline_interp(ncplane* n, const nccell* c, unsigned len,
   unsigned ypos, xpos;
   unsigned ret;
   ncplane_cursor_yx(n, &ypos, &xpos);
-  nccell dupc = CELL_TRIVIAL_INITIALIZER;
+  nccell dupc = NCCELL_TRIVIAL_INITIALIZER;
   if(nccell_duplicate(n, &dupc, c) < 0){
     return -1;
   }
@@ -3015,7 +3015,7 @@ char* ncplane_contents(ncplane* nc, int begy, int begx, unsigned leny, unsigned
   if(ret){
     for(unsigned y = ystart, targy = 0 ; y < ystart + leny ; ++y, targy += 2){
       for(unsigned x = xstart, targx = 0 ; x < xstart + lenx ; ++x, ++targx){
-        nccell ncl = CELL_TRIVIAL_INITIALIZER;
+        nccell ncl = NCCELL_TRIVIAL_INITIALIZER;
         // we need ncplane_at_yx_cell() here instead of ncplane_at_yx(),
         // because we should only have one copy of each wide EGC.
         int clen;
diff --git a/src/lib/render.c b/src/lib/render.c
index 5991f61888..7220476a14 100644
--- a/src/lib/render.c
+++ b/src/lib/render.c
@@ -480,14 +480,13 @@ postpaint_cell(const tinfo* ti, nccell* lastframe, int dimx,
       // we don't need to change it when under an opaque cell, because
       // that's always printed on top.
       if(!crender->s.p_beats_sprixel){
-        // leaving out SPRIXCELL_OPAQUE_KITTY here results in cruft
         if(state != SPRIXCELL_OPAQUE_SIXEL){
 //fprintf(stderr, "damaged due to opaque %d/%d\n", y, *x);
           crender->s.damaged = 1;
         }
       }
     }else{
-//fprintf(stderr, "damaged due to opaque else %d/%d\n", y, *x);
+//fprintf(stderr, "damaged due to lastframe disagree %d/%d\n", y, *x);
       crender->s.damaged = 1;
     }
     assert(!nccell_wide_right_p(targc));
@@ -631,8 +630,8 @@ int ncplane_mergedown_simple(ncplane* restrict src, ncplane* restrict dst){
   return ncplane_mergedown(src, dst, 0, 0, 0, 0, 0, 0);
 }
 
-// write the nccell's UTF-8 extended grapheme cluster to the provided FILE*.
-static int
+// write the nccell's UTF-8 extended grapheme cluster to the provided fbuf.
+static inline int
 term_putc(fbuf* f, const egcpool* e, const nccell* c){
   if(cell_simple_p(c)){
 //fprintf(stderr, "[%.4s] %08x\n", (const char*)&c->gcluster, c->gcluster); }
@@ -1138,7 +1137,7 @@ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){
       const int innerx = x - nc->margin_l;
       const size_t damageidx = innery * nc->lfdimx + innerx;
       unsigned r, g, b, br, bg, bb;
-      const nccell* srccell = &nc->lastframe[damageidx];
+      nccell* srccell = &nc->lastframe[damageidx];
       if(!rvec[damageidx].s.damaged){
         // no need to emit a cell; what we rendered appears to already be
         // here. no updates are performed to elision state nor lastframe.
@@ -1165,40 +1164,41 @@ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){
         //  * we are a partial glyph, and the previous was default on both, or
         //  * we are a no-foreground glyph, and the previous was default background, or
         //  * we are a no-background glyph, and the previous was default foreground
-        bool nobackground = cell_nobackground_p(srccell);
+        bool nobackground = nccell_nobackground_p(srccell);
+        bool rgbequal = nccell_rgbequal_p(srccell);
         if((nccell_fg_default_p(srccell)) || (!nobackground && nccell_bg_default_p(srccell))){
           if(raster_defaults(nc, nccell_fg_default_p(srccell),
                              !nobackground && nccell_bg_default_p(srccell), f)){
             return -1;
           }
         }
-        // if our cell has a non-default foreground, we can elide the
-        // non-default foreground set iff either:
-        //  * the previous was non-default, and matches what we have now, or
-        //  * we are a no-foreground glyph (iswspace() is true)
         if(nccell_fg_palindex_p(srccell)){ // palette-indexed foreground
           if(emit_fg_palindex(nc, f, srccell)){
             return -1;
           }
         }else if(!nccell_fg_default_p(srccell)){ // rgb foreground
+          // if our cell has a non-default foreground, we can elide the
+          // non-default foreground set iff either:
+          //  * the previous was non-default, and matches what we have now, or
+          //  * we are a no-foreground glyph (iswspace() is true) FIXME
           nccell_fg_rgb8(srccell, &r, &g, &b);
           if(nc->rstate.fgelidable && nc->rstate.lastr == r && nc->rstate.lastg == g && nc->rstate.lastb == b){
             ++nc->stats.s.fgelisions;
           }else{
-            if(term_fg_rgb8(&nc->tcache, f, r, g, b)){
-              return -1;
+            if(!rgbequal){ // if rgbequal, no need to set fg
+              if(term_fg_rgb8(&nc->tcache, f, r, g, b)){
+                return -1;
+              }
+              ++nc->stats.s.fgemissions;
+              nc->rstate.fgelidable = true;
+            }else{
+              r = nc->rstate.lastr; g = nc->rstate.lastg; b = nc->rstate.lastb;
             }
-            ++nc->stats.s.fgemissions;
-            nc->rstate.fgelidable = true;
           }
           nc->rstate.lastr = r; nc->rstate.lastg = g; nc->rstate.lastb = b;
           nc->rstate.fgdefelidable = false;
           nc->rstate.fgpalelidable = false;
         }
-        // if our cell has a non-default background, we can elide the
-        // non-default background set iff either:
-        //  * we do not use the background, because the cell is all-foreground,
-        //  * the previous was non-default, and matches what we have now, or
         if(nobackground){
           ++nc->stats.s.bgelisions;
         }else if(nccell_bg_palindex_p(srccell)){ // palette-indexed background
@@ -1206,6 +1206,10 @@ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){
             return -1;
           }
         }else if(!nccell_bg_default_p(srccell)){ // rgb background
+          // if our cell has a non-default background, we can elide the
+          // non-default background set iff either:
+          //  * we do not use the background, because the cell is all-foreground,
+          //  * the previous was non-default, and matches what we have now, or
           nccell_bg_rgb8(srccell, &br, &bg, &bb);
           if(nc->rstate.bgelidable && nc->rstate.lastbr == br && nc->rstate.lastbg == bg && nc->rstate.lastbb == bb){
             ++nc->stats.s.bgelisions;
@@ -1219,6 +1223,12 @@ rasterize_core(notcurses* nc, const ncpile* p, fbuf* f, unsigned phase){
           nc->rstate.lastbr = br; nc->rstate.lastbg = bg; nc->rstate.lastbb = bb;
           nc->rstate.bgdefelidable = false;
           nc->rstate.bgpalelidable = false;
+          // FIXME see above -- if we don't have to change the fg, but do need
+          // to change the bg, emit full block and no rgb at all
+          if(rgbequal){
+            // FIXME need one per column of original glyph
+            pool_load_direct(&nc->pool, srccell, " ", 1, 1);
+          }
         }
 //fprintf(stderr, "RAST %08x [%s] to %d/%d cols: %u %016" PRIx64 "\n", srccell->gcluster, pool_extended_gcluster(&nc->pool, srccell), y, x, srccell->width, srccell->channels);
         // this is used to invalidate the sprixel in the first text round,
diff --git a/src/lib/selector.c b/src/lib/selector.c
index 010dfb44d6..d39e0839a4 100644
--- a/src/lib/selector.c
+++ b/src/lib/selector.c
@@ -83,7 +83,7 @@ ncselector_body_width(const ncselector* n){
 static int
 ncselector_draw(ncselector* n){
   ncplane_erase(n->ncp);
-  nccell transchar = CELL_TRIVIAL_INITIALIZER;
+  nccell transchar = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_alpha(&transchar, NCALPHA_TRANSPARENT);
   nccell_set_bg_alpha(&transchar, NCALPHA_TRANSPARENT);
   // if we have a title, we'll draw a riser. the riser is two rows tall, and
@@ -163,7 +163,7 @@ ncselector_draw(ncselector* n){
   ++yoff;
   ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
   for(unsigned i = xoff + 1 ; i < dimx - 1 ; ++i){
-    nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+    nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
     ncplane_putc(n->ncp, &transc);
   }
   const int bodyoffset = dimx - bodywidth + 2;
@@ -187,7 +187,7 @@ ncselector_draw(ncselector* n){
     }
     ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
     for(int i = xoff + 1 ; i < (int)dimx - 1 ; ++i){
-      nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+      nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
       ncplane_putc(n->ncp, &transc);
     }
     n->ncp->channels = n->opchannels;
@@ -208,7 +208,7 @@ ncselector_draw(ncselector* n){
   // Bottom line of body (background and possibly down arrow)
   ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
   for(int i = xoff + 1 ; i < (int)dimx - 1 ; ++i){
-    nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+    nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
     ncplane_putc(n->ncp, &transc);
   }
   if(n->maxdisplay && n->maxdisplay < n->itemcount){
@@ -611,7 +611,7 @@ ncmultiselector_body_width(const ncmultiselector* n){
 static int
 ncmultiselector_draw(ncmultiselector* n){
   ncplane_erase(n->ncp);
-  nccell transchar = CELL_TRIVIAL_INITIALIZER;
+  nccell transchar = NCCELL_TRIVIAL_INITIALIZER;
   nccell_set_fg_alpha(&transchar, NCALPHA_TRANSPARENT);
   nccell_set_bg_alpha(&transchar, NCALPHA_TRANSPARENT);
   // if we have a title, we'll draw a riser. the riser is two rows tall, and
@@ -678,7 +678,7 @@ ncmultiselector_draw(ncmultiselector* n){
   ++yoff;
   ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
   for(unsigned i = xoff + 1 ; i < dimx - 1 ; ++i){
-    nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+    nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
     ncplane_putc(n->ncp, &transc);
   }
   const int bodyoffset = dimx - bodywidth + 2;
@@ -699,7 +699,7 @@ ncmultiselector_draw(ncmultiselector* n){
     }
     ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
     for(unsigned i = xoff + 1 ; i < dimx - 1 ; ++i){
-      nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+      nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
       ncplane_putc(n->ncp, &transc);
     }
     n->ncp->channels = n->descchannels;
@@ -729,7 +729,7 @@ ncmultiselector_draw(ncmultiselector* n){
   // Bottom line of body (background and possibly down arrow)
   ncplane_cursor_move_yx(n->ncp, yoff, xoff + 1);
   for(unsigned i = xoff + 1 ; i < dimx - 1 ; ++i){
-    nccell transc = CELL_TRIVIAL_INITIALIZER; // fall back to base cell
+    nccell transc = NCCELL_TRIVIAL_INITIALIZER; // fall back to base cell
     ncplane_putc(n->ncp, &transc);
   }
   if(n->maxdisplay && n->maxdisplay < n->itemcount){


Test patch:
diff --git a/src/tests/cell.cpp b/src/tests/cell.cpp
index af68fd7a4b..251ea8289c 100644
--- a/src/tests/cell.cpp
+++ b/src/tests/cell.cpp
@@ -14,11 +14,11 @@ TEST_CASE("Cell") {
   REQUIRE(nullptr != n_);
 
   SUBCASE("EGCs") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
-    CHECK(2 == nccell_load(n_, &c, "é"));
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
+    CHECK(2 == nccell_load(n_, &c, u8"é"));
     CHECK(1 == nccell_cols(&c));
     int cols;
-    CHECK(3 == nccell_load(n_, &c, "\x41\u0301"));
+    CHECK(3 == nccell_load(n_, &c, u8"\x41\u0301"));
     CHECK(1 == nccell_cols(&c));
     CHECK(4 == nccell_load(n_, &c, " ி"));
     cols = nccell_cols(&c);
@@ -29,48 +29,48 @@ TEST_CASE("Cell") {
     // musl+s390x (alpine) is reporting these EGCs to be 0 columns wide (they
     // ought be 1). not sure whether i've got a bug (s390x is big-endian), or
     // whether it does. just relaxed the tests for now FIXME.
-    CHECK(5 == nccell_load(n_, &c, "◌̈"));
+    CHECK(5 == nccell_load(n_, &c, u8"◌̈"));
     WARN(1 == nccell_cols(&c));
-    CHECK(9 == nccell_load(n_, &c, "นี้"));
+    CHECK(9 == nccell_load(n_, &c, u8"นี้"));
     WARN(1 == nccell_cols(&c));
 
     // type-3 woman playing water polo, 17 bytes (5 characters, 2 columns)
 #ifdef __linux__
-    CHECK(17 == nccell_load(n_, &c, "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f"));
+    CHECK(17 == nccell_load(n_, &c, u8"\U0001f93d\U0001f3fc\u200d\u2640\ufe0f"));
     WARN(2 == nccell_cols(&c));
     nccell_release(n_, &c);
 #endif
   }
 
   SUBCASE("Loadchar") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == nccell_load(n_, &c, " "));
     CHECK(cell_simple_p(&c));
     nccell_release(n_, &c);
   }
 
   SUBCASE("MultibyteWidth") {
-    CHECK(0 == ncstrwidth("", NULL, NULL));       // zero bytes, zero columns
-    CHECK(-1 == ncstrwidth("\x7", NULL, NULL));   // single byte, non-printable
-    CHECK(1 == ncstrwidth(" ", NULL, NULL));      // single byte, one column
-    CHECK(5 == ncstrwidth("abcde", NULL, NULL));  // single byte, one column
-    CHECK(1 == ncstrwidth("µ", NULL, NULL));      // two bytes, one column
-    CHECK(1 <= ncstrwidth("\U0001f982", NULL, NULL));     // four bytes, two columns
-    CHECK(3 <= ncstrwidth("平仮名", NULL, NULL)); // nine bytes, six columns
-    CHECK(1 == ncstrwidth("\U00012008", NULL, NULL)); // four bytes, 1 column
+    CHECK(0 == ncstrwidth(u8"", NULL, NULL));       // zero bytes, zero columns
+    CHECK(-1 == ncstrwidth(u8"\x7", NULL, NULL));   // single byte, non-printable
+    CHECK(1 == ncstrwidth(u8" ", NULL, NULL));      // single byte, one column
+    CHECK(5 == ncstrwidth(u8"abcde", NULL, NULL));  // single byte, one column
+    CHECK(1 == ncstrwidth(u8"µ", NULL, NULL));      // two bytes, one column
+    CHECK(1 <= ncstrwidth(u8"\U0001f982", NULL, NULL));     // four bytes, two columns
+    CHECK(3 <= ncstrwidth(u8"平仮名", NULL, NULL)); // nine bytes, six columns
+    CHECK(1 == ncstrwidth(u8"\U00012008", NULL, NULL)); // four bytes, 1 column
   }
 
   // test combining characters and ZWJs
   SUBCASE("MultiglyphWidth") {
-    CHECK(2 == ncstrwidth("\U0001F471", NULL, NULL));
+    CHECK(2 == ncstrwidth(u8"\U0001F471", NULL, NULL));
 #ifndef __APPLE__ // FIXME
-    CHECK(2 == ncstrwidth("\U0001F471\u200D", NULL, NULL));
-    CHECK(2 == ncstrwidth("\U0001F471\u200D\u2640", NULL, NULL));
+    CHECK(2 == ncstrwidth(u8"\U0001F471\u200D", NULL, NULL));
+    CHECK(2 == ncstrwidth(u8"\U0001F471\u200D\u2640", NULL, NULL));
 #endif
   }
 
   SUBCASE("SetItalic") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     unsigned dimy, dimx;
     notcurses_term_dim_yx(nc_, &dimy, &dimx);
     nccell_set_styles(&c, NCSTYLE_ITALIC);
@@ -83,7 +83,7 @@ TEST_CASE("Cell") {
   }
 
   SUBCASE("SetBold") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     unsigned dimy, dimx;
     notcurses_term_dim_yx(nc_, &dimy, &dimx);
     nccell_set_styles(&c, NCSTYLE_BOLD);
@@ -96,7 +96,7 @@ TEST_CASE("Cell") {
   }
 
   SUBCASE("SetUnderline") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     unsigned dimy, dimx;
     notcurses_term_dim_yx(nc_, &dimy, &dimx);
     nccell_set_styles(&c, NCSTYLE_UNDERLINE);
@@ -110,7 +110,7 @@ TEST_CASE("Cell") {
 
   /*SUBCASE("CellLoadTamil") {
     const char zerodeg[] = "\u0bb8\u0bc0\u0bb0\u0bc7\u0bb3\u0b95\u0bbf\u0b95\u0bbf\u0bb0\u0bbf";
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     size_t ulen = nccell_load(n_, &c, zerodeg);
     // First have U+0BB8 TAMIL LETTER SA U+0BC0 TAMIL VOWEL SIGN II
     // // e0 ae b8 e0 af 80
@@ -123,7 +123,7 @@ TEST_CASE("Cell") {
   }*/
 
   SUBCASE("CellSetFGAlpha"){
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 > nccell_set_fg_alpha(&c, -1));
     CHECK(0 > nccell_set_fg_alpha(&c, 4));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_OPAQUE));
@@ -137,7 +137,7 @@ TEST_CASE("Cell") {
   }
 
   SUBCASE("CellSetBGAlpha"){
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 > nccell_set_bg_alpha(&c, -1));
     CHECK(0 > nccell_set_bg_alpha(&c, 4));
     CHECK(0 == nccell_set_bg_alpha(&c, NCALPHA_OPAQUE));
@@ -151,7 +151,7 @@ TEST_CASE("Cell") {
 
   // white on a black background ought be unmolested for highcontrast
   SUBCASE("HighContrastWhiteOnBlackBackground"){
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     CHECK(0 == nccell_set_fg_rgb8(&c, 0xff, 0xff, 0xff));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_HIGHCONTRAST));
     CHECK(0 == nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT));
@@ -191,7 +191,7 @@ TEST_CASE("Cell") {
 
   // white on a white background ought be changed for highcontrast
   SUBCASE("HighContrastWhiteOnWhiteBackground"){
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     CHECK(0 == nccell_set_fg_rgb8(&c, 0xff, 0xff, 0xff));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_HIGHCONTRAST));
     CHECK(0 == nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT));
@@ -231,7 +231,7 @@ TEST_CASE("Cell") {
 
   // black on a black background must be changed for highcontrast
   SUBCASE("HighContrastBlackOnBlackBackground"){
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     CHECK(0 == nccell_set_fg_rgb8(&c, 0x0, 0x0, 0x0));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_HIGHCONTRAST));
     CHECK(0 == nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT));
@@ -271,7 +271,7 @@ TEST_CASE("Cell") {
 
   // black on a white background ought be unmolested for highcontrast
   SUBCASE("HighContrastBlackOnWhiteBackground"){
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     CHECK(0 == nccell_set_fg_rgb8(&c, 0x0, 0x0, 0x0));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_HIGHCONTRAST));
     CHECK(0 == nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT));
@@ -312,7 +312,7 @@ TEST_CASE("Cell") {
   // high contrast ought only be activated relevant to the background equal to
   // or below them, not above.
   SUBCASE("HighContrastBelowOnly"){
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     // top has a background of white
     CHECK(0 == nccell_set_bg_rgb8(&c, 0xff, 0xff, 0xff));
     CHECK(0 == nccell_set_fg_alpha(&c, NCALPHA_TRANSPARENT));
@@ -351,14 +351,14 @@ TEST_CASE("Cell") {
   }
 
   SUBCASE("CellLoadCharPrinting") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == nccell_load_char(n_, &c, '*'));
     CHECK(0 == strcmp(nccell_extended_gcluster(n_, &c), "*"));
   }
 
   // only space/newline is allowed from control char whitespace
   SUBCASE("CellLoadCharWhitespace") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(-1 == nccell_load_char(n_, &c, '\f'));
     CHECK(-1 == nccell_load_char(n_, &c, '\v'));
     CHECK(-1 == nccell_load_char(n_, &c, '\t'));
@@ -367,20 +367,40 @@ TEST_CASE("Cell") {
   }
 
   SUBCASE("CellLoadCharControl") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == nccell_load_char(n_, &c, '\0'));
     CHECK(-1 == nccell_load_char(n_, &c, 1));
     CHECK(-1 == nccell_load_char(n_, &c, '\b'));
   }
 
   SUBCASE("CellLoadEGC32") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == nccell_load_egc32(n_, &c, 0));
     CHECK(1 == nccell_load_egc32(n_, &c, 0x65));   // U+0061 LATIN SMALL LETTER A
     CHECK(2 == nccell_load_egc32(n_, &c, 0xb5c2)); // U+00B5 MICRO SIGN
     CHECK(4 == nccell_load_egc32(n_, &c, 0x82a69ff0)); // U+1F982 SCORPION
   }
 
+  // test the nccell_rgbequal_p() predicate. it ought return true only when
+  // both channels use RGB, and are equal.
+  SUBCASE("CellRGBEqual") {
+    nccell c = NCCELL_CHAR_INITIALIZER('x');
+    nccell_set_fg_default(&c);
+    nccell_set_bg_default(&c);
+    CHECK(0 == nccell_rgbequal_p(&c));
+    nccell_set_bg_palindex(&c, 0);
+    CHECK(nccell_bg_palindex_p(&c));
+    nccell_set_fg_palindex(&c, 0);
+    CHECK(nccell_fg_palindex_p(&c));
+    CHECK(0 == nccell_rgbequal_p(&c));
+    nccell_set_bg_rgb(&c, 0);
+    CHECK(0 == nccell_rgbequal_p(&c));
+    nccell_set_fg_rgb(&c, 0x1);
+    CHECK(0 == nccell_rgbequal_p(&c));
+    nccell_set_fg_rgb(&c, 0);
+    CHECK(nccell_rgbequal_p(&c));
+  }
+
   // common teardown
   CHECK(0 == notcurses_stop(nc_));
 }
diff --git a/src/tests/egcpool.cpp b/src/tests/egcpool.cpp
index 6ad158faec..b7889cb725 100644
--- a/src/tests/egcpool.cpp
+++ b/src/tests/egcpool.cpp
@@ -35,7 +35,7 @@ TEST_CASE("EGCpool") {
     const char* w1 = "\u00e0"; // (utf8: c3 a0)
     const char* w2 = "\u0061\u0300"; // (utf8: 61 cc 80)
     const char* w3 = "\u0061"; // (utf8: 61)
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(2 == nccell_load(n_, &c, w1));
     CHECK(1 == nccell_cols(&c));
     CHECK(3 == nccell_load(n_, &c, w2));
@@ -46,7 +46,7 @@ TEST_CASE("EGCpool") {
 
   SUBCASE("AddAndRemove") {
     const char* wstr = "\U0001242B"; // cuneiform numeric sign nine shar2
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     auto ulen = nccell_load(n_, &c, wstr);
     CHECK(1 == nccell_cols(&c)); // not considered wide, believe it or not
     REQUIRE(0 <= egcpool_stash(&pool_, wstr, ulen));
@@ -65,8 +65,8 @@ TEST_CASE("EGCpool") {
 
   SUBCASE("AddTwiceRemoveFirst") {
     const char* wstr = "\u8840"; // cjk unified ideograph, wide
-    nccell c1 = CELL_TRIVIAL_INITIALIZER;
-    nccell c2 = CELL_TRIVIAL_INITIALIZER;
+    nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
     auto u1 = nccell_load(n_, &c1, wstr); // bytes consumed
     auto u2 = nccell_load(n_, &c2, wstr);
     int o1 = egcpool_stash(&pool_, wstr, u1);
@@ -90,8 +90,8 @@ TEST_CASE("EGCpool") {
 
   SUBCASE("AddTwiceRemoveSecond") {
     const char* wstr = "\u8840"; // cjk unified ideograph, wide
-    nccell c1 = CELL_TRIVIAL_INITIALIZER;
-    nccell c2 = CELL_TRIVIAL_INITIALIZER;
+    nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
     auto u1 = nccell_load(n_, &c1, wstr); // bytes consumed
     auto u2 = nccell_load(n_, &c2, wstr);
     int o1 = egcpool_stash(&pool_, wstr, u1);
diff --git a/src/tests/erase.cpp b/src/tests/erase.cpp
index 121065bb87..72b6702ebb 100644
--- a/src/tests/erase.cpp
+++ b/src/tests/erase.cpp
@@ -12,7 +12,7 @@ TEST_CASE("Erase") {
   REQUIRE(n_);
 
   // fill the standard plane with 'x's
-  nccell nc = CELL_CHAR_INITIALIZER('x');
+  nccell nc = NCCELL_CHAR_INITIALIZER('x');
   CHECK(0 < ncplane_polyfill_yx(n_, 0, 0, &nc));
   nccell_release(n_, &nc);
   CHECK(0 == notcurses_render(nc_));
diff --git a/src/tests/fade.cpp b/src/tests/fade.cpp
index 8c3c86975c..e11d003565 100644
--- a/src/tests/fade.cpp
+++ b/src/tests/fade.cpp
@@ -40,7 +40,7 @@ TEST_CASE("Fade") {
   REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
   unsigned dimy, dimx;
   ncplane_dim_yx(n_, &dimy, &dimx);
-  nccell c = CELL_CHAR_INITIALIZER('*');
+  nccell c = NCCELL_CHAR_INITIALIZER('*');
   nccell_set_fg_rgb8(&c, 0xff, 0xff, 0xff);
   unsigned rgb = 0xffffffu;
   CHECK(!ncplane_set_scrolling(n_, true));
diff --git a/src/tests/fills.cpp b/src/tests/fills.cpp
index 94aebb986e..81b35bb65b 100644
--- a/src/tests/fills.cpp
+++ b/src/tests/fills.cpp
@@ -18,7 +18,7 @@ TEST_CASE("Fills") {
   SUBCASE("PolyfillNullGlyph") {
     unsigned dimx, dimy;
     ncplane_dim_yx(n_, &dimy, &dimx);
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 > ncplane_polyfill_yx(n_, dimy, dimx, &c));
   }
 
@@ -26,7 +26,7 @@ TEST_CASE("Fills") {
   SUBCASE("PolyfillOffplane") {
     unsigned dimx, dimy;
     ncplane_dim_yx(n_, &dimy, &dimx);
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     CHECK(0 > ncplane_polyfill_yx(n_, dimy, 0, &c));
     CHECK(0 > ncplane_polyfill_yx(n_, 0, dimx, &c));
     CHECK(0 > ncplane_polyfill_yx(n_, 0, -2, &c));
@@ -34,7 +34,7 @@ TEST_CASE("Fills") {
   }
 
   SUBCASE("PolyfillOnGlyph") {
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     struct ncplane_options nopts = {
       .y = 0,
       .x = 0,
@@ -61,13 +61,13 @@ TEST_CASE("Fills") {
   }
 
   SUBCASE("PolyfillStandardPlane") {
-    nccell c = CELL_CHAR_INITIALIZER('-');
+    nccell c = NCCELL_CHAR_INITIALIZER('-');
     CHECK(0 < ncplane_polyfill_yx(n_, 0, 0, &c));
     CHECK(0 == notcurses_render(nc_));
   }
 
   SUBCASE("PolyfillEmptyPlane") {
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     struct ncplane_options nopts = {
       .y = 0,
       .x = 0,
@@ -87,7 +87,7 @@ TEST_CASE("Fills") {
   }
 
   SUBCASE("PolyfillWalledPlane") {
-    nccell c = CELL_CHAR_INITIALIZER('+');
+    nccell c = NCCELL_CHAR_INITIALIZER('+');
     struct ncplane_options nopts = {
       .y = 0,
       .x = 0,
@@ -119,7 +119,7 @@ TEST_CASE("Fills") {
     unsigned dimy, dimx;
     ncplane_dim_yx(n_, &dimy, &dimx);
     REQUIRE(0 < ncplane_gradient(n_, -1, -1, dimy, dimx, "M", 0, c, c, c, c));
-    nccell cl = CELL_TRIVIAL_INITIALIZER;
+    nccell cl = NCCELL_TRIVIAL_INITIALIZER;
     uint64_t channels = 0;
     ncchannels_set_fg_rgb(&channels, 0x40f040);
     ncchannels_set_bg_rgb(&channels, 0x40f040);
@@ -149,7 +149,7 @@ TEST_CASE("Fills") {
     unsigned dimy, dimx;
     ncplane_dim_yx(n_, &dimy, &dimx);
     REQUIRE(0 < ncplane_gradient(n_, 0, 0, dimy, dimx, "V", 0, ul, ur, ll, lr));
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     uint64_t channels = 0;
     ncchannels_set_fg_rgb(&channels, 0x40f040);
     ncchannels_set_bg_rgb(&channels, 0x40f040);
@@ -248,10 +248,10 @@ TEST_CASE("Fills") {
     CHECK(0 == notcurses_render(nc_));
     // attr should change, but not the EGC/color
     CHECK(0 == ncplane_cursor_move_yx(n_, 0, 0));
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     nccell_on_styles(&c, NCSTYLE_BOLD);
     CHECK(0 < ncplane_format(n_, 0, 0, 0, 0, c.stylemask));
-    nccell d = CELL_TRIVIAL_INITIALIZER;
+    nccell d = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == ncplane_at_yx_cell(n_, 0, 0, &d));
     CHECK(d.stylemask == c.stylemask);
     CHECK(0x444444 == nccell_fg_rgb(&d));
@@ -271,7 +271,7 @@ TEST_CASE("Fills") {
     ncchannels_set_bg_rgb(&channels, 0);
     REQUIRE(0 < ncplane_stain(n_, 0, 0, 7, 7, channels, channels, channels, channels));
     CHECK(0 == notcurses_render(nc_));
-    nccell d = CELL_TRIVIAL_INITIALIZER;
+    nccell d = NCCELL_TRIVIAL_INITIALIZER;
     for(unsigned y = 0 ; y < 7 ; ++y){
       for(unsigned x = 0 ; x < 7 ; ++x){
         CHECK(1 == ncplane_at_yx_cell(n_, y, x, &d));
@@ -292,7 +292,7 @@ TEST_CASE("Fills") {
     ncchannels_set_bg_rgb(&channels, 0);
     REQUIRE(0 < ncplane_gradient(n_, 0, 0, 0, 0, "A", 0, channels, channels, channels, channels));
     CHECK(0 == notcurses_render(nc_));
-    nccell d = CELL_TRIVIAL_INITIALIZER;
+    nccell d = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == ncplane_at_yx_cell(n_, 0, 0, &d));
     CHECK(channels == d.channels);
     REQUIRE(cell_simple_p(&d));
@@ -312,7 +312,7 @@ TEST_CASE("Fills") {
     ncchannels_set_bg_rgb(&chan2, 0);
     CHECK(0 < ncplane_gradient(n_, 0, 0, 0, 3, "A", 0, chan1, chan2, chan1, chan2));
     CHECK(0 == notcurses_render(nc_));
-    nccell d = CELL_TRIVIAL_INITIALIZER;
+    nccell d = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == ncplane_at_yx_cell(n_, 0, 0, &d));
     CHECK(chan1 == d.channels);
     CHECK(cell_simple_p(&d));
@@ -366,8 +366,8 @@ TEST_CASE("Fills") {
     // make sure glyphs replace nulls
     CHECK(0 < ncplane_putstr(p1, "0123456789"));
     CHECK(0 == ncplane_mergedown_simple(p1, n_));
-    nccell cbase = CELL_TRIVIAL_INITIALIZER;
-    nccell cp = CELL_TRIVIAL_INITIALIZER;
+    nccell cbase = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cp = NCCELL_TRIVIAL_INITIALIZER;
     for(int i = 0 ; i < 10 ; ++i){
       CHECK(0 < ncplane_at_yx_cell(n_, 0, i, &cbase));
       CHECK(0 < ncplane_at_yx_cell(p1, 0, i, &cp));
@@ -411,8 +411,8 @@ TEST_CASE("Fills") {
     // make sure glyphs replace nulls
     CHECK(0 < ncplane_putstr(p1, "█▀▄▌▐🞵🞶🞷🞸🞹"));
     CHECK(0 == ncplane_mergedown_simple(p1, n_));
-    nccell cbase = CELL_TRIVIAL_INITIALIZER;
-    nccell cp = CELL_TRIVIAL_INITIALIZER;
+    nccell cbase = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cp = NCCELL_TRIVIAL_INITIALIZER;
     for(int i = 0 ; i < 10 ; ++i){
       CHECK(0 < ncplane_at_yx_cell(n_, 0, i, &cbase));
       CHECK(0 < ncplane_at_yx_cell(p1, 0, i, &cp));
@@ -425,7 +425,7 @@ TEST_CASE("Fills") {
     // make sure glyphs replace glyps
     CHECK(0 < ncplane_putstr(p3, "🞵🞶🞷🞸🞹█▀▄▌▐"));
     CHECK(0 == ncplane_mergedown_simple(p3, n_));
-    nccell c3 = CELL_TRIVIAL_INITIALIZER;
+    nccell c3 = NCCELL_TRIVIAL_INITIALIZER;
     for(int i = 0 ; i < 10 ; ++i){
       CHECK(0 < ncplane_at_yx_cell(n_, 0, i, &cbase));
       CHECK(0 < ncplane_at_yx_cell(p3, 0, i, &c3));
@@ -463,7 +463,7 @@ TEST_CASE("Fills") {
     };
     struct ncplane* p1 = ncplane_create(n_, &nopts);
     REQUIRE(p1);
-    nccell c1 = CELL_TRIVIAL_INITIALIZER;
+    nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < nccell_load(p1, &c1, "█"));
     CHECK(0 == nccell_set_bg_rgb(&c1, 0x00ff00));
     CHECK(0 == nccell_set_fg_rgb(&c1, 0x0000ff));
@@ -482,7 +482,7 @@ TEST_CASE("Fills") {
     };
     auto p2 = ncplane_create(n_, &n2opts);
     REQUIRE(p2);
-    nccell c2 = CELL_TRIVIAL_INITIALIZER;
+    nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < nccell_load(p2, &c2, "🞶"));
     CHECK(0 == nccell_set_bg_rgb(&c2, 0x00ffff));
     CHECK(0 == nccell_set_fg_rgb(&c2, 0xff00ff));
@@ -549,14 +549,14 @@ TEST_CASE("Fills") {
     CHECK(0 == notcurses_render(nc_));
     for(unsigned y = 0 ; y < DIMY ; ++y){
       for(unsigned x = 0 ; x < DIMX ; ++x){
-        nccell c1 = CELL_TRIVIAL_INITIALIZER;
+        nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
         CHECK(0 < ncplane_at_yx_cell(p1, y, x, &c1));
         if(y < 1 || y > 5 || x < 1 || x > 5){
           auto cstr = nccell_strdup(p1, &c1);
           CHECK(0 == strcmp(cstr, "▀"));
           free(cstr);
         }else{
-          nccell c2 = CELL_TRIVIAL_INITIALIZER;
+          nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
           CHECK(0 < ncplane_at_yx_cell(p2, y - 1, x - 1, &c2));
           CHECK(0 == nccellcmp(p1, &c1, p2, &c2));
           nccell_release(p2, &c2);
diff --git a/src/tests/geom.cpp b/src/tests/geom.cpp
index 1ee9f83ec2..f9d063f977 100644
--- a/src/tests/geom.cpp
+++ b/src/tests/geom.cpp
@@ -36,9 +36,9 @@ TEST_CASE("Geometry") {
       };
       auto n = ncplane_create(n_, &nopts);
       REQUIRE(n);
-      nccell tl = CELL_TRIVIAL_INITIALIZER, tr = CELL_TRIVIAL_INITIALIZER;
-      nccell bl = CELL_TRIVIAL_INITIALIZER, br = CELL_TRIVIAL_INITIALIZER;
-      nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+      nccell tl = NCCELL_TRIVIAL_INITIALIZER, tr = NCCELL_TRIVIAL_INITIALIZER;
+      nccell bl = NCCELL_TRIVIAL_INITIALIZER, br = NCCELL_TRIVIAL_INITIALIZER;
+      nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
       CHECK(0 == nccells_double_box(n, 0, 0, &tl, &tr, &bl, &br, &hl, &vl));
       CHECK(0 <= ncplane_perimeter(n, &tl, &tr, &bl, &br, &hl, &vl, 0));
       CHECK(0 == notcurses_render(nc_));
@@ -83,9 +83,9 @@ TEST_CASE("Geometry") {
       };
       auto n = ncplane_create(n_, &nopts);
       REQUIRE(n);
-      nccell tl = CELL_TRIVIAL_INITIALIZER, tr = CELL_TRIVIAL_INITIALIZER;
-      nccell bl = CELL_TRIVIAL_INITIALIZER, br = CELL_TRIVIAL_INITIALIZER;
-      nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+      nccell tl = NCCELL_TRIVIAL_INITIALIZER, tr = NCCELL_TRIVIAL_INITIALIZER;
+      nccell bl = NCCELL_TRIVIAL_INITIALIZER, br = NCCELL_TRIVIAL_INITIALIZER;
+      nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
       CHECK(0 == nccells_double_box(n, 0, 0, &tl, &tr, &bl, &br, &hl, &vl));
       CHECK(0 <= ncplane_perimeter(n, &tl, &tr, &bl, &br, &hl, &vl, 0));
       CHECK(0 == notcurses_render(nc_));
diff --git a/src/tests/palette.cpp b/src/tests/palette.cpp
index 9e8970fd8c..b9ce9ac420 100644
--- a/src/tests/palette.cpp
+++ b/src/tests/palette.cpp
@@ -40,7 +40,7 @@ TEST_CASE("Palette256") {
 
   // when we set a palette index, it ought change us from using default
   SUBCASE("FAttributes") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(nccell_fg_default_p(&c));
     nccell_set_fg_alpha(&c, NCALPHA_TRANSPARENT);
     CHECK(0 == nccell_set_fg_palindex(&c, 0x20));
@@ -51,7 +51,7 @@ TEST_CASE("Palette256") {
   }
 
   SUBCASE("BAttributes") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(nccell_bg_default_p(&c));
     nccell_set_bg_alpha(&c, NCALPHA_TRANSPARENT);
     CHECK(0 == nccell_set_bg_palindex(&c, 0x20));
@@ -63,13 +63,13 @@ TEST_CASE("Palette256") {
 
   // write it to an ncplane, and verify attributes via reflection
   SUBCASE("PutCAttrs") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(1 == nccell_load_char(n_, &c, 'X'));
     CHECK(0 == nccell_set_fg_palindex(&c, 0x20));
     CHECK(0 == nccell_set_bg_palindex(&c, 0x40));
     CHECK(1 == ncplane_putc_yx(n_, 0, 0, &c));
     nccell_release(n_, &c);
-    nccell r = CELL_TRIVIAL_INITIALIZER;
+    nccell r = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < ncplane_at_yx_cell(n_, 0, 0, &r));
     CHECK(nccell_fg_palindex_p(&r));
     CHECK(nccell_bg_palindex_p(&r));
@@ -81,7 +81,7 @@ TEST_CASE("Palette256") {
   }
 
   SUBCASE("RenderCAttrs") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     nccell_load_char(n_, &c, 'X');
     CHECK(0 == nccell_set_fg_palindex(&c, 0x20));
     CHECK(0 == nccell_set_bg_palindex(&c, 0x40));
@@ -90,7 +90,7 @@ TEST_CASE("Palette256") {
     CHECK(0 < ncplane_putc_yx(n_, 0, 0, &c));
     nccell_release(n_, &c);
     CHECK(0 == notcurses_render(nc_));
-    nccell r = CELL_TRIVIAL_INITIALIZER;
+    nccell r = NCCELL_TRIVIAL_INITIALIZER;
     auto egc = notcurses_at_yx(nc_, 0, 0, &r.stylemask, &r.channels);
     CHECK(nullptr != egc);
     free(egc);
diff --git a/src/tests/piles.cpp b/src/tests/piles.cpp
index 3bb771eba8..060e08b011 100644
--- a/src/tests/piles.cpp
+++ b/src/tests/piles.cpp
@@ -54,9 +54,9 @@ TEST_CASE("Piles") {
     CHECK(np == ncplane_parent_const(np));
     CHECK(1 == ncplane_y(np));
     CHECK(1 == ncplane_x(np));
-    nccell c = CELL_CHAR_INITIALIZER('X');
+    nccell c = NCCELL_CHAR_INITIALIZER('X');
     CHECK(0 < ncplane_polyfill_yx(np, 0, 0, &c));
-    nccell o = CELL_CHAR_INITIALIZER('O');
+    nccell o = NCCELL_CHAR_INITIALIZER('O');
     CHECK(0 < ncplane_polyfill_yx(n_, 0, 0, &o));
     CHECK(0 == ncpile_render(np));
     CHECK(0 == ncpile_render(n_));
@@ -91,9 +91,9 @@ TEST_CASE("Piles") {
     CHECK(np == ncplane_parent_const(np));
     CHECK(-1 == ncplane_y(np));
     CHECK(-1 == ncplane_x(np));
-    nccell c = CELL_CHAR_INITIALIZER('X');
+    nccell c = NCCELL_CHAR_INITIALIZER('X');
     CHECK(0 < ncplane_polyfill_yx(np, 0, 0, &c));
-    nccell o = CELL_CHAR_INITIALIZER('O');
+    nccell o = NCCELL_CHAR_INITIALIZER('O');
     CHECK(0 < ncplane_polyfill_yx(n_, 0, 0, &o));
     CHECK(0 == ncpile_render(np));
     CHECK(0 == ncpile_render(n_));
diff --git a/src/tests/plane.cpp b/src/tests/plane.cpp
index fe9c20ff46..14db1332f6 100644
--- a/src/tests/plane.cpp
+++ b/src/tests/plane.cpp
@@ -116,7 +116,7 @@ TEST_CASE("Plane") {
   // Verify we can emit a NUL character, and it advances the cursor after
   // wiping out whatever we printed it atop.
   SUBCASE("EmitNULCell") {
-    nccell c = CELL_CHAR_INITIALIZER('a');
+    nccell c = NCCELL_CHAR_INITIALIZER('a');
     CHECK(0 < ncplane_putc_yx(n_, 0, 0, &c));
     auto egc = ncplane_at_yx(n_, 0, 0, nullptr, nullptr);
     CHECK(0 == strcmp("a", egc));
@@ -126,7 +126,7 @@ TEST_CASE("Plane") {
     CHECK(0 == y);
     CHECK(1 == x);
     CHECK(0 == notcurses_render(nc_));
-    c = CELL_TRIVIAL_INITIALIZER;
+    c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < ncplane_putc_yx(n_, 0, 0, &c));
     egc = ncplane_at_yx(n_, 0, 0, nullptr, nullptr);
     CHECK(0 == strcmp("", egc));
@@ -353,9 +353,9 @@ TEST_CASE("Plane") {
     const char* w1 = "à"; // U+00E0, U+0000         (c3 a0)
     const char* w2 = "à"; // U+0061, U+0300, U+0000 (61 cc 80)
     const char* w3 = "a"; // U+0061, U+0000         (61)
-    nccell cell1 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell2 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell3 = CELL_TRIVIAL_INITIALIZER;
+    nccell cell1 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell2 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell3 = NCCELL_TRIVIAL_INITIALIZER;
     auto u1 = nccell_load(n_, &cell1, w1);
     auto u2 = nccell_load(n_, &cell2, w2);
     auto u3 = nccell_load(n_, &cell3, w3);
@@ -371,18 +371,18 @@ TEST_CASE("Plane") {
     const char* w1 = "à"; // U+00E0, U+0000         (c3 a0)
     const char* w2 = "à"; // U+0061, U+0300, U+0000 (61 cc 80)
     const char* w3 = "a"; // U+0061, U+0000         (61)
-    nccell cell1 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell2 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell3 = CELL_TRIVIAL_INITIALIZER;
+    nccell cell1 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell2 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell3 = NCCELL_TRIVIAL_INITIALIZER;
     auto u1 = nccell_load(n_, &cell1, w1);
     auto u2 = nccell_load(n_, &cell2, w2);
     auto u3 = nccell_load(n_, &cell3, w3);
     REQUIRE(2 == u1);
     REQUIRE(3 == u2);
     REQUIRE(1 == u3);
-    nccell cell4 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell5 = CELL_TRIVIAL_INITIALIZER;
-    nccell cell6 = CELL_TRIVIAL_INITIALIZER;
+    nccell cell4 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell5 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell cell6 = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == nccell_duplicate(n_, &cell4, &cell1));
     CHECK(0 == nccell_duplicate(n_, &cell5, &cell2));
     CHECK(0 == nccell_duplicate(n_, &cell6, &cell3));
@@ -397,8 +397,8 @@ TEST_CASE("Plane") {
   SUBCASE("CellMultiColumn") {
     const char* w1 = "\xf0\x9f\x91\xa9"; // U+1F469 WOMAN
     const char* w2 = "N";
-    nccell c1 = CELL_TRIVIAL_INITIALIZER;
-    nccell c2 = CELL_TRIVIAL_INITIALIZER;
+    nccell c1 = NCCELL_TRIVIAL_INITIALIZER;
+    nccell c2 = NCCELL_TRIVIAL_INITIALIZER;
     auto u1 = nccell_load(n_, &c1, w1);
     auto u2 = nccell_load(n_, &c2, w2);
     REQUIRE(0 < u1);
@@ -596,7 +596,7 @@ TEST_CASE("Plane") {
     ncplane_set_styles(n_, NCSCALE_NONE);
     REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
     REQUIRE(0 < ncplane_putstr(n_, STR1));
-    nccell testcell = CELL_TRIVIAL_INITIALIZER;
+    nccell testcell = NCCELL_TRIVIAL_INITIALIZER;
     REQUIRE(0 == ncplane_at_cursor_cell(n_, &testcell)); // want nothing at the cursor
     CHECK(0 == testcell.gcluster);
     CHECK(0 == testcell.stylemask);
@@ -634,7 +634,7 @@ TEST_CASE("Plane") {
     ncplane_set_styles(n_, NCSTYLE_NONE);
     REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
     REQUIRE(0 < ncplane_putstr(n_, STR1));
-    nccell testcell = CELL_TRIVIAL_INITIALIZER;
+    nccell testcell = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_at_cursor_cell(n_, &testcell); // should be nothing at the cursor
     CHECK(0 == testcell.gcluster);
     CHECK(0 == testcell.stylemask);
@@ -684,7 +684,7 @@ TEST_CASE("Plane") {
     unsigned newx;
     ncplane_cursor_yx(n_, &y, &newx);
     CHECK(newx == x);
-    nccell testcell = CELL_TRIVIAL_INITIALIZER;
+    nccell testcell = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == ncplane_cursor_move_yx(n_, y - 2, x - 1));
     REQUIRE(1 == ncplane_at_cursor_cell(n_, &testcell));
     CHECK(testcell.gcluster == htole(STR1[strlen(STR1) - 1]));
@@ -839,13 +839,13 @@ TEST_CASE("Plane") {
   }
 
   SUBCASE("Perimeter") {
-    nccell c = CELL_CHAR_INITIALIZER('X');
+    nccell c = NCCELL_CHAR_INITIALIZER('X');
     CHECK(0 == ncplane_perimeter(n_, &c, &c, &c, &c, &c, &c, 0));
     CHECK(0 == notcurses_render(nc_));
   }
 
   SUBCASE("EGCStained") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == ncplane_set_fg_rgb(n_, 0x444444));
     CHECK(1 == ncplane_putegc(n_, "A", nullptr));
     CHECK(0 == ncplane_set_fg_rgb(n_, 0x888888));
diff --git a/src/tests/rotate.cpp b/src/tests/rotate.cpp
index 952791d9ae..fb3e804a14 100644
--- a/src/tests/rotate.cpp
+++ b/src/tests/rotate.cpp
@@ -69,9 +69,9 @@ TEST_CASE("Rotate") {
     CHECK(0 == ncchannels_set_fg_alpha(&channels, NCALPHA_TRANSPARENT));
     CHECK(0 == ncchannels_set_bg_alpha(&channels, NCALPHA_TRANSPARENT));
     REQUIRE(0 >= ncplane_set_base(testn, "", 0, channels));
-    nccell tl = CELL_TRIVIAL_INITIALIZER, tr = CELL_TRIVIAL_INITIALIZER;
-    nccell bl = CELL_TRIVIAL_INITIALIZER, br = CELL_TRIVIAL_INITIALIZER;
-    nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+    nccell tl = NCCELL_TRIVIAL_INITIALIZER, tr = NCCELL_TRIVIAL_INITIALIZER;
+    nccell bl = NCCELL_TRIVIAL_INITIALIZER, br = NCCELL_TRIVIAL_INITIALIZER;
+    nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(-1 < nccell_prime(testn, &tl, "█", 0, ul));
     CHECK(-1 < nccell_prime(testn, &tr, "█", 0, ur));
     CHECK(-1 < nccell_prime(testn, &bl, "█", 0, ll));
diff --git a/src/tests/scrolling.cpp b/src/tests/scrolling.cpp
index d19c502693..2b050fcaaa 100644
--- a/src/tests/scrolling.cpp
+++ b/src/tests/scrolling.cpp
@@ -150,9 +150,9 @@ TEST_CASE("Scrolling") {
     REQUIRE(n);
     // verify that the new plane was started without scrolling
     CHECK(!ncplane_set_scrolling(n, true));
-    nccell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
-    nccell dl = CELL_TRIVIAL_INITIALIZER, dr = CELL_TRIVIAL_INITIALIZER;
-    nccell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
+    nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+    nccell dl = NCCELL_TRIVIAL_INITIALIZER, dr = NCCELL_TRIVIAL_INITIALIZER;
+    nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 == nccells_double_box(n, 0, 0, &ul, &ur, &dl, &dr, &hl, &vl));
     CHECK(0 > ncplane_box_sized(n, &ul, &ur, &dl, &dr, &hl, &vl, 2, 25, 0));
     CHECK(0 > ncplane_box_sized(n, &ul, &ur, &dl, &dr, &hl, &vl, 2, 21, 0));
@@ -269,7 +269,7 @@ TEST_CASE("Scrolling") {
     ncplane_set_scrolling(n_, true);
     auto np = ncplane_create(n_, &nopts);
     REQUIRE(nullptr != np);
-    nccell c = CELL_INITIALIZER('a', 0, NCCHANNELS_INITIALIZER(0xbb, 0, 0xbb, 0, 0, 0));
+    nccell c = NCCELL_INITIALIZER('a', 0, NCCHANNELS_INITIALIZER(0xbb, 0, 0xbb, 0, 0, 0));
     CHECK(0 < ncplane_polyfill_yx(np, 0, 0, &c));
     CHECK(0 == ncplane_cursor_move_yx(n_, ncplane_dim_y(n_) - 1, 0));
     CHECK(0 == notcurses_render(nc_));
@@ -297,7 +297,7 @@ TEST_CASE("Scrolling") {
     ncplane_set_scrolling(n_, true);
     auto np = ncplane_create(n_, &nopts);
     REQUIRE(nullptr != np);
-    nccell c = CELL_INITIALIZER('a', 0, NCCHANNELS_INITIALIZER(0xbb, 0, 0xbb, 0, 0, 0));
+    nccell c = NCCELL_INITIALIZER('a', 0, NCCHANNELS_INITIALIZER(0xbb, 0, 0xbb, 0, 0, 0));
     CHECK(0 < ncplane_polyfill_yx(np, 0, 0, &c));
     CHECK(0 == ncplane_cursor_move_yx(n_, ncplane_dim_y(n_) - 1, 0));
     CHECK(0 == notcurses_render(nc_));
diff --git a/src/tests/stacking.cpp b/src/tests/stacking.cpp
index 81fe71e8dc..769b6e63b4 100644
--- a/src/tests/stacking.cpp
+++ b/src/tests/stacking.cpp
@@ -26,6 +26,61 @@ TEST_CASE("Stacking") {
   struct ncplane* n_ = notcurses_stddim_yx(nc_, &dimy, &dimx);
   REQUIRE(nullptr != n_);
 
+  // whenever the foreground matches the background (using RGB color, *not*
+  // default colors not palette-indexed color), we ought emit a space with the
+  // specified background, or a full block with the specified foreground (only
+  // if UTF8 is available). default colors must not be merged (palette-indexed
+  // could be, but it's pointless). the transformation must only take place at
+  // raster time--the original output must be recoverable from the plane.
+  SUBCASE("FgMatchesBgRGB") {
+    // first we write an a with the desired background, but a distinct
+    // foreground. then we write an a with the two matching (via RGB).
+    // this ought generate a space with the desired background on the
+    // second cell.
+    ncplane_set_fg_default(n_);
+    CHECK(0 == ncplane_set_bg_rgb(n_, 0x808080));
+    CHECK(1 == ncplane_putchar(n_, 'a'));
+    CHECK(0 == ncplane_set_fg_rgb(n_, 0x808080));
+    CHECK(1 == ncplane_putchar(n_, 'a')); // ought become a space
+    // now we write an x with the desired foreground, but a distinct
+    // background. then we write an x with the two matching. this ought
+    // generate a full block with the desired foreground if UTF8 is
+    // available, and a space with the desired background otherwise.
+    ncplane_set_bg_default(n_);
+    CHECK(1 == ncplane_putchar(n_, 'x'));
+    CHECK(0 == ncplane_set_bg_rgb(n_, 0x808080));
+    CHECK(1 == ncplane_putchar(n_, 'x')); // ought become a space/block
+    CHECK(0 == notcurses_render(nc_));
+    // now we check the output. the plane ought have the characters as written,
+    // but we ought have rasterized the optimal forms.
+    uint64_t channels;
+    auto pblit = ncplane_at_yx(n_, 0, 1, nullptr, &channels);
+    CHECK(0 == strcmp("a", pblit));
+    CHECK(0x808080 == ncchannels_bg_rgb(channels));
+    CHECK(0x808080 == ncchannels_fg_rgb(channels));
+    free(pblit);
+    pblit = ncplane_at_yx(n_, 0, 3, nullptr, &channels);
+    CHECK(0 == strcmp("x", pblit));
+    CHECK(0x808080 == ncchannels_bg_rgb(channels));
+    CHECK(0x808080 == ncchannels_fg_rgb(channels));
+    free(pblit);
+    auto rblit = notcurses_at_yx(nc_, 0, 1, nullptr, &channels);
+    CHECK(0 == strcmp(" ", rblit));
+    CHECK(0x808080 == ncchannels_bg_rgb(channels));
+    free(rblit);
+    rblit = notcurses_at_yx(nc_, 0, 3, nullptr, &channels);
+    if(notcurses_canutf8(nc_)){
+      CHECK(0x808080 == ncchannels_fg_rgb(channels));
+      // FIXME we're not yet this advanced, and use space instead
+      // CHECK(0 == strcmp(u8"\u2588", rblit));
+      CHECK(0 == strcmp(u8" ", rblit));
+    }else{
+      CHECK(0 == strcmp(" ", rblit));
+      CHECK(0x808080 == ncchannels_bg_rgb(channels));
+    }
+    free(rblit);
+  }
+
   SUBCASE("LowerAtopUpperWhite") {
     struct ncplane_options opts = {
       .y = 0, .x = 0, .rows = 1, .cols = 1,
@@ -63,9 +118,7 @@ TEST_CASE("Stacking") {
     uint64_t channels;
     auto egc = notcurses_at_yx(nc_, 0, 0, nullptr, &channels);
     REQUIRE(nullptr != egc);
-    // ought yield space with white background FIXME currently just yields
-    // a lower half block
-    CHECK(0 == strcmp("\u2584", egc));
+    CHECK(0 == strcmp(u8" ", egc));
     free(egc);
     CHECK(0xffffff == ncchannels_fg_rgb(channels));
     CHECK(0xffffff == ncchannels_bg_rgb(channels));
@@ -106,9 +159,7 @@ TEST_CASE("Stacking") {
     uint64_t channels;
     auto egc = notcurses_at_yx(nc_, 0, 0, nullptr, &channels);
     REQUIRE(nullptr != egc);
-    // ought yield space with white background FIXME currently just yields
-    // an upper half block
-    CHECK(0 == strcmp("\u2580", egc));
+    CHECK(0 == strcmp(u8" ", egc));
     free(egc);
     CHECK(0xffffff == ncchannels_fg_rgb(channels));
     CHECK(0xffffff == ncchannels_bg_rgb(channels));
@@ -150,9 +201,7 @@ TEST_CASE("Stacking") {
       uint64_t channels;
       auto egc = notcurses_at_yx(nc_, 0, 0, nullptr, &channels);
       REQUIRE(nullptr != egc);
-      // ought yield space with white background FIXME currently just yields
-      // an upper half block
-      CHECK(0 == strcmp("\u2580", egc));
+      CHECK(0 == strcmp(u8" ", egc));
       free(egc);
       CHECK(0x00ff00 == ncchannels_fg_rgb(channels));
       CHECK(0x00ff00 == ncchannels_bg_rgb(channels));
@@ -197,9 +246,7 @@ TEST_CASE("Stacking") {
       uint64_t channels;
       auto egc = notcurses_at_yx(nc_, 0, 0, nullptr, &channels);
       REQUIRE(nullptr != egc);
-      // ought yield space with white background FIXME currently just yields
-      // an upper half block
-      CHECK(0 == strcmp("\u259a", egc)); // quadrant upper left and lower right
+      CHECK(0 == strcmp(u8" ", egc)); // quadrant upper left and lower right
       free(egc);
       CHECK(0xffffff == ncchannels_fg_rgb(channels));
       CHECK(0xffffff == ncchannels_bg_rgb(channels));
diff --git a/src/tests/wide.cpp b/src/tests/wide.cpp
index 905b811b17..0c520716ec 100644
--- a/src/tests/wide.cpp
+++ b/src/tests/wide.cpp
@@ -88,7 +88,7 @@ TEST_CASE("Wide") {
     int x = 0;
     for(auto & tcell : tcells){
       CHECK(0 == ncplane_cursor_move_yx(n_, 0, x));
-      nccell testcell = CELL_TRIVIAL_INITIALIZER;
+      nccell testcell = NCCELL_TRIVIAL_INITIALIZER;
       CHECK(0 < ncplane_at_cursor_cell(n_, &testcell));
       CHECK(!strcmp(nccell_extended_gcluster(n_, &tcell), nccell_extended_gcluster(n_, &testcell)));
       CHECK(0 == testcell.stylemask);
@@ -117,7 +117,7 @@ TEST_CASE("Wide") {
     CHECK(1 == y);
     CHECK(2 == x);
     CHECK(0 < ncplane_putegc_yx(n_, 0, 0, w, nullptr));
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < nccell_load(n_, &c, w));
     CHECK(0 < ncplane_putc_yx(n_, 1, 0, &c));
     nccell_release(n_, &c);
@@ -147,7 +147,7 @@ TEST_CASE("Wide") {
     ncplane_cursor_yx(n_, &y, &x);
     CHECK(0 == y);
     CHECK(1 + cols2 == x);
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_at_yx_cell(n_, 0, 0, &c);
     if(cols1 > 1){
       CHECK(0 == c.gcluster); // should be nothing
@@ -173,7 +173,7 @@ TEST_CASE("Wide") {
     ncplane_cursor_yx(n_, &y, &x);
     CHECK(0 == y);
     CHECK(3 == x);
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_at_yx_cell(n_, 0, 0, &c);
     if(ncstrwidth(wbashedl, NULL, NULL) > 1){
       CHECK(0 == c.gcluster); // should be nothing
@@ -202,7 +202,7 @@ TEST_CASE("Wide") {
     ncplane_cursor_yx(n_, &y, &x);
     CHECK(0 == y);
     CHECK(3 == x);
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_at_yx_cell(n_, 0, 0, &c);
     CHECK(0 == strcmp(nccell_extended_gcluster(n_, &c), SNAKE));
     ncplane_at_yx_cell(n_, 0, 1, &c);
@@ -235,7 +235,7 @@ TEST_CASE("Wide") {
     CHECK(0 == ncplane_rounded_box_sized(ncp, 0, 0, 3, 4, 0));
     CHECK(ncstrwidth(SCORPION, NULL, NULL) == ncplane_putegc_yx(ncp, 1, 1, SCORPION, nullptr));
     CHECK(0 == notcurses_render(nc_));
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(0 < ncplane_at_yx_cell(ncp, 1, 0, &c));
     CHECK(!strcmp(nccell_extended_gcluster(ncp, &c), "│"));
     nccell_release(ncp, &c);
@@ -255,7 +255,7 @@ TEST_CASE("Wide") {
 
   SUBCASE("RenderWides") {
     CHECK(0 <= ncplane_putstr(n_, "\u5f62\u5168"));
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     ncplane_at_yx_cell(n_, 0, 0, &c);
     CHECK(nccell_double_wide_p(&c));
     ncplane_at_yx_cell(n_, 0, 1, &c);
@@ -290,7 +290,7 @@ TEST_CASE("Wide") {
   // If an ncplane is moved atop the right half of a wide glyph, the entire
   // glyph should be oblitrated.
   SUBCASE("PlaneStompsWideGlyph"){
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     char* egc;
 
     // print two wide glyphs on the standard plane
@@ -412,7 +412,7 @@ TEST_CASE("Wide") {
     };
     struct ncplane* p = ncplane_create(n_, &nopts);
     REQUIRE(nullptr != p);
-    nccell c = CELL_CHAR_INITIALIZER('X');
+    nccell c = NCCELL_CHAR_INITIALIZER('X');
     CHECK(0 == ncplane_perimeter(p, &c, &c, &c, &c, &c, &c, 0));
     ncplane_set_bg_rgb8(n_, 0x20, 0x20, 0x20);
     CHECK(2 == ncplane_putegc_yx(n_, 1, 1, "六", nullptr));
@@ -431,7 +431,7 @@ TEST_CASE("Wide") {
     REQUIRE(nullptr != egc);
     CHECK(0 == strcmp(" ", egc));
     free(egc);
-    nccell cl = CELL_TRIVIAL_INITIALIZER, cr = CELL_TRIVIAL_INITIALIZER;
+    nccell cl = NCCELL_TRIVIAL_INITIALIZER, cr = NCCELL_TRIVIAL_INITIALIZER;
     CHECK(3 == ncplane_at_yx_cell(n_, 1, 1, &cl));
     CHECK(0 == strcmp("六", nccell_extended_gcluster(n_, &cl)));
     CHECK(0 == ncplane_at_yx_cell(n_, 1, 2, &cr));
@@ -915,7 +915,7 @@ TEST_CASE("Wide") {
   // the NUL backstop for long inlined UTF8).
   // U+1F427 PINCHED FINGERS → UTF8(f0 9f a4 8c)
   SUBCASE("ItalicEmoji") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     nccell_load(n_, &c, "\U0001F427");
     CHECK(0xa7909ff0 == htole(c.gcluster));
     nccell_on_styles(&c, NCSTYLE_ITALIC);
@@ -934,7 +934,7 @@ TEST_CASE("Wide") {
   }
 
   SUBCASE("StyleMaxEmoji") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     nccell_load(n_, &c, "\U0001F427");
     CHECK(0xa7909ff0 == htole(c.gcluster));
     nccell_on_styles(&c, NCSTYLE_MASK);
@@ -992,7 +992,7 @@ TEST_CASE("Wide") {
   // fill the screen with un-inlineable EGCs
 #ifndef __APPLE__  // FIXME
   SUBCASE("OfflineEGCs") {
-    nccell c = CELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_TRIVIAL_INITIALIZER;
     const char egc[] = "\U0001F471\u200D\u2640"; // all one EGC
     CHECK(0 < nccell_load(n_, &c, egc));
     ncplane_set_scrolling(n_, true);
diff --git a/src/tests/zaxis.cpp b/src/tests/zaxis.cpp
index 535c776b22..2e7f2f954a 100644
--- a/src/tests/zaxis.cpp
+++ b/src/tests/zaxis.cpp
@@ -110,8 +110,8 @@ TEST_CASE("ZAxis") {
   // verify that moving one above another, with no other changes, is reflected at
   // render time (requires explicit damage maintenance from move functionality).
   SUBCASE("ZAxisDamage") {
-    nccell cat = CELL_TRIVIAL_INITIALIZER;
-    nccell c = CELL_CHAR_INITIALIZER('x');
+    nccell cat = NCCELL_TRIVIAL_INITIALIZER;
+    nccell c = NCCELL_CHAR_INITIALIZER('x');
     REQUIRE(!nccell_set_fg_rgb8(&c, 0xff, 0, 0));
     REQUIRE(1 == ncplane_putc(n_, &c));
     CHECK(!notcurses_render(nc_));