NO

Author Topic: Getting image bits from handle  (Read 1808 times)

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Getting image bits from handle
« on: December 05, 2024, 02:58:26 AM »

The parameters:
Quote

hImgBitmap is a valid handle on the image. It can has been got by LoadImage, LoadBitmap...
__lpdwImageSize is a valid pointer on a variable that will receive the number of bytes of the image. Before calling the function set it to 0
__dwImageWidth & __dwImageHeight are image dimensions that you can get by calling GetObject like this GetObject(_hBitmap,sizeof(BITMAP),&_Bitmap)
Code: [Select]
LPBYTE BitmapToImage(HBITMAP __hImgBitmap,LPDWORD __lpdwImageSize,DWORD __dwImageWidth,DWORD __dwImageHeight)
{
    register        LPBYTE             _lpImageBits ;

    alignas(HANDLE) BITMAPINFO         _BitmapInfo ;
    alignas(HANDLE) HDC                _hDC ;

    _hDC = GetDC(NULL) ;  // *** Get a DC on the screen ***
    if(_hDC)
    {
        __stosq((unsigned long long *) &_BitmapInfo,0,sizeof(BITMAPINFO) / 8) ; // *** Fill the BITMAPINFO structure with 0 ***

        _BitmapInfo.bmiHeader.biBitCount    = 32 ;                              // *** Filling correctly these fields is required by GetDIBits ***
        _BitmapInfo.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER) ;
        _BitmapInfo.bmiHeader.biWidth       = __dwImageWidth ;                  // *** Image Width  ***
        _BitmapInfo.bmiHeader.biHeight      = __dwImageHeight ;                 // *** Image Height ***
        _BitmapInfo.bmiHeader.biPlanes      = 1 ;                               // *** Must always be set to 1 ***

// **************************************************************************************************************
// *** When calling GetDIBits with these minimum datas, it will fill correctly the BITMAPINFOHEADER structure ***
// **************************************************************************************************************
        if(GetDIBits(_hDC,__hImgBitmap,0,_BitmapInfo.bmiHeader.biHeight,NULL,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
        {
// ***************************************************
// *** Allocate memory to receive the bitmap bits ****
// ***************************************************
            _lpImageBits = (LPBYTE) VirtualAlloc(NULL,_BitmapInfo.bmiHeader.biSizeImage + 4095,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE) ;
            if(_lpImageBits)
            {
// *****************************************************************
// *** Now call GetDIBits again but for receiving the image bits ***
// *****************************************************************
                if(GetDIBits(_hDC,__hImgBitmap,0,_BitmapInfo.bmiHeader.biHeight,_lpImageBits,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
                {
// ************************************************************************
// *** The bitmap datas we got is under the form ARGB. A 32 bits bitmap ***
// ************************************************************************
                    DeleteDC(_hDC) ; // Delete the DC

                    *__lpdwImageSize = _BitmapInfo.bmiHeader.biSizeImage ; // *** Copy the imagesize (in bytes) into the variable passed in parameter ***

                    return (_lpImageBits) ; *** returns the image bits buffer address
                }

                VirtualFree(_lpImageBits,0,MEM_RELEASE) ; // *** GetDIBits failed so free the memory ***
            }
// *** If you arrive here this is because the memory allocation failed ***
        }

        DeleteDC(_hDC) ; // *** The first call to GetDIBits has failed ***
    }                    // *** Don't forget to free the memory ***

    return (NULL) ;      // *** return a NULL pointer to indicate it was not possible to do the job ***
}

One thing we could do is to call SetLastError with 0 has an error code, that will clear the Last Error code
https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setlasterror
Like this we would be sure that the error code was really set by our function

GetDIBits : https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getdibits
VirtualAlloc : https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
« Last Edit: December 05, 2024, 03:50:50 AM by HellOfMice »
--------------------------------
Kenavo

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #1 on: December 05, 2024, 03:15:42 AM »
To get a strong function you can add these lines at the beginning of the function:
Code: [Select]
if(__hImgBitmap) return (NULL) ;
if(!__lpdwImageSize) return (NULL) ;
if((!__dwImageWidth) || (!__dwImageHeight)) return (NULL) ;


We could declare a register pointer on the BITMAPINFOHEADER structure

register BITMAPINFOHEADER * _lpBmih ; or register LPBITMAPINFOHEADER _lpBmih ;

and initialize it as _lpBmih = &_BitmapInfo.bmiHeader ;
now replace all the occurences of _BitmapInfo.bmiHeader. with "_lpBmih ->"
« Last Edit: December 05, 2024, 03:47:45 AM by HellOfMice »
--------------------------------
Kenavo

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #2 on: December 05, 2024, 03:33:55 AM »
Final function

Code: [Select]
LPBYTE BitmapToImageEx(HBITMAP __hImgBitmap,LPDWORD __lpdwImageSize,DWORD __dwImageWidth,DWORD __dwImageHeight)
{
    register        LPBYTE                  _lpImageBits ;
    register        LPBITMAPINFOHEADER      _lpBmih ;

    alignas(HANDLE) BITMAPINFO              _BitmapInfo ;
    alignas(HANDLE) HDC                     _hDC ;

    if(!__hImgBitmap)                           return (NULL) ;
    if(!__lpdwImageSize)                        return (NULL) ;
    if((!__dwImageWidth) || (!__dwImageHeight)) return (NULL) ;
   
    *__lpdwImageSize = 0 ;
   
    SetLastError(0) ;
/*
    alignas(HANDLE)     BITMAP  _Bitmap ;
   
    if(!GetObject(__hImgBitmap,sizeof(BITMAP),&_Bitmap))    return (NULL) ;
   
    If you add these two lines remove "DWORD __dwImageWidth,DWORD __dwImageHeight"
    and replace

        _lpBmih->biWidth       = __dwImageWidth ;
        _lpBmih->biHeight      = __dwImageHeight ;
    with
        _lpBmih->biWidth       = _Bitmap.bmWidth ;
        _lpBmih->biHeight      = _Bitmap.bmHeight ;
*/
    _hDC = GetDC(NULL) ;
    if(_hDC)
    {
        __stosq((unsigned long long *) &_BitmapInfo,0,sizeof(BITMAPINFO) / 8) ;
       
        _lpBmih = &_BitmapInfo.bmiHeader ;

        _lpBmih->biBitCount    = 32 ;
        _lpBmih->biSize        = sizeof(BITMAPINFOHEADER) ;
        _lpBmih->biWidth       = __dwImageWidth ;
        _lpBmih->biHeight      = __dwImageHeight ;
        _lpBmih->biPlanes      = 1 ;

        if(GetDIBits(_hDC,__hImgBitmap,0,_lpBmih->biHeight,NULL,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
        {
            _lpImageBits = (LPBYTE) VirtualAlloc(NULL,_BitmapInfo.bmiHeader.biSizeImage + 4095,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE) ;
            if(_lpImageBits)
            {
                if(GetDIBits(_hDC,__hImgBitmap,0,_BitmapInfo.bmiHeader.biHeight,_lpImageBits,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
                {
                    DeleteDC(_hDC) ;

                    *__lpdwImageSize = _lpBmih->biSizeImage ;

                    return (_lpImageBits) ;
                }

                VirtualFree(_lpImageBits,0,MEM_RELEASE) ;
            }
        }

        DeleteDC(_hDC) ;
    }

    return (NULL) ;
}
« Last Edit: December 05, 2024, 03:50:17 AM by HellOfMice »
--------------------------------
Kenavo

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #3 on: December 05, 2024, 03:44:10 AM »
Now here is a function that has all the optimizations and corrections.
I did not want to give this function from the beginning to make you see all the steps when writing a function.

Code: [Select]
LPBYTE NewBitmapToImageEx(HBITMAP __hImgBitmap,LPDWORD __lpdwImageSize)
{
    register        LPBYTE                             _lpImageBits ;
    register        LPBITMAPINFOHEADER      _lpBmih ;

    alignas(HANDLE) BITMAPINFO              _BitmapInfo ;
    alignas(HANDLE) HDC                          _hDC ;
    alignas(HANDLE) BITMAP                     _Bitmap ;

    if(!__hImgBitmap)                        return (NULL) ;
    if(!__lpdwImageSize)                    return (NULL) ;
   
    *__lpdwImageSize = 0 ;

   if(!GetObject(__hImgBitmap,sizeof(BITMAP),&_Bitmap))    return (NULL) ;
   
    SetLastError(0) ;

    _hDC = GetDC(NULL) ;
    if(_hDC)
    {
        __stosq((unsigned long long *) &_BitmapInfo,0,sizeof(BITMAPINFO) / ;
       
        _lpBmih = &_BitmapInfo.bmiHeader ;

        _lpBmih->biBitCount    = 32 ;
        _lpBmih->biSize          = sizeof(BITMAPINFOHEADER) ;
        _lpBmih->biWidth        = _Bitmap.bmWidth ;
        _lpBmih->biHeight       = _Bitmap.bmHeight ;
        _lpBmih->biPlanes       = 1 ;

        if(GetDIBits(_hDC,__hImgBitmap,0,_lpBmih->biHeight,NULL,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
        {
            _lpImageBits = (LPBYTE) VirtualAlloc(NULL,_BitmapInfo.bmiHeader.biSizeImage + 4095,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE) ;
            if(_lpImageBits)
            {
                if(GetDIBits(_hDC,__hImgBitmap,0,_BitmapInfo.bmiHeader.biHeight,_lpImageBits,(LPBITMAPINFO) &_BitmapInfo,DIB_RGB_COLORS))
                {
                    DeleteDC(_hDC) ;

                    *__lpdwImageSize = _lpBmih->biSizeImage ;

                    return (_lpImageBits) ;
                }

                VirtualFree(_lpImageBits,0,MEM_RELEASE) ;
            }
        }

        DeleteDC(_hDC) ;
    }

    return (NULL) ;
}
« Last Edit: December 05, 2024, 03:58:20 AM by HellOfMice »
--------------------------------
Kenavo

Offline Vortex

  • Member
  • *
  • Posts: 942
    • http://www.vortex.masmcode.com
Re: Getting image bits from handle
« Reply #4 on: December 05, 2024, 08:52:02 AM »
Hello,

Thanks for sharing. Back home, I will have a closer look. I have similar functions coded with Masm and Poasm.
Code it... That's all...

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #5 on: December 05, 2024, 08:54:03 AM »
Hi Vortex,


I saw your routine to write image on disk, this gave me the idea to share image work in C


Thank you for sharing


Philippe
--------------------------------
Kenavo

Offline Vortex

  • Member
  • *
  • Posts: 942
    • http://www.vortex.masmcode.com
Re: Getting image bits from handle
« Reply #6 on: December 05, 2024, 08:00:01 PM »
Another idea, creating a bitmap from memory :

hWnd : Handle of the parent window
pBmp : Pointer to the memory portion storing the bitmap

Code: [Select]
HBITMAP CreateBmpFromMem (HWND hWnd,LPBYTE pBmp)
{
  HDC hDC;
  HBITMAP hBmp={0};
  VOID* ppvBits;
  BITMAPINFOHEADER* StartOfBmpInfoHdr;
  hDC=GetDC(0);
  if(!(hDC)){
      return 0;
    }
  StartOfBmpInfoHdr=(BITMAPINFOHEADER*)(pBmp+sizeof(BITMAPFILEHEADER));
  ppvBits=(pBmp+((BITMAPFILEHEADER*)pBmp)->bfOffBits);
  hBmp=CreateDIBitmap(hDC,StartOfBmpInfoHdr,CBM_INIT,ppvBits,(BITMAPINFO*)StartOfBmpInfoHdr,DIB_RGB_COLORS);
  ReleaseDC(hWnd,hDC);
  return hBmp;
}
Code it... That's all...

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #7 on: December 22, 2024, 05:21:00 PM »
I have no time for that now but we could save an image from clipboard
--------------------------------
Kenavo

Offline Vortex

  • Member
  • *
  • Posts: 942
    • http://www.vortex.masmcode.com
Re: Getting image bits from handle
« Reply #8 on: December 22, 2024, 07:55:25 PM »
Hi Philippe,

Here is an example for you :

Code: [Select]
include     SaveClipBoard.inc

.data
IID_IPicture  GUID <7BF80980h,0BF32h,101Ah,<8Bh,0BBh,00h,0AAh,00h,30h,0Ch,0ABh>>

message     db 'SaveClipBoard.exe filename.bmp',0

.data?
buffer      db 512 dup(?)

.code

start:

    call    main
    invoke  ExitProcess,0

main PROC uses esi

LOCAL hBmp      :DWORD
LOCAL pBitmap   :DWORD
LOCAL hGlobal   :DWORD
LOCAL pcbSize   :DWORD
LOCAL pStream   :DWORD
LOCAL pd        :PICTDESC

    lea         esi,buffer
    invoke      ParseCmdLine,esi
    cmp         eax,2
    je          @f

    invoke      StdOut,ADDR message
    ret
@@:
    invoke      GetDesktopWindow
    invoke      OpenClipboard,eax
    invoke      GetClipboardData,CF_BITMAP
    mov         hBmp,eax
    invoke      CloseClipboard

    mov         pd.cbSizeofstruct,SIZEOF PICTDESC   ; initialize the PICTDESC structure
    mov         pd.picType,PICTYPE_BITMAP
    push        hBmp
    pop         pd.bmp.hbitmap
   
    invoke      OleCreatePictureIndirect,ADDR pd,\
                ADDR IID_IPicture,TRUE,ADDR pBitmap
                                                    ; create the OLE image

    invoke      CreateStreamOnHGlobal,NULL,TRUE,ADDR pStream
   
                                                    ; create the destination stream
                                                    ; to save the icon
    lea         eax,pcbSize
   
    coinvoke    pBitmap,IPicture,SaveAsFile,pStream,\
                TRUE,eax
               
    invoke      GetHGlobalFromStream,pStream,\
                ADDR hGlobal
               
    invoke      GlobalLock,hGlobal                  ; get the address pointing
                                                    ; the icon in memory
   
    invoke      WriteFileToDisc,DWORD PTR [esi+4],\
                eax,pcbSize
                                                    ; save the bitmap to disc
    coinvoke    pBitmap,IPicture,Release
    coinvoke    pStream,IStream,Release

    ret
   
main ENDP

END start
Code it... That's all...

Offline HellOfMice

  • Member
  • *
  • Posts: 313
  • Never be pleased, always improve
Re: Getting image bits from handle
« Reply #9 on: December 22, 2024, 07:57:44 PM »
Thank You Vortex now we have all the functions


Philippe
--------------------------------
Kenavo