FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour TIMAGE and Blur
Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: TIMAGE and Blur

Posted: Tue Aug 11, 2026 04:05 AM

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
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1797
Joined: Thu Sep 05, 2019 05:32 AM

Re: TIMAGE and Blur

Posted: Tue Aug 11, 2026 04:31 PM

hi Antonio,

I have use your last Source Version, but exclude all between #pragma BEGINDUMP/#pragma ENDDUMP as it seems they are Dupe in FiveHC64.LIB ?

It seems to work :) did i miss something ?

greeting,

Jimmy
Posts: 44508
Joined: Thu Oct 06, 2005 05:47 PM

Re: TIMAGE and Blur

Posted: Wed Aug 12, 2026 05:12 AM

Dear Jimmy,

You are totally right :!:

Excellent finding, many thanks :idea:

regards, saludos

Antonio Linares
www.fivetechsoft.com

Continue the discussion