# Why replicating the higher bits of RGB565 when converting to RGBA8888?

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?

• 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. – PaulHK Oct 23 '18 at 4:07
• @PaulHK Please post that as an answer. It's complete as it stands, but as a comment it's not searchable. – Dan Hulme Oct 23 '18 at 11:24
• Yes thank you @PaulHK this answers correctly my question – wip Oct 24 '18 at 3:33

## 2 Answers

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)


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 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.

• 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. – wip Oct 25 '18 at 2:12
• 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