7

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?

wip
  • 1,851
  • 1
  • 13
  • 26

2 Answers2

7

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.

Simon F
  • 4,241
  • 12
  • 30
  • 2
    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: https://www.desmos.com/calculator/cvaqofyvbf . However I prefer PaulHK's answer since it is easier to understand. – wip Oct 25 '18 at 02:12
  • 1
    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.

    – Simon F Oct 25 '18 at 11:54
7

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)
PaulHK
  • 2,322
  • 10
  • 11
  • "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. – Dwayne Robinson Oct 15 '22 at 09:37