Dear Dutch,
JPG support
testblur.prg
#include "FiveWin.ch"
#include "Slider.ch"
STATIC oWnd
FUNCTION Main( cFile )
local oSlide1
local nVar1 := 0
local hBmp := 0, hPal := 0, hBlur := 0
local oSay, oLbl, cSay := "0"
local nW, nH
local cHow := ""
local nSlideW
// Optional: testblur.exe [imagepath]
if Empty( cFile ) .or. ! File( cFile )
cFile := cGetFile( ;
"Images (*.bmp;*.jpg;*.jpeg;*.png)|*.bmp;*.jpg;*.jpeg;*.png|", ;
"Select an image" )
endif
if Empty( cFile )
return nil
endif
// Load with fallbacks until we have a real HBITMAP
if ! LoadImgAsBmp( cFile, @hBmp, @hPal, @nW, @nH, @cHow )
MsgStop( "Could not load image:" + CRLF + cFile )
return nil
endif
nSlideW := Max( nW - 100, 200 )
// DIALOG (no DispBegin) so image paint stays visible.
// TRUEPIXEL is required: without it, PIXEL controls are converted to the
// dialog template with a 2x factor and end up OUTSIDE the client area
// (slider/say become invisible). Horizontal bar under the image.
DEFINE DIALOG oWnd TITLE "Test Blur - " + cFileName( cFile ) + " [" + cHow + "]" ;
SIZE Max( nW, nSlideW + 120 ) + 20, nH + 70 PIXEL TRUEPIXEL
@ nH + 12, 10 SAY oLbl PROMPT "Blur:" PIXEL OF oWnd SIZE 40, 18
@ nH + 10, 55 SAY oSay VAR cSay PIXEL OF oWnd SIZE 40, 18
@ nH + 8, 100 SLIDER oSlide1 VAR nVar1 OF oWnd ;
HORIZONTAL ;
BOTTOM DIRECTION ;
RANGE 0, 100 ;
MARKS 10 ;
SIZE nSlideW, 36 PIXEL ;
ON CHANGE ( cSay := AllTrim( Str( nVar1 ) ), ;
oSay:Refresh(), ;
If( ! Empty( hBlur ), DeleteObject( hBlur ), nil ), ;
hBlur := If( nVar1 > 0, Blur( hBmp, nVar1 ), 0 ), ;
oWnd:Refresh() )
ACTIVATE DIALOG oWnd CENTERED ;
ON PAINT ( hDC, cPS, Self, ;
PalBmpDraw( hDC, 0, 0, ;
If( nVar1 == 0 .or. Empty( hBlur ), hBmp, hBlur ), hPal ) ) ;
VALID ( If( ! Empty( hBlur ), DeleteObject( hBlur ), nil ), ;
If( ! Empty( hBmp ), DeleteObject( hBmp ), nil ), ;
If( ! Empty( hPal ), DeleteObject( hPal ), nil ), ;
.T. )
RETURN nil
//----------------------------------------------------------------------------//
// Returns .T. and fills hBmp/hPal/nW/nH. Tries several FWH loaders.
FUNCTION LoadImgAsBmp( cFile, hBmp, hPal, nW, nH, cHow )
local aImg, cExt, cTmp, h
hBmp := 0
hPal := 0
nW := 0
nH := 0
cHow := ""
cExt := Upper( cFileExt( cFile ) )
// 1) FW_ReadImage (GDI+ / FreeImage / BMP)
aImg := FW_ReadImage( nil, cFile )
if ! Empty( aImg ) .and. ! Empty( aImg[ 1 ] )
// HBITMAP is numeric; GDI+ image is pointer — convert if needed
if ValType( aImg[ 1 ] ) == "P"
hBmp := GDIP_GetHBitmap( aImg[ 1 ] )
hPal := 0
nW := aImg[ 3 ]
nH := aImg[ 4 ]
cHow := "FW_ReadImage/GdipPtr"
else
hBmp := aImg[ 1 ]
hPal := aImg[ 2 ]
nW := aImg[ 3 ]
nH := aImg[ 4 ]
cHow := "FW_ReadImage"
endif
endif
// 2) FreeImage direct (needs freeimage.dll on PATH / next to exe)
if Empty( hBmp )
h := FILoadImg( AllTrim( cFile ) )
if ! Empty( h )
hBmp := h
hPal := 0
nW := nBmpWidth( hBmp )
nH := nBmpHeight( hBmp )
cHow := "FILoadImg"
endif
endif
// 3) Classic PalBmpRead — BMP only
if Empty( hBmp ) .and. cExt == "BMP"
aImg := PalBmpRead( 0, cFile )
if ! Empty( aImg ) .and. ! Empty( aImg[ 1 ] )
hBmp := aImg[ 1 ]
hPal := aImg[ 2 ]
nW := nBmpWidth( hBmp )
nH := nBmpHeight( hBmp )
cHow := "PalBmpRead"
endif
endif
// 4) Last resort: GDI+ -> temp BMP -> PalBmpRead
if Empty( hBmp )
h := GDIP_ImageFromFile( cFile, .T. )
if ! Empty( h )
hBmp := h
hPal := 0
nW := nBmpWidth( hBmp )
nH := nBmpHeight( hBmp )
cHow := "GDIP_ImageFromFile"
endif
endif
if Empty( nW ) .and. ! Empty( hBmp )
nW := nBmpWidth( hBmp )
nH := nBmpHeight( hBmp )
endif
RETURN ( ! Empty( hBmp ) .and. ! Empty( nW ) .and. ! Empty( nH ) )
#pragma BEGINDUMP
#include <windows.h>
#include <hbapi.h>
void DoHBlur( INT iStart, INT iEnd, INT iRadio, BYTE *pbDestRGB, BYTE *pbSrcRGB, LONG *lTotalClrs, INT iBPP );
void DoHorizontalBlur( BITMAP SrcBmp, BYTE *pBmpDes, BYTE *pBmpBytes, INT iRadio );
void DoVBlur( INT iStart, INT iEnd, INT iRadio, BYTE *pBmpDes, BYTE *pBmpBytes, LONG *lTotalClrs, DWORD dwWidth, INT iCol, INT iBPP );
void DoVerticalBlur( BITMAP SrcBmp, BYTE *pBmpDes, BYTE *pBmpBytes, INT iRadio );
HBITMAP Blur( HBITMAP hSrcBmp, INT iRadio )
{
BITMAP SrcBmp;
BITMAPINFO sBmpInfo;
BYTE *pBmpBytes, *pBmpDes, *pBmpHBlur;
HBITMAP hbmDest, hbmSrc, hbmOldFrom, hbmOldDes, hbmOld, hbmOldBlur, hbmHBlur;
HDC dcSrc, dcDest, dcFrom, dcHBlur;
if( iRadio == 0 )
return 0;
GetObject(hSrcBmp, sizeof( BITMAP ), &SrcBmp);
#ifdef UNICODE
if( (SrcBmp.bmBitsPixel != 32) )
return 0;
#else
if( (SrcBmp.bmBitsPixel != 24) && (SrcBmp.bmBitsPixel != 32) )
return 0;
#endif
sBmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
sBmpInfo.bmiHeader.biWidth = SrcBmp.bmWidth;
sBmpInfo.bmiHeader.biHeight = SrcBmp.bmHeight;
sBmpInfo.bmiHeader.biBitCount = SrcBmp.bmBitsPixel;
sBmpInfo.bmiHeader.biPlanes = 1;
sBmpInfo.bmiHeader.biCompression = BI_RGB;
sBmpInfo.bmiHeader.biSizeImage = SrcBmp.bmBitsPixel * SrcBmp.bmWidth * SrcBmp.bmHeight / 8;
// Select HBITMAP Source ( hSrcBmp ) to Mem DC ( dcFrom )
dcFrom = CreateCompatibleDC( NULL );
hbmOldFrom = ( HBITMAP ) SelectObject( dcFrom, hSrcBmp );
hbmSrc = CreateDIBSection( dcFrom, ( LPBITMAPINFO )&sBmpInfo,
DIB_RGB_COLORS, ( void ** )&pBmpBytes, 0, 0);
dcSrc = CreateCompatibleDC( NULL );
hbmOld = (HBITMAP) SelectObject( dcSrc, hbmSrc );
// Fill the buffer with a copy of the bitmap's bits
BitBlt( dcSrc, 0, 0, SrcBmp.bmWidth, SrcBmp.bmHeight, dcFrom, 0, 0, SRCCOPY );
SelectObject( dcFrom, hbmOldFrom );
DeleteDC( dcFrom );
//Prepare Horizontal Bitmap Blur
dcHBlur = CreateCompatibleDC( NULL );
hbmHBlur = CreateDIBSection( dcHBlur, ( LPBITMAPINFO )&sBmpInfo,
DIB_RGB_COLORS, ( void ** )&pBmpHBlur, 0, 0);
hbmOldBlur = ( HBITMAP ) SelectObject( dcHBlur, hbmHBlur );
//Prepare Destination Bitmap
dcDest = CreateCompatibleDC( NULL );
hbmDest = CreateDIBSection( dcDest, ( LPBITMAPINFO )&sBmpInfo,
DIB_RGB_COLORS, ( void ** )&pBmpDes, 0, 0);
hbmOldDes = ( HBITMAP ) SelectObject( dcDest, hbmDest );
// Gaussian Horzintal blur bitmap
DoHorizontalBlur( SrcBmp, pBmpHBlur, pBmpBytes, iRadio );
BitBlt( dcDest, 0, 0, SrcBmp.bmWidth, SrcBmp.bmHeight, dcHBlur, 0, 0, SRCCOPY );
DoVerticalBlur( SrcBmp, pBmpDes, pBmpHBlur, iRadio );
SelectObject( dcSrc, hbmOld );
DeleteDC( dcSrc );
DeleteObject( hbmSrc );
SelectObject( dcHBlur, hbmOldBlur );
DeleteDC( dcHBlur );
DeleteObject( hbmHBlur );
SelectObject( dcDest, hbmOldDes );
DeleteDC( dcDest );
return hbmDest;
}
//-------------------------------------------------------------------------------//
void DoHorizontalBlur( BITMAP SrcBmp, BYTE *pBmpDes, BYTE *pBmpBytes, INT iRadio )
{
INT j;
INT iBPP = SrcBmp.bmBitsPixel >> 3;
for( j = 0; j < SrcBmp.bmHeight; j++ )
{
LPBYTE pbDestRGB = ( LPBYTE )&( ( DWORD * ) pBmpDes )[ j * SrcBmp.bmWidth ];
LPBYTE pbSrcRGB = ( LPBYTE )&( ( DWORD * ) pBmpBytes )[ j * SrcBmp.bmWidth ];
INT ky;
LONG lTotalClrs[ 3 ];
lTotalClrs[ 0 ] = 0;
lTotalClrs[ 1 ] = 0;
lTotalClrs[ 2 ] = 0;
//First pixel
for( ky = 0; ky <= iRadio; ky++ )
{
lTotalClrs[ 0 ] += pbSrcRGB[ ky * iBPP + 0 ];
lTotalClrs[ 1 ] += pbSrcRGB[ ky * iBPP + 1 ];
lTotalClrs[ 2 ] += pbSrcRGB[ ky * iBPP + 2 ];
}
pbDestRGB[ 0 ] = ( BYTE )( lTotalClrs[ 0 ] / ( iRadio + 1 ) );
pbDestRGB[ 1 ] = ( BYTE )( lTotalClrs[ 1 ] / ( iRadio + 1 ) );
pbDestRGB[ 2 ] = ( BYTE )( lTotalClrs[ 2 ] / ( iRadio + 1 ) );
pbSrcRGB += iBPP;
pbDestRGB += iBPP;
DoHBlur( 1, SrcBmp.bmWidth, iRadio, pbDestRGB, pbSrcRGB, lTotalClrs, iBPP );
}
}
//-------------------------------------------------------------------------------//
void DoHBlur( INT iStart, INT iEnd, INT iRadio, BYTE *pbDestRGB, BYTE *pbSrcRGB, LONG *lTotalClrs, INT iBPP )
{
INT i;
INT iAverage;
for( i = iStart; i < iEnd; i++ )
{
iAverage = iRadio * 2 + 1;
//Subtract pixel leave
if( ( i - iRadio ) > 0 )
{
lTotalClrs[ 0 ] -= pbSrcRGB[ -iRadio * iBPP + 0 ];
lTotalClrs[ 1 ] -= pbSrcRGB[ -iRadio * iBPP + 1 ];
lTotalClrs[ 2 ] -= pbSrcRGB[ -iRadio * iBPP + 2 ];
}
else
{
iAverage += i - iRadio;
}
//Add pixel entering
if( i + iRadio < iEnd )
{
lTotalClrs[ 0 ] += pbSrcRGB[ iRadio * iBPP + 0 ];
lTotalClrs[ 1 ] += pbSrcRGB[ iRadio * iBPP + 1 ];
lTotalClrs[ 2 ] += pbSrcRGB[ iRadio * iBPP + 2 ];
}
else
{
iAverage -= ( iRadio - iEnd + i ) + 1;
}
pbDestRGB[ 0 ] = ( BYTE )( lTotalClrs[ 0 ] / iAverage );
pbDestRGB[ 1 ] = ( BYTE )( lTotalClrs[ 1 ] / iAverage );
pbDestRGB[ 2 ] = ( BYTE )( lTotalClrs[ 2 ] / iAverage );
pbSrcRGB += iBPP;
pbDestRGB += iBPP;
}
}
//-------------------------------------------------------------------------------//
void DoVerticalBlur( BITMAP SrcBmp, BYTE *pBmpDes, BYTE *pBmpBytes, INT iRadio )
{
INT j;
INT iBPP = SrcBmp.bmBitsPixel >> 3;
for( j = 0; j < SrcBmp.bmWidth; j++ )
{
INT ky;
LONG lTotalClrs[ 3 ];
lTotalClrs[ 0 ] = 0;
lTotalClrs[ 1 ] = 0;
lTotalClrs[ 2 ] = 0;
//First pixel
for( ky = 0; ky <= iRadio; ky++ )
{
lTotalClrs[ 0 ] += pBmpBytes[ j * iBPP + ky * SrcBmp.bmWidth * iBPP + 0 ];
lTotalClrs[ 1 ] += pBmpBytes[ j * iBPP + ky * SrcBmp.bmWidth * iBPP + 1 ];
lTotalClrs[ 2 ] += pBmpBytes[ j * iBPP + ky * SrcBmp.bmWidth * iBPP + 2 ];
}
pBmpDes[ j * iBPP + 0 ] = ( BYTE )( lTotalClrs[ 0 ] / ( iRadio + 1 ) );
pBmpDes[ j * iBPP + 1 ] = ( BYTE )( lTotalClrs[ 1 ] / ( iRadio + 1 ) );
pBmpDes[ j * iBPP + 2 ] = ( BYTE )( lTotalClrs[ 2 ] / ( iRadio + 1 ) );
DoVBlur( 1, SrcBmp.bmHeight, iRadio, pBmpDes, pBmpBytes, lTotalClrs, SrcBmp.bmWidth, j , iBPP);
}
}
//-------------------------------------------------------------------------------//
void DoVBlur( INT iStart, INT iEnd, INT iRadio,
BYTE *pBmpDes, BYTE *pBmpBytes,
LONG *lTotalClrs, DWORD dwWidth, INT iCol, INT iBPP )
{
INT i;
INT iAverage;
for( i = iStart; i < iEnd; i++ )
{
iAverage = iRadio * 2 + 1;
//Subtract pixel leave
if( ( i - iRadio ) > 0 )
{
lTotalClrs[ 0 ] -= pBmpBytes[ iCol * iBPP + ( i - iRadio ) * dwWidth * iBPP + 0 ];
lTotalClrs[ 1 ] -= pBmpBytes[ iCol * iBPP + ( i - iRadio ) * dwWidth * iBPP + 1 ];
lTotalClrs[ 2 ] -= pBmpBytes[ iCol * iBPP + ( i - iRadio ) * dwWidth * iBPP + 2 ];
}
else
{
iAverage += i - iRadio;
}
//Add pixel entering
if( i + iRadio < iEnd )
{
lTotalClrs[ 0 ] += pBmpBytes[ iCol * iBPP + ( i + iRadio ) * dwWidth * iBPP + 0 ];
lTotalClrs[ 1 ] += pBmpBytes[ iCol * iBPP + ( i + iRadio ) * dwWidth * iBPP + 1 ];
lTotalClrs[ 2 ] += pBmpBytes[ iCol * iBPP + ( i + iRadio ) * dwWidth * iBPP + 2 ];
}
else
{
iAverage -= ( iRadio - iEnd + i ) + 1;
}
pBmpDes[ iCol * iBPP + i * dwWidth * iBPP + 0 ] = ( BYTE )( lTotalClrs[ 0 ] / iAverage );
pBmpDes[ iCol * iBPP + i * dwWidth * iBPP + 1 ] = ( BYTE )( lTotalClrs[ 1 ] / iAverage );
pBmpDes[ iCol * iBPP + i * dwWidth * iBPP + 2 ] = ( BYTE )( lTotalClrs[ 2 ] / iAverage );
}
}
HB_FUNC( BLUR )
{
hb_retnl( ( LONG ) Blur( ( HBITMAP ) hb_parnl( 1 ), hb_parni( 2 ) ) );
}
#pragma ENDDUMP