• ### Announcements

#### Archived

This topic is now archived and is closed to further replies.

# Why does this work?

## Recommended Posts

utwo007    122
Here''s a macro from a book that I''m reading that is supposed to convert three int s (one for red, one for green, and one for blue) into a 16-bit word used to represent a color. I''m sure you''ve all seen this before. #define _RGB16BIT555(r,g,b) ((b%32) + ((g%32) << 5) + ((r%32) << 10)) I don''t understand why this should work. I mean, consider the following: _RGB16BIT555(0,0,33); and... _RGB16BIT555(0,0,65) Both 33%32 and 65%32 = 1. Wouldn''t this yield the same shade of blue, even though the user obviously wanted two different shades? I''m sure the answer to this is obvious and I''m just overloading something. ---signature--- People get ready. I''m ready to play.

##### Share on other sites
_rpg_guy    122
Each color component gets 5 bits. You''re example uses numbers that break that 5 bit boundry. You only get 32 shades of each component. (2^5=32)

##### Share on other sites
Kwizatz    1392
Thats from Tricks of the Windows game programming gurus!

and yes the maximum value you get on 5 bits is 11111b = 0x1F = 31
on the _RGB16BIT565 macro green gets one more bit so its maximum is 111111b = 0x3f = 63

##### Share on other sites
utwo007    122
Yep! I knew someone had to have seen that before.

Anyway, I understand why each number is %32-ed. But I was under the understanding that this sort of macro should be used to convert the standard 256-levels into 32 levels in a logical manner, meaning a value of 32 would be less than 64, which would be less than 96. Using this macro, these would all yield the same exact shade.

Let me put it another way. Using graphics programs like Photoshop, we are all used to representing colors as such:

white = 255,255,255
green = 000,255,000

dark green = 000,064,000
darker green = 000,032,00

You see, the last two examples should yield 2 different shades of green. Using that macro, it wouldn't. If I did the following:

_RGB16BIT555(0,0,32);

I would expect a darker shade of blue than this:

_RGB16BIT(0,0,64);

And this would be the lightest shade of blue possible:

_RGB16BIT555(0,0,255);

And I'd be right about one thing -- the last one would equal the lightest shade possible because 255%32=31, which is the highest you can go on a 32-element scale of 0-31. However, this doesn't work the way it was intended when other values are entered. Why bother with the (%)? Why not just force the user to enter in a value between 0-31? It'd be easier to understand.

I mean, it's be easiest to enter a value between 0-255 because that's what we're all used to, but if the macro doesn't work for this purpose then what's the point? Shouldn't it be written like this?

#define _RGB16BIT555(r,g,b) ((((b+1)/8)-1) + ((((g+1)/8)-1) << 5) + ((((r+1)/8)-1) << 10))

Each color would be given a value between 0 and 31 (255+1=256; 256/8 = 32; 32-1=31; ). Most of this math can be done before the macro is called, and I can even write some simple comparative logic that decided if the value entered is closer to one multiple of 32 or the other.

ARRGH! I don't get it.

---signature---

Edited by - utwo007 on January 29, 2002 9:33:11 PM

##### Share on other sites
Guest Anonymous Poster
Isn''t ''%'' horribly slow, since it needs an idiv ? better do something like b&31. Though it will wrap around instead.

##### Share on other sites
Kwizatz    1392
maybe you can change it to:

#define _RGB16BIT555(r,g,b) (((b&0x1f)) + ((g&0x1f) << 5) + ((r&0x1f) << 10))

that way you just feed 0-31 values, and if you feed higher values the bits over 5 wont be taken into account

Edit: got green (g) twice, and no blue (b)

Edited by - kwizatz on January 29, 2002 10:33:31 PM

##### Share on other sites
utwo007    122
Well, I can do it this way:

the color is 255,128,0 (orange).

// Macro for creating color USHORT

#define _RGB16555(r,g,b) ((b)+(g << 5)+(r << 10))

// This function assumes the user has already locked the
// surface, obtained a pointer to the video buffer, calculated
// the memory pitch, and passed all this information to it.

int red = 255;
int green = 128;
int blue = 255;

int newred = ((red+1) >> 3) - 1;
int newgreen = ((green+1) >> 3) - 1;
int newblue = ((blue+1) >> 3) - 1;

256/8 = 32. Perfect! Because we''re dealing with 0-255 and not 1-256, we must first add 1 to the color element''s value. Then, we can devide by 8 easily by shifting the number three digits. ( >> 3 will effectively devide by 2^3, or 8). Then, we subtract the 1 back from the result.

Let''s see how this''ll work for our example (orange). We need a red value of 255, the highest allowable. Let''s transulate that to 16-bit color, shall we?

255+1=256
256/8=32
32-1=31

Woohoo! Now lets try 128 for the green element:

128+1=129
129/8=16 //since it''s an int, the remainder is truncated
16-1=15

Damned good!

And blue''s a no-brainer, so we''ll just go ahead and run the macro. Now newred is 31, newgreen is 15, and newblue is 0:

SomePixel = _RGB16555(newred,newgreen,newblue);

Pretty cool, huh? Now I can use the standard 255,255,255 design and convert it to 16bit color information using NO devides, NO modulus, and NO multiplies! I guess I could just get used to a 31,31,31 way of thinking, but I''m too stubborn!

What do you think? It isn''t perfect. Since the result of int >> 3 is truncated, it''s always rounded down. That''s no good, because in reality, the number 128, when converted from 8-bit format to 5-bit format, is actually closer 16 than 15. I could use some simple comparative logic to decide whether a number is closer to one multiple of 8 or another. I dunno.

---signature---

##### Share on other sites
ekenslow    122
Whoever wrote that macro is horribly misguided; they're trying to implement either a clamp or a scale function using modulo arithmetic, either of which just doesn't work. If you need to clamp the color values to the range 0-32 with no wraparound, you can just use the bitwise and operator, as pointed out earlier.

To really convert between color depths you need to rescale the RGB values to the range 0-32 rather than simply clamping them. Let's say their range is 0-255 (8 bits per color component). Assuming unsigned values for r, g, b, a working implementation of the macro might look like this (it isn't optimal, but it's good enough unless you're doing some serious format conversions every single frame):

#define SCALE( val, maxNew ) (int)((float)(val) / 256.0f * (maxNew))
#define _RGB16BIT555(r,g,b) (SCALE( b, 32 ) | (SCALE( g, 32 ) << 5) | (SCALE( r, 32 ) << 10))

Note this also fixes another problem with the original: the parameters within a macro definition must be parenthesized in order to ensure you get intended results in all cases.

If this macro actually was from a Tricks of the [etc..] Gurus book, I'm not surprised. I was very disappointed with the quality of code in both those books.

--
Eric

Edited by - ekenslow on January 30, 2002 1:40:40 AM

##### Share on other sites
buzzy_b    122
utwo007, why don't you just leave the number alone before you divide by 8? Why add one and then subtract one? If you left it, then

255 >> 3

should yield 31 (since its below 256, it'd be rounded down), and

128 >> 3

would yield the more accurate 16.

Just a thought.

[EDIT]
I just thought of another problem with what you proposed. What happens when you choose the color 0, or 1?

1+1=2
2/8=0 (an int gets truncated)
0-1=-1

-1 is not a valid color I'm afraid. My slightly revised way should take care of this.
[/EDIT]

--Buzzy

Edited by - buzzy_b on January 30, 2002 2:04:41 AM

##### Share on other sites
I''m reading the same book. The macro works...but I have no idea what''s happening. Im not to good with the whole macro and bit operation thing. Does anyone know of a good tutorial to help me out?

##### Share on other sites
I''m reading the same book. The macro works...but I have no idea what''s happening. Im not to good with the whole macro and bit operation thing. Does anyone know of a good tutorial to help me out?

##### Share on other sites
a person    118
either the author was high when he wrote the book or made some serious typos which the editor missed. NEVER use plus in anything that is combining bits like this. it is messy that way. also never use the AND operator to clamp values when you are supposed to scale them. finnally NEVER use floating point in anything that is realtime and involves combining color componnents into pixels.

#define _RGB16BIT555(r,g,b) ( (r&0xF8) << 8)) | ((g<<5)&0x3E0) | (b>>3) )

more efficent then previous macros, though you should be able to come up with this from the info given. make sure all the inputs are longs or at least shorts or things wont work. ints are fine as well (for those not in the know, (ints can be 16bit or 32bit depending on the compiler, most nowadays have ints as 32bit).

##### Share on other sites
utwo007    122
Thanks for the suggestiongs, folks. I knew something was fishy when I saw that. I''ve actually been please with LaMothe''s code so far. I''ve had to correct a few things that wouldn''t compile, but I figured that had more to do with my compiler than anything else (I use Dev-C++. I think the compiler begins with ''M''. hehe).

I''ll fix the macro so that is scales the colors properly. I don''t know what Lamothe was thinking.

Oh, and good point, buzzy_b. 0 and 1 would really mess things up.

---signature---

##### Share on other sites
ekenslow    122
''a person'', you must be smoking the same thing as LaMothe, because your macro is actually more broken than the original.

The mask values and bit shifts are all wrong, and you managed to break the rule that YOU mentioned about using AND to scale values.

As for doing floating point to scale the values, there''s really no other way to do it. You just have to bite the bullet and do the scaling, hopefully caching the result to be reused for several frames. Most applications don''t require explicit format conversion on a frame by frame basis anyway (most only need it at load time if at all).

--
Eric

##### Share on other sites
utwo007    122
I think I''m just going to stick with 0-31, actually. Writing a bunch of code to scale the numbers when I could just get used to 0-31 is plain lazy, and I''d rather have the speed that comes with not having to use the FPU and doing the multiplies and pushing more variables on the stack.

I mean, scaling would be hella useful if I were writing a graphics library for someone else, but I''ll just be using this myself, so...

---signature---