News:

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

Main Menu

gdiplus tests

Started by zedd151, March 12, 2025, 02:18:44 AM

Previous topic - Next topic

zedd151

Continuing on with my graphics related code... my first  time writing gdiplus code that was not already pre-canned into a procedure by someone else. It took me a hot minute to figure out that I needed a unicode string for the image path... :rofl:

Tests opening an image and displaying using gdiplus.
Using StretchBlt also, to fill the client area with the image, even when resized.
Image is a cropped version of hutchs "downtown Sydney Australia" banner from the forum.
Have tested this program using *.jpg, *.png, and non-animated *.gif files.

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


        cwidth          equ  1024              ; desired client area width
        cheight        equ  576                ; desired client area height

    .data
        gdipBmp        dd 0                    ; image object
        hBmpgdip        dd 0                    ; image HBITMAP
        picname        db "sydney.jpg", 0      ; image filename in ascii
        upicname        db 128 dup (0)          ; image file name in unicode
       
    .data?
        gdii        GdiplusStartupInput <?>
        GDIPtoken      dd ?
     
    .code

    start proc
    local hInstance:dword
        invoke GetModuleHandle, NULL
        mov hInstance, eax
       
        ;---------------------  GdiPlus Startup code -------
       
        mov gdii.GdiplusVersion, 1
        mov gdii.DebugEventCallback, 0
        mov gdii.SuppressBackgroundThread, 0
        mov gdii.SuppressExternalCodecs, 0
        invoke GdiplusStartup, addr GDIPtoken, addr gdii, 0
       
        ;----------------------------------------------------

        invoke MultiByteToWideChar, CP_ACP, 0, addr picname, -1, addr upicname, 128 ; convert ascii to unicode

        fn GdipCreateBitmapFromFile, addr upicname, addr gdipBmp        ; open image file, create image object
        invoke GdipCreateHBITMAPFromBitmap, gdipBmp, addr hBmpgdip, 0  ; get HBITMAP, from image object
        invoke GdipDisposeImage, gdipBmp                                ; dispose image object

        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0      ;; create dialog box, from resource dilalog
       
        ;--------------------  GdiPlus Shutdown code -------
       
        invoke GdiplusShutdown, GDIPtoken
       
        ;----------------------------------------------------
        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_SIZE
          invoke InvalidateRect, hWin, 0, 0
        .elseif uMsg == WM_PAINT
          invoke BeginPaint, hWin, addr ps
          mov hDC, eax                          ; window client area DC
         
          ;; get client area rectangle
          invoke GetClientRect, hWin, addr rct

          invoke CreateCompatibleDC, hDC
          mov mDC, eax                          ; memory DC

          invoke SelectObject, mDC, hBmpgdip    ; select image HBITMAP

          invoke StretchBlt, hDC, 0, 0, rct.right, rct.bottom, mDC, 0, 0, 276, 213, SRCCOPY

          invoke DeleteDC, mDC

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

    writefilex proc lpName:dword, lpData:dword, fl:dword
    local hOutput:dword
    local bw :dword
        invoke CreateFile, lpName, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0
        cmp eax, -1
      jne @F
        fn MessageBox, 0, lpName, "Couldn't read File!", MB_OK
        xor eax, eax
        xor ecx, ecx
        jmp xit
      @@:
        mov hOutput, eax
        invoke WriteFile, hOutput, lpData, fl, addr bw, NULL
        invoke CloseHandle, hOutput
        mov eax, 1
      xit:
        ret
    writefilex endp

    end
   
   

I think everything here is proper in regards to the gdiplus code.
What about using gdiplus code in WM_PAINT?? I have zero clue myself.
Using StretchBlt on a large image, when resizing smaller the results are not very appealing.
If nothing else, I have a way now to load and use .jpg, .png or .gif files in my programs, without relying on others work. *except as research material, into trying to understand gdi+.  :smiley:
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

NoCforMe

Here's your WM_PAINT handler for GDI+, good buddy:
.data
GDIhBitmap DD ? ;Global GDI+ bitmap handle

;=================================
; Open image before WM_PAINT runs:
; Note: "ImageFilename" must be Unicode
;=================================
INVOKE GdipLoadImageFromFile, OFFSET ImageFilename, OFFSET GDIhBitmap

;==================
; WM_PAINT handler:
;==================
LOCAL gdiHgraphics:DWORD

do_paint:
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):
Invoke GdipDrawImageRectI, gdiHgraphics, GDIhBitmap, X, Y, W, H

INVOKE EndPaint, hWin, ADDR ps
XOR EAX, EAX
RET

You've already done the first part (opening the image with GdipLoadImageFromFile(); that gets done outside of the WM_PAINT handler.

Many thanks to "mabdelouahab" of this forum who provided these magic incantations ...

Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on March 12, 2025, 04:28:03 AMHere's your WM_PAINT handler for GDI+, good buddy
; Display image at (X,Y) with dimensions (W,H):
    Invoke GdipDrawImageRectI, gdiHgraphics, GDIhBitmap, X, Y, W, H

Thanks chum.  :tongue:
And mabdelouahab, for the
Quote from: NoCforMe on March 12, 2025, 04:28:03 AMmagic incantations ...
:thumbsup:

I new there had to be a gdi+ way to draw the image ... back to more experimentation...

The MS documentation is weird when searching for gdiplus functions
"Creates a Graphics::Graphics object that is associated with a specified device context." ... like that tells ME something. lol

In a "real" program I would probably put the gdi+ startup code in either WM_INITDIALOG, or WM_CREATE depending on the GUI design, and the gdi+ shutdown call in WM_CLOSE.
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

NoCforMe

Yes, documentation. Ugh.
This Micro$oft Learn page has a table of all the "flat" GDI+ functions--that is, the functions removed from their C++ "wrapper" and exposed for us non-C++ programmers. It's a little clunky, but search for the function you want info on and it'll show the parameters and other good stuff if you click on it.
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on March 12, 2025, 05:00:17 AMYes, documentation. Ugh.
This Micro$oft Learn page has a table of all the "flat" GDI+ functions--that is, the functions removed from their C++ "wrapper" and exposed for us non-C++ programmers. It's a little clunky, but search for the function you want info on and it'll show the parameters and other good stuff if you click on it.
Thanks for the tip, and no thanks to Microsoft.  :biggrin:
I like the win32.hlp method of presenting information. Too bad gdi+, and some others never made it in there.
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

TimoVJL

Do we need GdipDeleteGraphics after GdipCreateFromHDC ?
May the source be with you

zedd151

Quote from: TimoVJL on March 12, 2025, 05:56:27 AMDo we need GdipDeleteGraphics after GdipCreateFromHDC ?
I dunno, I am still learning about gdi+, and why I am asking questions here.   :azn:

I just downloaded  from MS, windows-win32-gdiplus.pdf for my research...   :eusa_dance:

gdi+ reference
Button on the left of that page to Download PDF.


¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

zedd151

... whoops.  :eusa_boohoo:
AGAIN! Quoted the above post, meaning to modify it.  :greensml:
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

NoCforMe

Quote from: TimoVJL on March 12, 2025, 05:56:27 AMDo we need GdipDeleteGraphics after GdipCreateFromHDC ?
Maybe, to prevent resource leaks.
Although the "graphics" object might be automagically deleted by Windows on program exit.
Assembly language programming should be fun. That's why I do it.

adeyblue

QuoteDo we need GdipDeleteGraphics after GdipCreateFromHDC ?
Yes. GDIhBitmap also needs GdipDisposeImage-ing


zedd151

Quote from: adeyblue on March 12, 2025, 12:03:52 PM
QuoteDo we need GdipDeleteGraphics after GdipCreateFromHDC ?
Yes. GDIhBitmap also needs GdipDisposeImage-ing
Thanks adeyblue. I am still doing research on gdi+ stuff.

Haven't seen you around here in a hot minute...  :smiley:
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

NoCforMe

Quote from: adeyblue on March 12, 2025, 12:03:52 PM
QuoteDo we need GdipDeleteGraphics after GdipCreateFromHDC ?
Yes. GDIhBitmap also needs GdipDisposeImage-ing
OK, I looked that up in my GDI+ help file; it says
QuoteDisposes the specified Image object.

Hmm, that's not very helpful.
So what, exactly, does it mean to "dispose" [of?] an image object?
I know delete and destroy, but not dispose ...

Man, all this C++/"object" sophistry ...
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on March 12, 2025, 12:43:19 PMSo what, exactly, does it mean to "dispose" [of?] an image object?
I gunna try to find out...

I am going to try it where I would normally delete any gdi objects, either before EndPaint, or in WM_CLOSE.

Or maybe better, just before calling the gdiplus shutdown api.

I would have to see an example of how/when it is used. Or at least a more detailed description of the order of execution, i.e., where in the gdiplus chain of events this needs to be called. If that makes any sense. I'm tired and need sleep.  :tongue:

¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

zedd151

Quote from: NoCforMe on March 12, 2025, 12:43:19 PMSo what, exactly, does it mean to "dispose" [of?] an image object?
I gunna try to find out...

I am going to try it where I would normally delete any gdi objects, either before EndPaint, or in WM_CLOSE.

Or maybe better, just before calling the gdiplus shutdown api.

I would have to see an example of how/when it is used. Or at least a more detailed description of the order of execution, i.e., where in the gdiplus chain of events this needs to be called, if that makes any sense.
I'm tired and need sleep.  :tongue:

¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

TimoVJL

When object goes out of scope in OOP language, it will be destroyed after that.

So if object is created with GdipCreateFromHDC() in WM_PAINT, it should be destroyed with GdipDeleteGraphics() after it isn't needed anymore.
May the source be with you