Thanks Mark! Number examples work good for me. However your example only shift up. So I decided to try some things myself (thanks for the pointers and numbers, that helped me in my progress a lot).
The idea with contrast is that numbers below 0.5 go more towards 0.0 and above 0.5 go more towards 1.0.
I got the idea from this resource (used it also for saturation) which gives nice examples:
I filled in the formula for a value of 0.25 and 0.75 and then used the integer lerp with the same example.
With float math
lerp float: lerp(a, b, t) > result = a + t * (b-a)
a = 0.5 (middle grey)
b = 0.25 (noise)
t = 1.5 (contrast)
result = 0.5 + 1.5 * (0.25-0.5) = 0,125
a = 0.5 (middle grey)
b = 0.75 (noise)
t = 1.5 (contrast)
result = 0.5 + 1.5 * (0.75-0.5) = 0,875
With integer math (took 8 bit for convenience)
lerp int: lerp(a, b, t) > result = a + (t * (b-a)) >> 8).
a = 128 (middle grey) (0.5 * 256 )
b = 64 (noise) (0.25 * 256)
t = 384 (contrast)
result = 128 + (384 * (64-128) / 256 = 32 (0.125*256)
a = 128 (middle grey) (0.5 * 256)
b = 192 (noise) (0.75 * 256)
t = 384 (contrast)
result = 128 + (384 * (192-128) / 256 = 224 (0.875*256)
Since that worked good, I decided to check the min and max 0 and 255.
a = 128 (middle grey)
b = 0 (noise)
t = 384 (contrast)
result = 128 + (384 * (0-128) / 256 = - 64
a = 128 (middle grey)
b = 255 (noise)
t = 384 (contrast)
result = 128 + (384 * (255-128)/256 = 320
Solution (I think)
The problem is of course the number 384. Which is out of range of uint8_t. I experimented a bit fiddling with numbers like you explained:
256 + 128 = 384
And then doing lerp(128,192,256) + lerp(128,192,128).
However the range is actually -64 to 320. Which is 64 at each side (0-64 and 256+64).
So it would become: lerp(128,64,256) - 64 + lerp(128,64,128) - 64 (got this concept by just looking at the numbers, so I hope it works for other contrast values)
example with numbers:
128 + (256 * (192-128) / 256 = 192 - 64 = 128
128 + (128 * (192-128) / 256 = 160 - 64 = 96
224
pseudo-code:
a // always 128 (hardcoded)
b // input
scale_factor // contrast (now in floats to show the idea later hardcoded integers)
float scale_factor = 1.5; // contrast
float scale_factor_minus_one = scale_factor - 1;
uint8_t scale_factor_minus_one_frac8 = (uint8_t) scale_factor_minus_one * 256;
uint8_t delta = b - 128;
uint8_t scaled_part1 = scale8( delta, 256);
uint8_t scaled_part2 = scale8( delta, scale_factor_minus_one_frac8);
uint8_t result = (a + scaled_part1 - scale_factor_minus_one_frac8/2) + (a + scaled_part2 - scale_factor_minus_one_frac8/2);
// which we can write as
uint8_t result = 256 + scaled_part1 + scaled_part2 - scale_factor_minus_one_frac8;
I didn’t test it yet. So if you see mistakes please let my know.
Do you see optimimalisations? I still need a constrain to 0 - 255. How would I utilize qadd8 and qsub8 for that? I was
in doubt about the order.
Also I think I need to do it with 16bit math. Since I found that noise16 is more smooth (however I noticed that you put a smoothing function in the example code now as well).