From 531bdd0e8be3f63a6b8f6791f391b337fc065f69 Mon Sep 17 00:00:00 2001 From: Erik Schnetter Date: Mon, 23 Dec 2024 10:15:40 -0500 Subject: [PATCH] Implement generic 4x2 blitter --- src/lib/blit.c | 579 ++++++++++--------------------------------------- 1 file changed, 114 insertions(+), 465 deletions(-) diff --git a/src/lib/blit.c b/src/lib/blit.c index bcf09a691..f8636cc7e 100644 --- a/src/lib/blit.c +++ b/src/lib/blit.c @@ -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,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 - // 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)){ + 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; @@ -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, },