Sign in to follow this  
VanillaSnake21

24bit to 16 bit PLEASE help

Recommended Posts

Hi, I am going through the book by Ander LeMothe on Windows Programming in DirectX, and in one of its programs where he tells us how to load a 16 bit bitmap there is a part which i totally don't get and he doesn't explain it In the demo there is a part of the code that converts a 24bit bitmap into a 16 bit bitmap, but how he got to it I am absolutely clueless, here it is ///////////////////////////////////////////////////////////////////// / process each line and copy it into the primary buffer for (int index_y = 0; index_y < SCREEN_HEIGHT; index_y++) { for (int index_x = 0; index_x < SCREEN_WIDTH; index_x++) { // get BGR values, note the scaling down of the channels, so that they // fit into the 5.6.5 format /////////////////////////////////////////////////////////////////////////////// WHAT IS THIS? WHERE DOES HE GET THE + 0, +1, +2 and all the multiplication, and shifting? UCHAR blue = (bitmap.buffer[index_y*SCREEN_WIDTH*3 + index_x*3 + 0])>> 3, green = (bitmap.buffer[index_y*SCREEN_WIDTH*3 + index_x*3 + 1]) >> 3, red = (bitmap.buffer[index_y*SCREEN_WIDTH*3 + index_x*3 + 2]) >> 3; /////////////////////////////////////////////////////////////////////////////// // this builds a 16 bit color value in 5.6.5 format (green dominant mode) USHORT pixel = _RGB16BIT565(red,green,blue); // write the pixel primary_buffer[index_x + (index_y*ddsd.lPitch >> 1)] = pixel; } // end for index_x } // end for index_y Please help, ofcourse I can just copy the code and not worry how it works, but I really wan't to uderstand it, so that I can write a game myslelf. Thank You

Share this post


Link to post
Share on other sites
Well, it's 3 bytes per pixel.

It might be more clear written like this:


unsigned char* pixel = &bitmap.buffer[ (y * SCREEN_WIDTH + x) * 3];

unsigned char blue = pixel[0];
unsigned char green = pixel[1];
unsigned char red = pixel[2];

then he right shifts the values:

blue >>= 3;
green >>= 3;
red >>= 3;

this means that blue, green, and red each have 5 bits of data in them (8 - 3 = 5), so you can left-shift and OR these colors into one 16-bit value:

(note: should green only be shifted right by 2 bits, giving it 6 bits of data?)

unsigned short new_pixel = _RGB16BIT565(red,green,blue);


edit: ha! I am winnar!!lelevnty

Share this post


Link to post
Share on other sites
Assuming that the bitmap.buffer is an array which is filled like so:

{ blue, green, red, blue, green, red, etc... }

if you factor the 3 out of the index_y*SCREEN_WIDTH*3 + index_x*3 you get

3 * (index_y * SCREEN_WIDTH + index_x)

now the (index_y * SCREEN_WIDTH + index_x) gets the index value from a coordinate, essentially turning a 2d coordinate into a 1d coordinate.

Since the array is filled like such (see above), the coefficient 3 is necessary to get the actual location of the BGR value.

The +0, +1, and +2 go along with the coef. because the value of bitmap at index+0 is the BLUE value, +1 is the GREEN, +2 RED:

{ blue, green, red, blue, green, red, etc... }

The shift right by 3 essentially divides each value by 8. This is necessary to transform to 16 bit because a 24 bit bitmap stores RGB values as 8 bits each (r, g, b are all 8-bit integers). However, 16-bit bitmaps store RGB values as 5 or 6 bits each (r=5bit integer, g = 6 bit int, b = 5 bit int).

An 8-bit integer has a max value of 256
A 5-bit integer has a max value of 32

256 divided by 32 is 8, so there you go!

Hope you understood all that, reply if you need more clarification.

-edit: DANG! BEATEN BY RDRAGON1!!!!!!!111111

Share this post


Link to post
Share on other sites
more efficient:



unsigned int row_count = SCREEN_HEIGHT;
unsigned int col_count;

unsigned char const* src_pixel = &src_buffer[0];
unsigned short* dst_pixel = &dst_buffer[0];

while( row_count-- )
{
col_count = SCREEN_WIDTH;

while( col_count-- )
{
//convert 24-bit to 16-bit
unsigned char blue = src_pixel[0] >> 3;
unsigned char green = src_pixel[1] >> 2;
unsigned char red = src_pixel[2] >> 3;

//store as [blue:5][green:6][red:5]
*dst_pixel = (blue << 11) | (green << 5) | red;

++dst_pixel;
src_pixel += 3;
}
}





This is untested, I just whipped it up - but it looks okay... The order of blue/green/red in the destination might be wrong... The order in the source might be wrong too.

Share this post


Link to post
Share on other sites
Thought I'd add my 2 cents to this. I spent days trying to get Lamothe's code to work with different bitmaps. His engine is very inter-dependant, even if the code you use for converting bitmaps is correct, you'll run into problems with other initialization code. My recommendation after experiencing this nightmare:

1) Stick with the resolution format he uses in one of his projects and use that for ALL of your artwork and project settings.

2) Learn Direct3D so you can use DirectX extensions and take advantage of LoadSurfaceFromFile() etc.

If you go the 2nd route, I recommend Beginning Game Programming by Jonathan Harbour.

Share this post


Link to post
Share on other sites
Another 2 cents:

There are two 16 bit modes out there, 565bit and 555bit. Never ever rely on the fact that one of them is more often in use than the other. Always cater for the possibility of both.

Share this post


Link to post
Share on other sites
[quote]Original post by geekalert
now the (index_y * SCREEN_WIDTH + index_x) gets the index value from a coordinate, essentially turning a 2d coordinate into a 1d coordinate.

Since the array is filled like such (see above), the coefficient 3 is necessary to get the actual location of the BGR value.


OK, thanks for clearing up the part about the addition ( +0, +1, +2), I understand that the array is in { b , g , r ...} order, but what do you mean "to get the index value from a coordinate, turning it into 1D?" You mean turning it into a point? And also from the previous post, RDragon1 said that 3 is the size of 1 pixel in 24bit mode, I also get that, but why do we need to multiply everything by 3? Also, will it be the same if for the first color, I don't add 0, and just leave it as it is, or will it make a difference? Thank you guys for responding, I couldn't find this info anywhere.

Share this post


Link to post
Share on other sites
A bitmap is two dimensional usually, right? Yeah. But what if we store it in an array thats only one-dimensional? Like whoa, dude! Whaddawe do!?

Well, we just take each line, and append it to another:

Original bitmap (just an example with arbitrary values) is two-d:

1, 4, 6, 7, 2, 3
1, 4, 3, 3, 9, 8
3, 4, 6, 6, 1, 1
2, 3, 8, 7, 4, 3

So we need to store that bitmap in memory. Do we do the naive:


bitmap[6][4];

for(int x = 0; x < 6; x++)
{
for(int y = 0; y < 6; y++)
{
bitmap[x][y] = whatever;
}
}



Of course not! We want to be real programmers using 1D arrays, not little girly men!


bitmap[24]; // 24 = 6 * 4

for(int x = 0; x < 6; x++)
{
for(int y = 0; y < 4; y++)
{
new_array_bitmap[x + y*SCREEN_WIDTH] = original_2d_girly_bitmap[x][y];
}
}



Now you see what the x+(y*SCREEN_WIDTH) does? Probably not ;). So I'll throw in a picture for clarity:



I hope that helps you understand what x+(y*SCREENWIDTH) does.

As for the multiplied by 3... thats one of those "its so easy its hard" ones. Wait for my next post for a reply!

Share this post


Link to post
Share on other sites
Continued from my last post...

A coefficient of 3, huh? Well, I'll try my best to explain this one!

Let's look at an arbitrary array (correction, a MACHO array).

int random[] = { 9, 8, 6, 3, 4, 6 };

So, you wanna access the 3rd element of this array? No problem!

int third_element = random[2];

OMYGODITSATWO! Why isn't it a three? Well, remember that arrays in C++ start with index 0 (zero) (nil) (null) (etc.). Why? Well, thats because

C++ rox0rs!


Warning C++ may kick @$$

Now, for another problem (don't worry, these examples will make sense in the end). What if you had a structure called TwoInt, and they stored two integers?

struct TwoInt {
   int value_one;
   int value_two;
};

How would we store multiple TwoInt's in memory?
Let's look at the first way, the girly way:


TwoInt array[8];


Then you would do whatever it is you do with arrays. (but not THAT!)

But do you wanna be known as a girly man? Of course not!

Be a real programmer! Use PACKED DATA TYPES!

What's a packed data type/array? Well, instead of using those newfangled object oriented SOB's (just kidding, I love double-O), you could use a PACKED ARRAY!

Let's look at an example Packed Array:

We will structure it like so:
{ value_one, value_two, value_one, value_two, etc.. }
{ 0, 7, 4, 5, 2, 3, 6, 4, 2, 4, 6, 8, 4, 4, 6, 8 }

so array[0] is actually the value_one of the implicit TwoInt structure.
and array[1] is the value_two.

So, how do we get the value_one of.. say, the 5th implicit TwoInt?

Naive:

array[4];

Why is the above code wrong? Is it because I typed a 4 instead of 5? Think VanillaSnake, Think!

No! It's because array[4] actually points to the value_one of the 3rd TwoInt!
But what if we multiplied 4 by two?

array[8];

There you go! We've got the value_one of the 5th TwoInt!!!

This example can be applied to your BGR structures - think about it! You have three values, blue, red, and green, stored as a packed array.

Multiply the index value by 3, and you get the true index of the nth BGR structure.

Whew! Hope that explains everything! Reply with a Q if additional clarity is needed!

P.S. Please excuse me for mimicking the speaking style of "Decline of Video Gaming XMAS". Those are pretty funny tho!

Share this post


Link to post
Share on other sites
Quote:
Original post by VanillaSnake21Also, will it be the same if for the first color, I don't add 0, and just leave it as it is, or will it make a difference?


A +0 or no +0 wouldn't make a difference. Purists like us just put the +0 in there, because we're... well.... purists.

Share this post


Link to post
Share on other sites
Now I get almost everything, thankyou so much for the great explanations (extra thanks for the color coding :) ) I completely understood the need for the 3 coefficient, and I also got the stacked data structure (basically if we have like 3 structs with 2 members per struct, we just put all the members into a 1d array of 6 elements, and since its 2 members per struct we pultiply by 2 to get to the correct member) ok , i think im pretty comfortable with that,
but the last thing that I'm (still) a bit confused with is the (index_y*SCREEN_WIDTH + index_x), I get the part why we are doing this ( thanks again for clear details of your first post) I get that it's used to mconvert a 2d array into a 1d array, but I don't get the SCREEN_WIDTH part, in your example
you showed a 2d array , something like

int array[4][6]; (a girly :) array)

and we put it into a new_array[24] (MACHO array :))

so what I don't get is that in this example we are just taking a 2d array

array[x][y] and making it a
array[x*y]

but why can't we do the same with (index_y*SCREEN_WIDTH + index_x)

like for example a #define of SCREEN_WIDTH is 640, so for lets say if for instance if the for loop is in it's first cycle, meanning that index_x and index_y are 1
we get (1 * 640 + 1) thats 641, I kind of understand why we are multiplying index_y and SCREEN_WIDTH (or 640), that is to transform [x][y] array into the [x*y] array,
where x is the SCREEN_WIDTH and y is the index for height, that is the same as in your example where the array[4][6] turned into a array[24], but my question is why are we adding index_x to everthing else? Sorry if my question is not very clear :)

I really, really appreciate your help, you won't believe how long i searched the net for this, I even dropped an email to LeMothe (no responce, naturally), so thanks again for your help :)

PS.

Decline of Video Gaming XMAS really was funny :)


Share this post


Link to post
Share on other sites
y * SCREEN_WIDTH + x...

Let's take a look at our pic again:


When we are storing the bitmap as an array, what was originally at the point (0, 2) is stored at the location (12) of the array.

Does our algorithm do this? Out bitmap is 6 numbers wide, so

y * WIDTH + x = 2 * 6 + 0 = 12 DING DING! IM A WEINER!!!!!!!1111

Understand it now? How bout if I quote a few lines from Alex Russells DOS Game Programming guide:

Quote:

If we want to draw on the first line it is easy, it is just x pixels from the start. To draw on a line other than the first line we have to move down to the yth line. How do we do that? Each line is 320 pixels long. For each y we must move 320 pixels. Therefore the calculation of a pixel's position is:

Offset=y*320 + x;

Or, more generically:

Offset=y*screen_width + x;

Share this post


Link to post
Share on other sites
Quote:
Original post by VanillaSnake
meanning that index_x and index_y are 1


What did I tell you about C++/C arrays and indexes!!! They start from ___<- fill in teh blank >:( >:( >:(

edit - You know if you look at those frownie faces the right way, they'll smile back! Just proves that everyone/everything has a bit of good, no matter how evil they seem!

Share this post


Link to post
Share on other sites
ohhh, right, now I see :))) so the SCREEN_WIDTH is used to move down by y rows in the array, and the x is used as on offset from the beggining of any row.
Whew, those 3 lines of code took 3 weeks out of my still youg life, but now I get them, thanx so much!!!!

I was wonderng if I could ask you one more question, it's kinda with in the same field but Im not sure how it does what it's supposed to, the function-like
macro that we call once we got all the color extraction,


// this builds a 16 bit color value in 5.5.5 format (1-bit alpha mode)
#define _RGB16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) << 10))

// this builds a 16 bit color value in 5.6.5 format (green dominate mode)
#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))

// this builds a 32 bit color value in A.8.8.8 format (8-bit alpha mode)
#define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))


Ok, I understand (I think I do) that by using & operator were ANDing binary version of numbers to the color, that we passed in earlier, I think it's called a bit mask (correct me if Im wrong), what I'm confused about is + signes and shifting (and what does LaMothe mean when he says scale the channels

also there was another version of this macro earlier in the book that had modulus operators instead of ands, I completely don't understand why we would need a moudulus operator, heres the function

#define _RGB16BIT(r,g,b) ((b%32 + (g%32 << 5 ) + (r%32 << 10))


Again, Thanks a lot for great responses!!!

edit: I just went over the other bit shifting examples andI think I completely understand that first three macros, could you please explain why the modulud is needed in the last one. Tankx!!!!

[Edited by - VanillaSnake21 on March 30, 2006 7:53:37 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by VanillaSnake21
I really, really appreciate your help, you won't believe how long i searched the net for this, I even dropped an email to LeMothe (no responce, naturally


I'm surprised. Every time I e-mail him with a question about the code in one of his books, he usually responds within 24 hours (I was very surprised at how good about it he was). Are you sure you have the right e-mail?

-Gauvir_Mucca

Share this post


Link to post
Share on other sites
Quote:
Original post by Gauvir_Mucca
Quote:
Original post by VanillaSnake21
I really, really appreciate your help, you won't believe how long i searched the net for this, I even dropped an email to LeMothe (no responce, naturally


I'm surprised. Every time I e-mail him with a question about the code in one of his books, he usually responds within 24 hours (I was very surprised at how good about it he was). Are you sure you have the right e-mail?

-Gauvir_Mucca


Well, I was using the email that I found in the book

ceo@xgames3d.com

Is there any other one, or mayabe they just discontinued support for this book since it's kind of old

Share this post


Link to post
Share on other sites
Quote:
Original post by geekalert
Quote:
Original post by VanillaSnake
meanning that index_x and index_y are 1


What did I tell you about C++/C arrays and indexes!!! They start from ___<- fill in teh blank >:( >:( >:(

edit - You know if you look at those frownie faces the right way, they'll smile back! Just proves that everyone/everything has a bit of good, no matter how evil they seem!



but the indexes are not really indexes, they're just names of integers to be used in the for loop, in the code it's

int index_x = 0;
int index_y = 0;

Do I still have to use ___<- format?

Share this post


Link to post
Share on other sites
If you look at the two equations you asked about, they look exactly the same, except that instead of an AND operator, the 2nd equation uses the MODULO operator.

Let's prove that these two equations are the same, by looking at one example:

teh first one: b & 31
teh secnd one: b % 32

31 in binary is 000000000011111. If you AND any number with this 31, only the rightmost, or lower, five bits will stay intact. Try it out! Therefore, the value is effectively constrained to 5 bits. And since the color format is 5 bits for each blue, green, and red, this is exactly what we want.

b % 32 does the same thing, in a different way. Have you ever used rand()%some_number??? This contrains the value to be 0 < value < some_number. So once again, the input value is reduced to 5 bits wide.

Hope that helped, reply if further clarity is needed.

- geekalert

edit: you also asked why the auther chose the newer version with the AND operator. It's simple: a MODULO uses, internally, a DIVISION operation. Obviously, the AND operator is going to me MUCH faster than a DIVIDE.

A more advanced explanation:

In assembly, an AND operator looks like this:

and ax, [value]
ret

and a MODULO, like this:

mov bl, [value]
div bl
xor ax, ax
mov al, dl
ret

Which one looks like it will execute faster? Take a guess ;) (jeez, u dont even need to be an assembly guru to figure this one out!)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this