high-contrast text, simple linear average model #181

pull/331/head
nick black 5 years ago committed by Nick Black
parent 1ed6000193
commit 3cdac5ce28

@ -66,7 +66,7 @@ namespace ncpp
return cell_set_fchannel (&_cell, channel);
}
uint64_t blend_fchannel (unsigned channel, unsigned blends) noexcept
uint64_t blend_fchannel (unsigned channel, unsigned* blends) noexcept
{
return cell_blend_fchannel (&_cell, channel, blends);
}
@ -76,7 +76,7 @@ namespace ncpp
return cell_set_bchannel (&_cell, channel);
}
uint64_t blend_bchannel (unsigned channel, unsigned blends) noexcept
uint64_t blend_bchannel (unsigned channel, unsigned* blends) noexcept
{
return cell_blend_bchannel (&_cell, channel, blends);
}

@ -1240,24 +1240,29 @@ channels_set_bg_default(uint64_t* channels){
// 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(unsigned c1, unsigned c2, unsigned* blends){
if(channel_alpha(c2) == CELL_ALPHA_TRANSPARENT){
return c1; // do *not* increment *blends
}
unsigned rsum, gsum, bsum;
if(blends == 0){
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_rgb(c2, &rsum, &gsum, &bsum);
channel_set_rgb(&c1, rsum, gsum, bsum);
}
channel_set_alpha(&c1, channel_alpha(c2));
}else if(!channel_default_p(c2) && !channel_default_p(c1)){
rsum = (channel_r(c1) * blends + channel_r(c2)) / (blends + 1);
gsum = (channel_g(c1) * blends + channel_g(c2)) / (blends + 1);
bsum = (channel_b(c1) * blends + channel_b(c2)) / (blends + 1);
}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);
channel_set_alpha(&c1, channel_alpha(c2));
}
++*blends;
return c1;
}
@ -1287,12 +1292,12 @@ cell_set_fchannel(cell* cl, uint32_t channel){
// do not pass palette-indexed channels!
static inline uint64_t
cell_blend_fchannel(cell* cl, unsigned channel, unsigned blends){
cell_blend_fchannel(cell* cl, unsigned channel, unsigned* blends){
return cell_set_fchannel(cl, channels_blend(cell_fchannel(cl), channel, blends));
}
static inline uint64_t
cell_blend_bchannel(cell* cl, unsigned channel, unsigned blends){
cell_blend_bchannel(cell* cl, unsigned channel, unsigned* blends){
return cell_set_bchannel(cl, channels_blend(cell_bchannel(cl), channel, blends));
}

@ -3,9 +3,8 @@
// assign to r/g/b the minimum permutation summing to total, inc total by step
static void
minimize(unsigned *total, unsigned *r, unsigned *g, unsigned *b, unsigned step){
*total += step;
*b = *r >= 256 ? 256 : *r + step;
*r = *total - (*b + *g);
*r = (*total += step) - (*b + *g);
}
// derive the next color based on current state. *'total' ranges from 0 to 768
@ -14,7 +13,7 @@ minimize(unsigned *total, unsigned *r, unsigned *g, unsigned *b, unsigned step){
// to 256, the encoded values top out at 255 ({255, 256} -> 255). at all times,
// *'r' + *'g' + *'b' must sum to *'total'. on input, rgb specifies the return
// value. on output, rgb specifies the next return value, and total might be
// changed. if total was changed, r is miminimized, and g is then minimized,
// changed. if total was changed, r is minimized, and g is then minimized,
// subject to the total. thus we twist and shout through rgb space by 'step'.
static unsigned
generate_next_color(unsigned *total, unsigned *r, unsigned *g, unsigned *b,
@ -48,13 +47,15 @@ generate_next_color(unsigned *total, unsigned *r, unsigned *g, unsigned *b,
}else{
if(*r == 256 && *g == 256){
minimize(total, r, g, b, step);
}else if(*g == 256){
*r += step;
*b = 256;
}else{
*b -= step;
if(*g == 256){
*r += step;
*b = 256;
}else{
*b -= step;
}
*g = *total - (*r + *b);
}
*g = *total - (*r + *b);
}
return ret;
}
@ -83,7 +84,7 @@ int highcontrast_demo(struct notcurses* nc){
// right. start at the upper left, from the logical beginning of the array.
cell c = CELL_TRIVIAL_INITIALIZER;
cell_set_fg_alpha(&c, CELL_ALPHA_HIGHCONTRAST);
const char motto[] = "is this the high-test? it's that WMD. it *will* mass-destruct your ass. ";
const char motto[] = "high contrast text ";
do{
unsigned idx = iter % totcells; // first color for upper-left
for(int yx = 0 ; yx < dimy * dimx ; ++yx){
@ -95,7 +96,7 @@ int highcontrast_demo(struct notcurses* nc){
idx = (idx + 1) % totcells;
}
scrcolors[iter++ % totcells] = generate_next_color(&total, &r, &g, &b, STEP);
if(notcurses_render(nc)){
if(demo_render(nc)){
goto err;
}
}while(total <= 768);

@ -190,12 +190,45 @@ cell_locked_p(const cell* p){
// Extracellular state for a cell during the render process
struct crender {
int fgblends;
int bgblends;
unsigned fgblends;
unsigned bgblends;
ncplane *p;
bool damaged;
};
// Emit fchannel with RGB changed to contrast effectively against bchannel.
static uint32_t
highcontrast(uint32_t bchannel){
uint32_t rchannel = 0;
unsigned r = channel_r(bchannel);
unsigned g = channel_g(bchannel);
unsigned b = channel_b(bchannel);
//float lumi = 0.2126 * r + 0.7152 * g + 0.0722 * b;
/*
unsigned max = r > g ? r > b ? r : b : g > b ? g : b;
unsigned min = r < g ? r < b ? r : b : g < b ? g : b;
float rrgb = r / 255.0;
float grgb = g / 255.0;
float brgb = b / 255.0;
float rrel = rrgb <= 0.03928 ? rrgb / 12.92 : pow(((rrgb + 0.055) / 1.055), 2.4);
float grel = grgb <= 0.03928 ? grgb / 12.92 : pow(((grgb + 0.055) / 1.055), 2.4);
float brel = brgb <= 0.03928 ? brgb / 12.92 : pow(((brgb + 0.055) / 1.055), 2.4);
max = !max ? 1 : max;
unsigned sat = 10 * (max - min) / max;
if(sat < 3){
channel_set(&rchannel, 0xffffff);
}else{
channel_set(&rchannel, 0x0);
}
*/
if(r + g + b < 384){
channel_set(&rchannel, 0xffffff);
}else{
channel_set(&rchannel, 0x0);
}
return rchannel;
}
// Paints a single ncplane into the provided framebuffer 'fb'. Whenever a cell
// is locked in, it is compared against the last frame. If it is different, the
// 'damagevec' bitmap is updated with a 1.
@ -248,8 +281,8 @@ paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){
// still use a character we find here, but its color will come entirely
// from cells underneath us.
if(!crender->p){
// if the following is true, we're a real glyph, and not the right-h
// hand side of a wide glyph (or the null codepoint).
// if the following is true, we're a real glyph, and not the right-hand
// side of a wide glyph (or the null codepoint).
if( (targc->gcluster = vis->gcluster) ){ // index copy only
// we can't plop down a wide glyph if the next cell is beyond the
// screen, nor if we're bisected by a higher plane.
@ -271,14 +304,7 @@ paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){
cell_set_wide(targc);
}
}
if(cell_fg_palindex_p(vis)){
if(cell_fg_alpha(targc) == CELL_ALPHA_TRANSPARENT){
cell_set_fg_palindex(targc, cell_fg_palindex(vis));
}
}else if(cell_fg_alpha(targc) > CELL_ALPHA_OPAQUE && cell_fg_alpha(vis) < CELL_ALPHA_TRANSPARENT){
cell_blend_fchannel(targc, cell_fchannel(vis), crender->fgblends);
++crender->fgblends;
}
// Background color takes effect independently of whether we have a
// glyph. If we've already locked in the background, it has no effect.
// If it's transparent, it has no effect. Otherwise, update the
@ -287,9 +313,20 @@ paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){
if(cell_bg_alpha(targc) == CELL_ALPHA_TRANSPARENT){
cell_set_bg_palindex(targc, cell_bg_palindex(vis));
}
}else if(cell_bg_alpha(targc) > CELL_ALPHA_OPAQUE && cell_bg_alpha(vis) < CELL_ALPHA_TRANSPARENT){
cell_blend_bchannel(targc, cell_bchannel(vis), crender->bgblends);
++crender->bgblends;
}else if(cell_bg_alpha(targc) > CELL_ALPHA_OPAQUE){
cell_blend_bchannel(targc, cell_bchannel(vis), &crender->bgblends);
}
// Evaluate the background first, in case this is HIGHCONTRAST fg text.
if(cell_fg_palindex_p(vis)){
if(cell_fg_alpha(targc) == CELL_ALPHA_TRANSPARENT){
cell_set_fg_palindex(targc, cell_fg_palindex(vis));
}
}else if(cell_fg_alpha(targc) > CELL_ALPHA_OPAQUE){
uint32_t vchannel = cell_fchannel(vis);
if(cell_fg_alpha(vis) == CELL_ALPHA_HIGHCONTRAST){
vchannel = highcontrast(cell_bchannel(targc));
}
cell_blend_fchannel(targc, vchannel, &crender->fgblends);
}
if(cell_locked_p(targc)){

@ -102,10 +102,12 @@ SUBCASE("SetItalic") {
CHECK(0 > cell_set_fg_alpha(&c, -1));
CHECK(0 > cell_set_fg_alpha(&c, 4));
CHECK(0 == cell_set_fg_alpha(&c, CELL_ALPHA_OPAQUE));
CHECK(cell_fg_default_p(&c));
CHECK(cell_bg_default_p(&c));
CHECK(CELL_ALPHA_OPAQUE == cell_fg_alpha(&c));
CHECK(0 == cell_set_fg_alpha(&c, CELL_ALPHA_HIGHCONTRAST));
CHECK(CELL_ALPHA_HIGHCONTRAST == cell_fg_alpha(&c));
CHECK(cell_fg_default_p(&c));
CHECK(!cell_fg_default_p(&c));
CHECK(cell_bg_default_p(&c));
}

@ -73,13 +73,15 @@ TEST_CASE("ChannelBlend0") {
uint32_t c2 = 0;
channel_set_rgb(&c1, 0x80, 0x40, 0x20);
channel_set_rgb(&c2, 0x88, 0x44, 0x22);
uint32_t c = channels_blend(c1, c2, 0);
unsigned blends = 0;
uint32_t c = channels_blend(c1, c2, &blends);
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);
}
// blend of 1 ought perfectly average c1 and c2
@ -88,13 +90,15 @@ TEST_CASE("ChannelBlend1") {
uint32_t c2 = 0;
channel_set_rgb(&c1, 0x80, 0x40, 0x20);
channel_set_rgb(&c2, 0x0, 0x0, 0x0);
uint32_t c = channels_blend(c1, c2, 1);
unsigned blends = 1;
uint32_t c = channels_blend(c1, c2, &blends);
CHECK(!channel_default_p(c));
unsigned r, g, b;
channel_rgb(c, &r, &g, &b);
CHECK(0x40 == r);
CHECK(0x20 == g);
CHECK(0x10 == b);
CHECK(2 == blends);
}
// blend of 2 ought weigh c1 twice as much as c2
@ -103,13 +107,15 @@ TEST_CASE("ChannelBlend2") {
uint32_t c2 = 0;
channel_set_rgb(&c1, 0x60, 0x30, 0x0f);
channel_set_rgb(&c2, 0x0, 0x0, 0x0);
uint32_t c = channels_blend(c1, c2, 2);
unsigned blends = 2;
uint32_t c = channels_blend(c1, c2, &blends);
CHECK(!channel_default_p(c));
unsigned r, g, b;
channel_rgb(c, &r, &g, &b);
CHECK(0x40 == r);
CHECK(0x20 == g);
CHECK(0x0a == b);
CHECK(3 == blends);
}
// you can't blend into a default color at any positive number of blends
@ -117,25 +123,29 @@ TEST_CASE("ChannelBlendDefaultLeft") {
uint32_t c1 = 0;
uint32_t c2 = 0;
channel_set_rgb(&c2, 0x80, 0x40, 0x20);
uint32_t c = channels_blend(c1, c2, 0); // will replace
unsigned blends = 0;
uint32_t c = channels_blend(c1, c2, &blends); // will replace
CHECK(!channel_default_p(c));
unsigned r, g, b;
channel_rgb(c, &r, &g, &b);
CHECK(0x80 == r);
CHECK(0x40 == g);
CHECK(0x20 == b);
c = channels_blend(c1, c2, 1); // will not replace
CHECK(1 == blends);
c = channels_blend(c1, c2, &blends); // will not replace
CHECK(channel_default_p(c));
channel_rgb(c, &r, &g, &b);
CHECK(0 == r);
CHECK(0 == g);
CHECK(0 == b);
c = channels_blend(c1, c2, 2); // will not replace
CHECK(2 == blends);
c = channels_blend(c1, c2, &blends); // will not replace
CHECK(channel_default_p(c));
channel_rgb(c, &r, &g, &b);
CHECK(0 == r);
CHECK(0 == g);
CHECK(0 == b);
CHECK(3 == blends);
}
// you can't blend from a default color, but blend 0 sets it
@ -145,13 +155,15 @@ TEST_CASE("ChannelBlendDefaultRight") {
channel_set_rgb(&c1, 0x80, 0x40, 0x20);
CHECK(!channel_default_p(c1));
CHECK(channel_default_p(c2));
uint32_t c = channels_blend(c1, c2, 0);
unsigned blends;
uint32_t c = channels_blend(c1, c2, &blends);
CHECK(channel_default_p(c));
c = channels_blend(c1, c2, 1);
c = channels_blend(c1, c2, &blends);
CHECK(!channel_default_p(c));
unsigned r, g, b;
channel_rgb(c, &r, &g, &b);
CHECK(0x80 == r);
CHECK(0x40 == g);
CHECK(0x20 == b);
CHECK(2 == blends);
}

@ -107,9 +107,11 @@ TEST_CASE("NotcursesBase") {
CHECK(0 > channels_set_fg_alpha(&channel, 4));
CHECK(0 == channels_set_fg_alpha(&channel, CELL_ALPHA_OPAQUE));
CHECK(CELL_ALPHA_OPAQUE == channels_fg_alpha(channel));
CHECK(channels_fg_default_p(channel));
CHECK(channels_bg_default_p(channel));
CHECK(0 == channels_set_fg_alpha(&channel, CELL_ALPHA_HIGHCONTRAST));
CHECK(CELL_ALPHA_HIGHCONTRAST == channels_fg_alpha(channel));
CHECK(channels_fg_default_p(channel));
CHECK(!channels_fg_default_p(channel));
CHECK(channels_bg_default_p(channel));
}

Loading…
Cancel
Save