diff --git a/rust/src/visual.rs b/rust/src/visual.rs index 8f2d1ba04..6bcf49f7f 100644 --- a/rust/src/visual.rs +++ b/rust/src/visual.rs @@ -105,6 +105,7 @@ impl NcVisualOptions { lenx: NcDim, blitter: NcBlitter, flags: u64, + transcolor: u32, ) -> Self { Self { // provided plane @@ -123,6 +124,7 @@ impl NcVisualOptions { blitter, // bitmask over NCVISUAL_OPTION_* flags, + transcolor, } } @@ -135,6 +137,7 @@ impl NcVisualOptions { lenx: NcDim, blitter: NcBlitter, flags: u64, + transcolor: u32, ) -> Self { Self { n: null_mut(), @@ -152,6 +155,7 @@ impl NcVisualOptions { blitter, // bitmask over NCVISUAL_OPTION_* flags, + transcolor, } } diff --git a/src/lib/blit.c b/src/lib/blit.c index 277059848..f1535a755 100644 --- a/src/lib/blit.c +++ b/src/lib/blit.c @@ -31,6 +31,11 @@ trilerp(uint32_t c0, uint32_t c1, uint32_t c2){ return ret; } +static inline unsigned +rgba_trans_q(const unsigned char* p, uint32_t transcolor){ + return rgba_trans_p(*(const uint32_t*)p, transcolor); +} + // Retarded RGBA blitter (ASCII only). static inline int tria_blit_ascii(ncplane* nc, int linesize, const void* data, @@ -66,7 +71,7 @@ tria_blit_ascii(ncplane* nc, int linesize, const void* data, cell_set_bg_alpha(c, CELL_ALPHA_BLEND); cell_set_fg_alpha(c, CELL_ALPHA_BLEND); } - if(rgba_trans_p(rgbbase_up[3])){ + if(rgba_trans_q(rgbbase_up, bargs->transcolor)){ cell_set_bg_alpha(c, CELL_ALPHA_TRANSPARENT); cell_set_fg_alpha(c, CELL_ALPHA_TRANSPARENT); cell_set_blitquadrants(c, 0, 0, 0, 0); @@ -90,6 +95,7 @@ static inline int tria_blit(ncplane* nc, int linesize, const void* data, int leny, int lenx, const blitterargs* bargs){ //fprintf(stderr, "HALF %d X %d @ %d X %d (%p) place: %d X %d\n", leny, lenx, bargs->begy, bargs->begx, data, bargs->placey, bargs->placex); + uint32_t transcolor = bargs->transcolor; const int bpp = 32; int dimy, dimx, x, y; int total = 0; // number of cells written @@ -124,11 +130,11 @@ tria_blit(ncplane* nc, int linesize, const void* data, cell_set_bg_alpha(c, CELL_ALPHA_BLEND); cell_set_fg_alpha(c, CELL_ALPHA_BLEND); } - if(rgba_trans_p(rgbbase_up[3]) || rgba_trans_p(rgbbase_down[3])){ + if(rgba_trans_q(rgbbase_up, transcolor) || rgba_trans_q(rgbbase_down, transcolor)){ cell_set_bg_alpha(c, CELL_ALPHA_TRANSPARENT); - if(rgba_trans_p(rgbbase_up[3]) && rgba_trans_p(rgbbase_down[3])){ + if(rgba_trans_q(rgbbase_up, transcolor) && rgba_trans_q(rgbbase_down, transcolor)){ cell_set_fg_alpha(c, CELL_ALPHA_TRANSPARENT); - }else if(rgba_trans_p(rgbbase_up[3])){ // down has the color + }else if(rgba_trans_q(rgbbase_up, transcolor)){ // down has the color if(pool_blit_direct(&nc->pool, c, "\u2584", strlen("\u2584"), 1) <= 0){ return -1; } @@ -307,20 +313,21 @@ quadrant_solver(uint32_t tl, uint32_t tr, uint32_t bl, uint32_t br, static inline const char* qtrans_check(nccell* c, unsigned blendcolors, const unsigned char* rgbbase_tl, const unsigned char* rgbbase_tr, - const unsigned char* rgbbase_bl, const unsigned char* rgbbase_br){ + const unsigned char* rgbbase_bl, const unsigned char* rgbbase_br, + uint32_t transcolor){ uint32_t tl = 0, tr = 0, bl = 0, br = 0; channel_set_rgb8(&tl, rgbbase_tl[0], rgbbase_tl[1], rgbbase_tl[2]); channel_set_rgb8(&tr, rgbbase_tr[0], rgbbase_tr[1], rgbbase_tr[2]); channel_set_rgb8(&bl, rgbbase_bl[0], rgbbase_bl[1], rgbbase_bl[2]); channel_set_rgb8(&br, rgbbase_br[0], rgbbase_br[1], rgbbase_br[2]); const char* egc = NULL; - if(rgba_trans_p(rgbbase_tl[3])){ + if(rgba_trans_q(rgbbase_tl, transcolor)){ // top left is transparent - if(rgba_trans_p(rgbbase_tr[3])){ + if(rgba_trans_q(rgbbase_tr, transcolor)){ // all of top is transparent - if(rgba_trans_p(rgbbase_bl[3])){ + if(rgba_trans_q(rgbbase_bl, transcolor)){ // top and left are transparent - if(rgba_trans_p(rgbbase_br[3])){ + if(rgba_trans_q(rgbbase_br, transcolor)){ // entirety is transparent, load with nul (but not NULL) cell_set_fg_default(c); cell_set_blitquadrants(c, 0, 0, 0, 0); @@ -331,7 +338,7 @@ qtrans_check(nccell* c, unsigned blendcolors, egc = "▗"; } }else{ - if(rgba_trans_p(rgbbase_br[3])){ + if(rgba_trans_q(rgbbase_br, transcolor)){ cell_set_fg_rgb8(c, rgbbase_bl[0], rgbbase_bl[1], rgbbase_bl[2]); cell_set_blitquadrants(c, 0, 0, 1, 0); egc = "▖"; @@ -342,8 +349,8 @@ qtrans_check(nccell* c, unsigned blendcolors, } } }else{ // top right is foreground, top left is transparent - if(rgba_trans_p(rgbbase_bl[3])){ - if(rgba_trans_p(rgbbase_br[3])){ // entire bottom is transparent + if(rgba_trans_q(rgbbase_bl, transcolor)){ + if(rgba_trans_q(rgbbase_br, transcolor)){ // entire bottom is transparent cell_set_fg_rgb8(c, rgbbase_tr[0], rgbbase_tr[1], rgbbase_tr[2]); cell_set_blitquadrants(c, 0, 1, 0, 0); egc = "▝"; @@ -352,7 +359,7 @@ qtrans_check(nccell* c, unsigned blendcolors, cell_set_blitquadrants(c, 0, 1, 0, 1); egc = "▐"; } - }else if(rgba_trans_p(rgbbase_br[3])){ // only br is transparent + }else if(rgba_trans_q(rgbbase_br, transcolor)){ // only br is transparent cell_set_fchannel(c, lerp(tr, bl)); cell_set_blitquadrants(c, 0, 1, 1, 0); egc = "▞"; @@ -363,9 +370,9 @@ qtrans_check(nccell* c, unsigned blendcolors, } } }else{ // topleft is foreground for all here - if(rgba_trans_p(rgbbase_tr[3])){ - if(rgba_trans_p(rgbbase_bl[3])){ - if(rgba_trans_p(rgbbase_br[3])){ + if(rgba_trans_q(rgbbase_tr, transcolor)){ + if(rgba_trans_q(rgbbase_bl, transcolor)){ + if(rgba_trans_q(rgbbase_br, transcolor)){ cell_set_fg_rgb8(c, rgbbase_tl[0], rgbbase_tl[1], rgbbase_tl[2]); cell_set_blitquadrants(c, 1, 0, 0, 0); egc = "▘"; @@ -374,7 +381,7 @@ qtrans_check(nccell* c, unsigned blendcolors, cell_set_blitquadrants(c, 1, 0, 0, 1); egc = "▚"; } - }else if(rgba_trans_p(rgbbase_br[3])){ + }else if(rgba_trans_q(rgbbase_br, transcolor)){ cell_set_fchannel(c, lerp(tl, bl)); cell_set_blitquadrants(c, 1, 0, 1, 0); egc = "▌"; @@ -383,8 +390,8 @@ qtrans_check(nccell* c, unsigned blendcolors, cell_set_blitquadrants(c, 1, 0, 1, 1); egc = "▙"; } - }else if(rgba_trans_p(rgbbase_bl[3])){ - if(rgba_trans_p(rgbbase_br[3])){ // entire bottom is transparent + }else if(rgba_trans_q(rgbbase_bl, transcolor)){ + if(rgba_trans_q(rgbbase_br, transcolor)){ // entire bottom is transparent cell_set_fchannel(c, lerp(tl, tr)); cell_set_blitquadrants(c, 1, 1, 0, 0); egc = "▀"; @@ -393,7 +400,7 @@ qtrans_check(nccell* c, unsigned blendcolors, cell_set_blitquadrants(c, 1, 1, 0, 1); egc = "▜"; } - }else if(rgba_trans_p(rgbbase_br[3])){ // only br is transparent + }else if(rgba_trans_q(rgbbase_br, transcolor)){ // only br is transparent cell_set_fchannel(c, trilerp(tl, tr, bl)); cell_set_blitquadrants(c, 1, 1, 1, 0); egc = "▛"; @@ -454,7 +461,7 @@ quadrant_blit(ncplane* nc, int linesize, const void* data, nccell* c = ncplane_cell_ref_yx(nc, y, x); c->channels = 0; c->stylemask = 0; - const char* egc = qtrans_check(c, bargs->u.cell.blendcolors, rgbbase_tl, rgbbase_tr, rgbbase_bl, rgbbase_br); + const char* egc = qtrans_check(c, bargs->u.cell.blendcolors, rgbbase_tl, rgbbase_tr, rgbbase_bl, rgbbase_br, bargs->transcolor); if(egc == NULL){ uint32_t tl = 0, tr = 0, bl = 0, br = 0; channel_set_rgb8(&tl, rgbbase_tl[0], rgbbase_tl[1], rgbbase_tl[2]); @@ -587,7 +594,8 @@ sex_solver(const uint32_t rgbas[6], uint64_t* channels, unsigned blendcolors){ } static const char* -sex_trans_check(cell* c, const uint32_t rgbas[6], unsigned blendcolors){ +sex_trans_check(cell* c, const uint32_t rgbas[6], unsigned blendcolors, + uint32_t transcolor){ // bit is *set* where sextant *is not* // 32: bottom right 16: bottom left // 8: middle right 4: middle left @@ -606,7 +614,7 @@ sex_trans_check(cell* c, const uint32_t rgbas[6], unsigned blendcolors){ unsigned r = 0, g = 0, b = 0; unsigned div = 0; for(unsigned mask = 0 ; mask < 6 ; ++mask){ - if(rgba_trans_p(ncpixel_a(rgbas[mask]))){ + if(rgba_trans_p(rgbas[mask], transcolor)){ transstring |= (1u << mask); }else{ r += ncpixel_r(rgbas[mask]); @@ -684,7 +692,7 @@ sextant_blit(ncplane* nc, int linesize, const void* data, nccell* c = ncplane_cell_ref_yx(nc, y, x); c->channels = 0; c->stylemask = 0; - const char* egc = sex_trans_check(c, rgbas, bargs->u.cell.blendcolors); + const char* egc = sex_trans_check(c, rgbas, bargs->u.cell.blendcolors, bargs->transcolor); if(egc == NULL){ // no transparency; run a full solver egc = sex_solver(rgbas, &c->channels, bargs->u.cell.blendcolors); cell_set_blitquadrants(c, 1, 1, 1, 1); @@ -771,35 +779,35 @@ braille_blit(ncplane* nc, int linesize, const void* data, } } // FIXME fold this into the above? - if(!rgba_trans_p(ncpixel_a(*rgbbase_l0))){ + if(!rgba_trans_p(*rgbbase_l0, bargs->transcolor)){ egcidx |= 1u; fold_rgb8(&r, &g, &b, rgbbase_l0, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_l1))){ + if(!rgba_trans_p(*rgbbase_l1, bargs->transcolor)){ egcidx |= 2u; fold_rgb8(&r, &g, &b, rgbbase_l1, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_l2))){ + if(!rgba_trans_p(*rgbbase_l2, bargs->transcolor)){ egcidx |= 4u; fold_rgb8(&r, &g, &b, rgbbase_l2, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_r0))){ + if(!rgba_trans_p(*rgbbase_r0, bargs->transcolor)){ egcidx |= 8u; fold_rgb8(&r, &g, &b, rgbbase_r0, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_r1))){ + if(!rgba_trans_p(*rgbbase_r1, bargs->transcolor)){ egcidx |= 16u; fold_rgb8(&r, &g, &b, rgbbase_r1, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_r2))){ + if(!rgba_trans_p(*rgbbase_r2, bargs->transcolor)){ egcidx |= 32u; fold_rgb8(&r, &g, &b, rgbbase_r2, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_l3))){ + if(!rgba_trans_p(*rgbbase_l3, bargs->transcolor)){ egcidx |= 64u; fold_rgb8(&r, &g, &b, rgbbase_l3, &blends); } - if(!rgba_trans_p(ncpixel_a(*rgbbase_r3))){ + if(!rgba_trans_p(*rgbbase_r3, bargs->transcolor)){ egcidx |= 128u; fold_rgb8(&r, &g, &b, rgbbase_r3, &blends); } diff --git a/src/lib/internal.h b/src/lib/internal.h index 2c9301e11..300dc04b5 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -1370,10 +1370,17 @@ int drop_signals(void* nc); void ncvisual_printbanner(const notcurses* nc); // alpha comes to us 0--255, but we have only 3 alpha values to map them to. -// settled on experimentally. +// settled on experimentally. if transcolor is non-zero, match its lower 24 +// bits against the color, and treat a match as transparent. static inline bool -rgba_trans_p(unsigned alpha){ - if(alpha < 192){ +rgba_trans_p(uint32_t p, uint32_t transcolor){ + if(ncpixel_a(p) < 192){ + return true; + } + if(transcolor && + (ncpixel_r(p) == (transcolor & 0xff0000ull)) && + (ncpixel_g(p) == (transcolor & 0xff00ull)) && + (ncpixel_b(p) == (transcolor & 0xffull))){ return true; } return false; diff --git a/src/lib/kitty.c b/src/lib/kitty.c index c89d3fde9..f9f31edaa 100644 --- a/src/lib/kitty.c +++ b/src/lib/kitty.c @@ -53,12 +53,13 @@ b64idx(char b64){ // is only 1 pixel available, those 32 bits become 8 bytes. (pcount + 1) * 4 // bytes are used, plus a null terminator. we thus must receive 17. static inline void -base64_rgba3(const uint32_t* pixels, size_t pcount, char* b64, bool wipe[static 3]){ +base64_rgba3(const uint32_t* pixels, size_t pcount, char* b64, bool wipe[static 3], + uint32_t transcolor){ uint32_t pixel = *pixels++; unsigned r = ncpixel_r(pixel); unsigned g = ncpixel_g(pixel); unsigned b = ncpixel_b(pixel); - unsigned a = wipe[0] ? 0 : rgba_trans_p(ncpixel_a(pixel)) ? 0 : 255; + unsigned a = wipe[0] ? 0 : rgba_trans_p(pixel, transcolor) ? 0 : 255; //fprintf(stderr, "WIPE: %d %d %d\n", wipe[0], wipe[1], wipe[2]); b64[0] = b64subs[(r & 0xfc) >> 2]; b64[1] = b64subs[(r & 0x3 << 4) | ((g & 0xf0) >> 4)]; @@ -77,7 +78,7 @@ base64_rgba3(const uint32_t* pixels, size_t pcount, char* b64, bool wipe[static r = ncpixel_r(pixel); g = ncpixel_g(pixel); b = ncpixel_b(pixel); - a = wipe[1] ? 0 : rgba_trans_p(ncpixel_a(pixel)) ? 0 : 255; + a = wipe[1] ? 0 : rgba_trans_p(pixel, transcolor) ? 0 : 255; b64[5] = b64subs[b64[5] | ((r & 0xf0) >> 4)]; b64[6] = b64subs[((r & 0xf) << 2) | ((g & 0xc0) >> 6u)]; b64[7] = b64subs[g & 0x3f]; @@ -94,7 +95,7 @@ base64_rgba3(const uint32_t* pixels, size_t pcount, char* b64, bool wipe[static r = ncpixel_r(pixel); g = ncpixel_g(pixel); b = ncpixel_b(pixel); - a = wipe[2] ? 0 : rgba_trans_p(ncpixel_a(pixel)) ? 0 : 255; + a = wipe[2] ? 0 : rgba_trans_p(pixel, transcolor) ? 0 : 255; b64[10] = b64subs[b64[10] | ((r & 0xc0) >> 6)]; b64[11] = b64subs[r & 0x3f]; b64[12] = b64subs[(g & 0xfc) >> 2]; @@ -227,7 +228,8 @@ int sprite_kitty_cell_wipe(const notcurses* nc, sprixel* s, int ycell, int xcell static int write_kitty_data(FILE* fp, int linesize, int leny, int lenx, int cols, const uint32_t* data, int cdimy, int cdimx, - int sprixelid, sprixcell_e* tacache, int* parse_start){ + int sprixelid, sprixcell_e* tacache, int* parse_start, + uint32_t transcolor){ if(linesize % sizeof(*data)){ fclose(fp); return -1; @@ -271,7 +273,7 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, wipe[e] = 1; }else{ wipe[e] = 0; - if(rgba_trans_p(ncpixel_a(source[e]))){ + if(rgba_trans_p(source[e], transcolor)){ tacache[tyx] = SPRIXCELL_CONTAINS_TRANS; } } @@ -279,7 +281,7 @@ write_kitty_data(FILE* fp, int linesize, int leny, int lenx, } totalout += encodeable; char out[17]; - base64_rgba3(source, encodeable, out, wipe); + base64_rgba3(source, encodeable, out, wipe, transcolor); ncfputs(out, fp); } fprintf(fp, "\e\\"); @@ -326,7 +328,8 @@ int kitty_blit(ncplane* n, int linesize, const void* data, // closes fp on all paths if(write_kitty_data(fp, linesize, leny, lenx, cols, data, bargs->u.pixel.celldimy, bargs->u.pixel.celldimx, - bargs->u.pixel.spx->id, tacache, &parse_start)){ + bargs->u.pixel.spx->id, tacache, &parse_start, + bargs->transcolor)){ if(!reuse){ free(tacache); } diff --git a/src/lib/sixel.c b/src/lib/sixel.c index bb90a4be7..d8b8074fe 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -139,7 +139,7 @@ update_deets(uint32_t rgb, cdetails* deets){ static inline int extract_color_table(const uint32_t* data, int linesize, int begy, int begx, int cols, int leny, int lenx, int cdimy, int cdimx, sixeltable* stab, - sprixcell_e* tacache){ + sprixcell_e* tacache, uint32_t transcolor){ unsigned char mask = 0xc0; int pos = 0; // pixel position for(int visy = begy ; visy < (begy + leny) ; visy += 6){ // pixel row @@ -147,7 +147,7 @@ extract_color_table(const uint32_t* data, int linesize, int begy, int begx, int for(int sy = visy ; sy < (begy + leny) && sy < visy + 6 ; ++sy){ // offset within sprixel const uint32_t* rgb = (data + (linesize / 4 * sy) + visx); int txyidx = (sy / cdimy) * cols + (visx / cdimx); - if(rgba_trans_p(ncpixel_a(*rgb))){ + if(rgba_trans_p(ncpixel_a(*rgb), transcolor)){ if(tacache[txyidx] == SPRIXCELL_NORMAL){ tacache[txyidx] = SPRIXCELL_CONTAINS_TRANS; } @@ -460,7 +460,7 @@ int sixel_blit(ncplane* n, int linesize, const void* data, } if(extract_color_table(data, linesize, bargs->begy, bargs->begx, cols, leny, lenx, bargs->u.pixel.celldimy, bargs->u.pixel.celldimx, - &stable, tacache)){ + &stable, tacache, bargs->transcolor)){ if(!reuse){ free(tacache); }