⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 见招拆招 windows 程序设计 (四) .txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 5 页
字号:
mov eax,msg.wParam
ret
WinMain endp

WndProc proc hwnd:DWORD,message:DWORD,wParam :DWORD,lParam :DWORD

LOCAL hdc :HDC
LOCAL i :DWORD
LOCAL ps :PAINTSTRUCT 
.if message == WM_SIZE
mov eax,lParam ;cxClient = LOWORD (lParam)
and eax,0FFFFh
mov cxClient,eax

mov eax,lParam
shr eax,16
mov cyClient,eax ;cyClient = HIWORD (lParam)
ret 
.elseif message == WM_PAINT
invoke BeginPaint,hwnd,addr ps
mov hdc,eax

;MoveToEx (hdc, 0, cyClient / 2, NULL) 
mov ebx,cyClient
shr ebx,1
push ebx
invoke MoveToEx,hdc,0,ebx,NULL
pop ebx
;LineTo (hdc, cxClient, cyClient / 2) ; 
invoke LineTo,hdc,cxClient,ebx

; for (i = 0 ; i < NUM ; i++)
; { apt[i].x = i * cxClient / NUM ;
; apt[i].y = (int) (cyClient / 2 * (1 - sin (TWOPI * i / NUM))) ;
; }

fninit 
mov i,0
xor ebx,ebx
@@:
;apt[i].y = (int) (cyClient / 2 * (1 - sin (TWOPI * i / NUM))) 
fild i ; st = i
fidiv NUM ; st = i/NUM

fild cxClient ; st=cxClient, i/NUM 
fmul st,st(1) ; i*cxClient/NUM, i/NUM
fistp apt[ebx].x ; Set x coord, leaving st=i/NUM

fmul TWOPI ; st=2Pi*i/NUM
fsin ; st=sine(2*Pi*i/NUM) 
fld1 ; st=1.0, st(1)=sine(2*Pi*i/NUM)
fsubr ; st=(1.0 - sine(2*Pi*i/NUM) )

fimul cyClient ; st=(cyClient)*(1-sin(2*Pi*i/NUM) ) 
fistp apt[ebx].y ; Set y coord
shr apt[ebx].y,1 ; (cyClient/2)*(1-sin(2*Pi*i/NUM) )

;invoke wsprintf,addr szBuffer,CTXT("%d %d"),apt[ebx].x,apt[ebx].y
;invoke MessageBox,hwnd,addr szBuffer,NULL,NULL 

inc i
add ebx,8
cmp i,1000
jb @b

invoke Polyline, hdc, ADDR apt, NUM ; Draw sinewave with NUM points 
invoke EndPaint,hwnd,addr ps
ret
.elseif message == WM_DESTROY

invoke PostQuitMessage,NULL 
ret 
.endif 

invoke DefWindowProc,hwnd, message, wParam, lParam
ret
WndProc endp
END START

上述程序参考了“Ron Thomas Ron_Thom@Compuserve.com
www.rbthomas.freeserve.co.uk 31/3/99 ”的程序,不过他的程序貌只能显示一半的Sine函数,遂做部分修改。

    这个程序有一个含有1000个POINT结构的数组。随着环从0增加到999,结构的x成员设定为从0递增到数值cxClient。结构的y成员设定为一个周期的正弦曲线值,并被放大以填满显示区域。整个曲线的绘制仅仅使用了一个Polyline呼叫。因为Polyline函数是在设备驱动程序层次上实作的,因此它要比呼叫1000次LineTo快得多,结果如图5-5所示。



 图5-5 SINEWAVE显示 

边界框函数
    下面我想讨论的是Arc函数,它绘制椭圆曲线。然而,如果不先讨论一下Ellipse函数,那么Arc函数将难以理解;而如果不先讨论Rectangle函数,那么Ellipse函数又将难以理解;而如果讨论Ellipse和Rectangle函数,那么我又会讨论RoundRect、Chord和Pie函数。

    问题在于,Rectangle、Ellipse、RoundRect、Chord和Pie函数严格来说不是画线函数。没错,这些函数是在画线,但它们同时又填入画刷填入一个封闭区域。这个画刷内定为白色,因此当您第一次使用这些函数时,您可能不会注意到它们不只是画线。严格地说,这些函数属于后面「填入区域」的小节,不过,我还是在这里讨论它们。

    上面提到的函数有一个共同特性,即它们都是依据一个矩形边界框来绘图的。您定义一个包含该对象的框,即「边界框(bounding box)」;Windows就在这个框内画出该物件。

    这些函数中最简单的就是画一个矩形:

invoke Rectangle ,hdc, xLeft, yTop, xRight, yBottom
   点(xLeft, yTop)是矩形的左上角,(xRight, yBottom)是矩形的右下角。用函数Rectangle画出的图形如图5-6所示,矩形的边总是平行于显示器的水平和垂直边。

 


图5-6 使用Rectangle函数画出的图形

    以前写过图形程序的程序写作者熟悉像素偏差的问题。有些图形系统画出的图形包含右坐标和底坐标,而有些则只画到(而不包含)右坐标和底坐标。Windows采用后一种方法,不过有一种更简单的方法来思考这个问题。

    考虑下面的函数呼叫:
invoke Rectangle,hdc, 1, 1, 5, 4

    上面我们提到,Windows在边界框内画图。可以将显示器想象成一个网格,其中,每个像素都在一个网格单元内。边界框画在网格上,然后在边界框内画矩形,下面说明了图形画出来时的样子:
 

    将矩形和显示区域左上角分开的区域有l个像素宽。
我以前提到过,Rectangle严格地说不是画线函数,GDI也填入封闭区域。然而,因为内定用白色填入区域,因此GDI填入区域并不明显,看起来仿佛只有单纯的线。不过,有时候用户可能并非使用“白色”作为背景,比如我就设定了一个据说是“保护视力”的颜色作为应用程序的背景:

    

    您知道了如何画矩形,也就知道了如何画椭圆,因为它们使用的参数都是相同的:

invoke Ellipse,hdc, xLeft, yTop, xRight, yBottom

    用Ellipse函数画出的图形如图5-7所示(加上了虚线构成的边界框)。

 

图5-7 用Ellipse函数画出的图形

    画圆角矩形的函数使用与函数Rectangle及Ellipse函数相同的边界框,还包含另外两个参数:

invoke RoundRect,hdc, xLeft, yTop, xRight, yBottom,xCornerEllipse, yCornerEllipse

    用这个函数画出的图形如5-8所示。

 

   图5-8 用RoundRect函数画出的图形

    Windows使用一个小椭圆来画圆角,这个椭圆的宽为xCornerEllipse,高为yCornerEllipse。可以想象这个小椭圆分为了四个部分,一个象限一个,每个刚好用在矩形的一个角上。xCornerEllipse和yCornerEllipse的值越大,角就越明显。如果xCornerEllipse等于xLeft与xRight的差,且yCornerEllipse等于yTop与yBottom的差,那么RoundRect函数将画出一个椭圆。

   在绘制图5-8所示的圆角矩形时,用了下面的公式来计算角上椭圆的尺寸。

xCornerEllipse = (xRight - xLeft) / 4 ;
yCornerEllipse = (yBottom- yTop) / 4 ;

   这是一种简单的方法,但是结果看起来有点不对劲,因为角的弯曲部分在矩形长的一边要大些。要矫正这一问题,您可以让xCornerEllipse与yCornerEllipse的值相等(可以想象一下当椭圆的长轴和短轴差值最小时,椭圆变成什么样子)。

Arc、Chord和Pie函数都只要相同的参数:

invoke Arc,hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd
invoke Chord ,hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd
invoke Pie,hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd
    用Arc函数画出的线如图5-9所示;用Chord和Pie函数画出的线分别如图5-10和5-11所示。Windows用一条假想的线将(xStart, yStart)与椭圆的中心连接,从该线与边界框的交点开始,Windows按反时针方向,沿着椭圆画一条弧。Windows还用另一条假想的线将(xEnd,yEnd)与椭圆的中心连接,在该线与边界框的交点处,Windows停止画弧。
 

图5-9 Arc函数画出的线
 

图5-10 Chord函数画出的线


图5-11 Pie函数画出的线

   对于Arc函数,这样就结束了。因为弧只是一条椭圆形的线而已,而不是一个填入区域。对于Chord函数,Windows连接弧线的端点。而对于Pie函数,Windows将弧的两个端点与椭圆的中心相连接。弦与扇形图的内部以目前画刷填入。

    您可能不太明白在Arc、Chord和Pie函数中开始和结束位置的用法,为什么不简单地在椭圆的周在线指定开始和结束点呢?是的,您可以这么做,但是您将不得不算出这些点。Windows的方法在不要求这种精确性的条件下,却完成了相同的工作。

    程序5-3 LINEDEMO画一个矩形、一个椭圆、一个圆角矩形和两条线段,不过不是按这一顺序。程序表明了定义封闭区域的函数实际上对这些区域进行了填入,因为在椭圆后面的线被遮住了,结果如图5-12中所示。
程序5-3 LINEDEMO

LINEDEMO.ASM

;MASMPlus 代码模板 - 普通的 Windows 程序代码

.386
.Model Flat, StdCall
Option Casemap :None

Include windows.inc
Include user32.inc
Include kernel32.inc
Include gdi32.inc

includelib gdi32.lib
IncludeLib user32.lib
IncludeLib kernel32.lib
include macro.asm

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD


.DATA
szAppName db "LineDemo",0

.DATA?
hInstance dd ?
cxClient dd ?
cyClient dd ?
.CODE
START: ;从这里开始执行

invoke GetModuleHandle,NULL
mov hInstance,eax
invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT
invoke ExitProcess,0

WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,iCmdShow:DWORD
LOCAL wndclass :WNDCLASSEX
LOCAL msg :MSG
local hWnd :HWND
mov wndclass.cbSize,sizeof WNDCLASSEX 
mov wndclass.style,CS_HREDRAW or CS_VREDRAW 
mov wndclass.lpfnWndProc,offset WndProc

mov wndclass.cbClsExtra,0
mov wndclass.cbWndExtra,0

push hInst
pop wndclass.hInstance

invoke LoadIcon,NULL,IDI_APPLICATION
mov wndclass.hIcon,eax 

invoke LoadCursor,NULL,IDC_ARROW
mov wndclass.hCursor,eax 

invoke GetStockObject,WHITE_BRUSH
mov wndclass.hbrBackground,E

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -