diff --git a/README.md b/README.md index 57a213f7c..bcae9b80b 100644 --- a/README.md +++ b/README.md @@ -226,9 +226,9 @@ likely get blanks or � (U+FFFD, REPLACEMENT CHARACTER) for missing characters, and subsequent characters on the line may be misplaced. It is worth knowing that several terminals draw the block characters directly, -rather than loading them from a font. This is generally desirable. Quadrants -and sextants are not the place to demonstrate your design virtuosity. To -inspect your environment's rendering of drawing characters, run +rather than loading them from a font. This is generally desirable. Quadrants, +sextants, and octants are not the place to demonstrate your design virtuosity. +To inspect your environment's rendering of drawing characters, run `notcurses-info`. The desired output ought look something like this:
@@ -309,7 +309,8 @@ If things break or seem otherwise lackluster, **please** consult the
Notcurses will not make use of bitmap protocols unless the terminal positively
indicates support for them, even if NCBLIT_PIXEL
has been
requested. Likewise, sextants (NCBLIT_3x2
) won't be used without
- Unicode 13 support, etc. ncvisual_blit()
will use the best blitter
+ Unicode 13 support, octants (NCBLIT_4x2
) won't be used without
+ Unicode 16 support, etc. ncvisual_blit()
will use the best blitter
available, unless NCVISUAL_OPTION_NODEGRADE
is provided (in
which case it will fail).
diff --git a/doc/man/man1/notcurses-info.1.md b/doc/man/man1/notcurses-info.1.md
index 5d6202318..ac75451ac 100644
--- a/doc/man/man1/notcurses-info.1.md
+++ b/doc/man/man1/notcurses-info.1.md
@@ -17,7 +17,7 @@ terminal environment, including material loaded from **terminfo(5)** (based
on the **TERM** environment variable), replies from the terminal in
response to our queries, and built-in heuristics.
-The Unicode half block, quadrant, sextant, and Braille glyphs are all included
+The Unicode half block, quadrant, sextant, octant, and Braille glyphs are all included
in the output. If their appearance is irregular, it might behoove you to choose
another font.
@@ -70,6 +70,7 @@ The next five lines describe properties of the terminal environment:
* 2x1: Upper- and lower-half blocks are available
* 2x2: Quadrant blocks are available
* 3x2: Sextant blocks are available
+ * 4x2: Octant blocks are available
* 4x2: Braille characters are available
* img: Images can be decoded
* vid: Video can be decoded
@@ -93,7 +94,7 @@ To the right of this material is the Notcurses homepage's URI, and the
Notcurses logo (the latter only if bitmap graphics are available).
The final eleven lines, only printed when in a UTF8 locale, show various
-Unicode glyphs. The first four lines include the quadrant, sextant, and
+Unicode glyphs. The first four lines include the quadrant, sextant, octant, and
box-drawing characters. The next four lines include the entire Braille set.
The following two lines include many of the Symbols for Legacy Computing
introduced in Unicode 13. The final line includes many emoji.
diff --git a/doc/man/man3/notcurses_capabilities.3.md b/doc/man/man3/notcurses_capabilities.3.md
index 1e004437f..7493c3d58 100644
--- a/doc/man/man3/notcurses_capabilities.3.md
+++ b/doc/man/man3/notcurses_capabilities.3.md
@@ -34,6 +34,8 @@ notcurses_capabilities - runtime capability detection
**bool notcurses_cansextant(const struct notcurses* ***nc***);**
+**bool notcurses_canoctant(const struct notcurses* ***nc***);**
+
**bool notcurses_canbraille(const struct notcurses* ***nc***);**
**bool notcurses_canpixel(const struct notcurses* ***nc***);**
@@ -92,8 +94,10 @@ multimedia support capable of decoding videos.
**notcurses_canutf8** returns **true** if the configured locale uses
UTF-8 encoding, and the locale was successfully loaded.
+**notcurses_canoctant** returns **true** if the heuristics suggest
+that the terminal can properly render Unicode 16 octants.
**notcurses_cansextant** returns **true** if the heuristics suggest
-that the terminal can properly render Unicode 13 sextants. Likewise,
+that the terminal can properly render Unicode 13 sextants, and
**notcurses_canquadrant** and **notcurses_canhalfblock** return **true**
if the heuristics suggest that the terminal can properly render Unicode
quadrants and halfblocks, respectively. **notcurses_canbraille** returns
diff --git a/doc/man/man3/notcurses_direct.3.md b/doc/man/man3/notcurses_direct.3.md
index a0e84e196..207778322 100644
--- a/doc/man/man3/notcurses_direct.3.md
+++ b/doc/man/man3/notcurses_direct.3.md
@@ -114,6 +114,8 @@ notcurses_direct - the Direct Mode API
**bool ncdirect_cansextant(const struct ncdirect* ***nc***);**
+**bool ncdirect_canoctant(const struct ncdirect* ***nc***);**
+
**bool ncdirect_canbraille(const struct ncdirect* ***nc***);**
**bool ncdirect_canget_cursor(const struct ncdirect* ***nc***);**
diff --git a/doc/man/man3/notcurses_plot.3.md b/doc/man/man3/notcurses_plot.3.md
index ad4bcf464..fef84d58f 100644
--- a/doc/man/man3/notcurses_plot.3.md
+++ b/doc/man/man3/notcurses_plot.3.md
@@ -84,9 +84,9 @@ contribute to the plot. Supplying an **x** below the current window is an
error, and has no effect.
More granular block glyphs means more resolution in your plots, but they can
-be difficult to differentiate at small text sizes. Sextants and Braille allow
-for more resolution on the independent variable. It can be difficult to predict
-how the Braille glyphs will look in a given font.
+be difficult to differentiate at small text sizes. Octants, sextants, and Braille
+allow for more resolution on the independent variable. It can be difficult to
+predict how the Braille glyphs will look in a given font.
The same **ncplot_options** struct can be used with all ncplot types. The
**flags** field is a bitmask composed of:
diff --git a/doc/man/man3/notcurses_visual.3.md b/doc/man/man3/notcurses_visual.3.md
index 6229f27f8..1712bf744 100644
--- a/doc/man/man3/notcurses_visual.3.md
+++ b/doc/man/man3/notcurses_visual.3.md
@@ -24,6 +24,7 @@ typedef enum {
NCBLIT_2x1, // halves + 1x1
NCBLIT_2x2, // quadrants + 2x1
NCBLIT_3x2, // sextants + 1x1
+ NCBLIT_4x2, // octants + quadrants + 2x1 + 1x1
NCBLIT_BRAILLE, // 4 rows, 2 cols (braille)
NCBLIT_PIXEL, // pixel graphics
NCBLIT_4x1, // four vertical levels, (plots)
@@ -238,6 +239,7 @@ The different **ncblitter_e** values select from among available glyph sets:
* **NCBLIT_2x1**: Adds the half blocks (▄▀) to **NCBLIT_1x1**.
* **NCBLIT_2x2**: Adds left and right half blocks (▌▐) and quadrants (▖▗▟▙) to **NCBLIT_2x1**.
* **NCBLIT_3x2**: Adds sextants to **NCBLIT_1x1**.
+* **NCBLIT_4x2**: Adds octants to **NCBLIT_2x2**.
* **NCBLIT_BRAILLE**: 4 rows and 2 columns of braille (⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿).
* **NCBLIT_PIXEL**: Adds pixel graphics (these also work in ASCII).
@@ -344,8 +346,10 @@ or if both ***nc*** and ***n*** are **NULL**.
**ncvisual_media_defblitter** returns the blitter selected by **NCBLIT_DEFAULT**
in the specified configuration. If UTF8 is not enabled, this will always be
**NCBLIT_1x1**. If ***scale*** is **NCSCALE_NONE** or **NCSCALE_SCALE**, the
-aspect-preserving **NCBLIT_2x1** will be returned. If sextants are available
-(see **notcurses_cansextant**), this will be **NCBLIT_3x2**, or otherwise
+aspect-preserving **NCBLIT_2x1** will be returned. If octants are
+available (see **notcurses_canoctant**), this will be **NCBLIT_4x2**,
+or otherwise, if sextants are available (see
+**notcurses_cansextant**), this will be **NCBLIT_3x2**, or otherwise
**NCBLIT_2x2**.
# NOTES
@@ -359,7 +363,8 @@ a multimedia backend include **ncvisual_from_file** and
Sixel documentation can be found at [Dankwiki](https://nick-black.com/dankwiki/index.php?title=Sixel).
Kitty's graphics protocol is specified in [its documentation](https://sw.kovidgoyal.net/kitty/graphics-protocol.html).
-Bad font support can ruin **NCBLIT_2x2**, **NCBLIT_3x2**, **NCBLIT_4x1**,
+Bad font support can ruin **NCBLIT_2x2**, **NCBLIT_4x2**, **NCBLIT_3x2**,
+**NCBLIT_4x1**,
**NCBLIT_BRAILLE**, and **NCBLIT_8x1**. Braille glyphs ought ideally draw only
the raised dots, rather than drawing all eight dots with two different styles.
It's often best for the emulator to draw these glyphs itself.
diff --git a/include/ncpp/NotCurses.hh b/include/ncpp/NotCurses.hh
index 92d570141..72688fc01 100644
--- a/include/ncpp/NotCurses.hh
+++ b/include/ncpp/NotCurses.hh
@@ -126,6 +126,11 @@ namespace ncpp
return notcurses_cansextant (nc);
}
+ bool can_octant () const noexcept
+ {
+ return notcurses_canoctant (nc);
+ }
+
bool can_utf8 () const noexcept
{
return notcurses_canutf8 (nc);
diff --git a/include/notcurses/direct.h b/include/notcurses/direct.h
index 4f789a934..d7dbc3afe 100644
--- a/include/notcurses/direct.h
+++ b/include/notcurses/direct.h
@@ -394,6 +394,12 @@ ncdirect_cansextant(const struct ncdirect* nc){
return ncdirect_canutf8(nc) && ncdirect_capabilities(nc)->sextants;
}
+// Can we reliably use Unicode 16 octants?
+static inline bool
+ncdirect_canoctant(const struct ncdirect* nc){
+ return ncdirect_canutf8(nc) && ncdirect_capabilities(nc)->octants;
+}
+
// Can we reliably use Unicode Braille?
static inline bool
ncdirect_canbraille(const struct ncdirect* nc){
diff --git a/include/notcurses/ncseqs.h b/include/notcurses/ncseqs.h
index a02aa7ac7..1413909d4 100644
--- a/include/notcurses/ncseqs.h
+++ b/include/notcurses/ncseqs.h
@@ -56,6 +56,39 @@ extern "C" {
#define NCHALFBLOCKS L" ▀▄█"
#define NCQUADBLOCKS L" ▘▝▀▖▌▞▛▗▚▐▜▄▙▟█"
#define NCSEXBLOCKS L" 🬀🬁🬂🬃🬄🬅🬆🬇🬈🬊🬋🬌🬍🬎🬏🬐🬑🬒🬓▌🬔🬕🬖🬗🬘🬙🬚🬛🬜🬝🬞🬟🬠🬡🬢🬣🬤🬥🬦🬧▐🬨🬩🬪🬫🬬🬭🬮🬯🬰🬱🬲🬳🬴🬵🬶🬷🬸🬹🬺🬻█"
+#define NCOCTBLOCKS \
+ L" \U0001CEA8\U0001CEAB\U0001FB82\U0001CD00\U00002598\U0001CD01\U0001CD02"\
+ "\U0001CD03\U0001CD04\U0000259D\U0001CD05\U0001CD06\U0001CD07\U0001CD08\U00002580"\
+ "\U0001CD09\U0001CD0A\U0001CD0B\U0001CD0C\U0001FBE6\U0001CD0D\U0001CD0E\U0001CD0F"\
+ "\U0001CD10\U0001CD11\U0001CD12\U0001CD13\U0001CD14\U0001CD15\U0001CD16\U0001CD17"\
+ "\U0001CD18\U0001CD19\U0001CD1A\U0001CD1B\U0001CD1C\U0001CD1D\U0001CD1E\U0001CD1F"\
+ "\U0001FBE7\U0001CD20\U0001CD21\U0001CD22\U0001CD23\U0001CD24\U0001CD25\U0001CD26"\
+ "\U0001CD27\U0001CD28\U0001CD29\U0001CD2A\U0001CD2B\U0001CD2C\U0001CD2D\U0001CD2E"\
+ "\U0001CD2F\U0001CD30\U0001CD31\U0001CD32\U0001CD33\U0001CD34\U0001CD35\U0001FB85"\
+ "\U0001CEA3\U0001CD36\U0001CD37\U0001CD38\U0001CD39\U0001CD3A\U0001CD3B\U0001CD3C"\
+ "\U0001CD3D\U0001CD3E\U0001CD3F\U0001CD40\U0001CD41\U0001CD42\U0001CD43\U0001CD44"\
+ "\U00002596\U0001CD45\U0001CD46\U0001CD47\U0001CD48\U0000258C\U0001CD49\U0001CD4A"\
+ "\U0001CD4B\U0001CD4C\U0000259E\U0001CD4D\U0001CD4E\U0001CD4F\U0001CD50\U0000259B"\
+ "\U0001CD51\U0001CD52\U0001CD53\U0001CD54\U0001CD55\U0001CD56\U0001CD57\U0001CD58"\
+ "\U0001CD59\U0001CD5A\U0001CD5B\U0001CD5C\U0001CD5D\U0001CD5E\U0001CD5F\U0001CD60"\
+ "\U0001CD61\U0001CD62\U0001CD63\U0001CD64\U0001CD65\U0001CD66\U0001CD67\U0001CD68"\
+ "\U0001CD69\U0001CD6A\U0001CD6B\U0001CD6C\U0001CD6D\U0001CD6E\U0001CD6F\U0001CD70"\
+ "\U0001CEA0\U0001CD71\U0001CD72\U0001CD73\U0001CD74\U0001CD75\U0001CD76\U0001CD77"\
+ "\U0001CD78\U0001CD79\U0001CD7A\U0001CD7B\U0001CD7C\U0001CD7D\U0001CD7E\U0001CD7F"\
+ "\U0001CD80\U0001CD81\U0001CD82\U0001CD83\U0001CD84\U0001CD85\U0001CD86\U0001CD87"\
+ "\U0001CD88\U0001CD89\U0001CD8A\U0001CD8B\U0001CD8C\U0001CD8D\U0001CD8E\U0001CD8F"\
+ "\U00002597\U0001CD90\U0001CD91\U0001CD92\U0001CD93\U0000259A\U0001CD94\U0001CD95"\
+ "\U0001CD96\U0001CD97\U00002590\U0001CD98\U0001CD99\U0001CD9A\U0001CD9B\U0000259C"\
+ "\U0001CD9C\U0001CD9D\U0001CD9E\U0001CD9F\U0001CDA0\U0001CDA1\U0001CDA2\U0001CDA3"\
+ "\U0001CDA4\U0001CDA5\U0001CDA6\U0001CDA7\U0001CDA8\U0001CDA9\U0001CDAA\U0001CDAB"\
+ "\U00002582\U0001CDAC\U0001CDAD\U0001CDAE\U0001CDAF\U0001CDB0\U0001CDB1\U0001CDB2"\
+ "\U0001CDB3\U0001CDB4\U0001CDB5\U0001CDB6\U0001CDB7\U0001CDB8\U0001CDB9\U0001CDBA"\
+ "\U0001CDBB\U0001CDBC\U0001CDBD\U0001CDBE\U0001CDBF\U0001CDC0\U0001CDC1\U0001CDC2"\
+ "\U0001CDC3\U0001CDC4\U0001CDC5\U0001CDC6\U0001CDC7\U0001CDC8\U0001CDC9\U0001CDCA"\
+ "\U0001CDCB\U0001CDCC\U0001CDCD\U0001CDCE\U0001CDCF\U0001CDD0\U0001CDD1\U0001CDD2"\
+ "\U0001CDD3\U0001CDD4\U0001CDD5\U0001CDD6\U0001CDD7\U0001CDD8\U0001CDD9\U0001CDDA"\
+ "\U00002584\U0001CDDB\U0001CDDC\U0001CDDD\U0001CDDE\U00002599\U0001CDDF\U0001CDE0"\
+ "\U0001CDE1\U0001CDE2\U0000259F\U0001CDE3\U00002586\U0001CDE4\U0001CDE5\U00002588"
#define NCBRAILLEEGCS \
L"\u2800\u2801\u2808\u2809\u2802\u2803\u280a\u280b\u2810\u2811\u2818\u2819\u2812\u2813\u281a\u281b"\
"\u2804\u2805\u280c\u280d\u2806\u2807\u280e\u280f\u2814\u2815\u281c\u281d\u2816\u2817\u281e\u281f"\
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 7ba75da26..99998701c 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -68,6 +68,7 @@ typedef enum {
NCBLIT_2x1, // halves + 1x1 (space) ▄▀
NCBLIT_2x2, // quadrants + 2x1 ▗▐ ▖▀▟▌▙
NCBLIT_3x2, // sextants (*NOT* 2x2) 🬀🬁🬂🬃🬄🬅🬆🬇🬈🬉🬊🬋🬌🬍🬎🬏🬐🬑🬒🬓🬔🬕🬖🬗🬘🬙🬚🬛🬜🬝🬞
+ NCBLIT_4x2, // octants
NCBLIT_BRAILLE, // 4 rows, 2 cols (braille) ⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿
NCBLIT_PIXEL, // pixel graphics
// these blitters are suitable only for plots, not general media
@@ -1641,6 +1642,7 @@ typedef struct nccapabilities {
bool halfblocks;// we assume halfblocks, but some are known to lack them
bool quadrants; // do we have (good, vetted) Unicode 1 quadrant support?
bool sextants; // do we have (good, vetted) Unicode 13 sextant support?
+ bool octants; // do we have (good, vetted) Unicode 16 octant support?
bool braille; // do we have Braille support? (linux console does not)
} nccapabilities;
@@ -1756,6 +1758,12 @@ notcurses_cansextant(const struct notcurses* nc){
return notcurses_canutf8(nc) && notcurses_capabilities(nc)->sextants;
}
+// Can we reliably use Unicode 16 octants?
+__attribute__ ((nonnull (1))) __attribute__ ((pure)) static inline bool
+notcurses_canoctant(const struct notcurses* nc){
+ return notcurses_canutf8(nc) && notcurses_capabilities(nc)->octants;
+}
+
// Can we reliably use Unicode Braille?
__attribute__ ((nonnull (1))) __attribute__ ((pure)) static inline bool
notcurses_canbraille(const struct notcurses* nc){
@@ -3511,6 +3519,7 @@ API ALLOC struct ncplane* ncvisual_subtitle_plane(struct ncplane* parent,
// the specified scaling method. Currently, this means:
// - if lacking UTF-8, NCBLIT_1x1
// - otherwise, if not NCSCALE_STRETCH, NCBLIT_2x1
+// - otherwise, if octants are known to be good, NCBLIT_4x2
// - otherwise, if sextants are not known to be good, NCBLIT_2x2
// - otherwise NCBLIT_3x2
// NCBLIT_2x2 and NCBLIT_3x2 both distort the original aspect ratio, thus
diff --git a/src/demo/keller.c b/src/demo/keller.c
index ddd759c30..83b2ee07f 100644
--- a/src/demo/keller.c
+++ b/src/demo/keller.c
@@ -16,6 +16,7 @@ visualize(struct notcurses* nc, struct ncvisual* ncv){
{ NCBLIT_2x1, notcurses_canhalfblock, },
{ NCBLIT_2x2, notcurses_canquadrant, },
{ NCBLIT_3x2, notcurses_cansextant, },
+ { NCBLIT_4x2, notcurses_canoctant, },
{ NCBLIT_PIXEL, notcurses_canpixel, },
};
struct ncplane* stdn = notcurses_stdplane(nc);
diff --git a/src/info/main.c b/src/info/main.c
index c879f0581..d8b9631d2 100644
--- a/src/info/main.c
+++ b/src/info/main.c
@@ -463,6 +463,7 @@ tinfo_debug_styles(const notcurses* nc, struct ncplane* n, const char* indent){
tinfo_debug_cap(n, "2x1", notcurses_canhalfblock(nc));
tinfo_debug_cap(n, "2x2", notcurses_canquadrant(nc));
tinfo_debug_cap(n, "3x2", notcurses_cansextant(nc));
+ tinfo_debug_cap(n, "4x2", notcurses_canoctant(nc));
tinfo_debug_cap(n, "4x2", notcurses_canbraille(nc));
tinfo_debug_cap(n, "img", notcurses_canopen_images(nc));
tinfo_debug_cap(n, "vid", notcurses_canopen_videos(nc));
diff --git a/src/lib/blit.c b/src/lib/blit.c
index 24e019e9f..f77424f69 100644
--- a/src/lib/blit.c
+++ b/src/lib/blit.c
@@ -711,6 +711,88 @@ sextant_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
return total;
}
+// Bit is set where octant is present:
+// 0 1
+// 2 3
+// 4 5
+// 6 7
+// Same as NCOCTBLOCKS but as array of fixed-width strings
+static const char octant_egcs[256][5] = {
+ "\x20", "\U0001CEA8", "\U0001CEAB", "\U0001FB82", "\U0001CD00", "\U00002598", "\U0001CD01", "\U0001CD02",
+ "\U0001CD03", "\U0001CD04", "\U0000259D", "\U0001CD05", "\U0001CD06", "\U0001CD07", "\U0001CD08", "\U00002580",
+ "\U0001CD09", "\U0001CD0A", "\U0001CD0B", "\U0001CD0C", "\U0001FBE6", "\U0001CD0D", "\U0001CD0E", "\U0001CD0F",
+ "\U0001CD10", "\U0001CD11", "\U0001CD12", "\U0001CD13", "\U0001CD14", "\U0001CD15", "\U0001CD16", "\U0001CD17",
+ "\U0001CD18", "\U0001CD19", "\U0001CD1A", "\U0001CD1B", "\U0001CD1C", "\U0001CD1D", "\U0001CD1E", "\U0001CD1F",
+ "\U0001FBE7", "\U0001CD20", "\U0001CD21", "\U0001CD22", "\U0001CD23", "\U0001CD24", "\U0001CD25", "\U0001CD26",
+ "\U0001CD27", "\U0001CD28", "\U0001CD29", "\U0001CD2A", "\U0001CD2B", "\U0001CD2C", "\U0001CD2D", "\U0001CD2E",
+ "\U0001CD2F", "\U0001CD30", "\U0001CD31", "\U0001CD32", "\U0001CD33", "\U0001CD34", "\U0001CD35", "\U0001FB85",
+ "\U0001CEA3", "\U0001CD36", "\U0001CD37", "\U0001CD38", "\U0001CD39", "\U0001CD3A", "\U0001CD3B", "\U0001CD3C",
+ "\U0001CD3D", "\U0001CD3E", "\U0001CD3F", "\U0001CD40", "\U0001CD41", "\U0001CD42", "\U0001CD43", "\U0001CD44",
+ "\U00002596", "\U0001CD45", "\U0001CD46", "\U0001CD47", "\U0001CD48", "\U0000258C", "\U0001CD49", "\U0001CD4A",
+ "\U0001CD4B", "\U0001CD4C", "\U0000259E", "\U0001CD4D", "\U0001CD4E", "\U0001CD4F", "\U0001CD50", "\U0000259B",
+ "\U0001CD51", "\U0001CD52", "\U0001CD53", "\U0001CD54", "\U0001CD55", "\U0001CD56", "\U0001CD57", "\U0001CD58",
+ "\U0001CD59", "\U0001CD5A", "\U0001CD5B", "\U0001CD5C", "\U0001CD5D", "\U0001CD5E", "\U0001CD5F", "\U0001CD60",
+ "\U0001CD61", "\U0001CD62", "\U0001CD63", "\U0001CD64", "\U0001CD65", "\U0001CD66", "\U0001CD67", "\U0001CD68",
+ "\U0001CD69", "\U0001CD6A", "\U0001CD6B", "\U0001CD6C", "\U0001CD6D", "\U0001CD6E", "\U0001CD6F", "\U0001CD70",
+ "\U0001CEA0", "\U0001CD71", "\U0001CD72", "\U0001CD73", "\U0001CD74", "\U0001CD75", "\U0001CD76", "\U0001CD77",
+ "\U0001CD78", "\U0001CD79", "\U0001CD7A", "\U0001CD7B", "\U0001CD7C", "\U0001CD7D", "\U0001CD7E", "\U0001CD7F",
+ "\U0001CD80", "\U0001CD81", "\U0001CD82", "\U0001CD83", "\U0001CD84", "\U0001CD85", "\U0001CD86", "\U0001CD87",
+ "\U0001CD88", "\U0001CD89", "\U0001CD8A", "\U0001CD8B", "\U0001CD8C", "\U0001CD8D", "\U0001CD8E", "\U0001CD8F",
+ "\U00002597", "\U0001CD90", "\U0001CD91", "\U0001CD92", "\U0001CD93", "\U0000259A", "\U0001CD94", "\U0001CD95",
+ "\U0001CD96", "\U0001CD97", "\U00002590", "\U0001CD98", "\U0001CD99", "\U0001CD9A", "\U0001CD9B", "\U0000259C",
+ "\U0001CD9C", "\U0001CD9D", "\U0001CD9E", "\U0001CD9F", "\U0001CDA0", "\U0001CDA1", "\U0001CDA2", "\U0001CDA3",
+ "\U0001CDA4", "\U0001CDA5", "\U0001CDA6", "\U0001CDA7", "\U0001CDA8", "\U0001CDA9", "\U0001CDAA", "\U0001CDAB",
+ "\U00002582", "\U0001CDAC", "\U0001CDAD", "\U0001CDAE", "\U0001CDAF", "\U0001CDB0", "\U0001CDB1", "\U0001CDB2",
+ "\U0001CDB3", "\U0001CDB4", "\U0001CDB5", "\U0001CDB6", "\U0001CDB7", "\U0001CDB8", "\U0001CDB9", "\U0001CDBA",
+ "\U0001CDBB", "\U0001CDBC", "\U0001CDBD", "\U0001CDBE", "\U0001CDBF", "\U0001CDC0", "\U0001CDC1", "\U0001CDC2",
+ "\U0001CDC3", "\U0001CDC4", "\U0001CDC5", "\U0001CDC6", "\U0001CDC7", "\U0001CDC8", "\U0001CDC9", "\U0001CDCA",
+ "\U0001CDCB", "\U0001CDCC", "\U0001CDCD", "\U0001CDCE", "\U0001CDCF", "\U0001CDD0", "\U0001CDD1", "\U0001CDD2",
+ "\U0001CDD3", "\U0001CDD4", "\U0001CDD5", "\U0001CDD6", "\U0001CDD7", "\U0001CDD8", "\U0001CDD9", "\U0001CDDA",
+ "\U00002584", "\U0001CDDB", "\U0001CDDC", "\U0001CDDD", "\U0001CDDE", "\U00002599", "\U0001CDDF", "\U0001CDE0",
+ "\U0001CDE1", "\U0001CDE2", "\U0000259F", "\U0001CDE3", "\U00002586", "\U0001CDE4", "\U0001CDE5", "\U00002588",
+};
+
+// Bit is set where Braille dot is present:
+// 0 1
+// 2 3
+// 4 5
+// 6 7
+// Similar to NCBRAILLEEGCS, but in a different order since we number the bits differently
+static const char braille_egcs[256][5] = {
+ "\u2800", "\u2801", "\u2808", "\u2809", "\u2802", "\u2803", "\u280a", "\u280b",
+ "\u2810", "\u2811", "\u2818", "\u2819", "\u2812", "\u2813", "\u281a", "\u281b",
+ "\u2804", "\u2805", "\u280c", "\u280d", "\u2806", "\u2807", "\u280e", "\u280f",
+ "\u2814", "\u2815", "\u281c", "\u281d", "\u2816", "\u2817", "\u281e", "\u281f",
+ "\u2820", "\u2821", "\u2828", "\u2829", "\u2822", "\u2823", "\u282a", "\u282b",
+ "\u2830", "\u2831", "\u2838", "\u2839", "\u2832", "\u2833", "\u283a", "\u283b",
+ "\u2824", "\u2825", "\u282c", "\u282d", "\u2826", "\u2827", "\u282e", "\u282f",
+ "\u2834", "\u2835", "\u283c", "\u283d", "\u2836", "\u2837", "\u283e", "\u283f",
+ "\u2840", "\u2841", "\u2848", "\u2849", "\u2842", "\u2843", "\u284a", "\u284b",
+ "\u2850", "\u2851", "\u2858", "\u2859", "\u2852", "\u2853", "\u285a", "\u285b",
+ "\u2844", "\u2845", "\u284c", "\u284d", "\u2846", "\u2847", "\u284e", "\u284f",
+ "\u2854", "\u2855", "\u285c", "\u285d", "\u2856", "\u2857", "\u285e", "\u285f",
+ "\u2860", "\u2861", "\u2868", "\u2869", "\u2862", "\u2863", "\u286a", "\u286b",
+ "\u2870", "\u2871", "\u2878", "\u2879", "\u2872", "\u2873", "\u287a", "\u287b",
+ "\u2864", "\u2865", "\u286c", "\u286d", "\u2866", "\u2867", "\u286e", "\u286f",
+ "\u2874", "\u2875", "\u287c", "\u287d", "\u2876", "\u2877", "\u287e", "\u287f",
+ "\u2880", "\u2881", "\u2888", "\u2889", "\u2882", "\u2883", "\u288a", "\u288b",
+ "\u2890", "\u2891", "\u2898", "\u2899", "\u2892", "\u2893", "\u289a", "\u289b",
+ "\u2884", "\u2885", "\u288c", "\u288d", "\u2886", "\u2887", "\u288e", "\u288f",
+ "\u2894", "\u2895", "\u289c", "\u289d", "\u2896", "\u2897", "\u289e", "\u289f",
+ "\u28a0", "\u28a1", "\u28a8", "\u28a9", "\u28a2", "\u28a3", "\u28aa", "\u28ab",
+ "\u28b0", "\u28b1", "\u28b8", "\u28b9", "\u28b2", "\u28b3", "\u28ba", "\u28bb",
+ "\u28a4", "\u28a5", "\u28ac", "\u28ad", "\u28a6", "\u28a7", "\u28ae", "\u28af",
+ "\u28b4", "\u28b5", "\u28bc", "\u28bd", "\u28b6", "\u28b7", "\u28be", "\u28bf",
+ "\u28c0", "\u28c1", "\u28c8", "\u28c9", "\u28c2", "\u28c3", "\u28ca", "\u28cb",
+ "\u28d0", "\u28d1", "\u28d8", "\u28d9", "\u28d2", "\u28d3", "\u28da", "\u28db",
+ "\u28c4", "\u28c5", "\u28cc", "\u28cd", "\u28c6", "\u28c7", "\u28ce", "\u28cf",
+ "\u28d4", "\u28d5", "\u28dc", "\u28dd", "\u28d6", "\u28d7", "\u28de", "\u28df",
+ "\u28e0", "\u28e1", "\u28e8", "\u28e9", "\u28e2", "\u28e3", "\u28ea", "\u28eb",
+ "\u28f0", "\u28f1", "\u28f8", "\u28f9", "\u28f2", "\u28f3", "\u28fa", "\u28fb",
+ "\u28e4", "\u28e5", "\u28ec", "\u28ed", "\u28e6", "\u28e7", "\u28ee", "\u28ef",
+ "\u28f4", "\u28f5", "\u28fc", "\u28fd", "\u28f6", "\u28f7", "\u28fe", "\u28ff",
+};
+
// fold the r, g, and b components of the pixel into *r, *g, and *b, and
// increment *foldcount
static inline void
@@ -722,13 +804,14 @@ fold_rgb8(unsigned* restrict r, unsigned* restrict g, unsigned* restrict b,
++*foldcount;
}
-// Braille blitter. maps 4x2 to each cell. since we only have one color at
-// our disposal (foreground), we lose some fidelity. this is optimal for
-// visuals with only two colors in a given area, as it packs lots of
-// resolution. always transparent background.
+// generic 4x2 blitter, used for octant and Braille. maps 4x2 to each
+// cell. since we only have one color at our disposal (foreground), we
+// lose some fidelity. this is optimal for visuals with only two
+// colors in a given area, as it packs lots of resolution. always
+// transparent background.
static inline int
-braille_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
- const blitterargs* bargs){
+blit_4x2(ncplane* nc, int linesize, const void* data, int leny, int lenx,
+ const blitterargs* bargs, const char egcs[256][5]){
const bool blendcolors = bargs->flags & NCVISUAL_OPTION_BLEND;
unsigned dimy, dimx, x, y;
int total = 0; // number of cells written
@@ -774,31 +857,31 @@ braille_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
}
}
}
- // braille block is ordered (where 1 is the LSB)
- // 1 4
- // 2 5
- // 3 6
- // 4 7
+ // 4x2 block is ordered (where 0 is the LSB)
+ // 0 1
+ // 2 3
+ // 4 5
+ // 6 7
// FIXME fold this into the above?
if(!rgba_trans_p(*rgbbase_l0, bargs->transcolor)){
egcidx |= 1u;
fold_rgb8(&r, &g, &b, rgbbase_l0, &blends);
}
- if(!rgba_trans_p(*rgbbase_l1, bargs->transcolor)){
+ if(!rgba_trans_p(*rgbbase_r0, bargs->transcolor)){
egcidx |= 2u;
- fold_rgb8(&r, &g, &b, rgbbase_l1, &blends);
+ fold_rgb8(&r, &g, &b, rgbbase_r0, &blends);
}
- if(!rgba_trans_p(*rgbbase_l2, bargs->transcolor)){
+ if(!rgba_trans_p(*rgbbase_l1, bargs->transcolor)){
egcidx |= 4u;
- fold_rgb8(&r, &g, &b, rgbbase_l2, &blends);
+ fold_rgb8(&r, &g, &b, rgbbase_l1, &blends);
}
- if(!rgba_trans_p(*rgbbase_r0, bargs->transcolor)){
+ if(!rgba_trans_p(*rgbbase_r1, bargs->transcolor)){
egcidx |= 8u;
- fold_rgb8(&r, &g, &b, rgbbase_r0, &blends);
+ fold_rgb8(&r, &g, &b, rgbbase_r1, &blends);
}
- if(!rgba_trans_p(*rgbbase_r1, bargs->transcolor)){
+ if(!rgba_trans_p(*rgbbase_l2, bargs->transcolor)){
egcidx |= 16u;
- fold_rgb8(&r, &g, &b, rgbbase_r1, &blends);
+ fold_rgb8(&r, &g, &b, rgbbase_l2, &blends);
}
if(!rgba_trans_p(*rgbbase_r2, bargs->transcolor)){
egcidx |= 32u;
@@ -832,11 +915,7 @@ braille_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
if(blends){
nccell_set_fg_rgb8(c, r / blends, g / blends, b / blends);
}
- // UTF-8 encodings of the Braille Patterns are always 0xe2 0xaX 0xCC,
- // where 0 <= X <= 3 and 0x80 <= CC <= 0xbf (4 groups of 64).
- char egc[4] = { 0xe2, 0xa0, 0x80, 0x00 };
- egc[2] += egcidx % 64;
- egc[1] += egcidx / 64;
+ const char* egc = egcs[egcidx];
if(pool_blit_direct(&nc->pool, c, egc, strlen(egc), 1) <= 0){
return -1;
}
@@ -847,6 +926,18 @@ braille_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
return total;
}
+static inline int
+braille_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
+ const blitterargs* bargs){
+ return blit_4x2(nc, linesize, data, leny, lenx, bargs, braille_egcs);
+}
+
+static inline int
+octant_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
+ const blitterargs* bargs){
+ return blit_4x2(nc, linesize, data, leny, lenx, bargs, octant_egcs);
+}
+
// NCBLIT_DEFAULT is not included, as it has no defined properties. It ought
// be replaced with some real blitter implementation by the calling widget.
// The order of contents is critical for 'egcs': ncplane_as_rgba() uses these
@@ -867,6 +958,34 @@ static struct blitset notcurses_blitters[] = {
{ .geom = NCBLIT_3x2, .width = 2, .height = 3,
.egcs = NCSEXBLOCKS, .plotegcs = L" 🬞🬦▐🬏🬭🬵🬷🬓🬱🬹🬻▌🬲🬺█",
.blit = sextant_blit, .name = "sex", .fill = false, },
+ { .geom = NCBLIT_4x2, .width = 2, .height = 4,
+ .egcs = NCOCTBLOCKS,
+ .plotegcs = (L"\0x20"
+ L"\U0001cea0"
+ L"\U00002597"
+ L"\U0001CD96"
+ L"\U0001CD91"
+ L"\U0001CEA3"
+ L"\U00002582"
+ L"\U0001CDCB"
+ L"\U0001CDD3"
+ L"\U0001CDCD"
+ L"\U00002596"
+ L"\U0001CDBB"
+ L"\U00002584"
+ L"\U0001CDE1"
+ L"\U0001CDDC"
+ L"\U0001CD48"
+ L"\U0001CDBF"
+ L"\U0001CDDE"
+ L"\U00002586"
+ L"\U0001CDDF"
+ L"\U0000258C"
+ L"\U0001CDC0"
+ L"\U00002599"
+ L"\U0001CDE4"
+ L"\U0001CDE0"),
+ .blit = octant_blit, .name = "oct", .fill = false, },
{ .geom = NCBLIT_BRAILLE, .width = 2, .height = 4,
.egcs = NCBRAILLEEGCS,
.plotegcs = L"⠀⢀⢠⢰⢸⡀⣀⣠⣰⣸⡄⣄⣤⣴⣼⡆⣆⣦⣶⣾⡇⣇⣧⣷⣿",
@@ -898,13 +1017,22 @@ const struct blitset* lookup_blitset(const tinfo* tcache, ncblitter_e setid,
if(setid == NCBLIT_DEFAULT){ // ought have resolved NCBLIT_DEFAULT before now
return NULL;
}
- // without braille support, NCBLIT_BRAILLE decays to NCBLIT_3x2
+ // without braille support, NCBLIT_BRAILLE decays to NCBLIT_4x2
if(setid == NCBLIT_BRAILLE){
if(tcache->caps.braille){
return ¬curses_blitters[setid - 1];
}else if(!may_degrade){
return NULL;
}
+ setid = NCBLIT_4x2;
+ }
+ // without octant support, NCBLIT_4x2 decays to NCBLIT_3x2
+ if(setid == NCBLIT_4x2){
+ if(tcache->caps.octants){
+ return ¬curses_blitters[setid - 1];
+ }else if(!may_degrade){
+ return NULL;
+ }
setid = NCBLIT_3x2;
}
// without bitmap support, NCBLIT_PIXEL decays to NCBLIT_3x2
diff --git a/src/lib/blitset.h b/src/lib/blitset.h
index 69ed55e3c..554b54a50 100644
--- a/src/lib/blitset.h
+++ b/src/lib/blitset.h
@@ -35,6 +35,9 @@ rgba_blitter_default(const tinfo* tcache, ncscale_e scale){
if(scale == NCSCALE_NONE || scale == NCSCALE_SCALE){
return NCBLIT_2x1;
}
+ if(tcache->caps.octants){
+ return NCBLIT_4x2;
+ }
if(tcache->caps.sextants){
return NCBLIT_3x2;
}
diff --git a/src/poc/blitters.c b/src/poc/blitters.c
index ca383c46d..0b6f6f798 100644
--- a/src/poc/blitters.c
+++ b/src/poc/blitters.c
@@ -28,6 +28,7 @@ int main(int argc, char** argv){
NCBLIT_2x1, // full/(upper|left) blocks
NCBLIT_2x2, // quadrants
NCBLIT_3x2, // sextants
+ NCBLIT_4x2, // octants
NCBLIT_BRAILLE, // 4 rows, 2 cols (braille)
NCBLIT_PIXEL, // pixel graphics
-1,
diff --git a/src/tests/blit.cpp b/src/tests/blit.cpp
index 727ebf741..41aade6ff 100644
--- a/src/tests/blit.cpp
+++ b/src/tests/blit.cpp
@@ -10,6 +10,7 @@ TEST_CASE("Blit") {
SUBCASE("BlitterStrings") {
CHECK(0 == strcmp("pixel", notcurses_str_blitter(NCBLIT_PIXEL)));
+ CHECK(0 == strcmp("oct", notcurses_str_blitter(NCBLIT_4x2)));
CHECK(0 == strcmp("sex", notcurses_str_blitter(NCBLIT_3x2)));
CHECK(0 == strcmp("quad", notcurses_str_blitter(NCBLIT_2x2)));
CHECK(0 == strcmp("half", notcurses_str_blitter(NCBLIT_2x1)));
diff --git a/src/tests/plot.cpp b/src/tests/plot.cpp
index cc0f2e203..77bcf57ba 100644
--- a/src/tests/plot.cpp
+++ b/src/tests/plot.cpp
@@ -256,6 +256,33 @@ TEST_CASE("Plot") {
ncuplot_destroy(p);
}
+ SUBCASE("Octantlot1Row") {
+ ncplane_options nopts = {
+ .y = 1, .x = 1, .rows = 1, .cols = 25,
+ .userptr = nullptr, .name = "plot", .resizecb = nullptr, .flags = 0,
+ .margin_b = 0, .margin_r = 0,
+ };
+ auto ncp = ncplane_create(n_, &nopts);
+ REQUIRE(ncp);
+ ncplot_options popts;
+ memset(&popts, 0, sizeof(popts));
+ popts.maxchannels = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0, 0, 0);
+ popts.minchannels = NCCHANNELS_INITIALIZER(0, 0xff, 0, 0, 0, 0);
+ ncchannels_set_bg_alpha(&popts.minchannels, NCALPHA_BLEND);
+ ncchannels_set_fg_alpha(&popts.minchannels, NCALPHA_BLEND);
+ popts.gridtype = NCBLIT_4x2;
+ auto p = ncuplot_create(ncp, &popts, 0, 0);
+ REQUIRE(p);
+ for(auto i = 0 ; i < 5 ; ++i){
+ for(auto j = 0 ; j < 5 ; ++j){
+ CHECK(0 == ncuplot_add_sample(p, i * 10 + j * 2, i));
+ CHECK(0 == ncuplot_add_sample(p, i * 10 + j * 2 + 1, j));
+ }
+ }
+ CHECK(0 == notcurses_render(nc_));
+ ncuplot_destroy(p);
+ }
+
SUBCASE("BraillePlot1Row") {
ncplane_options nopts = {
.y = 1, .x = 1, .rows = 1, .cols = 25,