This is one of my favorite sites on the internet: https://graphics.stanford.edu/~seander/bithacks.html
I recommend Matt Godbolt's Talk(s) about compiler cleverness: https://www.youtube.com/watch?v=bSkpMdDe4g4 (see the 30 minute mark for the multiplication example)
> This is one of my favorite sites on the internet: https://graphics.stanford.edu/~seander/bithacks.html
Yes - I also enjoyed the book "Hacker's Delight", but haven't got round to reading the second edition yet.
But I think what you're aiming to say is:
r << 5 | r << 2 | r >> 1 amounts (because r is constrained to three bits) to being the same as
r << 5 + r << 2 + r >> 1
And r << 5 is r * 32, r << 2 is r * 4, and r >> 1 is floor(r / 2)
So the whole thing is
r * 32 + r * 4 + floor(r * 0.5)
which is the same as
floor(r * 32 + r * 4 + r * 0.5)
or
floor(r * 36.5)
Also, by implicitly doing the mapping to 8-bit first and the gamma stuff later, some precision is lost, but this shouldn't be a concern until you reach around five input bits per component.
Indeed internally my emulator calculates 16-bits per channel, performs gamma, saturation, luminance adjust, color emulation etc, and then reduces that to your target display. I have support for 30-bit monitors, but so far that only works on Xorg. I even considered floating-point internally for colors, but felt that was way too much overkill in the end.
uint64_t convert(uint64_t color, uint sourceDepth, uint targetDepth) {
if(sourceDepth == 0 || targetDepth == 0) return 0;
while(sourceDepth < targetDepth) {
color = (color << sourceDepth) | color;
sourceDepth += sourceDepth;
}
if(targetDepth < sourceDepth) color >>= (sourceDepth - targetDepth);
return color;
}
In fact, you probably should use normalization when converting arbitrary levels. A floating point multiply is almost certainly cheaper than a while loop. But hey, why not? Just don't use it for any real-time stuff unless you build a color palette cache.
> For instance, the Sega Genesis encodes 9-bit colors, giving 3-bits per channel.
> The solution to this is that the source bits should repeat to fill in all of the target bits.
This is interesting and efficient approach. Interpolating a 3-bit number (0-7) into a 8-bit one (0-255) can be done by dividing the first one by 7 (to normalize it) and the multiplying it by 255 (to stretch it to the full range). The operation order can be changed to avoid having smaller-than-1 floating numbers in the first step. So, we basically need to multiply each number by 255/7~36.4. So, these multiplications give exactly the same results as repeating the bits in the byuu's article and bits operations are much cheaper. I have some intuition on how does it work (we're increasing each "part" of the number by the same factor), but not a math explanation.