Proper UINT32 colour arithmetic

Started by
17 comments, last by JNT 13 years, 5 months ago
I have many divisions and additions of UINT32s in my program, where UINT32 represents a colour.

My problem is that my implementation is very naive. If I want to halve the magnitude of all the colours represented by the UINT32 I simply do Colour / 2... And if I want to add two colours together I simply do Colour1 + Colour2...

This works up to a point, but not sufficiently well. In many cases where the scene is supposed to be greyscale throughout, I get undesired colours appearing like blues and greens, which suggests a colour shift due to the method of colour addition and subtraction I am using being inadequate.

How do I properly divide and add the colour values of UINT32s?
Advertisement
Why don't you just use 4 bytes, one for each of R, G, B and A components? If you just divide the whole UINT32 with 2 then you shift all its bits one position to the right, so set bits can move to different channels and you don't want that. The same problem exists with addition and multiplication. If for whatever reason you cannot use 4 separate bytes, you can isolate each component with bitwise operations, perform arithmetic on each component separately, and then substitute the 8 bits of each component back to the UINT32 bit positions from where you extracted it. For example:

UINT32 color;UINT32 c0 = color & 0x000000FF; UINT32 c1 = (color & 0x0000FF00) >> 8; UINT32 c2 = (color & 0x00FF0000) >> 16; UINT32 c3 = (color & 0xFF000000) >> 24; c0 = (c0 * 2) & 0x000000FF;c1 = (c1 * 2) & 0x000000FF;c2 = (c2 * 2) & 0x000000FF;c3 = (c3 * 2) & 0x000000FF;color = c0 | (c1 << 8) | (c2 << 16) | (c3 << 24);


You should wrap these bit-twiddling operations in functions though. This makes the calculations heavier though, so if you need more performance you can look at SSE instructions and their corresponding compiler instrinsics.

[Edited by - D_Tr on October 15, 2010 9:14:06 AM]
Thank you very much.

I could probably use separate instances of a smaller data type to represent each colour component whilst performing the caluclations, however I would then need to convert those separate sata types into a UINT32.

If for example I were to use a float to describe the magnitude of each colour compenent, the arithmetic would become trivial, but how would I then convert those 3 floats, each represnting a colour component, into a UINT32?

Could I for instance use the D3DXColor structure and then simply cast it into UINT32 using (UINT32)Colour; ?
Quote:Original post by forsandifs
Could I for instance use the D3DXColor structure and then simply cast it into UINT32 using (UINT32)Colour; ?

Are you using C++? If yes, you can :)
If you want to do the calculations yourself, it's pretty easy too

float r, g, b, a;unsigned int packedColor = 0;packedColor = (packedColor | (unsigned int)r*255) << 8;packedColor = (packedColor | (unsigned int)g*255) << 8;packedColor = (packedColor | (unsigned int)b*255) << 8;packedColor = (packedColor | (unsigned int)a*255);


You basically need to convert from float to UINT32 by multiplying by 255 (Note: you'll lose some precision due to rounding). After that you pack it into a UINT32 using some bitwise arithmetic.
Here is multi-byte addition (from the Hacker's Delight book)

unsigned int MultibyteAdd( unsigned int a, unsigned int b ){	unsigned int s = ( x & 0x7f7f7f7f ) + ( y & 0x7f7f7f7f );	s = ( x + y ) & 0x80808080 + s;	return s;}


Division and multiplication is more complicated but for the special cases of dividing or multiplying by a power of two you can use shift operators. The problem is that when bits overflow from one element to another you have a bug. The safest thing is to convert each component to float, do your math and convert it back to a packed integer. This will work safely for all arithmetic operations.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Thank you very much guys. :)

I ended up getting it work by doing all the calculations with D3DXCOLOR but then converting appropriately to UINT32 by using the very useful D3DCOLOR_COLORVALUE macro provided by DirectX like D3DCOLOR_COLORVALUE( D3DXCOLOR1.r, D3DXCOLOR1.g, D3DXCOLOR1.b, D3DXCOLOR1.a ); This macro returns a correct UINT32 value of a colour defined by a float for each component.

Simply casting from D3DXCOLOR to UINT32 can sometimes introduce very significant error.
Quote:Original post by deathkrush
Here is multi-byte addition (from the Hacker's Delight book)

*** Source Snippet Removed ***

Division and multiplication is more complicated but for the special cases of dividing or multiplying by a power of two you can use shift operators. The problem is that when bits overflow from one element to another you have a bug. The safest thing is to convert each component to float, do your math and convert it back to a packed integer. This will work safely for all arithmetic operations.


That doesn't work.
If 0x00000040 and 0x00000040 were added together, you'd get 0x00000100 with that function, I do believe (please correct me if I'm wrong!!)

I don't have Hacker's Delight unfortunately, but would this work?
unsigned int MultibyteAdd( unsigned int a, unsigned int b ){	unsigned int s = ( x & 0x7f7f7f7f ) + ( y & 0x7f7f7f7f );	s = (( x + y ) & 0x80808080) | s;	return s;}
Whoops, I misinterpreted the funky notation in the book. This should work:

unsigned int MultibyteAdd( unsigned int x, unsigned int y ){	unsigned int s = ( x & 0x7f7f7f7f ) + ( y & 0x7f7f7f7f );	s = ( ( x ^ y ) & 0x80808080 ) ^ s;	return s;}
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
I implemented this loong time ago... may be of interest for you.

http://www.flipcode.com/archives/Color_Manipulation.shtml


Hoppe this helps,

Unai.

This topic is closed to new replies.

Advertisement