Archived

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

Tony Chamblee

Alpha-Transparency/Alpha-Blending in DirectX && DirectDraw

Recommended Posts

Okay, well I''ve done quite a bit with alphablending using inline assembly. Yes, it can be done with directdraw by locking the surface

The basic idea is to take a certain percentage of the source color, the opposite percentage which will add to 100% of the destination color, and add them together.

You have to do this for your red, green, AND blue values separately. Otherwise some WEIRD things will happen hehe

If you would like to see my implementation in assembly, it''s available from my website at http://blackhole.thenexus.bc.ca/ under the downloads section as ''vbDABL''. It''s opensource of course

Otherwise, gamedev.net has a couple nice articles about it... I would advise reading those.

David

Share this post


Link to post
Share on other sites

OK...now I know what Alpha-Blending is...what it''s for.

Now here''s my next puzzle.

I''m still relatively inexperienced to the _standard_ world of game design. I''m used to using the blitter to copy sprites to a backbuffer, then flipping the backbuffer and the primary surface, hence making animation via pageflipping. My question is, where does Alpha-Blending come in this loop? (I obviously have very little understanding of Alpha-Blending.)

*Tony Chamblee*

Nucleus Software

Share this post


Link to post
Share on other sites
Alpha-blending allows you to blit an image with partial transparency. If you just want a particular color masked out, ddraw can do that for u already. If you want a blending effect (say blue energy from a space ship), you have to blend the two colors. Blending comes when you blit the sprite to the surface. Instead of a usual ddraw blit, you pocy the pixel to the screen one at a time using your blitting routine.

Share this post


Link to post
Share on other sites
Okay, well are you using vbdabl or your own alphablending method? (just checking hehe)

Essentially, yes, you should use two separate surfacedesc''s because then you can get the pitch of either one of the surfaces from this.

David

Share this post


Link to post
Share on other sites
Well I haven''t seen any useful posts to this thread (no offense you guys , and I''m about to go to bed, seeing as how it is after 2:30 in the morning. But I feel like helping somebody, and I hope this is of some use to you.

Well first of all, you will need a source surface.. and for alpha blending.. you''d want to blit that to a destination surface.. the same as a regular blit.

Here is some simple (but inefficient) pseudocode to do a regular blit. Then I''ll try and provide some insight on how to modify it to perform an alpha blended blit. You''ll have to excuse my lack of knowledge of DDraw, since I wrote a wrapper class for it a year ago and haven''t really touched DDraw directly since :/

(remember this is pseudo-code
It doesnt perform clipping or anything..

/* same arguments (basically) that BltFast takes
i hope my indentation is maintained
note that this assumes 8-bit, etc.. and it only intended to demonstrate the concept of a manual blit */

void RegularBlit(Surface Dest, Surface Src, int DestX, int DestY, RECT SrcRect)
{
int x, y; /* current position that we are working with */
int Xmin, Xmax, Ymin, Ymax; /* bounds of the dest rectangle */
int SrcPitch, DestPitch; /* Source and Dest pitches */
unsigned char *SrcMem, *DestMem; /* Source and Dest surface memory pointers */

/* this should be pretty simple. Xmin-Xmax and Ymin-Ymax are the coordinates on the destination surface that we will be covering */
Xmin = DestX, Ymin = DestY;
Xmax = Xmin + (SrcRect.Right - SrcRect.Left);
Ymax = Ymin + (SrcRect.Bottom - SrcRect.Top);

Lock(Src);
Lock(Dest);
// set pitches and pointers
SrcPitch = Src.Pitch, SrcMem = Src.Mem;
DestPitch = Dest.Pitch, DestMem = Dest.Mem;

// advance pointers to the point where blitting begins
SrcMem = SrcMem + SrcRect.Top*SrcPitch + SrcRect.Left;
DestMem = DestMem + DestY*DestPitch + DestX;

/* Copy each pixel in the area bounded */
for(y=Ymin;y {
for(x=Xmin;x {
SrcMem[x] = DestMem[x]; // copy the pixel
}
/* advance the pointers to the next row */
SrcMem += SrcPitch;
DestMem += DestPitch;
}

Unlock(Src);
Unlock(Dest);
}

I hope that illustrated the concept of a manual blit.. I apologize if I am being to simplistic, but it never hurts to provide more information
Well not too often anyway.

But you see how in the loop, it merely copied the value from the source to the destination? If you want to do an alpha blend, you will have to examine the two values, extract the red, green, and blue components, mix the colors, and then combine them back together again.
Sound hard? Well I''ll try and do my best to explain it

In 8-bit palettized mode, the R,G,B colors are stored in the palette of course. In 16+ bit color modes, they are stored directly in the pixel value themselves. You would have to check the surface properties to determine the exact bit masks to extract the red, green, and blue from 16+ bit surfaces, but usually it goes like this.

For 16-bit:
Imagine your 16 bits.. the colors are usually packed like so:
15 0
RRRRR GGGGGG BBBBB
5 bits for red, 6 for green, and 5 for blue.
Green gets the extra bit because our eyes are most sensitive to yellow-green light, and can determine differences better.

So given a number that had your 16 bit pixel in it..
you would extract the red, green, and blue like so..

Red = Pixel >> 11;
Green = (Pixel >> 5) & 0x3F;
Blue = Pixel & 0x1F;

This yields values where Red and Blue are from 0-31, whereas Green is from 0-63. Complex? yep :/

ok..
for 24 bit, it''s simpler.. it''s just 3 bytes.. generally arranged like this in memory
BGRBGRBGR... etc

Blue = Mem[0];
Green = Mem[1];
Red = Mem[2];
Each value is from 0-255.

for 32-bit, it''s generally the same way, except the fourth byte is ignored..
BGRXBGRXBGRX where X is whatever.. (often an alpha channel.. but not in DDraw..)

Ok.. so you have your Red, Green, and Blue values..
How do you mix them?

I''ll abbreviate source red, green, and blue as SR, SG, SB
and destination as DR, DG, DB

You also will need an alpha value. This can be passed to your function. It is a number which represents how transparent the source bitmap is. 0 is fully transparent, and the max value (I''m going to use an 8-bit alpha, so the max value will be 255) is fully opaque.

So you have SR, SG, SB, DR, DG, DB, and Alpha for a single pixel.
To blend them, you would use this algorithm, assuming all the values are from 0-255.
Remember when you extract the values from 16-bit surfaces, the values are not from 0-255, so you would have to multiply them to get them in the proper range. In the above 16 bit example, Red and Blue would be multiplied by 8, while Green would be multiplied by 4.

(I''m doing this from memory.. and it''s 3am.. But I think it''s correct

if (SR > DR) NewRed = ((SR-DR) * Alpha / 255) + DR;
else NewRed = DR - ((DR-SR) * Alpha / 255);
if (SG > DG) NewGreen = ((SG-DG) * Alpha / 255) + DG;
else NewGreen = DG - ((DG-SG) * Alpha / 255);
if (SB > DB) NewBlue = ((SB-DB) * Alpha / 255) + DB;
else NewBlue = DB - ((DB-SB) * Alpha / 255);

After you have your new red, green, and blue values, you combine them back together using the reverse of what you did to extract them..

So for 8-bit palettized, this requires a lookup table.. or a nearest color match.. I wont go into this right now, as it doesn''t exactly deal with the concept of alpha blending.

For 16-bit, with the usual bit-packing, you would combine them like this:

NewPixel = (NewRed << 11) / (NewGreen << 5) / NewBlue;

You would then write your new pixel (or your new R,B,G values) into the destination memory.

I wish I could provide you with some working code, but like a previous poster mentioned, this is slow.. so I too converted mine to optimized inline asm with lookup tables and such.. (so it''s just 1 add, one subtract, and one mov per channel

An interesting idea that you might try is having Alpha vary as you blit.. ie.. you pass in 3 surfaces, source, dest, and an alpha map.. and you read the Alpha value from the Alpha map as you blit.. gives a great effect

I apologize that I can''t paste working code, but I hope that I have given you some insight into how to perform an alpha blend. So unfortunately, the coding and optimizing are up to you
I would recommend getting a plain blitter working first if you aren''t accustomed to dealing with surfaces directly (and in different bit depths) before you try and tackle alpha blended blitters.

One final note: Accessing surfaces stored in video memory is VERY slow, especially reading from them. If you are going to use alpha blending, or any function that directly accesses surfaces, make sure that all of the surfaces you pass to that function are explicitly created in system memory. Check the DirectDraw docs on CreateSurface for more info on this. Note that DirectDraw creates surfaces in video memory by default. Furthermore, the backbuffers that DirectDraw creates for you in the flipping chain are generally always created in video memory. So if you want to use alpha blending to blit to one of these.. I would highly recommend that you create your backbuffer in system memory (not as part of a flipping chain), and then BltFast it to the primary surface. (You can use the WaitForVerticalRetrace() [I think that''s the name anyway] function in the DirectDraw interface to synch to the vertical retrace).

Wow, I''ve been working on this for almost an hour.
Well I sincerely hope my efforts help you and/or somebody else!

If you need any advice, feel free to e-mail me.
But no whining

Adam Milazzo
adamm@san.rr.com

Share this post


Link to post
Share on other sites
Ugh.. the message board sure messed up my nice spacing in there
I hope it''s still understandable..
And that one ''to'' that should be ''too'', ignore that
Hehe.. man i''m tired.. time for sleep..
Goodnight all, and good luck

Share this post


Link to post
Share on other sites
Eeek.. the message board also messed up this line too.
(And it''s an important one!)

NewPixel = (NewRed << 11) / (NewGreen << 5) / NewBlue;

Those slashes should be /''s (the pipe character, used for bitwise-OR).. makes a serious difference
I typed pipes.. not sure why it felt like it should mess up my code and put in slashes!
Oh well

Share this post


Link to post
Share on other sites
DDraw should provide some mechanism for alpha-blended blt''ing, so that hardware can accelerate it.

I don''t know if DDraw does provide such a system or not - I''m just coming to (ie, I haven''t yet) looking at alpha-blending myself.

But, one thing to bear in mind is that even if DDraw provides a system to do it, who''s to say whether or not the driver and/or chipset support it (properly)?

For such "non-standard" functionality, I''d want to provide a pure software alternative, just in case.

TheTwistedOne
http://www.angrycake.com

Share this post


Link to post
Share on other sites
Tsk. DirectDraw should support alpha-blended blts, but doesn''t (in version 7, anyway).

There is support for it in Direct3D, though, so it may be possible to do some sort of faking it. To be honest, though, it''d be easier to write your own.

Bah. I hope they fix this. What a stupid oversight.

TheTwistedOne
http://www.angrycake.com

Share this post


Link to post
Share on other sites
DirectDraw does do alpha blits, either with a constant alpha value or using an alpha map. However, you can only do that if the vidcard supports it in hardware. It will not be emulated in software if the card doesn''t support it. I believe it''s done using the BltFX method. I tried it a while ago, but gave it up when I found out about the hardware requirement. My system sucks. The same rule applies for rotated blitting unfortunately.

Share this post


Link to post
Share on other sites
This is where querying the Video Card capabilities comes into play. You set up a function pointer that points to 1 of two functions (The directdraw hardware accelerated one if the video card supports it, and the one that you write for yourself). Therefore when the program starts up, you check to see if their video card can alpha blend for you (Faster), or if you have to do it yourself (Slower but works nonetheless). I do the same thing for loading surfaces into Video RAM which is a requirement if you plan on Alpha Blending (DirectDraw cannot hardware blit unless the image is in VidMem).

In conclusion, you gotta KNOW DirectDraw in order to maximize its use.


Rock on,
- Tom

Share this post


Link to post
Share on other sites
in 24bit mode, that means there are 3 bytes of info per color per pixel... In other words, three bytes will be right next to each other, each one dedicated solely to r, g, and b values.

I'm not sure on the exact code, but get a pointer to memory.. (this was just ripped from 16-bit code... dunno if 24bit will work like this or not).

BYTE *lpmem;
DDSURFACEDESC2 ddsd;

memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
lpdds->Lock(NULL, &ddsd, DDLOCK_WAIT / DDLOCK_SURFACEMEMORYPTR, NULL);

// set the memory pitch
*lpitch = ddsd.lPitch >> 1;

// return pointer to surface
lpmem = ((BYTE *)ddsd.lpSurface);
I'm not sure about the byte... but maybe..

then store the first byte of info from the pointer, into the red value, then the next into the green and the next into the blue, at least i think RGB is the order.
so...
red =? lpmem;
lpmem++;
green = lpmem;
lpmem++
blue = lpmem;

I have no idea if this works or not... but It's just what seems like would be logical.

If anybody has the time to try this, please post your results and what you had to modify. I'm interested in seeing if this way is possible.

Edited by - iwasbiggs on 4/15/00 2:05:07 AM

Share this post


Link to post
Share on other sites
I''m kind of interested in this BLTFX thing [hardware-accelerated alpha blending]. I ran DDCAPS and found something rather strange. Under Driver Caps Alpha is unchecked. But under HEL Caps [emulation], it is checked!

I''m running Windows 2000 with a Voodoo 3 2000 and DirectX 7a run-time/SDK. Of course Win2k has all those fancy transparency affects [via GDI]. Could that have anything to do with this?

Now the other thing, is that in the SDK docs, it doesn''t sound like this functionality is supported at all [even if the appropriate hardware is present?]:

"The IDirectDrawSurface7::Blt method performs a bit block transfer (blit). This method does not support z-buffering or alpha blending (see alpha channel) during blit operations."

"All DDBLT_ALPHA flag values. Not currently implemented."

There is always the D3D textured quads method, and the write-your-own blitter method... but I''m kind of wondering about this method. Anyone have any experience with it?

Nathan.


Share this post


Link to post
Share on other sites
Changing a grouped RGB color into 3 individual colors can be done like this:
(not optimized at all)

//You have your color value, which, if you are doing
//alpha blending, you would get from the source and dest
//surfaces.
int Color = RGB( 200, 100, 50 );

Red = (Color & 16711680) / 65280;
Green = (Color & 65280) / 255;
Blue = (Color & 255)

//Red is now equal to 200, Green to 100, and Blue to 50

Hope that helped...
-Derek

Share this post


Link to post
Share on other sites