first pass at fast blending

dankamongmen/speedy-blend
nick black 4 years ago
parent 900dd20e1c
commit e944319019
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -678,26 +678,24 @@ void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols);
// them, we just don't fuck wit' 'em here. Do not pass me palette-indexed
// channels! I will eat them.
static inline unsigned
channels_blend(unsigned c1, unsigned c2, unsigned* blends){
channels_blend(uint32_t c1, uint32_t c2, unsigned* blends, unsigned* rsum,
unsigned* gsum, unsigned* bsum){
if(channel_alpha(c2) == CELL_ALPHA_TRANSPARENT){
return c1; // do *not* increment *blends
}
unsigned rsum, gsum, bsum;
channel_rgb(c2, &rsum, &gsum, &bsum);
bool c2default = channel_default_p(c2);
if(*blends == 0){
// don't just return c2, or you set wide status and all kinds of crap
if(channel_default_p(c2)){
channel_set_default(&c1);
}else{
channel_set_rgb(&c1, rsum, gsum, bsum);
channel_rgb(c2, rsum, gsum, bsum);
}
channel_set_alpha(&c1, channel_alpha(c2));
}else if(!c2default && !channel_default_p(c1)){
rsum = (channel_r(c1) * *blends + rsum) / (*blends + 1);
gsum = (channel_g(c1) * *blends + gsum) / (*blends + 1);
bsum = (channel_b(c1) * *blends + bsum) / (*blends + 1);
channel_set_rgb(&c1, rsum, gsum, bsum);
*rsum += channel_r(c2);
*gsum += channel_g(c2);
*bsum += channel_b(c2);
channel_set_alpha(&c1, channel_alpha(c2));
}
++*blends;
@ -706,13 +704,17 @@ channels_blend(unsigned c1, unsigned c2, unsigned* blends){
// do not pass palette-indexed channels!
static inline uint64_t
cell_blend_fchannel(cell* cl, unsigned channel, unsigned* blends){
return cell_set_fchannel(cl, channels_blend(cell_fchannel(cl), channel, blends));
cell_blend_fchannel(cell* cl, unsigned channel, struct crender* cr){
return cell_set_fchannel(cl, channels_blend(cell_fchannel(cl), channel,
&cr->fgblends, &cr->frsum,
&cr->fgsum, &cr->fbsum));
}
static inline uint64_t
cell_blend_bchannel(cell* cl, unsigned channel, unsigned* blends){
return cell_set_bchannel(cl, channels_blend(cell_bchannel(cl), channel, blends));
cell_blend_bchannel(cell* cl, unsigned channel, struct crender* cr){
return cell_set_bchannel(cl, channels_blend(cell_bchannel(cl), channel,
&cr->bgblends, &cr->brsum,
&cr->bgsum, &cr->bbsum));
}
#ifdef __cplusplus

@ -136,6 +136,7 @@ cellcmp_and_dupfar(const notcurses* nc, egcpool* dampool, cell* damcell,
const ncplane* srcplane, const cell* srccell){
bool srcsimple = cell_simple_p(srccell);
if(!srcsimple && !enforce_utf8(nc)){
// FIXME
}
if(damcell->attrword == srccell->attrword){
if(damcell->channels == srccell->channels){
@ -208,22 +209,38 @@ highcontrast(uint32_t bchannel){
return conrgb;
}
// adjust an otherwise locked-in cell if highcontrast has been requested. this
// should be done at the end of rendering the cell, so that contrast is solved
// against the real background.
// Finalize color of a cell. adjust an otherwise locked-in cell if highcontrast
// has been requested, and divide down the blended color sums. this should be
// done at the end of rendering the cell, so that contrast is solved against
// the real background.
static inline void
lock_in_highcontrast(cell* targc, struct crender* crender){
if(crender->highcontrast){
// highcontrast weighs the original at 1/4 and the contrast at 3/4
if(!cell_fg_default_p(targc)){
crender->fgblends = 3;
uint32_t fchan = cell_fchannel(targc);
uint32_t bchan = cell_bchannel(targc);
cell_set_fchannel(targc, channels_blend(highcontrast(bchan), fchan, &crender->fgblends));
// give highcontrast-adjusted color the influence of 3 planes
// FIXME this ought just be 2/3 or something, independent of plane #
for(int i = 0 ; i < 3 ; ++i){
cell_set_fchannel(targc, channels_blend(highcontrast(bchan), fchan,
&crender->fgblends, &crender->frsum,
&crender->fgsum, &crender->fbsum));
}
}else{
cell_set_fg(targc, highcontrast(cell_bchannel(targc)));
}
}
if(crender->fgblends){
cell_set_fg_rgb(targc, crender->frsum / crender->fgblends,
crender->fgsum / crender->fgblends,
crender->fbsum / crender->fgblends);
}
if(crender->bgblends){
cell_set_fg_rgb(targc, crender->frsum / crender->fgblends,
crender->fgsum / crender->fgblends,
crender->fbsum / crender->fgblends);
}
}
// Paints a single ncplane into the provided scratch framebuffer 'fb', and
@ -314,7 +331,7 @@ paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec,
cell_set_bg_palindex(targc, cell_bg_palindex(vis));
}
}else if(cell_bg_alpha(targc) > CELL_ALPHA_OPAQUE){
cell_blend_bchannel(targc, cell_bchannel(vis), &crender->bgblends);
cell_blend_bchannel(targc, cell_bchannel(vis), crender);
}
// Evaluate the background first, in case this is HIGHCONTRAST fg text.
if(cell_fg_palindex_p(vis)){
@ -325,7 +342,7 @@ paint(notcurses* nc, ncplane* p, cell* lastframe, struct crender* rvec,
if(cell_fg_alpha(vis) == CELL_ALPHA_HIGHCONTRAST){
crender->highcontrast = true;
}
cell_blend_fchannel(targc, cell_fchannel(vis), &crender->fgblends);
cell_blend_fchannel(targc, cell_fchannel(vis), crender);
// crender->highcontrast can only be true if we just set it, since we're
// about to set targc opaque based on crender->highcontrast (and this
// entire stanza is conditional on targc not being CELL_ALPHA_OPAQUE).

@ -69,19 +69,18 @@ TEST_CASE("ChannelSetDefault") {
// blend of 0 ought set c1 to c2
TEST_CASE("ChannelBlend0") {
struct crender cr{};
uint32_t c1 = 0;
uint32_t c2 = 0;
channel_set_rgb(&c1, 0x80, 0x40, 0x20);
channel_set_rgb(&c2, 0x88, 0x44, 0x22);
unsigned blends = 0;
uint32_t c = channels_blend(c1, c2, &blends);
uint32_t c = channels_blend(c1, c2, &cr.fgblends, &cr.frsum, &cr.fgsum, &cr.fbsum);
CHECK(!channel_default_p(c));
unsigned r, g, b;
channel_rgb(c, &r, &g, &b);
CHECK(0x88 == r);
CHECK(0x44 == g);
CHECK(0x22 == b);
CHECK(1 == blends);
CHECK(0x88 == cr.frsum);
CHECK(0x44 == cr.fgsum);
CHECK(0x22 == cr.fbsum);
CHECK(1 == cr.fgblends);
}
// blend of 1 ought perfectly average c1 and c2

Loading…
Cancel
Save