0

I have some C/C++ code where I have a 16-bit number (uint16_t), and I need to swap the first 5 bits with the last 5 bits, keeping their left-to-right order within each block of 5 bits. The middle 6 bits need to remain intact. I am not great at bitwise maths/operations so help would be appreciated!

Conceptually speaking, the switching of positions would look like:

ABCDEFGHIJKLMNOP becomes LMNOPFGHIJKABCDE

or more literally...

10101000000001010 becomes 0101000000010101.

Any help would be much appreciated!

phuclv
  • 37,963
  • 15
  • 156
  • 475
rj_code
  • 57
  • 5
  • 5
    I would suggest learning about bitwise operations (and, or, shifts). It's not that hard and this knowledge may be useful in the future. Also, sometimes a pen and paper is the best friend. For example, you need to find out how to turn `ABCDEFGHIJKLMNOP` into `00000000000LMNOP`, then, `00000000000LMNOP` into `LMNOP00000000000`, etc. Write this down and start searching. – Daniel Langr Nov 06 '20 at 10:25
  • 2
    They did not call you stupid, they effectively broke down your problem into subproblems that are solvable by finding and applying the correct bitwise operator. – Botje Nov 06 '20 at 10:44
  • By not providing a solution yet taking the time to tell someone to go search for the answer somewhere else, saying "it's not that hard", you're not exactly being helpful when I've already said I find this hard in my question. I've avoided asking a question here for a long time because I see this stuff on every post. – rj_code Nov 06 '20 at 10:52
  • 2
    @rj_code That's because such "homework" questions are generally off-topic and considered bad here. See [How do I ask and answer homework questions?](https://meta.stackoverflow.com/q/334822/580083) for some more details. This is why people do not like do-something-for-me only questions and try to provide some hint for OP to make the work. You said that _any help would be appreciated_ and I suggested you to take a pen and paper, write down what you want to achieve, and then find out how (by the way, it was only a comment, not an answer). You called yourself stupid, I would never do this. – Daniel Langr Nov 06 '20 at 17:55
  • there's no C/C++ language. You either write in C **or** C++, choose one – phuclv Nov 11 '20 at 01:59

2 Answers2

2

First, you should check if whatever library you use doesn't have a RGB-BGR swap for R5G6B5 pixels already.

Here is a literal translation of what you wrote in your question. It is probably too slow for real-time video:

uint16_t rgbswap(uint16_t in) {
    uint16_t r = (in >> 11) & 0b011111;
    uint16_t g = (in >> 5)  & 0b111111;
    uint16_t b = (in >> 0)  & 0b011111;

    return b << 11 | g << 5 | r << 0;
}
Botje
  • 26,269
  • 3
  • 31
  • 41
  • Since you seem to care about nice code alignment, you may consider changing `(in⎵>>⎵5)` to `(in⎵>>⎵⎵ 5)`. Which will align digits of the same order of magnitude as well (just a suggestion :). – Daniel Langr Nov 06 '20 at 10:37
  • Thanks @Botje, that worked nicely. Speed is not a big concern in my use-case, and I'm just doing some simple low-level stuff with no library involved. Thanks, you've helped me understand more about bit-shifting. Thanks again for taking the time! Cheers. – rj_code Nov 06 '20 at 10:48
  • 1
    @DanielLangr heh, it did not occur to me to space them out that way. I think this way is good enough ;) – Botje Nov 06 '20 at 10:49
  • 1
    *It is probably too slow for real-time video* These kinds of bit twiddling routines optimize really well. But for real-time video, leveraging SIMD or GPU may be worthwhile. – Eljay Nov 06 '20 at 12:26
  • 2
    This can be more efficiently expressed as `(in & 0b11111100000) | ((uint16_t) (in << 11)) | (in >> 11)`. However, most compilers should be smart enough to do this by themselves: https://godbolt.org/z/abjhx4 – Falk Hüffner Nov 08 '20 at 11:08
  • The speed of this routine is likely limited by the memory bandwidth of the CPU. It takes more time to get the bits from memory than it takes to shuffle them. A modern CPU will also start fetching the next pixel, so there is 0 advantage to making this faster. The main problem for real-time video would be that the display is 24 bits anyway, so you'd be better of combining this swap with the zero-padding that you need anyway. I.e. pad it to `BBBBB000GGGGGG00RRRRR000` directly. Copying each pixel twice from memory to CPU and back will be slower. – MSalters Nov 10 '20 at 16:42
0

Instead of breaking the input into 3 separate R, G and B components you can work on R and B in parallel by shifting to the higher bits

uint16_t rgb2bgr(uint16_t in)
{
    uint16_t r0b = in & 0b1111100000011111;
    uint16_t b0r = ((r0b << 22) | r0b) >> 11;
    return b0r | (in & 0b11111100000);
}

Another alternative to use multiplication to swap R and B

uint16_t rgb2bgr_2(uint16_t in)
{
    uint16_t r0b = in & 0b1111100000011111;
    uint16_t b0r = r0b * 0b1000000000000000000000100000 >> 16;
    return b0r | (in & 0b11111100000);
}

It's basically this technique which is useful for extracting bits or moving bits around

You can check the compiled result on Godbolt to see the multiplication method produces shorter output, but it's only useful if you have a fast multiplier

phuclv
  • 37,963
  • 15
  • 156
  • 475