From 97571e86afc9fd4d37077d14cd5ca234080689b2 Mon Sep 17 00:00:00 2001 From: nick black Date: Wed, 15 Jul 2020 20:28:14 -0400 Subject: [PATCH] ncplane_putegc_yx() copies directly as opposed to going through putc() #797 --- USAGE.md | 15 ++++++++ include/notcurses/notcurses.h | 3 +- src/lib/internal.h | 7 ---- src/lib/notcurses.c | 65 ++++++++++++++++++++++++++++++----- tests/wide.cpp | 8 ++--- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/USAGE.md b/USAGE.md index b3718d7bb..7f63a5ad1 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1626,6 +1626,21 @@ void cell_release(struct ncplane* n, cell* c); #define NCSTYLE_PROTECT 0x00010000ul #define NCSTYLE_ITALIC 0x01000000ul +// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the +// result is not tied to the ncplane, and persists across erases / destruction. +static inline char* +cell_strdup(const struct ncplane* n, const cell* c){ + char* ret; + if(cell_simple_p(c)){ + if( (ret = (char*)malloc(2)) ){ // cast is here for C++ clients + ret[0] = c->gcluster; + ret[1] = '\0'; + } + }else{ + ret = strdup(cell_extended_gcluster(n, c)); + } + return ret; +} // Set the specified style bits for the cell 'c', whether they're actively // supported or not. diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index a48582b1a..76804fa31 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -729,7 +729,8 @@ cell_simple_p(const cell* c){ // is invalidated by any further operation on the plane 'n', so...watch out! API const char* cell_extended_gcluster(const struct ncplane* n, const cell* c); -// Extract the EGC from 'c' as a nul-terminated string. +// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the +// result is not tied to the ncplane, and persists across erases / destruction. static inline char* cell_strdup(const struct ncplane* n, const cell* c){ char* ret; diff --git a/src/lib/internal.h b/src/lib/internal.h index 10524d505..edf634a10 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -410,13 +410,6 @@ pool_egc_copy(const egcpool* e, const cell* c){ return ret; } -// copy the UTF8-encoded EGC out of the cell, whether simple or complex. the -// result is not tied to the ncplane, and persists across erases / destruction. -static inline char* -cell_egc_copy(const ncplane* n, const cell* c){ - return pool_egc_copy(&n->pool, c); -} - // For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to // ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to // even semi-portably recover the palette) proceeds via: map each 8-bit to diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 4f98705e2..24e3a4c3c 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1284,17 +1284,66 @@ int ncplane_putc_yx(ncplane* n, int y, int x, const cell* c){ } int ncplane_putegc_yx(ncplane* n, int y, int x, const char* gclust, int* sbytes){ - cell c = CELL_TRIVIAL_INITIALIZER; - int primed = cell_prime(n, &c, gclust, n->attrword, n->channels); + int cols; + int bytes = utf8_egc_len(gclust, &cols); if(sbytes){ - *sbytes = primed; + *sbytes = bytes; } - if(primed < 0){ + // if scrolling is enabled, check *before ncplane_cursor_move_yx()* whether + // we're past the end of the line, and move to the next line if so. + bool wide = cols > 1; + if(x == -1 && y == -1 && n->x + wide >= n->lenx){ + if(!n->scrolling){ + return -1; + } + scroll_down(n); + } + if(ncplane_cursor_move_yx(n, y, x)){ return -1; } - int ret = ncplane_putc_yx(n, y, x, &c); - cell_release(n, &c); - return ret; + if(*gclust == '\n'){ + if(n->scrolling){ + scroll_down(n); + return 0; + } + } + // A wide character obliterates anything to its immediate right (and marks + // that cell as wide). Any character placed atop one half of a wide character + // obliterates the other half. Note that a wide char can thus obliterate two + // wide chars, totalling four columns. + cell* targ = ncplane_cell_ref_yx(n, n->y, n->x); + if(n->x > 0){ + if(cell_double_wide_p(targ)){ // replaced cell is half of a wide char + if(targ->gcluster == 0){ // we're the right half + cell_obliterate(n, &n->fb[nfbcellidx(n, n->y, n->x - 1)]); + }else{ + cell_obliterate(n, &n->fb[nfbcellidx(n, n->y, n->x + 1)]); + } + } + } + uint64_t channels = n->channels & ~CELL_WIDEASIAN_MASK; + if(wide){ + channels |= CELL_WIDEASIAN_MASK; + } + if(cell_prime(n, targ, gclust, n->attrword, channels) < 0){ + return -1; + } + if(wide){ // must set our right wide, and check for further damage + if(n->x < n->lenx - 1){ // check to our right + cell* candidate = &n->fb[nfbcellidx(n, n->y, n->x + 1)]; + if(n->x < n->lenx - 2){ + if(cell_wide_left_p(candidate)){ + cell_obliterate(n, &n->fb[nfbcellidx(n, n->y, n->x + 2)]); + } + } + cell_obliterate(n, candidate); + cell_set_wide(candidate); + candidate->channels = channels; + candidate->attrword = n->attrword; + } + } + n->x += cols; + return cols; } int ncplane_putsimple_stainable(ncplane* n, char c){ @@ -1867,7 +1916,7 @@ void ncplane_erase(ncplane* n){ // we must preserve the background, but a pure cell_duplicate() would be // wiped out by the egcpool_dump(). do a duplication (to get the attrword // and channels), and then reload. - char* egc = cell_egc_copy(n, &n->basecell); + char* egc = cell_strdup(n, &n->basecell); memset(n->fb, 0, sizeof(*n->fb) * n->lenx * n->leny); egcpool_dump(&n->pool); egcpool_init(&n->pool); diff --git a/tests/wide.cpp b/tests/wide.cpp index 017fa439b..848bf6706 100644 --- a/tests/wide.cpp +++ b/tests/wide.cpp @@ -284,7 +284,7 @@ TEST_CASE("Wide") { // should be wide char 1 REQUIRE(3 == ncplane_at_yx_cell(n_, 0, 0, &c)); - egc = cell_egc_copy(n_, &c); + egc = cell_strdup(n_, &c); REQUIRE(egc); CHECK(!strcmp("\xe5\x85\xa8", egc)); CHECK(cell_double_wide_p(&c)); @@ -297,7 +297,7 @@ TEST_CASE("Wide") { cell_init(&c); // should be wide char 1 right side REQUIRE(0 == ncplane_at_yx_cell(n_, 0, 1, &c)); - egc = cell_egc_copy(n_, &c); + egc = cell_strdup(n_, &c); REQUIRE(egc); CHECK(!strcmp("", egc)); CHECK(cell_double_wide_p(&c)); @@ -311,7 +311,7 @@ TEST_CASE("Wide") { // should be wide char 2 REQUIRE(3 == ncplane_at_yx_cell(n_, 0, 2, &c)); - egc = cell_egc_copy(n_, &c); + egc = cell_strdup(n_, &c); REQUIRE(egc); CHECK(!strcmp("\xe5\xbd\xa2", egc)); CHECK(cell_double_wide_p(&c)); @@ -324,7 +324,7 @@ TEST_CASE("Wide") { cell_init(&c); // should be wide char 2 right side CHECK(0 == ncplane_at_yx_cell(n_, 0, 3, &c)); - egc = cell_egc_copy(n_, &c); + egc = cell_strdup(n_, &c); REQUIRE(egc); CHECK(!strcmp("", egc)); CHECK(cell_double_wide_p(&c));