Skip to main content
GameDev.net gamedev.net
🔒 Locked

16 Bit Color on DirectDraw Surfaces

Started by TheTrust Dec 19, 2004 at 10:23 PM 5 replies 2.5k views
Original Post
TheTrust
TheTrust
Okay I hate to post yet another message regarding problems with 16bit color macros but I have spent 6 hours pouring over these forums and have not found a message that seems to nail the issue I have. I have tried multiple solutions provided in this forum with no luck. I am trying to create a basic 2d engine using directX9 here is my problem. First the latest Macro I have been using to build the 16 bit word for the colour. #define _16BIT(r,g,b) ((b>>3)|((g>>2)<<5)|((r>>3)<<11)) okay, I will openly admit I know next to nothing about bit shifting and dont have a good grasp on what this does. I have tried about 12 different macros that I have found online all have the same problem. Reds dont appear Green is off in shade Blue appears to be fine here is the code I am using to fill a screen by displaying random pixels //lock the surface lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL); //setup video buffer UCHAR *video_buffer = NULL; //attempt to draw on screen video_buffer = (UCHAR *)ddsd.lpSurface; for(int i=0; i < 500; i++) { int x = rand()%800; int y = rand()%600; video_buffer[x*2 + (y*ddsd.lPitch)] = RGB(255,0,0); } lpddsprimary->Unlock(NULL); Now I am no expert but if 16bot colour is posing such a problem has or should Microsoft build a function into directdraw to build the values from and RGB set for us??? I would greatly appreciate any help anyone can provide. TheTrust It's only impossible until its not
Liam M
Liam M
Well Trust, after taking a look at your macro, there's something you probably dont know about yet. You see, there are TWO types of 16 bit colour, 5.5.5 (which is actually 15 bit), or 5.6.5. This is an extremely annoying quirk, as the type your computer uses is determined by your hardware (and quite possibly your drivers).

here are two new macros, try them both, that operate in both 5.5.5 and 5.5.6

//5.5.5 macro
#define 16BIT555(r,g,b) ((b & 31) + ((g & 31) << 5) + ((r & 31) <<10))

//5.5.6 macro
#define 16BIT556(r,g,b) ((b & 31)+ ((g & 63) << 5) + ((r & 31) <<11))


As for determining which type the graphics card is using, heres the function you will need:

HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat);

LPDDPIXELFORMAT has these members:

DWORD dwSize; //size of struct
DWORD dwFlags; //flags describe the surface
DWORD dwRGBBitCount; //the number of bits for all channels (15, 16, 24, 32 ect.)

now what you do is fill in the dw size member by calling OurPixel.dwSize = sizeof(OurPixel)

then call the function like this:

YourSurface -> GetPixelFormat(&OurPixel);

then check if the flags member is set to DDPF_RGB, (meaning your screen is in an RGB mode), and if so, then test the dwRGBBitCount, and test what its equal to (either 15, 16, 24 or 32). this will determine which macro to use.

Excuse the spelling errors, but i hope this helps you out, tell me if it doesnt. Good luck mate.
prowst
prowst
Computers represent data using a base 2 numbering system.
So each bit can be 0 or 1. With 16 bits, you can represent
2^16 different values. Using an unsigned 16 bit integer
the range of values it can store are from 0 to 65535.
(0 to 2^16 - 1)

A neat little trick is bit shifting. If you take the bits
and shift them left one position, you are in effect
multiplying by 2. If you take the bits and shift them to
the right one position, you are in effect dividing by 2.
You can shift bits further than one position. If you shift
left 2 positions, you multiply by 4. Shift 3 positions,
multiply by 8. Etc.

Using the most common 16bit Packed RGB encoding 5.6.5
Meaning that all the color information is packed into
each 16bit integer using 5 bits for red, 6 bits for
green, and 5 bits for blue. (5 + 6 + 5 = 16)

So if there are only 5 bits available for the red channel,
you can have a range of values from 0 to 31. Using 6 bits
for the green channel you can have a range of values from
0 to 63. Using 5 bits for the blue channel, you can have a
range of values from 0 to 31.

These macros that people use are a handy way to pack all
the red, green, and blue intensities into a 16bit unsigned
integer. Using some bit manipulation we can make sure
that each r,g,b value is in the correct range. If you didnt
you would overwrite information in your other color channels,
causing you to see an entirely different color than you expected.

If I can remember right, the macro I used was:
#define RGB16(r, g, b) (((r&31) << 11) + ((g&63) << 5) + (b&31))
I used the bitwise AND operator to keep the correct range of values for
each color channel. I used bit shifting to move the r, g, b values
into the correct order.

Quote:
Original post by TheTrust
UCHAR *video_buffer = NULL;
video_buffer = (UCHAR *)ddsd.lpSurface;

Since you are in 16bit mode, you should do this:
USHORT *video_buffer = NULL;
video_buffer = (USHORT *)ddsd.lpSurface;

The size of a pointer is 4 bytes in either case,
but when you increment the pointer, you move 2
bytes forward instead of 1 byte (UCHAR).

Also the the pitch is reported in the number of
bytes. Since you are using 16bit (2bytes) you
need to divide this value by 2.
USHORT pitch = ddsd.lPitch / 2;

Then to plot your pixel all you need to do is
this:
video_buffer[x + y * pitch] = RGB16(255, 0, 0);

liam666 mentioned the other 16bit format 5.5.5
that you should also take into consideration.

There a many articles here on gamedev that address
this issue that you should read through. Click Here
And I'm sure you can find other websites with google that
you can read as well.

Quote:
Original post by TheTrust
I am trying to create a basic 2d engine using directX9

The functions you are using looks like you are
using DirectDraw7 which isnt DirectX9. You might have
DirectX9 SDK installed, but you can use any previous
version with it.

Anyway, best of luck!

-prowst
prowst
prowst
I think I just realized something as well.

Quote:
Original post by TheTrust
#define _16BIT(r,g,b) ((b>>3)|((g>>2)<<5)|((r>>3)<<11))
UCHAR *video_buffer = NULL;
video_buffer = (UCHAR *)ddsd.lpSurface;
video_buffer[x*2 + (y*ddsd.lPitch)] = RGB(255,0,0);


Your using the win32 RGB macro. But even if you used
the macro you mentioned _16BIT, thats going to give
you a 16 bit integer but you are assiging it to an
UCHAR which is only 8 bits!

Quote:
Original post by TheTrust
Reds dont appear
Green is off in shade
Blue appears to be fine


R.G.B = 00000.000000.00000

So you're only using 8 bits!
R.G.B = -----------000.00000

Thats why you see no red, some shades of green,
and blue works fine!

Right? Can someone verify that?

-prowst
Liam M
Liam M
Hey yeah, that would explain alot, should be a DWORD. BTW, another more primordial use for bit shifts is division by powers of two, EG:


640 << 5 == 640 / 32;// << is divide
640 >> 5 == 640 * 32;// >> is multiply

2 * 2 * 2 * 2 * 2 = 32

This is pretty much for optimisation, but if your working on an engine, this is very handy (mostly because division is very slow, and bitshifts are VERY fast)

PS: wouldnt this sort of error count as truncation? I would think the compiler should pick it up.
TheTrust
TheTrust
Thanks guys,

Finally an answer that makes some sense. I will try using a DWORD instead of the UCHAR and hope that that fixes it. I will let you guys know.

as for using both 5,6,5 and 5,5,5 would it be wise to check the surfaces pixel properties then use a function pointer to pick which of lets say 2 SetPixel() functions I write???

anywho thanks again guys.

TheTrust
It's only impossible until its not
TheTrust
TheTrust
DUDE I GOT RED!

wow never thaught I would say that.

so for everyone out there that maybe experiencing the same frustration I have been experiencing here is what finally worked for me.

This Macro

#define _16BIT(r,g,b) ((b>>3)|((g>>2)<<5)|((r>>3)<<11))

and this code

//lock the surface
lpddsprimary->Lock(NULL,&ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);

USHORT *video_buffer = NULL;

//attempt to draw on screen
video_buffer = (USHORT *)ddsd.lpSurface;

for(int i=0; i < 500; i++)
{
int x = rand()%800;
int y = rand()%600;
video_buffer[x + (y*(ddsd.lPitch/2))] = (USHORT)_16BIT(255,0,0);
}
lpddsprimary->Unlock(NULL);

thank you again guys.

I am sure I wont be a stranger

TheTrust
It's only impossible until its not

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.