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

As I am needing code for 32 bit to do just that...
I can load a .png image from file just fine, 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.

zedd151

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.

adeyblue

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.

zedd151

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:

Vortex

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

zedd151

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:



zedd151

Quote from: zedd151 on April 10, 2025, 05:33:25 AMThat looks like something I can work with.
Then again, maybe not.


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

jj2007

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 uses GdipCreateBitmapFromStream

zedd151

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:

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... You cannot view this attachment.

jj2007

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



zedd151


jj2007

Better? The png file is too big for the forum, just edit the *.rc file to add your images. The order may matter.

zedd151

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:

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.