7
$\begingroup$

I have seen in some computer graphics software's code bases that sometimes the higher bits of RGB565-format image data are replicated into the lower bits when converting to higher-bit-depth format RGBA8888.

I have found for example the comment by user "eq" in this gamedev.net thread:

I prefer to replicate the higher bits into the undefined lower bits:
R8 = (R5 << 3) | (R5 >> 2);

However I do not understand the reason behind.

What use the purpose of replicating those bits into the converted data?

$\endgroup$
0

2 Answers 2

7
$\begingroup$

Without replicating the bits the LSBs will be 0, so for the maximum value of 0x1f (max for 5 bits) it would expand to 0xf8 when converted to 8 bit. What you want is 0xff so the range of 0x00->0x1f will be mapped to 0x00->0xff instead of 0x00->0xf8. Without merging the LSB you would not be able to convert 0x1f,0x1f,0x1f to white (0xff,0xff,0xff). Incidentally this is the same as N*0xff/0x1f.

Example: 

left shift only (<< 3)
%---00001 -> %00001000     (0x01 -> 0x08)
%---10000 -> %10000000     (0x10 -> 0x80)
%---11111 -> %11111000     (0x1f -> 0xf8)

merging MSB to LSB 
%---00001 -> %00001000     (0x01 -> 0x08)
%---10000 -> %10000100     (0x10 -> 0x84)
$---11111 -> %11111111     (0x1f -> 0xff)
$\endgroup$
1
  • $\begingroup$ "Incidentally this is the same as N*0xff/0x1f" Note it differs for values {3,7,24,28} which yield {24,57,198,231} with bit replication but {25,58,197,230} for round(N*255/31). All other values are the same (using floor or ceil instead of causes more error). So, it's a quite close approximation. However floor(N*33/4) does exactly match for all N with bit replication. $\endgroup$ Commented Oct 15, 2022 at 9:37
7
$\begingroup$

There's actually a reasonably good mathematical reason for doing bit replication:

First note that the n-bit string, $N$, actually represents the value $\frac{N}{2^n-1}$ and we want to produce the m-bit string, $M$, where $n<m$ and $$\frac{N}{2^n-1}\approx\frac{M}{2^m-1}$$

We first scale numerator and denominator with $$\frac{N.(2^n+1)}{(2^n-1)(2^n+1)}\approx \frac{M}{2^m-1}$$ and this simplifies to $$\frac{N.(2^n+1)}{2^{2n}-1}\approx \frac{M}{2^m-1}$$

In your case, $n\in \{5,6\}$ and $m=8$ and we can "stop" here, but but the process can be repeated, (ad nauseum), if m >> n.

We next make the approximation... $$\frac{N.(2^n+1)}{2^{2n}}\approx \frac{M}{2^m}$$ which simplifies to $$\frac{N.(2^n+1)}{2^{2n-m}}\approx M $$

Note that $N.(2^n+1)$ is equivalent to repeating the n-bit string, to create a 2n-bit string, and the division shifts off the $2n-m$ LSBs to leave an M bit result.

QED

Of course, the 'correct' calculation is $M=\lfloor(\frac{(2^m-1) N}{2^n-1}+\frac{1}{2}\rfloor$ but this approximation, in general, works most of the time. Of course there are times when it's inaccurate, but IIRC only by one bit and relatively infrequently.

$\endgroup$
2
  • 2
    $\begingroup$ Thank you for a detailed explanation with nice formulas. I was curious about the error introduced by the approximation so I made this graph that compares both formulas: desmos.com/calculator/cvaqofyvbf . However I prefer PaulHK's answer since it is easier to understand. $\endgroup$
    – wip
    Commented Oct 25, 2018 at 2:12
  • 1
    $\begingroup$ Minor quibble, if m >= 2n then you need to change your "approximation" equation. An extreme, example, is if n=1, then you need to repeat the string 8 times (i.e. perform log2(8)=3 steps). Of course, if you pad with "10...0" instead of all zeros, then on average you'll have a lower error, but lose the extremes. "However I prefer PaulHK's answer" :-) Well, there's no accounting for taste 8P. $\endgroup$
    – Simon F
    Commented Oct 25, 2018 at 11:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.