none
Help with bitwise maths please

    Question

  • I need help with some mind bending bitwise operators, the following bit of code converts 32bit RGBA8888 pixels to 16bit RGBA5551 pixels with buf1 being the image in a byte[]:

    var a=BitConverter.ToUInt32(new[]{buf1[i], buf1[i+1], buf1[i+2], buf1[i+3]}, 0);
    var b=BitConverter.GetBytes((ushort)(((a>>16)&0x8000|(a>>9)&0x7C00|(a>>6)&0x03E0|(a>>3)&0x1F)));

    thats just somthing I found but I need to make a slightly unusual change, I want to change the 32bit RGBA8888 pixels to 16bit RGBA 4444 pixels and then back again, this will not be used as any standard image.
    Monday, April 30, 2012 5:11 AM

Answers

  • Hmm, you should not use BitConverter for this kind of stuff, it's way too slow. In particular converting to 4444 is easy because you don't have to read the whole 8888 pixel, you can simply read 2 input bytes to produce 1 output byte.

    Here's a complete example for RGBA8888 to RGBA4444:

            static byte[] Convert8888to4444(byte[] buf1) {
                byte[] buf2 = new byte[buf1.Length / 2];
    
                for (int i1 = 0, i2 = 0; i2 < buf2.Length; i1 += 2, i2++) {
                    buf2[i2] = (byte)(((buf1[i1] >> 4) & 0x0f) | (buf1[i1 + 1] & 0xf0));
                }
    
                return buf2;
            }
    

    And one for RGBA4444 to RGBA8888:

            static byte[] Convert4444to8888(byte[] buf1) {
                byte[] buf2 = new byte[buf1.Length * 2];
    
                for (int i1 = 0, i2 = 0; i1 < buf1.Length; i1++, i2 += 2) {
                    byte b = buf1[i1];
                    buf2[i2] = (byte)((b & 0x0f) << 4);
                    buf2[i2 + 1] = (byte)(b & 0xf0);
                }
    
                return buf2;
            }
    

    Monday, April 30, 2012 8:32 AM
    Moderator

All replies

  • Hmm, you should not use BitConverter for this kind of stuff, it's way too slow. In particular converting to 4444 is easy because you don't have to read the whole 8888 pixel, you can simply read 2 input bytes to produce 1 output byte.

    Here's a complete example for RGBA8888 to RGBA4444:

            static byte[] Convert8888to4444(byte[] buf1) {
                byte[] buf2 = new byte[buf1.Length / 2];
    
                for (int i1 = 0, i2 = 0; i2 < buf2.Length; i1 += 2, i2++) {
                    buf2[i2] = (byte)(((buf1[i1] >> 4) & 0x0f) | (buf1[i1 + 1] & 0xf0));
                }
    
                return buf2;
            }
    

    And one for RGBA4444 to RGBA8888:

            static byte[] Convert4444to8888(byte[] buf1) {
                byte[] buf2 = new byte[buf1.Length * 2];
    
                for (int i1 = 0, i2 = 0; i1 < buf1.Length; i1++, i2 += 2) {
                    byte b = buf1[i1];
                    buf2[i2] = (byte)((b & 0x0f) << 4);
                    buf2[i2 + 1] = (byte)(b & 0xf0);
                }
    
                return buf2;
            }
    

    Monday, April 30, 2012 8:32 AM
    Moderator
  • There's a slight problem, when its converted back to 8888 white is no longer completely white because the lower 4 bits are always zero, what would be the best solution for this?

    Ok all I need to do is multiply the RGBA8888 output bytes by 1.0666 and round up.

    Tuesday, May 01, 2012 7:19 PM
  • "multiply the RGBA8888 output bytes by 1.0666 and round up."

    Well, that will give you the white but it will also shift others colors (except black) a bit. Not to say that there are better options... you could or the pixel with 0x0f0f0f0f which will give you white but then the black will no longer be black. Or you could or with 0x08080808 which doesn't give you black or white but somehow seems more correct because its the middle ground.

    And of course, there's dithering which doesn't necessarily gives you white but is commonly used in such cases to improve the perceived visual quality of image by reducing banding.

    Wednesday, May 02, 2012 5:55 AM
    Moderator