TIMAGE and Blur

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: TIMAGE and Blur

Post by Antonio Linares »

Dear Dutch,

JPG support 👍

testblur.prg

Code: Select all | Expand

#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
User avatar
Jimmy
Posts: 1799
Joined: Thu Sep 05, 2019 5:32 am
Location: Hamburg, Germany

Re: TIMAGE and Blur

Post by Jimmy »

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
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: TIMAGE and Blur

Post by Antonio Linares »

Dear Jimmy,

You are totally right :!:

Excellent finding, many thanks :idea:

regards, saludos

Antonio Linares
www.fivetechsoft.com
Post Reply