The MASM Forum

General => The Workshop => Windows Graphics Programming => Topic started by: zedd151 on April 10, 2025, 01:49:24 AM

Title: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 01:49:24 AM
As I am needing code for 32 bit to do just that...
I can load a .png image from file just fine (https://masm32.com/board/index.php?msg=138003), but I need to use .png resources, to keep down the number of files for the end user.

From hutch's code in the Masm64 SDK library, he has this:
ResImageLoad proc ResID:QWORD

    LOCAL hRes  :QWORD
    LOCAL pRes  :QWORD
    LOCAL lRes  :QWORD
    LOCAL pbmp  :QWORD
    LOCAL hBmp  :QWORD
    LOCAL npng  :QWORD

    mov hRes, rv(FindResource,0,ResID,RT_RCDATA)        ; find the resource
    mov pRes, rv(LoadResource,0,hRes)                  ; load the resource
    mov lRes, rv(SizeofResource,0,hRes)                ; get its size
    mrm npng, "@@@@.@@@"                                ; temp file name

    invoke save_file,npng,pRes,lRes                    ; create the temp file

    invoke GdipCreateBitmapFromFile,L(npng),ADDR pbmp  ; create a GDI+ bitmap
    invoke GdipCreateHBITMAPFromBitmap,pbmp,ADDR hBmp,0 ; create normal bitmap handle from it
    invoke GdipDisposeImage,pbmp                        ; remove the GDI+ bitmap

    invoke DeleteFile,npng                              ; delete the temp file

    mov rax, hBmp                                      ; return the bitmap handle in RAX

    ret

ResImageLoad endp

Edited:

While that does work.... but it only works if using BitBlt or other GDI32 functions to draw the image (which does not retain the transparency), and does not work with GdipDrawImageRectI apparently.

I mistakenly said previously that it did work.

I made a simple test for it though to find out for certain, and found it only worked as if the .png was treated as a BITMAP and is not compatible with gdiplus ... :sad:

So basically the methods that hutch used to load the .png resource are of no use to me, for my purposes.

So does anyone know how to properly load a .png resource for use with GdipDrawImageRectI???
Edited for clarity and to fix an error on my part.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 04:13:53 AM
I have seen some clues to use "GdipCreateBitmapFromStream" but will that retain the transparency? The "Bitmap" in the name makes me think otherwise.

I have also seen some convoluted code using that function, but it was in an .if block with seemingly unrelated code and hard to follow.

If that is the correct function needed, how do I set up the code to use it?
I am 99% clueless on gdiplus code. The little I do know, does not help me to use .png resources with transparency instead of .png files with GdipDrawImageRectI.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: adeyblue on April 10, 2025, 04:41:41 AM
Least resistance:
SHCreateStreamOnModuleResourceW, it's not exported by name, it's ordinal 628 in shlwapi.dll.
Then
GdipCreateBitmapFromStream

SHCreateStream... is undocumented though, and it's already changed dlls once, so you know, use at your own risk.

The alternative is CreateStreamOnHGlobal or SHCreateMemStream and use the code in the OP to get the resource data but instead of saving it to a file, write it to the stream instead. Might need to seek the stream back to the start.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 04:44:48 AM
Well, that sounds complicated. Remember I am a newby to some of this stuff...

I'll try some things regarding the last alternative. I remember seeing similar code during my research... I'll be back later. :smiley:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: Vortex on April 10, 2025, 05:22:25 AM
Hi Zedd,

QuoteI have seen some clues to use "GdipCreateBitmapFromStream"

Here is an example for you :

   .IF uMsg==WM_CREATE


        invoke  FindResource,0,100,RT_RCDATA
        mov     hRes,eax
       
        invoke  SizeofResource,hInstance,eax
        mov     RsrcSize,eax
       
        invoke  LoadResource,0,hRes
        invoke  LockResource,eax
        mov     pImage,eax
       
        invoke  GlobalAlloc,GMEM_MOVEABLE,RsrcSize
        mov     hMem,eax

        invoke  GlobalLock,eax
        mov     pMem,eax        ; copy the image to the global memory
        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
        invoke  GdipCreateHBITMAPFromBitmap,BmpImage,ADDR hBitmap,0
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 05:33:25 AM
Hi Vortex.

That looks like something I can work with.  :thumbsup:  While I do not understand this Stream stuff, the other functions are somewhat familiar...  :smiley:


Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 05:42:52 AM
Quote from: zedd151 on April 10, 2025, 05:33:25 AMThat looks like something I can work with.
Then again, maybe not.
(https://i.postimg.cc/XYYDHc80/untitled.png)

Your example displays the image as a flat bitmap, not as an image with transparency.

Updated later:
But I fixed your code to work with png's that do have transparency, It is in post #19
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 10, 2025, 08:03:55 AM
Quote from: zedd151 on April 10, 2025, 04:13:53 AMI have seen some clues to use "GdipCreateBitmapFromStream" but will that retain the transparency?

Do you have an example of a transparent png file? GuiImage (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1404) uses GdipCreateBitmapFromStream
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 08:10:29 AM
Sample of my current code that uses GdipLoadImageFromFile to load png and GdipDrawImageRectI to display it

        include \masm32\include\masm32rt.inc
        include \masm32\include\gdiplus.inc
        includelib \masm32\lib\gdiplus.lib
   
        DlgProc            proto :dword, :dword, :dword, :dword
        LoadImageFromFile  proto :dword, :dword
        DrawImage          proto :dword, :dword, :dword, :dword

        cwidth              equ 240  ; desired client area width
        cheight            equ 240  ; desired client area height
       
    .data
        StartupInfo        GdiplusStartupInput <1, 0, 0, 0>
        GdiplusStatus      dd 0
        token              dd 0
        hRed                dd 0
       
    .const
   
        redpng db "red.png", 0
       
    .code

    start proc
    local hInstance:dword
        invoke GetModuleHandle, NULL
        mov hInstance, eax
        mov eax, offset StartupInfo
        mov GdiplusStartupInput.GdiplusVersion[eax], 1
        mov GdiplusStatus, eax
        invoke GdiplusStartup, addr token, addr StartupInfo, 0
        invoke LoadImageFromFile, addr redpng, addr hRed
       
        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0  ;; create dialog box, from resource dilalog

        invoke GdipDisposeImage, hRed
        invoke GdiplusShutdown, GdiplusStatus
        invoke ExitProcess, eax
    start endp

    DlgProc proc hWin:dword, uMsg:dword, wParam:dword, lParam:dword
    local hDC:dword, ps:PAINTSTRUCT, rct:RECT, hBrush:dword, hBrush_old:dword
    local mDC:dword, hBmp:dword, hBmp_old:dword, hPen:dword, hPen_old:dword
    local x:dword, y:dword, wwid:dword, whgt:dword, cwid:dword, chgt:dword
        .if uMsg == WM_INITDIALOG

          ;; resizing Client Area to exact dimensions, specified by cwidth and cheight

          invoke GetWindowRect, hWin, addr rct ;; get window current dimensions.
          mov eax, rct.right                  ;; obtain current window width by subtracting left boundary
          sub eax, rct.left                    ;; from the right boundary
          mov wwid, eax                        ;; store current window width
          mov eax, rct.bottom                  ;; obtain current window height by subtracting top boundary
          sub eax, rct.top                    ;; from the bottom boundary
          mov whgt, eax                        ;; store current window height

          invoke GetClientRect, hWin, addr rct ;; get client area current dimensions.
          mov eax, rct.right                  ;; obtain current client width by subtracting left boundary
          sub eax, rct.left                    ;; from the right boundary
          mov cwid, eax                        ;; store current client area width
          mov eax, rct.bottom                  ;; obtain current client height by subtracting top boundary
          sub eax, rct.top                    ;; from the bottom boundary
          mov chgt, eax                        ;; store client area height

          ;; calculate the difference between desired client area width and current client area width
          mov eax, cwidth
          sub eax, cwid

          ;; adjust the window width according to the difference calculated above
          add wwid, eax

          ;; calculate the difference between desired client area height and current client area height
          mov eax, cheight
          sub eax, chgt
          ;; adjust the window height according to the difference calculated height
          add whgt, eax

          ;; center the main window
          ;; obtain client area of the desktop, not including the task bar
          invoke SystemParametersInfoA, SPI_GETWORKAREA, 0, addr rct, 0

          ;; center window width
          mov eax, rct.right
          sub eax, wwid
          sar eax, 1
          mov x, eax

          ;; center window height
          mov eax, rct.bottom
          sub eax, whgt
          sar eax, 1
          mov y, eax

          ;; implement the centering of the resized main window
          invoke MoveWindow, hWin, x, y, wwid, whgt, TRUE

        .elseif uMsg == WM_ERASEBKGND
          mov eax, 1
          ret
        .elseif uMsg == WM_PAINT
          invoke BeginPaint, hWin, addr ps
          mov hDC, eax                          ; window client area DC
          invoke CreateCompatibleDC, hDC
          mov mDC, eax                          ; memory DC

          invoke CreateCompatibleBitmap, hDC, ps.rcPaint.right, ps.rcPaint.bottom
          mov hBmp, eax                        ; compatible bitmap handle
          invoke SelectObject, mDC, hBmp
          mov hBmp_old, eax

          ;; ###########################################################

          ;; Here I change the background color of the main window
          invoke CreateSolidBrush, 00FFBF7Fh  ;; a nice light blue colr
          mov hBrush, eax  ;; save the brush handle

          ;; get client area rectangle
          invoke GetClientRect, hWin, addr rct

          ;; fill the client area rectangle with chosen color
          invoke FillRect, mDC, addr rct, hBrush              ; fill rectangle in memory DC

          ;; delete the brush, as it is no longer needed
          invoke DeleteObject, hBrush

          ;; ###########################################################


          invoke DrawImage, mDC, hRed, 80, 80


          ;; ###########################################################

          invoke BitBlt, hDC, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, mDC, 0, 0, SRCCOPY
          invoke SelectObject, mDC, hBmp_old
          invoke DeleteObject, hBmp
          invoke DeleteDC, mDC

          invoke EndPaint, hWin, addr ps
        .elseif uMsg == WM_CLOSE
          invoke EndDialog, hWin, 0
        .endif
        xor eax, eax
        ret
    DlgProc endp

    LoadImageFromFile proc pImgName:dword, pImage:dword
    local wcBuffer[256]:byte
        invoke MultiByteToWideChar, CP_ACP, 0, pImgName, -1, addr wcBuffer, 256
        invoke GdipLoadImageFromFile, addr wcBuffer, pImage
        mov eax, pImage
        ret
    LoadImageFromFile endp

    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

    end
Results: (https://i.postimg.cc/3xW6nF8D/untitled.png)

Quote from: jj2007 on April 10, 2025, 08:03:55 AMDo you have an example of a transparent png file?
right click this image and Save As... red.png
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 10, 2025, 08:22:00 AM
I found an example, and it works :thumbsup:

GuiParas equ "GdipCreateBitmapFromStream demo", w300, h500, b LiteBlueGreen, icon Printer    ; width+height, background colour
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
  GuiImage 101, fit
  GuiImage 100, fit    ; 100 is RCDATA ID
GuiEnd

(https://i.postimg.cc/ykBVbNrT/Transparent-Png-Demo.jpg) (https://postimg.cc/ykBVbNrT)
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 08:23:54 AM
Quote from: jj2007 on April 10, 2025, 08:22:00 AMI found an example, and it works :thumbsup:
I downloaded it, the .asm file is empty
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 10, 2025, 08:28:04 AM
Better? The png file is too big for the forum, just edit the *.rc file to add your images. The order may matter.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 08:33:51 AM
Quote from: jj2007 on April 10, 2025, 08:28:04 AMBetter?

GuiParas equ "GdipCreateBitmapFromStream demo", w300, h500, b LiteBlueGreen, icon Printer    ; width+height, background colour
 include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
  GuiImage 101, fit
  GuiImage 100, fit    ; 100 is RCDATA ID
GuiEnd
Funny guy. Where is the assembly code? You know that I don't use MasmBasic.

A nice simple example with only the needed code will do, like the one I posted in #8 - nice and simple.  :tongue:

without the extra baggage:
;  gdiplus.GdiplusShutdown
;  gdiplus.GdiplusStartup
;  gdiplus.GdipCreatePen1
;  gdiplus.GdipDeleteGraphics
;  gdiplus.GdipCreateBitmapFromHBITMAP
;  gdiplus.GdipSaveImageToFile
;  gdiplus.GdipDisposeImage
;  gdiplus.GdipCreateFromHDC
;  gdiplus.GdipSetSmoothingMode
;  gdiplus.GdipCreateBitmapFromStream
;  gdiplus.GdipCreateHBITMAPFromBitmap
;  gdiplus.GdipImageGetFrameDimensionsList
;  gdiplus.GdipImageGetFrameCount
;  gdiplus.GdipImageSelectActiveFrame
;  gdiplus.GdipGetPropertyItemSize
;  gdiplus.GdipGetPropertyItem
;  gdiplus.GdipGetImageWidth
;  gdiplus.GdipGetImageHeight
;  gdiplus.GdipDrawImageRectI
;  gdiplus.GdipDrawImageRectRectI
;  gdiplus.GdipImageRotateFlip
;  gdiplus.GdipGetImageEncodersSize
;  gdiplus.GdipGetImageEncoders
;  gdiplus.GdipCreatePath
;  gdiplus.GdipAddPathArcI
;  gdiplus.GdipClosePathFigure
;  gdiplus.GdipCreateSolidFill
;  gdiplus.GdipFillPath
;  gdiplus.GdipDeleteBrush
;  gdiplus.GdipDrawPath
;  gdiplus.GdipDeletePath
;  gdiplus.GdipSetPenLineJoin
;  gdiplus.GdipFillRectangleI
;  gdiplus.GdipFillEllipseI
;  gdiplus.GdipDrawLinesI
;  gdiplus.GdipDeletePen
;  gdiplus.GdipGetPathWorldBounds
;  gdiplus.GdipWarpPath
;  gdiplus.GdipResetPath
;  gdiplus.GdipAddPathRectangles
;  gdiplus.GdipAddPathEllipse
;  gdiplus.GdipAddPathArc
;  gdiplus.GdipAddPathPie
;  gdiplus.GdipAddPathBeziers
;  gdiplus.GdipAddPathCurve
;  gdiplus.GdipAddPathCurve2
;  gdiplus.GdipAddPathClosedCurve2
;  gdiplus.GdipAddPathClosedCurve
;  gdiplus.GdipAddPathLine2
  :rolleyes:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 08:57:58 AM
Dunno if this'll help, but I checked my picture-viewer program, which uses GDI+ to display images, and it certainly does work with .png files.

Here's most of the WM_PAINT handler:
INVOKE BeginPaint, hWin, ADDR ps
MOV hDC, EAX

; Get graphics "object" from DC handle:
INVOKE GdipCreateFromHDC, hDC, ADDR gdiHgraphics

; Display image at (X,Y) with dimensions (W,H):
; Many thanks to "mabdelouahab" from the MASM32 forum for this.
INVOKE GdipDrawImageRectI, gdiHgraphics, GdiHbitmap,
ImgStruct.imgX, ImgStruct.imgY, ImgStruct.imgWscaled, ImgStruct.imgHscaled
INVOKE EndPaint, hWin, ADDR ps

and I'm using this to load the file, after converting the filename to Unicode:
INVOKE GdipLoadImageFromFile, ADDR unicodeName, OFFSET GdiHbitmap

So it is possible.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 08:58:39 AM
Of 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.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 09:02:09 AM
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.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 09:25:02 AM
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 (https://forum.codejock.com/load-pngs-from-resource-dll_topic10281.html)). Of course, a lot of them will deal with weird platforms like Wix, but some look usable in a Win32 app.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 09:29:21 AM
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 (https://forum.codejock.com/load-pngs-from-resource-dll_topic10281.html))
It doesnt mention gdiplus anywhere. So probably loading .pngs without transparency (alpha channel).  No good for my purposes.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 10:04:57 AM
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!!!
(https://i.postimg.cc/2yr9vP8n/untitled.png)

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!
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 10:42:59 AM
: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.

(https://i.postimg.cc/XJPj2cmH/untitled.png)

; 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:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 11:08:34 AM
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.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 11:16:15 AM
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:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: six_L on April 10, 2025, 12:29:07 PM
_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
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 12:32:28 PM
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.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 10, 2025, 12:58:20 PM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 01:06:27 PM
Can somebody tell me what are streams and why would I want to use them to deal with bitmap images?
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 01:14:45 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?
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...
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 10, 2025, 01:38:48 PM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 10, 2025, 01:53:01 PM
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.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: six_L on April 10, 2025, 02:53:33 PM
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
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 03:10:25 PM
Quote from: six_L on April 10, 2025, 02:53:33 PMi didn't test the code.
Thanks, six_L  :thumbsup:

You didn't really have to though, I would have gotten around to it sooner or later.
I'll test this tomorrow. Its just past midnight here, I have to go to sleep now.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 10, 2025, 10:45:34 PM
Quote from: zedd151 on April 10, 2025, 08:33:51 AMA nice simple example with only the needed code will do

I know, but that would be hours of work - and indeed work that I've done already and don't want to repeat. I just wanted to demonstrate that transparency can be done with streaming from resources, using GdipCreateBitmapFromStream

@NoCforMe: What is streaming? Simply doing a job from a buffer, instead of reading from an open file. So instead of passing a file handle., you typically supply the address of a callback proc of yours to the streaming function, and when that API function calls you there, you hand over a bunch of bytes from a buffer. One often used example is the EM_STREAMIN message for RichEdit controls.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 10, 2025, 10:48:05 PM
Quote from: jj2007 on April 10, 2025, 10:45:34 PM@NoCforMe: What is streaming? Simply doing a job from a buffer, instead of reading from an open file.
Thats Microsoft for ya. Why not call it what it is, Microsoft? 

@jj: hours of work? For a simple no nonsense example? Surely you jest.  Have you forgotten how to write masm compatible (meaning ml.exe) assembly without MasmBasic? or uasm for that matter?
:joking:

It would take me hours, or even days (weeks, months, years - for some projects  :tongue: ), but that is because gdiplus (and some other apis) and some of its functions and methods are very new to me. And the MS docs are no great help, in many cases. It takes a lot of trial and error on my part. Then posting my test code here for verification of legitimacy, usefulness, error checking and even debugging. Now all of that takes time. And cooperation and assistance of other members here obviously, which could  take even more time.

Anyway after sleeping on it, I have come to the conclusion that loading images from a file is a hell of a lot easier and simpler than loading image resources using gdiplus. I might just leave Connect 4 to use that method.
.... thinking further....
And in case the user deletes the image files (I could have them embedded in the executable) and write them back to file if they are 'missing'. A kind of backup plan of sorts.
The user could still use their own images to replace mine, but would have to keep the same names for them; if those are inadvertently deleted, they would be replaced with my original images automatically. I kinda like that idea.
.... more thinking ....
In fact, I would not have to supply the images files separately then, just let the program 'install' them (write them to file) upon first use.... (maybe this is not Campus material here anymore? Whaddya think?) I will be testing this method in the coming days...  :biggrin:
... still more thinking ...
Not sure what AV software (or other 'security' software or settings) would think about it though. That could be an issue for some users, and scare them away from my wares.   :eusa_boohoo:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 11, 2025, 02:59:26 AM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 03:21:24 AM
Quote from: TimoVJL on April 11, 2025, 02:59:26 AMAt campus people should be calm, not a room for Retardos  :biggrin:
??? Who ???
Lemme guess...   :rolleyes:
Oh, I know.  :biggrin:
Who else would it be...    :tongue:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 04:36:06 AM
Looking at a couple of files from masm32 library

bitmapfrommemory.asm
BitmapFromMemory  PROC  pMemory:DWORD, dwFileSize:DWORD
    LOCAL hResource:DWORD,  pGlobal:DWORD,      pStream:DWORD
    LOCAL hImage:DWORD,    pPicture:DWORD,    hBitmap:DWORD
  ;invoke CoInitialize, NULL
    mov pStream, NULL
    mov pPicture, NULL    ; NULL pointers for later use
    invoke CoTaskMemAlloc, dwFileSize  ; copy picture into task memory
    .if !eax
        ; oops! we didn't get the memory
        ; the last error code was set for us, and EAX is zero, so just return
        ret
    .endif
    mov pGlobal, eax
    invoke _MemCopy, pMemory, pGlobal, dwFileSize
    ; create a stream for the picture object's creator
    invoke CreateStreamOnHGlobal, pGlobal, TRUE, ADDR pStream
    or eax,eax
    jz @f
    invoke CoTaskMemFree,pGlobal
    xor eax,eax
    ret
@@:
    invoke OleLoadPicture, pStream, NULL, TRUE, ADDR IID_IPicture, ADDR pPicture
    or eax,eax
    jz @f
    mov eax, pStream
    call release_pStream
    xor eax,eax
    ret
@@:
    ; now we are ready to get the hBipmap, we farm this out for reuseability
    invoke BitmapFromPicture, pPicture
    mov hBitmap, eax
    mov eax, pStream
    call release_pStream
    mov eax, pPicture
    call release_pPicture
    mov eax, hBitmap                ; hBitmap is our return value, stuff it
    ret                            ; we're all done
BitmapFromMemory ENDP

bitmapfromresource.asm
BitmapFromResource  PROC hModule: dword, ResNumber:DWORD
    LOCAL hResource:DWORD,  dwFileSize:DWORD, hImage:DWORD

    ; get a resource handle (address) and resource length from the executable
    invoke FindResource, hModule, ResNumber, ADDR szImage
    or eax,eax
    jnz @f
    invoke SetLastError, ERROR_FILE_NOT_FOUND
    xor eax,eax
    ret
@@:
    mov hResource, eax
    invoke LoadResource, hModule, eax
    invoke LockResource, eax
    mov hImage, eax
    invoke SizeofResource, hModule, hResource
    mov dwFileSize, eax
    .IF dwFileSize      ; we use the resource size to determine if we got a
                        ; legit image file to open
        invoke BitmapFromMemory, hImage, dwFileSize
    .ELSE
        invoke SetLastError, ERROR_FILE_NOT_FOUND
        xor eax,eax
    .ENDIF

    ; everything's been done for us now, just return
    ret                    ; we're all done

BitmapFromResource  ENDP

bitmapfrompicture.asm
BitmapFromPicture PROC pPicture:DWORD
    LOCAL tempDC:DWORD,  tempBitmap:DWORD,  OldBitmap:DWORD
    LOCAL dwWidth:DWORD, dwHeight:DWORD,    compDC:DWORD
    LOCAL hmWidth:DWORD, hmHeight:DWORD,    neghmHeight:DWORD

    ; check we have an object
    .IF pPicture == 0
        ; whoops, no object passed in
        invoke SetLastError, ERROR_INVALID_PARAMETER
        xor eax,eax
        ret
    .ENDIF
   
    ; get a DC to work with
    invoke GetDC, NULL          ; screen DC
    mov compDC, eax
    invoke CreateCompatibleDC, compDC
    .IF !eax
        ; whoops, didn't get a DC
        ; but at least we had SetLastError called for us
        invoke ReleaseDC,NULL,compDC
        xor eax,eax
        ret
    .ENDIF
    mov tempDC, eax

    ; read out the width and height of the IPicture object
    ; (IPicture)pPicture::get_Width(*hmWidth)
    lea eax, hmWidth
    push eax
    mov eax, pPicture
    push eax
    mov eax, [eax]
    call [eax].IPicture.get_Width

    ; (IPicture)pPicture::get_Height(*hmHeight)
    lea eax, hmHeight
    push eax
    mov eax, pPicture
    push eax
    mov eax, [eax]
    call [eax].IPicture.get_Height

    ; convert himetric to pixels
    invoke GetDeviceCaps, compDC, LOGPIXELSX
    invoke MulDiv, hmWidth, eax, HIMETRIC_INCH
    mov dwWidth, eax

    invoke GetDeviceCaps, compDC, LOGPIXELSY
    invoke MulDiv, hmHeight, eax, HIMETRIC_INCH
    mov dwHeight, eax
    xor eax, eax
    sub eax, hmHeight
    mov neghmHeight, eax

    invoke CreateCompatibleBitmap, compDC, dwWidth, dwHeight
    .IF !eax
        ; whoops, didn't get a bitmap
        ; but at least we had SetLastError called for us\
        ; clean up the DC
        invoke ReleaseDC,NULL,compDC
        invoke DeleteDC, tempDC
        xor eax,eax
        ret
    .ENDIF
    mov tempBitmap, eax

    invoke SelectObject, tempDC, tempBitmap
    .IF !eax
        ; whoops, didn't select our bitmap
        ; but at least we had SetLastError called for us
        invoke ReleaseDC,NULL,compDC
        invoke DeleteDC, tempDC
        invoke DeleteObject, tempBitmap
        xor eax,eax
        ret
    .ENDIF
    mov OldBitmap, eax

    ; ok, now we have our bitmap mounted onto our temporary DC, let's blit to it
    ; (IPicture)pPicture::Render(hdc, x, y, cx, cy,                            \
    ;                            xpos_himetric, ypos_himetric,                \
    ;                            xsize_himetric, ysize_himetric, *rectBounds)
    push NULL  ; *rectBounds
    push neghmHeight
    push hmWidth
    push hmHeight
    push 0
    push dwHeight
    push dwWidth
    push 0
    push 0
    push tempDC
    mov eax, pPicture
    push eax
    mov eax, [eax]
    call [eax].IPicture.Render
    test eax, eax
    .IF SIGN?
        ; the call failed!
        push eax
        ; do some clean up first
        invoke ReleaseDC,NULL,compDC
        invoke DeleteDC, tempDC
        invoke DeleteObject, tempBitmap
        pop eax
        ; need to parse out the return fail value
        .IF eax == E_FAIL
        .ELSEIF eax == E_INVALIDARG
        .ELSEIF eax == E_OUTOFMEMORY
        .ELSEIF eax == E_POINTER
        .ELSE
        .ENDIF   
        invoke SetLastError, eax
        xor eax,eax
        ret
    .ENDIF

    ; we now have the bitmap blitted, let's get it off the dc and clean up.
    ; we're not going to check for errors, cause we did our importaint thing
    ; and if these fail now, other things will fall apart anyway

    invoke ReleaseDC,NULL,compDC
    invoke SelectObject, tempDC, OldBitmap
    invoke DeleteDC, tempDC
   
    mov eax, tempBitmap    ; the bitmap handle is the return value
    ret                    ; we're all done

BitmapFromPicture ENDP               

I will experiment with these next... :biggrin:  ... leaving out anything that converts the image to HBITMAP

Will OleLoadPicture load a png as 32 bit RGBA, or 24 bit RGB??? Ima gonna find out.  :cool:
I'll read up on that function in the meantime...  :biggrin:

If nothing else, it will add a couple of tools into my image toolbox.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: Vortex on April 11, 2025, 05:04:19 AM
Hi Zedd,

You are welcome. I am glad if I can be helpful. You can also try my image library based on OLE technology :

https://masm32.com/board/index.php?msg=82060
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 05:06:46 AM
Quote from: Vortex on April 11, 2025, 05:04:19 AMHi Zedd,

You are welcome. I am glad if I can be helpful. You can also try my image library based on OLE technology :

https://masm32.com/board/index.php?msg=82060
Thanks, I'll take a look at it.   :thumbsup:

I have obviously been working on my game code lately. One can never have too many options for opening and displaying images.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 11, 2025, 05:08:03 AM
Quote from: jj2007 on April 10, 2025, 10:45:34 PM@NoCforMe: What is streaming? Simply doing a job from a buffer, instead of reading from an open file. So instead of passing a file handle., you typically supply the address of a callback proc of yours to the streaming function, and when that API function calls you there, you hand over a bunch of bytes from a buffer. One often used example is the EM_STREAMIN message for RichEdit controls.
First of all, thank you for that straightforward explanation. Much appreciated.

A little further probing, if you don't mind:
With regard to bitmap images, does that mean that you fill your buffer with the bitmap data first? And would that be the raw data (like the file bytes), or is it another type of data, acquired from some Win32 function?

Still not sure what the advantage is here, but this sounds very similar to the way I "load" bitmaps by putting the raw .BMP data in my .data section, then using my GetBMPhandle() code to "open" the bitmap and return a usable handle. Avoids the use of a resource file (I call it using a "fake resource file", which is just an include file full of DB statements).
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: Vortex on April 11, 2025, 06:33:35 AM
Hi Zedd,

Using SHCreateMemStream is simplifying the code. Plus, you don't need resources as you can embed any binary data into your executable with Hutch's file data assembler fda.exe. This tool converts any binary file to a MS COFF object module :

\masm32\fda logo.png logo.obj pPNG

pPNG is the label identifying the binary data.

fda creates an include file named logo.inc to define the size of the embedded data :

; -----------------------------------------------------
; Include the contents of this file in your source file
; to access the data as an OFFSET and use the equate as
; the byte count for the file data in the object module
; -----------------------------------------------------
EXTERNDEF pPNG:DWORD
ln_pPNG equ <4516>

The modified code :

include logo.inc

ApiFunc TYPEDEF PROTO :DWORD,:DWORD
.
.
    .IF uMsg==WM_CREATE
.
.       
        invoke  LoadLibrary,ADDR shlwapi
        mov     hDll,eax
       
        invoke  GetProcAddress,eax,ADDR func

        ASSUME  eax:PTR ApiFunc
        invoke  eax,ADDR pPNG,ln_pPNG  ; ln_pPNG is defined in logo.inc
        ASSUME  eax:NOTHING            ; created by file data assembler fda

        mov     pStream,eax

        invoke  GdipCreateBitmapFromStream,pStream,ADDR PNGimage
        invoke  GdipCreateHBITMAPFromBitmap,PNGimage,ADDR hBitmap,0

        invoke  GdipDisposeImage,PNGimage
        coinvk  pStream,IStream,Release
        invoke  FreeLibrary,hDll

Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 06:57:29 AM
Thanks Vortex, I'll look at that shortly. I'm away from the computer at the moment.  :smiley:


Later:
Okay, I back inside briefly.
That looks very interesting.  :thumbsup:

I will have a better look at it after I finish up with some yard work...

More Later:
Kind of neat the way that you create the object file just before assembling&linking... I was looking for a call in the source code.  :joking:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: NoCforMe on April 11, 2025, 07:10:03 AM
Quote from: zedd151 on April 11, 2025, 06:57:29 AMThanks Vortex, I'll look at that shortly. I'm away from the computer at the moment.  :smiley:
Zedd, I think we need to get you fitted with some kind of tracking device, maybe an ankle monitor. That way we can keep track of where you are at any given time.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 07:12:57 AM
Quote from: NoCforMe on April 11, 2025, 07:10:03 AMThat way we can keep track of where you are at any given time.
I'll never tell.  :cool:

Oh wait. I did already.  :toothy:

Fortunately, there wasn't a lot that needed to be done in the front yard. Just a bit of weed whacking along the western fence line. All done for today.  :biggrin:  anything else can wait for tomorrow.

Now I'm back on the back porch enjoying the weather, for those keeping track of my whereabouts.   :tongue:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 09:27:45 PM
After trying several different methods of loading images other than by loading the image file, I have come to the conclusion that loading images from file is the easiest way to do it.

I will no longer seek to load image resources or any other variation at this time. I have a few projects that I would like to finish very soon, so to expedite that process, I will choose the path of least resistance and load the images from file.

Thank you to everyone that has offered the various other methods, or solutions.

I will re-explore those options at some other time in the future.  :thumbsup:
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 11, 2025, 10:10:09 PM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 11, 2025, 10:41:29 PM
Quote from: Vortex on April 11, 2025, 06:33:35 AMUsing SHCreateMemStream is simplifying the code. Plus, you don't need resources as you can embed any binary data into your executable with Hutch's file data assembler fda.exe. This tool converts any binary file to a MS COFF object module

Hi Vortex. I did indeed test your program, and it works very well.  :thumbsup:  :thumbsup:  (two thumbs up)

I made some adjustments to the source as usual to allow drawing .png images with true transparency.
; original code by Vortex
; modified by zedd151

include LoadPNGfromMem.inc
include logo.inc

ApiFunc TYPEDEF proto :dword, :dword
DrawImage proto :dword, :dword, :dword, :dword

.data

ClassName  db 'ImageClass', 0
AppName    db 'Display PNG from memory', 0
shlwapi    db 'shlwapi.dll', 0
func        db 'SHCreateMemStream', 0

.data?

hInstance  dd ?
hBitmap    dd ?
GdiPStatus  dd ?
StartupInfo GdiplusStartupInput <?>
token      dd ?
hDll        dd ?
PNGimage    dd ?
pStream    dd ?

.code

start:

    invoke  GetModuleHandle, 0
    mov    hInstance, eax
    invoke  GetCommandLine
    invoke  WinMain, hInstance, 0, eax, SW_SHOWDEFAULT
    invoke  ExitProcess, eax

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+1
    mov    wc.lpszMenuName, 0
   
    mov    wc.lpszClassName, offset ClassName
    invoke  LoadIcon, eax, IDI_APPLICATION
    mov    wc.hIcon, eax
    mov    wc.hIconSm, eax
    invoke  LoadCursor, 0, 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, 290, 90, eax, eax, \
            hInst, eax
           
    mov    hwnd, eax
    invoke  ShowWindow, hwnd, SW_SHOWNORMAL
    invoke  UpdateWindow, hwnd
   
    .while  TRUE
            invoke GetMessage, addr msg, 0, 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 rct:RECT
    local hdc:HDC
    local hMemDC:HDC
    local hBackBrush:HBRUSH
    local hMemBmp:HBITMAP
    local hMemBmp_old:HBITMAP
    local bm:BITMAP
    .if uMsg==WM_CREATE
        mov eax, offset StartupInfo
        mov GdiplusStartupInput.GdiplusVersion[eax], 1
        mov GdiPStatus, eax
        invoke GdiplusStartup, addr token, addr StartupInfo, 0
        invoke LoadLibrary, addr shlwapi
        mov hDll, eax
        invoke GetProcAddress, eax, addr func
        assume eax:ptr ApiFunc
        invoke eax, addr pPNG, ln_pPNG ; ln_pPNG is defined in logo.inc
        assume eax:nothing ; created by file data assembler fda
        mov pStream, eax
        invoke GdipCreateBitmapFromStream, pStream, addr PNGimage
    .elseif uMsg == WM_ERASEBKGND
        mov eax, 1
        ret
    .elseif uMsg==WM_PAINT
        invoke BeginPaint, hWnd, addr ps
        mov hdc, eax
        invoke GetClientRect, hWnd, addr rct
        invoke  CreateCompatibleDC, hdc
        mov    hMemDC, eax
        invoke  CreateCompatibleBitmap, hdc, rct.right, rct.bottom
        mov    hMemBmp, eax
        invoke SelectObject, hMemDC, hMemBmp
        mov hMemBmp_old, eax
        invoke CreateSolidBrush, 00FFDFBFh  ;; a nice light blue colr
        mov hBackBrush, eax  ;; save the brush handle
        invoke FillRect, hMemDC, addr rct, hBackBrush              ; fill rectangle in memory DC
        invoke DeleteObject, hBackBrush
       
        invoke DrawImage, hMemDC, PNGimage, 20, 12

        invoke BitBlt, hdc, 0, 0, rct.right, rct.bottom, hMemDC, 0, 0, SRCCOPY
        invoke SelectObject, hMemDC, hMemBmp_old
        invoke DeleteObject, hMemBmp
        invoke DeleteDC, hMemDC
        invoke EndPaint, hWnd, addr ps
    .elseif uMsg==WM_DESTROY
        invoke GdipDisposeImage, PNGimage
        coinvk pStream, IStream, Release
        invoke FreeLibrary, hDll
        invoke DeleteObject, hBitmap
        invoke GdiplusShutdown, GdiPStatus
        invoke PostQuitMessage, 0
    .ELSE
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .endif
    xor eax, eax
    ret
WndProc endp

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

end start

I have also modified your .png image to have the background as tranparent 32 bit RGBA, replacing the white background color with transparency for demonstration here. The light blue color is the window background color, not part of the image (for clarification).

Results upon running the modified program:
(https://i.postimg.cc/521CdPhG/untitled.png)

Original image 24 bit RGB, has actual white background 00FFFFFFh:
(https://i.postimg.cc/7LtGzq58/logo.png)

It is definitely a viable contender to replace loading .png images at runtime by the end user, even though that seems to be the easiest method. Never mind what I said in my last post. I confess that I hadn't looked at your program in any great detail, before posting that. I had briefly forgotten about it, until looking through this thread... I have occasional memory leaks, I forget things often.

I will be integrating your methods into my Connect4 project!  :smiley:
I'll deposit a substantial fee to your top secret account in the Cayman Islands.  :biggrin:

Quote from: TimoVJL on April 11, 2025, 10:10:09 PMThis was a good topic for GDI+
Yes. More to come... there are a lot of things I want to learn how to do with gdiplus.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: adeyblue on April 12, 2025, 08:20:08 AM
QuoteThis was a good topic for GDI+
This is what happens if you treat unknowns as things to attack and learn from rather than things to be scared of

Quote from: TimoVJL on April 11, 2025, 10:10:09 PMIf users abandon Windows XP, SHCreateMemStream will be best choice for streams for GDI+.
If it's loaded it with ordinal 12 rather than by name, it goes back to Win2000.
(https://img001.prntscr.com/file/img001/eEXWdGEwSru1Nl7W5KslcA.png) (https://prnt.sc/bNrTI3xbU_Wk)
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 12, 2025, 08:22:56 AM
It did not occur to me immediately. That is a list for SHCreateMemStream from each version and service pack of Windows, maybe not all of them though.

I must be having a senior moment.  :rolleyes:

Originally this post contained something very different.

Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: Biterider on April 12, 2025, 03:35:41 PM
Hi adeyblue
From were did you get that information?
It can be usefull for a lot of other things!

Regards, Biterider
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 14, 2025, 12:47:40 AM
Quote from: Biterider on April 12, 2025, 03:35:41 PMFrom were did you get that information?

QuoteWhat are the ordinal numbers for SHCreateMemStream in various Windows versions?


ChatGPT said:
The SHCreateMemStream function in Windows is a utility function provided by Shell32.dll (or sometimes Shlwapi.dll) that creates a memory-based IStream object. The "ordinal number" of a function refers to its position in the export table of a DLL, and some functions (especially undocumented or internal ones) can be exported by ordinal only (i.e., without a name).

Here's a breakdown of SHCreateMemStream ordinal numbers across different Windows versions:

🧾 Known Ordinal Numbers for SHCreateMemStream
DLL    Windows Version    Ordinal    Notes
shlwapi.dll    Windows XP–7    12    Exported by ordinal only (not by name)
shlwapi.dll    Windows 8/8.1    Deprecated    Function moved or removed
shell32.dll    Windows 10+    Not by ordinal    Exported by name instead
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 14, 2025, 01:01:24 AM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: jj2007 on April 14, 2025, 01:05:37 AM
M$ Learn (https://learn.microsoft.com/en-gb/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream?redirectedfrom=MSDN): Prior to Windows Vista, this function was not included in the public Shlwapi.h file, nor was it exported by name from Shlwapi.dll. To use it on earlier systems, you must call it directly from the Shlwapi.dll file as ordinal 12.
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 14, 2025, 02:03:40 AM
Quote from: jj2007 on April 14, 2025, 01:05:37 AMM$ Learn (https://learn.microsoft.com/en-gb/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream?redirectedfrom=MSDN): Prior to Windows Vista, this function was not included in the public Shlwapi.h file, nor was it exported by name from Shlwapi.dll. To use it on earlier systems, you must call it directly from the Shlwapi.dll file as ordinal 12.
In other words, if you are running any OS prior to Windows Vista, maybe an OS upgrade in long overdue? Just kidding, kind of.  :biggrin:

Of course a lot of users use a virtual machine for running older OS's, sometimes used for compatibility testing of programs for older Windows versions.

And a few diehards that run antique hardware that can only run older OS versions.


Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: guga on April 14, 2025, 02:34:47 AM
The export SHCreateMemStream function located at shlwapi - which is also called "SHCreateMemStreamStub" (originallly from it´s pdb) is a export helper (uses delayload library).

In Windows10 the true function SHCreateMemStream is located inside the dll shcore.

Don´t know about other windows versions, but if in other versions of Windows, the shcore.dll also contains the SHCreateMemStream function, better call them directly. For example:

call 'shcore.SHCreateMemStream' 0, 100
If on windowsXP-7, the export is also a delayload import (I don´t doubt it can be), and if on those older systems the shcore also contains the SHCreateMemStream, then, there´s no worries. Just load this function from shcore rather then shlwapi


Note: Internally, the api SHCreateMemStream is a Com object, that uses classes such as the ones from: https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/wrl/implements.h (https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/wrl/implements.h)
https://github.com/microsoft/wil/blob/master/tests/workarounds/wrl/wrl/implements.h (https://github.com/microsoft/wil/blob/master/tests/workarounds/wrl/wrl/implements.h)
such as: CanCastT and friends
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 14, 2025, 05:00:59 AM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: TimoVJL on April 15, 2025, 12:18:02 AM
DELETED
Title: Re: How to properly load a .png resource for use with GdipDrawImageRectI?
Post by: zedd151 on April 15, 2025, 02:26:57 AM
A quick mention of issues with various API's and their functions is common during discussion of any topic.

But continuing discussion like this may be more suited in its own topic? (Especially since the discussion does not return to the main intent of the topic.)

Just asking btw, not demanding...  :smiley:

I do not want any posts removed from here, but am asking politely that maybe a new topic might be more appropriate if this discussion about "shlwapi" and company is going to continue.