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

#105


Yes, I realized (too late btw) that I am loading the image each time WM_PAINT is called. That code now fixed... see below in post #107... the bad code, and  attachment removed from  this post

This is just a demonstration of the quality of the rendered layered images.
I think the results look great.
The code here, not so much...  :joking:

Probably would look even better and more realistic with a drop shadow. I can add that in PhotoShop next time.  :biggrin:

TimoVJL

Long time ago DednDave gave a template for handling Windows messages using handlers.
Same idea as Petzold in his books.

BTW:
Pelles C users can use WinMFProtoAddIn for it.
It was created after one standalone version written in C++

May the source be with you

zedd151

#107
Quote from: zedd151 on April 08, 2025, 01:54:54 AMGdi+ code to load image from file and display it at x, y
DrawImageFromFile proc hDC:dword, pImgName, x:dword, y:dword
local wcBuffer[256]:byte, Image:dword, wid:dword, hgt:dword, pGraphics:dword
    invoke GdipCreateFromHDC, hDC, addr pGraphics

    invoke MultiByteToWideChar, CP_ACP, 0, pImgName, -1, addr wcBuffer, 256 ;;;; not wanted if used in WM_PAINT
    invoke GdipLoadImageFromFile, addr wcBuffer, addr Image  ;;; not wanted if used in WM_PAINT
    invoke GdipGetImageWidth, Image, addr wid
    invoke GdipGetImageHeight, Image, addr hgt
    invoke GdipDrawImageRectI, pGraphics, Image, x, y, wid, hgt
    invoke GdipDisposeImage, Image
    invoke GdipDeleteGraphics, pGraphics
    ret
DrawImageFromFile endp

Yes, I realized (too late btw) that I am loading the image each time WM_PAINT is called.  :toothy:
In production code, that could be disastrous.

I will split off the file handling code, in the next example. :rolleyes:
Excerpt from the 'fixed' version:
    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 backpng, addr backImage
        invoke LoadImageFromFile, addr redpng, addr redImage
        invoke LoadImageFromFile, addr forepng, addr foreImage
        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0  ;; create dialog box, from resource dilalog

        invoke GdipDisposeImage, backImage
        invoke GdipDisposeImage, redImage
        invoke GdipDisposeImage, foreImage
        invoke GdiplusShutdown, GdiplusStatus
        invoke ExitProcess, eax
    start endp
   
    LoadImageFromFile proc pImgName, pImage
    local wcBuffer[256]:byte
        invoke MultiByteToWideChar, CP_ACP, 0, pImgName, -1, addr wcBuffer, 256
        invoke GdipLoadImageFromFile, addr wcBuffer, 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
It may still need some tweaking, the gdiplus code that is.

It works, though. Nice animation graphics. :biggrin:


Fixed source attached.
Just for sinsi:
The red disc is perfectly centered.  :tongue:

zedd151

#108
Just for fun and to be thorough, I tried my earlier method of mixing gdiplus methods with GDI methods:
Load png file with gdiplus
gdip_loadpic proc lp_pixname:dword, lp_gdipBmp:dword, lp_hBmpgdip:dword
local uni_pixname[512]:byte                                                        ; use local var for unicode path to image name
    invoke MultiByteToWideChar, CP_ACP, 0, lp_pixname, -1, addr uni_pixname, 256    ; convert ascii to unicode
    fn GdipCreateBitmapFromFile, addr uni_pixname, lp_gdipBmp                      ; open image file using unicode path to image, create image object
    mov eax, lp_gdipBmp
    mov eax, [eax]
    invoke GdipGetImageWidth,  eax,  addr pw
    mov eax, lp_gdipBmp
    mov eax, [eax]
    invoke GdipGetImageHeight, eax, addr ph
    mov eax, lp_gdipBmp                                                             
    mov eax, [eax]                                                                  ; get gdipBmp into eax (from lp_gdipBmp)
    invoke GdipCreateHBITMAPFromBitmap, eax, lp_hBmpgdip, 0                        ; get HBITMAP, from image object
    mov eax, lp_gdipBmp
    mov eax, [eax]                                                                  ;; get gdipBmp into eax (from lp_gdipBmp)
    invoke GdipDisposeImage, eax                                                    ; dispose gdipBmp
    ret
gdip_loadpic endp

Select the HBITMAP's as a usual Object
And either TransparentBlt them (shown here) or BitBlt them (not shown)
      invoke SelectObject, mDC, hBmpBack
      invoke TransparentBlt, hDC, 0, 0, 80, 240, mDC, 0, 0, 80, 240, 0
     
      invoke SelectObject, mDC, hBmpRed
      invoke TransparentBlt, hDC, 0, 40, 80, 80, mDC, 0, 0, 80, 80, 0

      invoke SelectObject, mDC, hBmpFore
      invoke TransparentBlt, hDC, 0, 0, 80, 240, mDC, 0, 0, 80, 240, 0
The result was that the parts that were supposed to be transparent, there is black color drawn, definitely not transparent.
I even experimented using ...SetBkMode, TRANSPARENT - no change.

I didn't really think it would work, now I know for certain that it will not. :smiley:


I won't even bother attaching this disaster.  :rofl:  :joking:

zedd151

#109
Using the same code as in gdip2.zip from above, I have added drop shadow effects to the red disc and the foreground image in Photoshop to make it look more realistic.


NoCforMe

Quote from: zedd151 on April 08, 2025, 06:32:02 AMThe result was that the parts where alpha was zero, there is black color drawn, definitely not transparent.
Wait a sec; I could be wrong, but I thought zero alpha was opaque, not transparent.

?????

BTW, your discs look like either red blood cells or big red candy tablets ... pretty nice rendering there, judging by the pic you posted.
Assembly language programming should be fun. That's why I do it.

zedd151

#111
Quote from: NoCforMe on April 08, 2025, 08:01:32 AM
Quote from: zedd151 on April 08, 2025, 06:32:02 AMThe result was that the parts where alpha was zero, there is black color drawn, definitely not transparent.
Wait a sec; I could be wrong, but I thought zero alpha was opaque, not transparent.

?????
The point being the image was not displayed as transparent using BitBlt or TransparentBlt. But the image certainly has transparent parts in it. So probably not compatible with plain old GDI functions.

Quote from: NoCforMe on April 08, 2025, 08:01:32 AMpretty nice rendering there, judging by the pic you posted.
I swiped it off the net. Looked perfect for connect 4, to spruce up the graphics eventually. I'll have to play around with the foreground image and both the red and blue discs to make them look 'just right'. Once done it should look slightly 3 dimensional, but in 2D. Much better than the primitive bitmaps currently used during testing.

NoCforMe

Yeah, when you play with this stuff, good bitmaps become less of a "nice thing to have" and more of a requirement, as you've discovered.

I'm not at any kind of high level, but find I can do a lot with good ol' simple Paint Shop Pro from Windoze 2000 days). Attached is a very li'l program for which I made a bunch of bitmaps, from scratch: created the watch face as one image with no hand, and then created 12 hands at different rotation angles. It was actually kinda fun, and as you can see successful. (Oh, kinda forgot: I used CorelDraw to draw the clockface. Very handy piece of software!)

Except for one thing: on my rig (Windows 7, older computah) it flickers about once every 6-7 sec. What do you see? Maybe time to learn double-buffering?
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on April 08, 2025, 11:20:50 AMit flickers about once every 6-7 sec. What do you see?
As steely dan said, no static at all...
Well no flicker that is.  :biggrin:


Couple minutes later...
OH! You meant on your stopwatch. Definitely flickers.

I thought that you were talking about my demo, I had an issue like that recently. I saw no flicker at all but others noticed it.
==============================================================
        .elseif uMsg == WM_ERASEBKGND
          mov eax, 1
          ret
        .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 CreateCompatibleBitmap, hDC, rct.right, rct.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

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

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

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

          ;; do all of your drawing here to mDC (memory DC)




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

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

          invoke EndPaint, hWin, addr ps
=====================================
Dialog box template with double buffer in WM_PAINT attached

sinsi

Quote from: zedd151 on April 08, 2025, 06:32:02 AMThe result was that the parts where alpha was zero, there is black color drawn, definitely not transparent.
I even experimented using ...SetBkMode, TRANSPARENT - no change.
You can't use black as the transparent colour.

You could also try AlphaBlend function

daydreamer

Well I done both simple coding draw black version of sprite below jet, as shadow before I had skill and access to good 2d paint programs
And recent years using 3d app to make pre-rendered jet with shadow below in my jet fighter PC gdi game
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

zedd151

Quote from: sinsi on April 08, 2025, 08:36:13 PMYou can't use black as the transparent colour.
I think that the issue was that the png image used RGBA color not RGB. In any case, the gdip rendering functions worked as expected. The BitBlt an TransparentBlt tests were just experimental, and inconsequential.  :biggrin:

jj2007

Quote from: zedd151 on April 08, 2025, 10:16:29 PMused RGBA color not RGB

That is one source of confusion with GdiPlus, indeed.

zedd151

#118
Quote from: jj2007 on April 08, 2025, 11:41:30 PMThat is one source of confusion with GdiPlus, indeed.
No confusion. I just didn't take that into consideration (RGBA) when playing around with BitBlt and TransparentBlt.
Properly using the gdiplus drawing functions of course produces the correct and desired results.

And looks fantastic, btw.  :biggrin:


Never mind my ill-advised, out-of-the-box, and from-out-past-left-field experimentations with GDI.  :rolleyes:

  I am already making full sized game board graphics for connect 4. I will then slice up the graphics one each for the 4 corners and the 4 edges, one for the round cutout, etc. Then experiment with reassembling the full sized board in code...

Then again, .png files are not huge, so I might just take the easier approach and keep the full sized .png images for use in the game. Not sure yet if I will leave them external to the program, or put them in resources.




zedd151

Animation example attached using the new graphics elements...  :eusa_dance:
    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 640   ; desired client area width
        cheight             equ 640   ; desired client area height

    .data
        StartupInfo         GdiplusStartupInput <0>
        GdiplusStatus       dd 0
        token               dd 0
        backpng             db "back.png", 0
        redpng              db "red2.png", 0
        forepng             db "fore.png", 0
       
        backImage           dd 0
        redImage            dd 0
        foreImage           dd 0
        aniX                dd 40
        aniY                dd 40
       
    .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 backpng, addr backImage
        invoke LoadImageFromFile, addr redpng, addr redImage
        invoke LoadImageFromFile, addr forepng, addr foreImage
        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0  ;; create dialog box, from resource dilalog

        invoke GdipDisposeImage, backImage
        invoke GdipDisposeImage, redImage
        invoke GdipDisposeImage, foreImage
        invoke GdiplusShutdown, GdiplusStatus
        invoke ExitProcess, eax
    start endp
   
    LoadImageFromFile proc pImgName, pImage
    local wcBuffer[256]:byte
        invoke MultiByteToWideChar, CP_ACP, 0, pImgName, -1, addr wcBuffer, 256
        invoke GdipLoadImageFromFile, addr wcBuffer, 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

    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
          invoke SetTimer, hWin, 100, 500, 0
        .elseif uMsg == WM_TIMER
          .if wParam == 100
            invoke KillTimer, hWin, 100
            invoke SetTimer, hWin, 100, 15, 0
             .if aniY < 520
               add aniY, 10
               invoke InvalidateRect, hWin, 0, 0
             .endif
          .endif
        .elseif uMsg == WM_ERASEBKGND
          mov eax, 1
          ret
        .elseif uMsg == WM_PAINT
          invoke BeginPaint, hWin, addr ps
          mov hDC, eax
          invoke GetClientRect, hWin, addr rct
          invoke CreateCompatibleDC, hDC
          mov mDC, eax
          invoke CreateCompatibleBitmap, hDC, rct.right, rct.bottom
          mov hBmp, eax
          invoke SelectObject, mDC, hBmp
          mov hBmp_old, eax
          invoke CreateSolidBrush, 00FF7F3Fh
          mov hBrush, eax
          invoke FillRect, mDC, addr rct, hBrush
          invoke DeleteObject, hBrush

          invoke DrawImage, mDC, backImage, 0, 0    ; gdi+ draw image from file to x, y
         
          invoke DrawImage, mDC, redImage, 40, aniY ; gdi+ draw image from file to x, y
         
          invoke DrawImage, mDC, foreImage, 0, 0    ; gdi+ draw image from file to x, y
         
          invoke BitBlt, hDC, 0, 0, rct.right, rct.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

    end



I see no flickering or other adverse issues. Lemme know please, if you see anything erroneous, either in the code itself  or the display of the animation.  :smiley: