| C# GDI+ | Win32 GDI | 用途 |
Graphics.FillPie | Pie | 繪製填充扇形 (半圓) |
Graphics.FillEllipse | Ellipse + 畫刷 | 繪製填充橢圓/圓 |
Brushes.White/Black | GetStockObject(WHITE_BRUSH/BLACK_BRUSH) | 取得系統預設畫刷 |
Pen | CreatePen / GetStockObject | 建立/取得畫筆 |
| - | SelectObject | 將 GDI 物件選入 DC |
| - | MoveToEx/LineTo | 繪製直線 (修飾細節) |
核心繪圖程序:_PaintTaiJi
_PaintTaiJi proc
LOCAL hBrush:HBRUSH ; 黑畫刷
LOCAL wBrush:HBRUSH ; 白畫刷
LOCAL hPen:HPEN ; 白畫筆
; 1. 繪製外圓邊框
invoke Ellipse, hDc, 0, 0, 200, 200
; 2. 取得系統預設畫刷
invoke GetStockObject, BLACK_BRUSH
mov hBrush, eax
invoke GetStockObject, WHITE_BRUSH ; 注意:原碼用 DC_BRUSH 非標準,此處修正
mov wBrush, eax
; 3. 左半扇形 (黑)
invoke SelectObject, hDc, hBrush
invoke Pie, hDc, 0, 0, 200, 200, 100, 0, 100, 200
; 4. 右下小圓 (白) - 修飾右半圓邊界
invoke Ellipse, hDc, 50, 100, 150, 200
; 5. 切換白畫刷
invoke SelectObject, hDc, wBrush
; 6. 右上小扇形 (白)
invoke Pie, hDc, 50, 0, 150, 100, 100, 0, 100, 100
; 7. 上小白圓 (陰中之陽)
invoke Ellipse, hDc, 95, 145, 105, 155
; 8. 擦除上小圓殘留邊框 (白線覆蓋)
invoke MoveToEx, hDc, 100, 1, NULL
xor eax, eax
mov ah, 255
mov al, 255
shl eax, 8
mov al, 255 ; eax = 0x00FFFFFF (白色)
invoke CreatePen, PS_SOLID, 2, eax
mov hPen, eax
invoke SelectObject, hDc, hPen
invoke LineTo, hDc, 99, 99
; 9. 下小黑圓 (陽中之陰)
invoke SelectObject, hDc, hBrush
invoke Ellipse, hDc, 95, 45, 105, 55
ret
_PaintTaiJi endp
完整 MASM32 專案程式碼
.386
.model flat, stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include gdi32.inc
includelib gdi32.lib
.data?
hInstance dd ?
hWinMain dd ?
stPs PAINTSTRUCT <>
stRect RECT <>
hDc dd ?
.const
szClassName db 'MyTaiJi', 0
szCaptionMain db '組合語言繪製太極八卦圖', 0
.code
;--- 視窗程序 ---
_ProcWinMain proc uses ebx edi esi, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
mov eax, uMsg
.if eax == WM_PAINT
invoke BeginPaint, hWnd, addr stPs
mov hDc, eax
invoke GetClientRect, hWnd, addr stRect
call _PaintTaiJi
invoke EndPaint, hWnd, addr stPs
.elseif eax == WM_CLOSE
invoke DestroyWindow, hWinMain
invoke PostQuitMessage, NULL
.else
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax, eax
ret
_ProcWinMain endp
;--- 建立視窗與訊息迴圈 ---
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke RtlZeroMemory, addr @stWndClass, sizeof @stWndClass
invoke LoadCursor, NULL, IDC_ARROW
mov @stWndClass.hCursor, eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize, sizeof WNDCLASSEX
mov @stWndClass.style, CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc, offset _ProcWinMain
mov @stWndClass.hbrBackground, COLOR_WINDOW + 1
mov @stWndClass.lpszClassName, offset szClassName
invoke RegisterClassEx, addr @stWndClass
invoke CreateWindowEx, WS_EX_CLIENTEDGE, \
offset szClassName, offset szCaptionMain, \
WS_OVERLAPPEDWINDOW, \
100, 100, 600, 400, \
NULL, NULL, hInstance, NULL
mov hWinMain, eax
invoke ShowWindow, hWinMain, SW_SHOWNORMAL
invoke UpdateWindow, hWinMain
.while TRUE
invoke GetMessage, addr @stMsg, NULL, 0, 0
.break .if eax == 0
invoke TranslateMessage, addr @stMsg
invoke DispatchMessage, addr @stMsg
.endw
ret
_WinMain endp
;--- 程式入口 ---
start:
call _WinMain
invoke ExitProcess, NULL
end start
關鍵技術細節
- 座標系統:GDI 使用邏輯座標,原點在客戶區左上角,X 向右、Y 向下
- Pie 參數:`Pie(hdc, x1, y1, x2, y2, x3, y3, x4, y4)` 以矩形 `(x1,y1)-(x2,y2)` 為邊界,`(x3,y3)` 為起始弧端點,`(x4,y4)` 為結束弧端點
- GDI 物件管理:`SelectObject` 返回舊物件,繪圖結束應恢復 (本範例為簡化省略)
- 色彩格式:`CreatePen` 參數為 `COLORREF` (0x00BBGGRR),白色 = 0x00FFFFFF
- 編譯環境:MASM32 v8+ / RadASM / Windows XP SP2 以上
執行結果

延伸練習
- 加入 `WM_SIZE` 處理,支援視窗縮放時圖形自適應
- 使用 `SetWorldTransform` 實現旋轉動畫
- 嘗試用 `Path` API (BeginPath/EndPath/StrokeAndFillPath) 重構繪圖邏輯
- 移植至 x64 組合語言 (調整呼叫慣例、暫存器使用)
- 上一篇文章: 直流開關電源保護電路設計全解析:過流、過壓、軟啟動與過熱四重防護
- 下一篇文章: MASM32 實作視窗淡入淡出效果 (AnimateWindow API)
Copyright ©
程式碼之家 All Rights Reserved