News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

How to properly load a .png resource for use with GdipDrawImageRectI?

Started by zedd151, April 10, 2025, 01:49:24 AM

Previous topic - Next topic

zedd151

Quote from: NoCforMe on April 10, 2025, 08:58:39 AMOf course, I'm not loading resources: I'm loading files from disk.
But if that can be done, there must be a way to load a resource as well.
What you are doing, is basically what I am doing. Did you not look at my code?
We are both loading a file. I want to use .png resources.

I am certain there is a way to do it but those that do know only give cryptic clues, and not offer any actual working assembly code examples of loading a .png resource with transparency using gdiplus and displaying it with DrawImageRectI - in a simple example without all of the unnecessary baggage.
Also the Microsoft documentation leaves a lot to be desired.

I can live with my methods for now, they work. But somewhere down the line I would like to publicly offer some executables without needing to also have a bunch of .png files with it.  :biggrin:

Later tonight I'll ask ChatGPT or Google Gemini for C code to do what I want, then port it to assembly (if it works). :tongue: 

Or even ask for Masm compatible assembly code...  ( they kinda suck with assembly though)  :biggrin: 

Yes, that's cheating but it might be the fastest route to success.

NoCforMe

Quote from: zedd151 on April 10, 2025, 09:02:09 AMLater tonight I'll ask ChatGPT or Google Gemini for C code to do what I want, then port it to assembly (if it works). :tongue: 
I hate to give you this advice, I really do, but two things:
1. Forget "AI". You don't want to deal with their hallucinations.
2. Have you tried searching online for (non-"AI") solutions?
I did a search for "loading .png resource" and got several hits, a few of which look promising (like this one). Of course, a lot of them will deal with weird platforms like Wix, but some look usable in a Win32 app.
Assembly language programming should be fun. That's why I do it.

zedd151

Taking another look at Vortex's example:
        invoke  CreateStreamOnHGlobal,hMem,TRUE,ADDR pStream
        invoke  GdipCreateBitmapFromStream,pStream,ADDR BmpImage
        invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0

If I omit "invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0"
"BmpImage" might just be the handle I need to accomplish my goals. I believe that gdi32 needs a HBITMAP handle, where gdiplus needs an image handle.  (I think I'm onto something)  :biggrin:

I am away from my computer at the moment. When I get back inside I will test my theory.  :biggrin:

Quotea few of which look promising (like this one)
It doesnt mention gdiplus anywhere. So probably loading .pngs without transparency (alpha channel).  No good for my purposes.

zedd151

Quote from: zedd151 on April 10, 2025, 09:29:21 AMTaking another look at Vortex's example:
        invoke  CreateStreamOnHGlobal,hMem,TRUE,ADDR pStream
        invoke  GdipCreateBitmapFromStream,pStream,ADDR BmpImage
        invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0

If I omit "invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0"
"BmpImage" might just be the handle I need to accomplish my goals. I believe that gdi32 needs a HBITMAP handle, where gdiplus needs an image handle.
Success!!!


Thank you Vortex! You had the code *almost* right for working with gdiplus, I made a few adjustments. I will clean up the code and post it!

zedd151

:biggrin:  I got it! A big thanks to Vortex. :thumbsup:  Your code was *almost* what I needed. I had tweaked it to be more suitable for using with .png images with transparency.



; Original code by Vortex
; Modified for use with png with alpha channel and gdiplus drawing by zedd151

include    LoadImageFromRsrc.inc

.data

ClassName  db 'ImagrClass', 0
AppName    db 'LoadPNGfromRsrc', 0

.data?
    hInstance          dd ?
    hBitmap            dd ?
    pNumbOfBytesRead    dd ?
    StartupInfo        GdiplusStartupInput <?>
    GdiplusStatus      dd ?
    token              dd ?
    BmpImage            dd ?
    hMem                dd ?
    pMem                dd ?
    pStream            dd ?
    fsize              dd ?

.code
start:
    invoke  GetModuleHandle, NULL
    mov    hInstance, eax
    invoke  GetCommandLine
    invoke  WinMain, hInstance, NULL, eax, SW_SHOWDEFAULT
    invoke  ExitProcess, eax

DrawImage proc hDC:dword, Image:dword, x:dword, y:dword
local wid:dword, hgt:dword, pGraphics:dword
    invoke GdipCreateFromHDC, hDC, addr pGraphics
    invoke GdipGetImageWidth, Image, addr wid
    invoke GdipGetImageHeight, Image, addr hgt
    invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
    invoke GdipDeleteGraphics, pGraphics
    ret
DrawImage endp
   
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:dword
    local wc:WNDCLASSEX
    local msg:MSG
    local hwnd:HWND
    xor eax, eax
    mov wc.cbSize, SIZEOF WNDCLASSEX
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, offset WndProc
    mov wc.cbClsExtra, eax
    mov wc.cbWndExtra, eax
    push hInstance
    pop wc.hInstance
    mov wc.hbrBackground, COLOR_WINDOW+2
    mov wc.lpszMenuName, eax
    mov wc.lpszClassName, offset ClassName
    invoke LoadIcon, eax, IDI_APPLICATION
    mov wc.hIcon, eax
    mov wc.hIconSm, eax
    invoke LoadCursor, NULL, IDC_ARROW
    mov wc.hCursor, eax
    invoke RegisterClassEx, addr wc
    xor eax, eax
    invoke CreateWindowEx, eax, addr ClassName, addr AppName, \
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, \
            CW_USEDEFAULT, 200, 200, eax, eax, \
            hInst, eax
    mov hwnd, eax
    invoke ShowWindow, hwnd, SW_SHOWNORMAL
    invoke UpdateWindow, hwnd
    .WHILE TRUE
        invoke GetMessage, addr msg, NULL, 0, 0
    .BREAK .if (!eax)
        invoke TranslateMessage, addr msg
        invoke DispatchMessage, addr msg
    .ENDW
    mov eax, msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    local ps:PAINTSTRUCT
    local hdc:HDC
    local hMemDC:HDC
    local bm:BITMAP
    local hRes:dword
    local RsrcSize:dword
    local pImage:dword
    .IF uMsg==WM_CREATE
        invoke FindResource, 0, 100, RT_RCDATA
        mov hRes, eax
        invoke SizeofResource, hInstance, eax
        mov RsrcSize, eax
        invoke LoadResource, 0, hRes
        mov pImage, eax
        invoke GlobalAlloc, GPTR, RsrcSize
        mov hMem, eax
        invoke MemCopy, pImage, eax, RsrcSize
        mov eax, offset StartupInfo
        mov GdiplusStartupInput.GdiplusVersion[eax], 1
        mov GdiplusStatus, eax
        invoke GdiplusStartup, addr token, addr StartupInfo, 0
        invoke CreateStreamOnHGlobal, hMem, TRUE, addr pStream
        invoke GdipCreateBitmapFromStream, pStream, addr BmpImage
    .ELSEIF uMsg==WM_PAINT
        invoke BeginPaint, hWnd, addr ps
        mov hdc, eax
        ; ====================================
       
        invoke DrawImage, hdc, BmpImage, 50, 35 ; draws png with transparency
       
        ; ====================================
        invoke EndPaint, hWnd, addr ps
    .ELSEIF uMsg==WM_DESTROY
        invoke DeleteObject, hBitmap
        invoke GdipDisposeImage, BmpImage
        invoke GdiplusShutdown, GdiplusStatus
        coinvk pStream, IStream, Release
        invoke PostQuitMessage, NULL
    .ELSE
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .ENDIF
    xor eax, eax
    ret
WndProc endp

end start

The code here is simplified a bit, down to the bare necessities. There may be leftover unused variables.
It may be exapnded upon, as needed.

Thanks to all that provided suggestions, no matter how cryptic.  :thumbsup:

NoCforMe

So what was it specifically you had to change to make it work with loading the image as a resource?
Enquiring minds want to know.
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on April 10, 2025, 11:08:34 AMSo what was it specifically you had to change to make it work with loading the image as a resource?
Enquiring minds want to know.
The image as resource loading was fine as is.
I omitted the "invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0" call and used BmpImage as the image handle. Voila. The HBITMAP was only needed for gdi32 drawing with BitBlt, et.al.

Of course I had to also add the code to draw with gdiplus as well. I removed all of the gdi32 code except for BeginPaint and EndPaint.
A double buffer probably a good idea if many images are to be drawn, or if drawn frequently. That's how I would do it. But since the code I posted is only a 'proof of concept' example, its fine as is.

Download the example. Test it, tweak it. Etc.

Now how easy it will be to use the code for multiple images, it remains to be seen. To be updated soon in a thread near you!  :biggrin:

six_L

_GdipCreateImageFromResource proc ResID:QWORD
    LOCAL @hImage:QWORD
    LOCAL hRes:QWORD
    LOCAL pRes:QWORD
    LOCAL lRes:QWORD
    LOCAL @IStream:QWORD
   
    invoke    FindResource,0,ResID,RT_RCDATA
    mov    hRes, rax
    invoke    LoadResource,0,hRes
    mov    pRes, rax                    ; load the resource
    invoke    SizeofResource,0,hRes                ; get its size
    mov    lRes, rax   
   
    invoke    pSHCreateMemStream,pRes,lRes
    mov    @IStream,rax
    invoke    FreeResource,hRes
    invoke    GdipLoadImageFromStream,@IStream,addr @hImage
    mov    rax,@IStream                             
    mov    rcx,rax                               
    mov    rax,[rax]
    call    QWORD PTR [rax + 16]                ; @IStream params mov into rcx,invoke release,@IStream

    mov    rax,@hImage
    ret


_GdipCreateImageFromResource endp
invoke LoadLibrary,CStr("Shlwapi.dll")
.if rax
mov hShlwapi_dll,rax
invoke GetProcAddress,hShlwapi_dll,CStr("SHCreateMemStream")
.if rax
mov pSHCreateMemStream,rax
.else
invoke MessageBox,NULL,CStr("Get SHCreateMemStream Failed"),CStr("GetProcAddress"),MB_OK
.endif
.else
invoke MessageBox,NULL,CStr("Shlwapi.dll load Failed"),CStr("LoadLibrary"),MB_OK
.endif
Say you, Say me, Say the codes together for ever.

zedd151

Thanks for your contribution six_L, I'll test it in a little while.  :thumbsup:

But wait. That is 64 bit code. It will take me a little time to port it to 32 bit especially the "call qword ptr  [rax+16]" to get it right, due to the differences of 64 bit and 32 bit.
Unless you have a 32 bit version of it. I don't have time for that right now. But thanks anyway.

The code itself looks more compact than the code I have been experimenting with here today though.

TimoVJL

May the source be with you

NoCforMe

Can somebody tell me what are streams and why would I want to use them to deal with bitmap images?
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on April 10, 2025, 01:06:27 PMCan somebody tell me what are streams and why would I want to use them to deal with bitmap images?
I haven't the foggiest clue either.  :joking:  I only know that it somehow works. It was much easier to load an image from a file than to load it from resources. That's for sure.  :biggrin:  I might follow hutchs lead, but expand on his method to be able to use gdiplus drawing. Just for simplicities sake. That gives me an idea...

TimoVJL

May the source be with you

NoCforMe

Quote from: TimoVJL on April 10, 2025, 01:38:48 PM
Quote from: NoCforMe on April 10, 2025, 01:06:27 PMCan somebody tell me what are streams and why would I want to use them to deal with bitmap images?
streams are used in C++ and GDI+ is written in C++ with objects.
WTF???? That doesn't tell me anything!
What are they? What do they do?
Sheesh.
Assembly language programming should be fun. That's why I do it.

six_L

QuoteIt will take me a little time to port it to 32 bit especially the "call qword ptr  [rax+16]" to get it right.
i didn't test the code.
_GdipCreateImageFromResource proc ResID:DWORD
LOCAL @hImage:DWORD
LOCAL hRes:DWORD
LOCAL pRes:DWORD
LOCAL lRes:DWORD
LOCAL @IStream:DWORD

invoke FindResource,0,ResID,RT_RCDATA
mov hRes, eax
invoke LoadResource,0,hRes
mov pRes, eax ; load the resource
invoke SizeofResource,0,hRes ; get its size
mov lRes, eax

invoke pSHCreateMemStream,pRes,lRes
mov @IStream,eax
invoke FreeResource,hRes
invoke GdipLoadImageFromStream,@IStream,addr @hImage
mov eax,@IStream
push eax ;
mov eax,[eax]
call DWORD PTR [eax + 8] ; @IStream params push stack,invoke release,@IStream
sub esp,4

mov eax,@hImage
ret

_GdipCreateImageFromResource endp
Say you, Say me, Say the codes together for ever.