Implement generic 4x2 blitter

This commit is contained in:
Erik Schnetter 2024-12-23 10:15:40 -05:00 committed by nick black
parent bf9c31db27
commit 531bdd0e8b

View File

@ -711,447 +711,87 @@ sextant_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
return total;
}
// Bit is *set* where octant *is*:
// Bit is set where octant is present:
// 0 1
// 2 3
// 4 5
// 6 7
// Same as NCOCTBLOCKS but as array of characters
static const char* const octant_egcs[256] = {
"\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",
// 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",
};
// Solve for the cell rendered by this 4x2 sample. None of the input pixels may
// be transparent (that ought already have been handled). We use exhaustive
// search, which might be quite computationally intensive for the worst case
// (all eight pixels are different colors). We want to solve for the 2-partition
// of pixels that minimizes total source distance from the resulting lerps.
static const char*
oct_solver(const uint32_t rgbas[8], uint64_t* channels, unsigned blendcolors,
unsigned nointerpolate){
// Each element within the set of 256 has an inverse element within
// the set, for which we would calculate the same total differences,
// so just handle the first 128.
//
// We loop over the bitstrings, dividing the pixels into two sets,
// and then taking a general lerp over each set. we then compute the
// sum of absolute differences, and see if it's the new minimum.
int best = -1;
uint32_t mindiff = UINT_MAX;
for(size_t glyph = 0; glyph < 128; ++glyph){
unsigned rsum0 = 0, rsum1 = 0;
unsigned gsum0 = 0, gsum1 = 0;
unsigned bsum0 = 0, bsum1 = 0;
int insum = 0;
int outsum = 0;
for(unsigned mask = 0 ; mask < 8 ; ++mask){
if(glyph & (1u << mask)){
if(!nointerpolate || !insum){
rsum0 += ncpixel_r(rgbas[mask]);
gsum0 += ncpixel_g(rgbas[mask]);
bsum0 += ncpixel_b(rgbas[mask]);
++insum;
}
}else{
if(!nointerpolate || !outsum){
rsum1 += ncpixel_r(rgbas[mask]);
gsum1 += ncpixel_g(rgbas[mask]);
bsum1 += ncpixel_b(rgbas[mask]);
++outsum;
}
}
}
uint32_t l0 = generalerp(rsum0, gsum0, bsum0, insum);
uint32_t l1 = generalerp(rsum1, gsum1, bsum1, outsum);
uint32_t totaldiff = 0;
for(unsigned mask = 0 ; mask < 8 ; ++mask){
unsigned r, g, b;
if(glyph & (1u << mask)){
ncchannel_rgb8(l0, &r, &g, &b);
}else{
ncchannel_rgb8(l1, &r, &g, &b);
}
uint32_t rdiff = rgb_diff(ncpixel_r(rgbas[mask]), ncpixel_g(rgbas[mask]),
ncpixel_b(rgbas[mask]), r, g, b);
totaldiff += rdiff;
}
if(totaldiff < mindiff){
mindiff = totaldiff;
best = glyph;
ncchannels_set_fchannel(channels, l0);
ncchannels_set_bchannel(channels, l1);
}
if(totaldiff == 0){ // can't beat that!
break;
}
}
assert(best >= 0 && best < 128);
if(blendcolors){
ncchannels_set_fg_alpha(channels, NCALPHA_BLEND);
ncchannels_set_bg_alpha(channels, NCALPHA_BLEND);
}
return octant_egcs[best];
}
static const char*
oct_trans_check(nccell* c, const uint32_t rgbas[8], unsigned blendcolors,
uint32_t transcolor, unsigned nointerpolate){
unsigned transstring = 0;
unsigned r = 0, g = 0, b = 0;
unsigned div = 0;
for(unsigned mask = 0 ; mask < 8 ; ++mask){
if(rgba_trans_p(rgbas[mask], transcolor)){
transstring |= (1u << mask);
}else if(!nointerpolate || !div){
r += ncpixel_r(rgbas[mask]);
g += ncpixel_g(rgbas[mask]);
b += ncpixel_b(rgbas[mask]);
++div;
}
}
if(transstring == 0){ // there was no transparency
return NULL;
}
nccell_set_bg_alpha(c, NCALPHA_TRANSPARENT);
// there were some transparent pixels. since they get priority, the foreground
// is just a general lerp across non-transparent pixels.
const char* egc = octant_egcs[transstring ^ 0xff];
nccell_set_bg_alpha(c, NCALPHA_TRANSPARENT);
//fprintf(stderr, "transtring: %u egc: %s\n", transtring, egc);
if(*egc == ' '){ // entirely transparent
nccell_set_fg_alpha(c, NCALPHA_TRANSPARENT);
return "";
}else{ // partially transparent, thus div >= 1
//fprintf(stderr, "div: %u r: %u g: %u b: %u\n", div, r, g, b);
cell_set_fchannel(c, generalerp(r, g, b, div));
if(blendcolors){
nccell_set_fg_alpha(c, NCALPHA_BLEND);
}
cell_set_blitquadrants(c, !(transstring & 5u), !(transstring & 10u),
!(transstring & 20u), !(transstring & 40u));
}
//fprintf(stderr, "OCT-BQ: 0x%x\n", cell_blittedquadrants(c));
return egc;
}
// octant blitter. maps 4x2 to each cell. since we only have two colors at
// our disposal (foreground and background), we lose some fidelity.
static inline int
octant_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx,
const blitterargs* bargs){
const unsigned nointerpolate = bargs->flags & NCVISUAL_OPTION_NOINTERPOLATE;
const bool blendcolors = bargs->flags & NCVISUAL_OPTION_BLEND;
unsigned dimy, dimx, x, y;
int total = 0; // number of cells written
ncplane_dim_yx(nc, &dimy, &dimx);
//fprintf(stderr, "octblitter %dx%d -> %d/%d+%d/%d\n", leny, lenx, dimy, dimx, bargs->u.cell.placey, bargs->u.cell.placex);
const unsigned char* dat = data;
int visy = bargs->begy;
for(y = bargs->u.cell.placey ; visy < (bargs->begy + leny) && y < dimy ; ++y, visy += 4){
if(ncplane_cursor_move_yx(nc, y, bargs->u.cell.placex < 0 ? 0 : bargs->u.cell.placex)){
return -1;
}
int visx = bargs->begx;
for(x = bargs->u.cell.placex ; visx < (bargs->begx + lenx) && x < dimx ; ++x, visx += 2){
uint32_t rgbas[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
memcpy(&rgbas[0], (dat + (linesize * visy) + (visx * 4)), sizeof(*rgbas));
if(visx < bargs->begx + lenx - 1){
memcpy(&rgbas[1], (dat + (linesize * visy) + ((visx + 1) * 4)), sizeof(*rgbas));
if(visy < bargs->begy + leny - 1){
memcpy(&rgbas[3], (dat + (linesize * (visy + 1)) + ((visx + 1) * 4)), sizeof(*rgbas));
if(visy < bargs->begy + leny - 2){
memcpy(&rgbas[5], (dat + (linesize * (visy + 2)) + ((visx + 1) * 4)), sizeof(*rgbas));
if(visy < bargs->begy + leny - 3){
memcpy(&rgbas[7], (dat + (linesize * (visy + 3)) + ((visx + 1) * 4)), sizeof(*rgbas));
}
}
}
}
if(visy < bargs->begy + leny - 1){
memcpy(&rgbas[2], (dat + (linesize * (visy + 1)) + (visx * 4)), sizeof(*rgbas));
if(visy < bargs->begy + leny - 2){
memcpy(&rgbas[4], (dat + (linesize * (visy + 2)) + (visx * 4)), sizeof(*rgbas));
if(visy < bargs->begy + leny - 3){
memcpy(&rgbas[6], (dat + (linesize * (visy + 3)) + (visx * 4)), sizeof(*rgbas));
}
}
}
nccell* c = ncplane_cell_ref_yx(nc, y, x);
c->channels = 0;
c->stylemask = 0;
const char* egc = oct_trans_check(c, rgbas, blendcolors, bargs->transcolor, nointerpolate);
if(egc == NULL){ // no transparency; run a full solver
egc = oct_solver(rgbas, &c->channels, blendcolors, nointerpolate);
cell_set_blitquadrants(c, 1, 1, 1, 1);
}
//fprintf(stderr, "oct EGC: %s channels: %016lx\n", egc, c->channels);
if(*egc){
if(pool_blit_direct(&nc->pool, c, egc, strlen(egc), 1) <= 0){
return -1;
}
++total;
}else{
nccell_release(nc, c);
}
}
}
return total;
}
// 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
@ -1164,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
@ -1216,32 +857,32 @@ 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
// 7 8
// 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)){
egcidx |= 2u;
fold_rgb8(&r, &g, &b, rgbbase_l1, &blends);
}
if(!rgba_trans_p(*rgbbase_l2, bargs->transcolor)){
egcidx |= 4u;
fold_rgb8(&r, &g, &b, rgbbase_l2, &blends);
}
if(!rgba_trans_p(*rgbbase_r0, bargs->transcolor)){
egcidx |= 8u;
egcidx |= 2u;
fold_rgb8(&r, &g, &b, rgbbase_r0, &blends);
}
if(!rgba_trans_p(*rgbbase_l1, bargs->transcolor)){
egcidx |= 4u;
fold_rgb8(&r, &g, &b, rgbbase_l1, &blends);
}
if(!rgba_trans_p(*rgbbase_r1, bargs->transcolor)){
egcidx |= 16u;
egcidx |= 8u;
fold_rgb8(&r, &g, &b, rgbbase_r1, &blends);
}
if(!rgba_trans_p(*rgbbase_l2, bargs->transcolor)){
egcidx |= 16u;
fold_rgb8(&r, &g, &b, rgbbase_l2, &blends);
}
if(!rgba_trans_p(*rgbbase_r2, bargs->transcolor)){
egcidx |= 32u;
fold_rgb8(&r, &g, &b, rgbbase_r2, &blends);
@ -1274,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;
}
@ -1289,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
@ -1317,23 +966,23 @@ static struct blitset notcurses_blitters[] = {
L"\U0001CD96"
L"\U0001CD91"
L"\U0001CEA3"
L"\U00002582"
L"\U00002582"
L"\U0001CDCB"
L"\U0001CDD3"
L"\U0001CDCD"
L"\U00002596"
L"\U00002596"
L"\U0001CDBB"
L"\U00002584"
L"\U00002584"
L"\U0001CDE1"
L"\U0001CDDC"
L"\U0001CD48"
L"\U0001CDBF"
L"\U0001CDDE"
L"\U00002586"
L"\U00002586"
L"\U0001CDDF"
L"\U0000258C"
L"\U0000258C"
L"\U0001CDC0"
L"\U00002599"
L"\U00002599"
L"\U0001CDE4"
L"\U0001CDE0"),
.blit = octant_blit, .name = "oct", .fill = false, },