Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

How to convert Image data (JPEG/PNG/GIF) to hBITMAP ?


  • Please log in to reply
49 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
This has reference to PhiLho's Include bitmaps in your scripts!

The following should give a rough idea of what I want to do:


Gui, Add, Picture, hWndBITMAP, C:\Dummy.bmp

FileGetSize, dataSz, C:\somefile.jpg
FileRead, jpegData, C:\somefile.jpg
; Actually jpegData will be embedded within the script as hextext

; Convert the jpegData to hBitmap
; [color=red]_hBitmap[/color] will be a pointer to converted data

SendMessage STM_SETIMAGE, IMAGE_BITMAP, _hBitmap, , ahk_id %BITMAP%

I would appreciate any help... Please.

Edit:

I somewhat understand it might be possible with COM : < External Link >
The following is Module1.bas from the above package:

Attribute VB_Name = "Module1"
Option Explicit

Private Declare Function CreateStreamOnHGlobal Lib "ole32" (ByVal hGlobal As Long, ByVal fDeleteOnRelease As Long, ppstm As Any) As Long
Private Declare Function OleLoadPicture Lib "olepro32" (pStream As Any, ByVal lSize As Long, ByVal fRunmode As Long, riid As Any, ppvObj As Any) As Long
Private Declare Function CLSIDFromString Lib "ole32" (ByVal lpsz As Any, pclsid As Any) As Long
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal uFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal dwLength As Long)

Public Function LoadFile(ByVal FileName As String) As Byte()
    Dim FileNo As Integer, b() As Byte
    On Error GoTo Err_Init
    If Dir(FileName, vbNormal Or vbArchive) = "" Then
        Exit Function
    End If
    FileNo = FreeFile
    Open FileName For Binary Access Read As #FileNo
    ReDim b(0 To LOF(FileNo) - 1)
    Get #FileNo, , b
    Close #FileNo
    LoadFile = b
    Exit Function
Err_Init:
    MsgBox Err.Number & " - " & Err.Description
End Function

Public Function PictureFromByteStream(b() As Byte) As IPicture
    Dim LowerBound As Long
    Dim ByteCount  As Long
    Dim hMem  As Long
    Dim lpMem  As Long
    Dim IID_IPicture(15)
    Dim istm As stdole.IUnknown

    On Error GoTo Err_Init
    If UBound(b, 1) < 0 Then
        Exit Function
    End If
   
    LowerBound = LBound(b)
    ByteCount = (UBound(b) - LowerBound) + 1
    hMem = GlobalAlloc(&H2, ByteCount)
    If hMem <> 0 Then
        lpMem = GlobalLock(hMem)
        If lpMem <> 0 Then
            MoveMemory ByVal lpMem, b(LowerBound), ByteCount
            Call GlobalUnlock(hMem)
            If CreateStreamOnHGlobal(hMem, 1, istm) = 0 Then
                If CLSIDFromString(StrPtr("{7BF80980-BF32-101A-8BBB-00AA00300CAB}"), IID_IPicture(0)) = 0 Then
                  Call OleLoadPicture(ByVal ObjPtr(istm), ByteCount, 0, IID_IPicture(0), PictureFromByteStream)
                End If
            End If
        End If
    End If
   
    Exit Function
   
Err_Init:
    If Err.Number = 9 Then
        'Uninitialized array
        MsgBox "You must pass a non-empty byte array to this function!"
    Else
        MsgBox Err.Number & " - " & Err.Description
    End If
End Function

:roll:

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
This external link looks promising: Load JPEG from file

void LoadDibFromFile(LPCTSTR szFile, CDib *pCDib) { 

// open file


        HANDLE hFile = CreateFile(szFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
        _ASSERTE(INVALID_HANDLE_VALUE != hFile);


        // get file size
        DWORD dwFileSize = GetFileSize(hFile, NULL);
        _ASSERTE(-1 != dwFileSize);

        LPVOID pvData = NULL;

        // alloc memory based on file size
        HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
        _ASSERTE(NULL != hGlobal);

        pvData = GlobalLock(hGlobal);
        _ASSERTE(NULL != pvData);

        DWORD dwBytesRead = 0;

        // read file and store in global memory
        BOOL bRead = ReadFile(hFile, pvData, dwFileSize, &dwBytesRead, NULL);
        _ASSERTE(FALSE != bRead);
        GlobalUnlock(hGlobal);
        CloseHandle(hFile);

        LPSTREAM pstm = NULL;

        // create IStream* from global memory
        HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm);
        _ASSERTE(SUCCEEDED(hr) && pstm);


        // Create IPicture from image file
        LPPICTURE pPicture;
        hr = ::OleLoadPicture(pstm, dwFileSize, FALSE, IID_IPicture, (LPVOID
*)&pPicture);
        _ASSERTE(SUCCEEDED(hr) && pPicture);
        pstm->Release();


        // get the bitmap handle
        HBITMAP hBitmap;
        if (S_OK != pPicture->get_Handle((OLE_HANDLE FAR *) &hBitmap))
        {
                TRACE("Couldn't get bitmap handle from picture\n");
                ASSERT(FALSE);
        }


        // create a memory DC for the bitmap
        HDC memDC = CreateCompatibleDC(NULL);
        HBITMAP oldBitmap;
        oldBitmap = (HBITMAP) SelectObject(memDC, hBitmap);


        // find the size of the DIB to be created
        BITMAPINFO bInfo;
        ZeroMemory(&bInfo, sizeof(bInfo));
        bInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bInfo.bmiHeader.biBitCount = 0;

        if (!GetDIBits(memDC, hBitmap, 0, 0, NULL, &bInfo, DIB_RGB_COLORS))
        {
                DWORD error = GetLastError();
                ASSERT(FALSE);
        }

        int totalSize = PackedDibGetInfoHeaderSize(&bInfo) +
            PackedDibGetColorTableSize(&bInfo) +
            PackedDibGetBitsSize(&bInfo);


        // allocate and create a packed DIB
        LPBITMAPINFO packedBitmap = (LPBITMAPINFO) new BYTE[totalSize];
        CopyMemory(packedBitmap, &bInfo, sizeof(BITMAPINFO));

        if (!GetDIBits(
                memDC,
                hBitmap,
                0,
                PackedDibGetHeight(&bInfo),
                (void *) (((BYTE *) packedBitmap) +
                                  PackedDibGetInfoHeaderSize(&bInfo) +
                                  PackedDibGetColorTableSize(&bInfo)),
                packedBitmap,
                DIB_RGB_COLORS))
        {
                DWORD error = GetLastError();
                ASSERT(FALSE);
        }


        // attach the packed DIB to the CDib object.  The object is now responsible
        // for deletion (it should do this in the destructor)
        pCDib->AttachMemory((LPVOID) packedBitmap, TRUE);


        // clean up the memory DC
        SelectObject(memDC, oldBitmap);
        DeleteDC(memDC);


}


Andreone
  • Members
  • 257 posts
  • Last active: Mar 19 2008 05:30 PM
  • Joined: 20 Jul 2007
GDIPlus.dll allows to load jpeg.
I found this article of codeprojet http://www.codeproje...nailsViewer.asp that demonstrates how to use GDIPlus.dll to load any (supported) file format.
The example is in C++.

Andreone
  • Members
  • 257 posts
  • Last active: Mar 19 2008 05:30 PM
  • Joined: 20 Jul 2007

; Actually jpegData will be embedded within the script as hextext

Sorry, I forgot that part, so my response is not adequate.
However, I think I remember that GDIPlus has function like LoadImageFromStream, which can help.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

However, I think I remember that GDIPlus has function like LoadImageFromStream, which can help.


I have never experimented with GDI+ except some test scripts of PhiLho :(
If it has to be GDI+ or COM I need some working example to get started.

:roll:

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
I don't have time to test it until the weekend, but, the procedure would be like this.

Obtain the hGlobal from the jpeg data using GlobalAlloc() with GMEM_MOVABLE, and copy the data to it.
Obtain the pStream from the hGlobal using CreateStreamOnHGlobal().

Then, if GdiPlus.dll is available:
Obtain pBitmap from pStream using GdipLoadImageFromStream()/GdipCreateBitmapFromStream().
Obtain hBitmap from pBitmap using GdipCreateHBITMAPFromBitmap().

If GdiPlus isn't avaiable:
Obtain pPicture from pStream using OleLoadPicture().
Obtain hBitmap from pPicture using pPicture->get_Handle.

PS. OleLoadPicture() worked only with BMP, not with JPG etc, in my test. And, the documentation says the format should be Bitmap, Metafile, or Icon.

Andreone
  • Members
  • 257 posts
  • Last active: Mar 19 2008 05:30 PM
  • Joined: 20 Jul 2007

If it has to be GDI+ or COM I need some working example to get started.

The article I gave to you is actually a demonstration project, and you can download the full source code (and of course it's free). This is a base to start from.

CodeProject is a MFC/C++/.NET programation website, where most of the articles are tutorial/how-to/sample/demonstration with code source and samples provided.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
I don't think the article linked will be of much help as it uses the GDI+ wrapper for C++, which can't be directly used inside the AHK script that must use GDI+ flat APIs. So, don't waste too much time on it.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks for looking into this Sean. :)

Can you please take a look at this : SHCreateMemStream
Can it simplify the code ?

:)

Edit: Never mind.. That function does not exist in Shlwapi.dll :(

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Edit: Never mind.. That function does not exist in Shlwapi.dll :(

Don't be disappointed. SHCreateMemStream() only saves the step of allocating the hGlobal, which is fairly straight-forward. I think there are a few examples already in the forum:
GlobalAlloc(GMEM_MOVABLE, nLen) -> GlobalLock() -> RtlMoveMemory() -> GlobalUnlock().

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

GDIPlus.dll allows to load jpeg.
I found this article of codeprojet http://www.codeproje...nailsViewer.asp that demonstrates how to use GDIPlus.dll to load any (supported) file format.
The example is in C++.

I thought I better shut up, but I think I took it enough.
So, you never search the forum. What made you think that we need the external article for this kind of basic operations like GdipLoadImage...()? There are already numerous examples on it in the forum.

I remember you as I found your scripts kinda insulting to the forum members, well at least to me. Again, what made you think no members have ever done those kind of basic things? The last script of yours I saw could be done by almost every member in this forum, if he/she is not new, I suppose.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
It took a lot less time than I thought. Here is one example.
One thing a bit strange was no need to call explicitly CoInitialize().
Need FileHelper.ahk and require GdiPlus.dll.

#Include FileHelper.ahk
sFile := A_Temp . "\temp.jpg"  ; Adjust the path to the real image file.
nSize := WriteMemory(sFile, Buffer)

Gui, +LastFound
Gui, Show, w800 h600 Center, Picture
hGui := WinExist()

[color=red]; From here, the actually needed part[/color]
hData := DllCall("GlobalAlloc", "Uint", 2, "Uint", nSize)
pData := DllCall("GlobalLock", "Uint", hData)
DllCall("RtlMoveMemory", "Uint", pData, "Uint", &Buffer, "Uint", nSize)
DllCall("GlobalUnlock", "Uint", hData)
DllCall("ole32\CreateStreamOnHGlobal", "Uint", hData, "int", True, "UintP", pStream)

pToken := Gdip_Startup()
DllCall("gdiplus\GdipCreateBitmapFromStream", "Uint", pStream, "UintP", pBitmap)
DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Uint", pBitmap, "UintP", hBitmap, "Uint", 0)
DllCall("gdiplus\GdipGetImageWidth" , "Uint", pBitmap, "UintP", nW)
DllCall("gdiplus\GdipGetImageHeight", "Uint", pBitmap, "UintP", nH)

hDC := DllCall("GetDC", "Uint", hGui)
mDC := DllCall("CreateCompatibleDC", "Uint", hDC)
hBM := DllCall("SelectObject", "Uint", mDC, "Uint", hBitmap)

DllCall("SetStretchBltMode", "Uint", hDC, "int", 4)
DllCall("StretchBlt", "Uint", hDC, "int", 0, "int", 0, "int", 800, "int", 600, "Uint", mDC, "int", 0, "int", 0, "int", nW, "int", nH, "Uint", 0x00CC0020)

DllCall("SelectObject", "Uint", mDC, "Uint", hBM)
DllCall("DeleteDC", "Uint", mDC)
DllCall("ReleaseDC", "Uint", hGui, "Uint", hDC)
DllCall("DeleteObject", "Uint", hBitmap)
DllCall("gdiplus\GdipDisposeImage", "Uint", pBitmap)
pToken := Gdip_Shutdown(pToken)

DllCall(NumGet(NumGet(1*pStream)+8), "Uint", pStream)
Return

GuiClose:
Gui, Destroy
ExitApp

Gdip_Startup()
{
	If	!DllCall("GetModuleHandle", "str", "gdiplus")
		 DllCall("LoadLibrary"    , "str", "gdiplus")
	VarSetCapacity(si, 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UintP", pToken, "Uint", &si, "Uint", 0)
	Return	pToken
}

Gdip_Shutdown(pToken)
{
	DllCall("gdiplus\GdiplusShutdown", "Uint", pToken)
	If	hModule := DllCall("GetModuleHandle", "str", "gdiplus")
		   DllCall("FreeLibrary", "Uint", hModule)
}


SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
Thanks for providing the complete woking code, Sean.
That is very kind of you.

I see why I was not able to kick start. I was DLL calling GDI+ without a prior GdiplusStartup and ErrorLevel of 18 showed up. :oops:
Where can can I get constants for GDI+ errorlevel ?

I have been shying away from GDI+ and would have remained so if not for this help.

Thanks again, :)

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

I see why I was not able to kick start. I was DLL calling GDI+ without a prior GdiplusStartup and ErrorLevel of 18 showed up. :oops:
Where can can I get constants for GDI+ errorlevel ?

This page may be of help:
<!-- m -->http://msdn2.microso...y/ms534175.aspx<!-- m -->

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
I have been blind :oops: .. Thanks again.