Jump to content
  • Advertisement
Sign in to follow this  
forsandifs

Proper UINT32 colour arithmetic

This topic is 2824 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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; ?

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites
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;
}


Share this post


Link to post
Share on other sites
I implemented this loong time ago... may be of interest for you.

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


Hoppe this helps,

Unai.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!