|
The code to load our texture is the same as our Alpha Transparency lesson's code, so I won't show it again.
After the image has been loaded (in InitScene) we call our process_alpha() procedure to generate an alpha
channel for our texture.
process_alpha() determines the texture format we have and then calls the appropriate procedure to do the actual
texture processing.
// Function:process_alpha
// Whazzit:Scans through a texture setting its alpha component to the same value as the
// red component. Our image is grayscale so red == blue == green. You'd get
// some funky effects if you loaded a non-grayscale image since only the red
// would control alpha.
void process_alpha(void){
HRESULT hr;
D3DSURFACE_DESC surf_desc;
D3DLOCKED_RECT rect;
//We use GetLevelDesc to obtain the width and height of the texture. We could
//hard-code the values, but that's bad programming practice.
hr=g_texture->GetLevelDesc(0,&surf_desc);
if(FAILED(hr)){
FatalError(hr,"Error getting surface description");
}
//After the texture is locked, the pBits member of the rect structure will point to
//the actual data bits.
hr=g_texture->LockRect(0, //Level 0, the only one since we didn't use mipmaps
&rect, //This will be filled with info if the lock suceeds
NULL, //Lock the whole texture
D3DLOCK_NOSYSLOCK); //This allows our debugger to keep running
if(FAILED(hr)){
FatalError(hr,"Error locking texture");
}
//Let's see what texture format we have. We requested 32-bit ARGB, but it's not
//necessarily what we got.
switch(surf_desc.Format){
case D3DFMT_A8R8G8B8: //32-bit ARGB, what we requested
process_alphaA8R8G8B8(surf_desc.Width,surf_desc.Height,&rect);
break;
case D3DFMT_A4R4G4B4: //16-bit ARGB, not ideal, but not bad
process_alphaA4R4G4B4(surf_desc.Width,surf_desc.Height,&rect);
break;
case D3DFMT_R8G8B8: //These 3 formats have no alpha channel
case D3DFMT_R5G6B5: //Hopefully we'd never be given this
case D3DFMT_R3G3B2:
g_texture->UnlockRect(0);
FatalError("Texture had no alpha channel, aborting");
break;
default: //Default handler. Other formats (like 1-bit alpha) aren't
//suitable for this example, so we bail out and tell the user 'why'.
g_texture->UnlockRect(0);
FatalError("Your video card does not support a texture format with sufficent alpha precision.");
break;
};
g_texture->UnlockRect(0);
}
// Function:process_alphaA8R8G8B8
// Whazzit:This processes our texture if it's 32-bit ARGB. This is the ideal format.
// It's easy and fast to process with a full 8-bits of precision for the
// alpha channel.
void process_alphaA8R8G8B8(DWORD p_width,DWORD p_height,D3DLOCKED_RECT *p_rect){
DWORD x,y;
DWORD pitch_diff;
//When we lock our texture, we will get a pointer to our 32-bit ARGB data. Note that
//because of the byte ordering on Intel we would specify a 32-bit value as 0xAARRGGBB
//But our structure is laid out in reverse order BGRA. Those wacky Intel-ians.
struct my_colour{
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
} *col;
//Sometimes extra space needs to be allocated for each 'row' of texture data.
//The width is how wide the data is per row, the pitch is how much is actually allocated.
//The pitch will always be at least as big as the width, but may be larger if the driver
//reqires data to be on a specific memory alignment boundary.
//
//pitch_diff is the amount we have to add to skip the non-data space to reach the next line
pitch_diff=(p_rect->Pitch/4) - p_width;
//Using a pointer to 'my_colour' simplifies our dealings with the various colour components
col=(my_colour *)p_rect->pBits;
//Now we loop through the whole texture setting the alpha component equal to the red component.
//Since our image was grayscale, we could have used the blue or green instead of red and it
//would have made no difference.
for(y=0;y < p_height;y++){
for(x=0;x < p_width; x++){
col->alpha=col->red;
col++;
}
//Add pitch_diff to skip to the next line.
col+=pitch_diff;
}
}
// Function:process_alphaA4R4G4B4
// Whazzit:This processes our texture if it's 16-bit ARGB. Though it lacks the
// precision of the 32-bit ARGB, it's still pretty good and it takes up
// 1/2 of the memory.
void process_alphaA4R4G4B4(DWORD p_width,DWORD p_height,D3DLOCKED_RECT *p_rect){
DWORD x,y;
DWORD pitch_diff;
//With this texture format, we have 4 bits for each of the alpha, red, green, and blue
//channels.
struct my_colour{
unsigned char blue:4;
unsigned char green:4;
unsigned char red:4;
unsigned char alpha:4;
}*col;
//Sometimes extra space needs to be allocated for each 'row' of texture data.
//The width is how wide the data is per row, the pitch is how much is actually allocated.
//The pitch will always be at least as big as the width, but may be larger if the driver
//reqires data to be on a specific memory alignment boundary.
//
//pitch_diff is the amount we have to add to skip the non-data space to reach the next line
pitch_diff=(p_rect->Pitch/2) - p_width;
//Using a pointer to 'my_colour' simplifies our dealings with the various colour components
col=(my_colour *)p_rect->pBits;
//Now we loop through the whole texture setting the alpha component equal to the red component.
//Since our image was grayscale, we could have used the blue or green instead of red and it
//would have made no difference.
for(y=0;y < p_height;y++){
for(x=0;x < p_width; x++){
col->alpha=col->red;
col++;
}
//Add pitch_diff to skip to the next line.
col+=pitch_diff;
}
}
|