I'm working on my implementation of a gaussian blur algorithm. The code I'm using is based off of one from a gamedev.net article:
http://www.gamedev.net/reference/programming/features/imageproc/page2.asp
While the code didn't appear to be API specific, my implementation uses Direct3D. I'm not sure whether the problem lies within Direct3D or the algorithm itself. The reason I'm doing this is to create a glow effect as described in these documents:
http://www.gamasutra.com/view/feature/2107/realtime_glow.php
http://developer.nvidia.com/docs/IO/8230/D3DTutorial_EffectsNV.pdf
Below is my code for performing the gaussian blur (just the algorithm itself):
.....
// Perform the gaussian blur
if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
{
pdwPixels = (DWORD*) LockedRect.pBits;
Pitch = (INT) LockedRect.Pitch;
// Horizontal blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;
for( int k = 0; k < GaussWidth; k++ )
{
INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
INT y = j;
DWORD dwColour = pdwPixels[y*Pitch+x];
DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;
dwAlpha += a * GaussFact[k];
dwRed += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue += b * GaussFact[k];
}
// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
}
}
// Vertical blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;
for( int k = 0; k < GaussWidth; k++ )
{
INT x = i;
INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
DWORD dwColour = pdwPixels[y*Pitch+x];
DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;
dwAlpha += a * GaussFact[k];
dwRed += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue += b * GaussFact[k];
}
dwAlpha /= GaussSum;
dwRed /= GaussSum;
dwGreen /= GaussSum;
dwBlue /= GaussSum;
// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
}
}
// Unlock the texture
pTempSurface->UnlockRect();
.....
And here is the full source to the function I wrote to do this (including Direct3D code).
//--------------------------------------------------------------------------------------
// Name: DoGaussianBlur
// Desc: Performs the gaussian blur algorithm on the entire render target, then saves the
// output to a texture of the same size and format. This code assumes the texture
// is 32-bit (D3DFMT_A8R8G8B8).
//--------------------------------------------------------------------------------------
bool DoGaussianBlur( INT Width, INT Height, IDirect3DSurface9* pRTSurface, IDirect3DTexture9** ppTexture )
{
IDirect3DSurface9* pTempSurface = NULL;
DWORD dwRed = 0, dwGreen = 0, dwBlue = 0, dwAlpha = 0;
const INT GaussWidth = 7;
INT GaussFact[GaussWidth] = { 1, 6, 15, 20, 15, 6, 1 };
INT GaussSum = 64;
D3DLOCKED_RECT LockedRect;
DWORD* pdwPixels = NULL;
INT Pitch = 0;
// Verify this texture
if( !pRTSurface )
return false;
// Create a temporary surface.
HRESULT hr = lpD3DDevice->CreateOffscreenPlainSurface( Width, Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pTempSurface, NULL );
if( FAILED( hr ) )
{
OutputDebugStringA( "Error creating temporary surface!\n" );
return false;
}
// Copy the contents of the render target to the newly created surface
hr = lpD3DDevice->GetRenderTargetData( pRTSurface, pTempSurface );
if( FAILED( hr ) )
{
OutputDebugStringA( "Error getting render target data!\n" );
SAFE_RELEASE( pTempSurface );
return false;
}
// Perform the gaussian blur
if( SUCCEEDED( pTempSurface->LockRect( &LockedRect, NULL, 0 ) ) )
{
pdwPixels = (DWORD*) LockedRect.pBits;
Pitch = (INT) LockedRect.Pitch;
// Horizontal blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;
for( int k = 0; k < GaussWidth; k++ )
{
INT x = i - ( ( GaussWidth - 1 ) >> 1 ) + k;
INT y = j;
DWORD dwColour = pdwPixels[y*Pitch+x];
DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;
dwAlpha += a * GaussFact[k];
dwRed += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue += b * GaussFact[k];
}
// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha/GaussSum, dwRed/GaussSum, dwGreen/GaussSum, dwBlue/GaussSum );
}
}
// Vertical blur
for( int i = 1; i < Width - 1; i++ )
{
for( int j = 1; j < Height - 1; j++ )
{
// Clear colour fields
dwRed = dwGreen = dwBlue = dwAlpha = 0;
for( int k = 0; k < GaussWidth; k++ )
{
INT x = i;
INT y = j - ( ( GaussWidth - 1 ) >> 1 ) + k;
DWORD dwColour = pdwPixels[y*Pitch+x];
DWORD a = ( dwColour >> 24 ) & 0xFF;
DWORD r = ( dwColour >> 16 ) & 0xFF;
DWORD g = ( dwColour >> 8 ) & 0xFF;
DWORD b = ( dwColour >> 0 ) & 0xFF;
dwAlpha += a * GaussFact[k];
dwRed += r * GaussFact[k];
dwGreen += g * GaussFact[k];
dwBlue += b * GaussFact[k];
}
dwAlpha /= GaussSum;
dwRed /= GaussSum;
dwGreen /= GaussSum;
dwBlue /= GaussSum;
// Draw blurred pixel
pdwPixels[j*Pitch+i] = D3DCOLOR_ARGB( dwAlpha, dwRed, dwGreen, dwBlue );
}
}
// Unlock the texture
pTempSurface->UnlockRect();
}
else
{
OutputDebugStringA( "Error locking surface!\n\n" );
SAFE_RELEASE( pTempSurface );
return false;
}
// Create a dynamic texture to write the blurred surface to.
if( !ppTexture )
{
hr = lpD3DDevice->CreateTexture( Width, Height, 0, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, ppTexture, NULL );
if( FAILED( hr ) )
{
SAFE_RELEASE( pTempSurface );
OutputDebugStringA( "Error creating copy texture!\n" );
return false;
}
}
DWORD* pdwSurface = NULL;
DWORD* pdwTexture = NULL;
D3DLOCKED_RECT LockedSurface, LockedTexture;
bool bError = false;
// Lock the temporary surface
if( SUCCEEDED( pTempSurface->LockRect( &LockedSurface, NULL, 0 ) ) )
{
// Lock the texture
if( SUCCEEDED( (*ppTexture)->LockRect( 0, &LockedTexture, NULL, 0 ) ) )
{
// Copy the data from the surface to the texture
pdwSurface = (DWORD*) LockedSurface.pBits;
pdwTexture = (DWORD*) LockedTexture.pBits;
::CopyMemory( pdwTexture, pdwSurface, sizeof( pdwSurface ) );
// Unlock
(*ppTexture)->UnlockRect(0);
}
else
{
OutputDebugStringA( "Error locking duplicate texture!\n" );
bError = true;
}
// Unlock
pTempSurface->UnlockRect();
}
else
{
OutputDebugStringA( "Error locking temporary surface!\n" );
bError = true;
}
return !bError;
}
The crash occurs when reading the first few pixels for modification. I followed the algorithm exactly (or at least to my best understanding). Is there a better way to do this? Or should I try another approach to the glow effect entirely? Thanks.