The MASM Forum

General => The Workshop => Windows Graphics Programming => Topic started by: LordAdef on April 11, 2025, 09:13:39 AM

Title: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 11, 2025, 09:13:39 AM
Hi everyone!

Guys, I need help... Could a good soul post a working example of an anti-aliased circle using GDI+?
I really tried before posting, and I failed miserably. I gave up trying...

In fact, I am achieving anti-alias using the stretch bitblt trick, and I would be glad to post it here and compare the Antialiasing results.

Cheers
Alex
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 09:40:06 AM
I cheated and asked ChatGPT
It worked after a couple of minor fixes!
    include \masm32\include\masm32rt.inc
    include \masm32\include\gdiplus.inc
    includelib \masm32\lib\gdiplus.lib


.data
gdiplusStartupInput GdiplusStartupInput <1, 0, 0, 0>
gdiplusToken      dd ?
hPen              dd ?
hGraphics        dd ?
hWnd              dd ?
msg              MSG <>
wc                WNDCLASS <>
className        db "CircleWin",0
windowTitle      db "Anti-Aliased Circle with GDI+",0
penWidth          REAL4 3.0
xPos              REAL4 50.0
yPos              REAL4 50.0
circleWidth      REAL4 200.0
circleHeight      REAL4 200.0

.code
start:
    ; Register window class
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, offset WndProc
    mov wc.cbClsExtra, 0
    mov wc.cbWndExtra, 0
    push    0
    call    GetModuleHandle
    mov    wc.hInstance, eax
    mov    wc.hbrBackground, COLOR_WINDOW+1
    mov    wc.lpszClassName, offset className
    mov    wc.lpszMenuName, 0
    mov    wc.hIcon, 0
    mov    wc.hCursor, 0
    invoke  RegisterClass, addr wc

    ; Create the window
    invoke  CreateWindowEx, 0, addr className, addr windowTitle,\
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,\
            400, 300, 0, 0, wc.hInstance, 0
    mov    hWnd, eax

    ; Show and update window
    invoke ShowWindow, hWnd, SW_SHOWNORMAL
    invoke UpdateWindow, hWnd

    ; Message loop
msg_loop:
    invoke GetMessage, addr msg, 0, 0, 0
    test  eax, eax
    jz    end_loop
    invoke TranslateMessage, addr msg
    invoke DispatchMessage, addr msg
    jmp    msg_loop

end_loop:
    invoke ExitProcess, 0

WndProc proc hWin:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        LOCAL ps:PAINTSTRUCT
        LOCAL hdc:HDC
    .if uMsg == WM_PAINT

        invoke BeginPaint, hWin, addr ps
        mov    hdc, eax

        ; Initialize GDI+
        invoke GdiplusStartup, addr gdiplusToken, addr gdiplusStartupInput, 0

        ; Create graphics object from HDC
        invoke GdipCreateFromHDC, hdc, addr hGraphics

        ; Set anti-aliasing mode
        invoke GdipSetSmoothingMode, hGraphics, 2 ; SmoothingModeAntiAlias = 2

        ; Create a red pen with 3-pixel width
        invoke GdipCreatePen1, 0FFFF0000h, penWidth, 2, addr hPen

        ; Draw ellipse (x, y, width, height)
        invoke GdipDrawEllipse, hGraphics, hPen, xPos, yPos, circleWidth, circleHeight

        ; Cleanup
        invoke GdipDeletePen, hPen
        invoke GdipDeleteGraphics, hGraphics
        invoke GdiplusShutdown, gdiplusToken

        invoke EndPaint, hWin, addr ps
        ret

    .elseif uMsg == WM_DESTROY
        invoke PostQuitMessage, 0
        ret
    .endif

    invoke DefWindowProc, hWin, uMsg, wParam, lParam
    ret
WndProc endp

end start

(https://i.postimg.cc/6qcHTSBT/untitled.png)

If nothing else, it will give you a starting point.
This code may contain errors of one form or another... AI generated.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: NoCforMe on April 11, 2025, 09:44:31 AM
Coulda saved a lot of characters there.
The only relevant part seems to be
        invoke GdipSetSmoothingMode, hGraphics, 2 ; SmoothingModeAntiAlias = 2
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:07:53 AM
Filled circle

    include \masm32\include\masm32rt.inc
    include \masm32\include\gdiplus.inc
    includelib \masm32\lib\gdiplus.lib


.data
gdiplusStartupInput GdiplusStartupInput <1, 0, 0, 0>
gdiplusToken      dd ?
hPen              dd ?
hGraphics        dd ?
hWnd              dd ?
msg              MSG <>
wc                WNDCLASS <>
className        db "CircleWin",0
windowTitle      db "Anti-Aliased Circle with GDI+",0
penWidth          REAL4 3.0
xPos              REAL4 50.0
yPos              REAL4 50.0
circleWidth      REAL4 200.0
circleHeight      REAL4 200.0
hBrush dd 0

.code
start:
    ; Register window class
    mov wc.style, CS_HREDRAW or CS_VREDRAW
    mov wc.lpfnWndProc, offset WndProc
    mov wc.cbClsExtra, 0
    mov wc.cbWndExtra, 0
    push    0
    call    GetModuleHandle
    mov    wc.hInstance, eax
    mov    wc.hbrBackground, COLOR_WINDOW+1
    mov    wc.lpszClassName, offset className
    mov    wc.lpszMenuName, 0
    mov    wc.hIcon, 0
    mov    wc.hCursor, 0
    invoke  RegisterClass, addr wc

    ; Create the window
    invoke  CreateWindowEx, 0, addr className, addr windowTitle,\
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,\
            400, 300, 0, 0, wc.hInstance, 0
    mov    hWnd, eax

    ; Show and update window
    invoke ShowWindow, hWnd, SW_SHOWNORMAL
    invoke UpdateWindow, hWnd

    ; Message loop
msg_loop:
    invoke GetMessage, addr msg, 0, 0, 0
    test  eax, eax
    jz    end_loop
    invoke TranslateMessage, addr msg
    invoke DispatchMessage, addr msg
    jmp    msg_loop

end_loop:
    invoke ExitProcess, 0

WndProc proc hWin:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
        LOCAL ps:PAINTSTRUCT
        LOCAL hdc:HDC
    .if uMsg == WM_PAINT

        invoke BeginPaint, hWin, addr ps
        mov    hdc, eax

        ; Initialize GDI+
        invoke GdiplusStartup, addr gdiplusToken, addr gdiplusStartupInput, 0

        ; Create graphics object from HDC
        invoke GdipCreateFromHDC, hdc, addr hGraphics

        ; Set anti-aliasing mode
        invoke GdipSetSmoothingMode, hGraphics, 2 ; SmoothingModeAntiAlias = 2

        ; Create a solid brush (ARGB = 0xFF0000FF = opaque blue)
        invoke GdipCreateSolidFill, 0FF0000FFh, addr hBrush

        ; Fill the ellipse
        invoke GdipFillEllipse, hGraphics, hBrush, xPos, yPos, circleWidth, circleHeight

        ; Cleanup
        invoke GdipDeletePen, hPen
        invoke GdipDeleteGraphics, hGraphics
        invoke GdiplusShutdown, gdiplusToken

        invoke EndPaint, hWin, addr ps
        ret

    .elseif uMsg == WM_DESTROY
        invoke PostQuitMessage, 0
        ret
    .endif

    invoke DefWindowProc, hWin, uMsg, wParam, lParam
    ret
WndProc endp

end start

(https://i.postimg.cc/4d5v19qS/untitled.png)

If nothing else, it will give you a starting point.
This code may contain errors of one form or another... AI generated
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:09:31 AM
Quote from: NoCforMe on April 11, 2025, 09:44:31 AMCoulda saved a lot of characters there.
The only relevant part seems to be
        invoke GdipSetSmoothingMode, hGraphics, 2 ; SmoothingModeAntiAlias = 2

You are assuming he is already familiar with setting up and using gdiplus, I assume the opposite unless told otherwise by the OP.
Says the OP:
" I am achieving anti-alias using the stretch bitblt trick,"

Where'd Alex go?
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 11, 2025, 10:30:38 AM
Hi guys!

Thank you both a bunch!!

I am actually not used to GDI+, but spent this afternoon learning about it.
I must say the stretch bitblt trick actually works!  :eusa_dance:
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:32:04 AM
Quote from: LordAdef on April 11, 2025, 10:30:38 AMI must say the stretch bitblt trick actually works!
I have never heard of that trick...
Code? Or at least a screenshot?  :smiley:

I myself, am starting to learn some things about gdiplus as well.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 11, 2025, 10:37:40 AM
Quote from: zedd151 on April 11, 2025, 10:32:04 AMCode? Or at least a screenshot?  :smiley:
Code, for sure!
Let me isolate the code and create a proper example and I post here for you. I am using it in a larger project.

In short, you upscale what you want and bitBlt it in a back buffer.
The you Stretchblt scaling down to your original scale, but.. using HALFTONE:

inv SetStretchBltMode, [esi].parent_dc, HALFTONE

Tomorrow I'll post an example.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:39:22 AM
Quote from: LordAdef on April 11, 2025, 10:37:40 AM
Quote from: zedd151 on April 11, 2025, 10:32:04 AMCode? Or at least a screenshot?  :smiley:
Code, for sure!
Let me isolate the code and create a proper example and I post here for you. I am using it in a larger project.

In short, you upscale what you want and bitBlt it in a back buffer.
The you Stretchblt scaling down to your original scale, but.. using HALFTONE:

inv SetStretchBltMode, [esi].parent_dc, HALFTONE

Tomorrow I'll post an example.
Oh, ok. That explains enough. No need to post the example unless you want to. Other members especially new ones would probably appreciate it.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 11, 2025, 10:42:20 AM
Quote from: zedd151 on April 11, 2025, 10:39:22 AMOh, ok. That explains enough. No need to post the example unless you want to. Other members especially new ones would probably appreciate it.
We can compare the two side by side. It will be fun.
In my current code, one can set the amount of scale, so it will be interesting
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:45:36 AM
That's not my code but AI generated code via ChatGPT, I just want to make that clear. There are still some errors in it, like mixing .data and .data? Plus gdiplus startup and shutdown code in WM_PAINT would execute every time WM_PAINT is triggered. Not how I would do it. If I have time tomorrow, I'll clean up that code a bit, and use a dialog box instead of a window, my preferred method lately.
 
... we can still compare them...  :smiley:
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 11, 2025, 10:53:36 AM

Got it.
Let's do this!
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 10:56:14 AM
Okay, I'm game.
I learned something else new today, by having ChatGPT show me about anti-aliasing and how it's done with gdiplus. It's a win-win!   :thumbsup:  I helped you and also learned something new for myself in the process  ...  :biggrin:
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 11, 2025, 12:41:19 PM
Quote from: zedd151 on April 11, 2025, 10:45:36 AMNot how I would do it. If I have time tomorrow, I'll clean up that code a bit, and use a dialog box instead of a window...

Here is my version of gdiplus antialiased filled circle, I had some spare time already tonight.

    ; This source code written by zedd151 @ masm32.com
    include \masm32\include\masm32rt.inc

    include \masm32\include\gdiplus.inc
    includelib \masm32\lib\gdiplus.lib

    DlgProc proto :dword, :dword, :dword, :dword

        cwidth              equ 300 ; desired client area width
        cheight            equ 300 ; desired client area height

    .data
        StartupInfo        GdiplusStartupInput <1, 0, 0, 0>
        token              dd 0
        GdiplusStatus      dd 0
        hGraphics          dd 0

        hGdiBrush          dd 0
       
        xPos                REAL4 50.0
        yPos                REAL4 50.0
        circleWidth        REAL4 200.0
        circleHeight        REAL4 200.0
       
    .code

    start proc
    local hInstance:dword
        invoke GetModuleHandle, NULL
        mov hInstance, eax
       
        mov eax, offset StartupInfo
        mov GdiplusStatus, eax
        invoke GdiplusStartup, addr token, addr StartupInfo, 0                  ; Initialize gdiplus
       
        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0              ;; create dialog box, from resource dilalog


        invoke GdiplusShutdown, GdiplusStatus                                  ; Shutdown gdiplus

        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 x:dword, y:dword, wwid:dword, whgt:dword, cwid:dword, chgt:dword
    local mDC:dword, hBmp:dword, hBmp_old: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

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


          ; Create graphics object from HDC
          invoke GdipCreateFromHDC, mDC, addr hGraphics
         
          ; Set anti-aliasing mode
          invoke GdipSetSmoothingMode, hGraphics, 2 ; SmoothingModeAntiAlias = 2
         
          ; Create a solid brush (ARGB = 0xFF0000FF = opaque blue)
          invoke GdipCreateSolidFill, 0FF0000FFh, addr hGdiBrush
         
          ; Fill the ellipse - xPos, yPos, circleWidth, circleHeight are all REAL4 floating point!!
          invoke GdipFillEllipse, hGraphics, hGdiBrush, xPos, yPos, circleWidth, circleHeight

          invoke GdipDeleteBrush, hGdiBrush                                      ; Cleanup hGdiBrush
          invoke GdipDeleteGraphics, hGraphics                                    ; Cleanup hGraphics
         
          ;; ###########################################################

          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

    end

Result
(https://i.postimg.cc/NFfgy53t/filled-circle.png)

Zoomed in 800%
(https://i.postimg.cc/WbzNTqm6/antialias.png)
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 12, 2025, 06:47:19 AM
As a bonus, here is an antialiased filled rectangle.  :smiley:

        ; This source code written by zedd151 @ masm32.com
        include \masm32\include\masm32rt.inc
       
        include \masm32\include\gdiplus.inc
        includelib \masm32\lib\gdiplus.lib
       
        DlgProc        proto :dword, :dword, :dword, :dword

        cwidth          equ 240
        cheight        equ 240

    .data
        gdiplusStartupInput GdiplusStartupInput <1, 0, 0, 0>
        GdiplusToken    dd gdiplusStartupInput
       
        rectX          real4 20.0
        rectY          real4 70.0
        rectWidth      real4 200.0
        rectHeight      real4 100.0
        rectColor      dd 0FF0000FFh ; Blue color (ARGB)
       
    .data?
        hInstance      dd ?
       
        hwndDialog      dd ?
   
    .code
   
    start proc
        ; Get the instance handle
        invoke GetModuleHandle, 0
        mov hInstance, eax
        ; Initialize GDI+
        invoke GdiplusStartup, addr GdiplusToken, addr gdiplusStartupInput, 0
        ; Create and display the dialog box
        invoke DialogBoxParam, hInstance, 100, 0, addr DlgProc, 0
        ; DialogBoxParam returns here after the dialog is closed
        ; Shutdown GDI+
        invoke GdiplusShutdown, GdiplusToken
        invoke ExitProcess, 0
    start endp
   
    ; Dialog procedure
    DlgProc proc hDlg, uMsg, wParam, lParam
    local hdc:dword, ps:PAINTSTRUCT, graphics:dword, brush:dword, rct:RECT
    local hBrush:dword, mDC:dword, hBmp:dword, hBmp_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, hDlg, 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, hDlg, 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, hDlg, x, y, wwid, whgt, TRUE
 
    .elseif uMsg == WM_ERASEBKGND
        mov eax, 1
        ret
    .elseif uMsg == WM_PAINT
        ; Get the HDC for the dialog
        invoke BeginPaint, hDlg, addr ps
        mov hdc, eax
       
        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, hDlg, 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

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

        ; Create a Graphics object from the HDC
        invoke GdipCreateFromHDC, mDC, addr graphics
        ; Set antialiasing
        invoke GdipSetSmoothingMode, graphics, 2
        ; Create a solid brush
        invoke GdipCreateSolidFill, rectColor, addr brush
        ; Draw the rectangle
        invoke GdipFillRectangle, graphics, brush, rectX, rectY, rectWidth, rectHeight
        invoke GdipDeleteBrush, brush
        invoke GdipDeleteGraphics, graphics
         
        ;; ###########################################################

        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, hDlg, addr ps
    .elseif uMsg == WM_CLOSE
        invoke EndDialog, hDlg, 0 ; Use EndDialog for dialog boxes
    .endif
        xor eax, eax
        ret
    DlgProc endp
   
    end

untitled.PNG

Zoomed 800%:
untitled.PNG
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 13, 2025, 06:18:04 AM
Hi guys,
I glued this example a bit in a hurry, but it works at least. Built with UASM. Attached the zip file with source code and .exe

In short, this is a way to use anti aliasing without GDI+. Purely with GDI.
The .exe file in the zip is using an upscale factor of 4. Meaning... how much 'bigger' we are drawing the thing in the back buffer. 

If interested, all the fun happens in the upscale_draw procedure.

GDI tends to be faster than GDI+. But I didn't benchmark comparing the two systems (mine, and Zedd's).


The left circle is in pure GDI. On the right, the same circle with the process applied:

(https://i.postimg.cc/MnsH9dB0/Anti-aliasing.png) (https://postimg.cc/MnsH9dB0)

Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 13, 2025, 06:23:16 AM
That looks pretty good.  :thumbsup:

No worries about the timing. Where I would use it the speed limiting factor are mostly the users mouse clicks. Unless it will be used for a fast paced game. Then the time it takes could be critical to game performance.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: NoCforMe on April 13, 2025, 06:25:30 AM
So it appears that you're really not processing the image at all, apart from scaling it up and back down again, amiright? I mean, you're not applying Bresenham's algorithm or anything like that.

Looks like all the magic happens here
    inv SetStretchBltMode, dc, HALFTONE
due to the HALFTONE parameter.

Pretty slick.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 13, 2025, 06:27:56 AM
Quote from: NoCforMe on April 13, 2025, 06:25:30 AMSo it appears that you're really not processing the image at all, apart from scaling it up and back down again, amiright? I mean, you're not applying Bresenham's algorithm or anything like that.

Looks like all the magic happens here
    inv SetStretchBltMode, dc, HALFTONE
due to the HALFTONE parameter.

Pretty slick.
Yep,

Print the shape upscaled in the back buffer, than print it to your main dc. No processing.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 13, 2025, 06:29:37 AM
Quote from: NoCforMe on April 13, 2025, 06:25:30 AMSo it appears that you're really not processing the image at all
I'd call it minimal processing, since it does do more than nothing.

QuotePretty slick.
Yes, and very good results. I would have to compare several different image sizes between gdi and gdiplus to make a better determination of how good the results are. But so far, looks really good.
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 13, 2025, 06:31:07 AM
Quote from: zedd151 on April 13, 2025, 06:23:16 AMNo worries about the timing. 
It's masm land after all, we want 'fast' :dazzled:
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 13, 2025, 06:34:27 AM
Zedd, I've been doing this for quite a while. But for shapes. 
Two things to bare in mind:

1. line thickness expands inward and outwards, and must be scaled. We need to calculate the offset of this expansion outwards, or it may hit the rect boundary.

2. the Drawback: for larger shapes, you need a larger back buffer. I am currently using it to create buttons
Title: Re: Help with Antialiasing a circle with GDI+
Post by: zedd151 on April 13, 2025, 06:35:29 AM
Ok. brb I gotta go...
Title: Re: Help with Antialiasing a circle with GDI+
Post by: jj2007 on April 13, 2025, 05:21:25 PM
Quote from: LordAdef on April 13, 2025, 06:18:04 AMall the fun happens in the upscale_draw procedure

The docs say you should add this:
    inv SetStretchBltMode, dc, HALFTONE  ;-> downsize back to original anti-aliasing
    inv SetBrushOrgEx, dc, 800, 800, 0
However, I can't see any difference. Well done, Alex :thumbsup:
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 14, 2025, 06:03:33 AM
Quote from: jj2007 on April 13, 2025, 05:21:25 PM
Quote from: LordAdef on April 13, 2025, 06:18:04 AMall the fun happens in the upscale_draw procedure

The docs say you should add this:
    inv SetStretchBltMode, dc, HALFTONE  ;-> downsize back to original anti-aliasing
    inv SetBrushOrgEx, dc, 800, 800, 0
However, I can't see any difference. Well done, Alex :thumbsup:
Thanks Johen!
    inv SetBrushOrgEx, dc, 800, 800, 0

No clue... not broken..didn't fixed it :dazzled:

I am very curious to time both systems and see whats happening
Title: Re: Help with Antialiasing a circle with GDI+
Post by: LordAdef on April 15, 2025, 10:16:38 AM
Hi guys,

I've created a new thread in the Laboratory (since is timing related), where I compare the 2 system.
thread is here: https://masm32.com/board/index.php?topic=12708.0 (https://masm32.com/board/index.php?topic=12708.0)