全部博文(290)
分类:
2007-12-06 22:59:42
Tutorial 4: Painting with Text
In this tutorial, we will learn how to "paint" text in the client area of a window. We'll also learn about device context.
在这一课中,我们将学习如何在窗口的客户区中”绘制”文本(字符串) 。我们还将学习设备描述表。(设备环境)
Theory:
原理:
Text in Windows is a type of GUI object. Each character is composed of numerous pixels (dots) that are lumped together into a distinct pattern. That's why it's called "painting" instead of "writing". Normally, you paint text in your own client area (actually, you can paint outside client area but that's another story). Putting text on screen in Windows is drastically different from DOS. In DOS, you can think of the screen in 80x25 dimension. But in Windows, the screen are shared by several programs. Some rules must be enforced to avoid programs writing over each other's screen. Windows ensures this by limiting painting area of each window to its own client area only. The size of client area of a window is also not constant. The user can change the size anytime. So you must determine the dimensions of your own client area dynamically.
文本在windows中属于GUI对象类型.每一个字符由许多像素组成,这些像素被集中在一起就成为一清晰的图案 .这就是它为什么被认为是”绘制”而不是”写”的原因. 通常,你都在应有程序的的客户区绘制一文本. ( 实际上,你也可以在客户外绘制文本,但那是另外一回事).在Widnows中绘制文本和在DOS中绘制文本是截然不同的.你DOS中,你可以认为屏幕在80*25的一个区域范围内。但是在windows中,屏幕被多个应用程序共享.一些规则必须被执行以便允许应用程序彼此之间能重绘屏幕.windows限制了应用程序只能在它的客户区内绘画从而保证了这一点( 屏幕共享).客户区的大小并不是固定的,用户可以在任何时候改变它.所以你必须动态的求出你自己客户区的面积大小.
Before you can paint something on the client area, you must ask for permission from Windows. That's right, you don't have absolute control of the screen as you were in DOS anymore. You must ask Windows for permission to paint your own client area. Windows will determine the size of your client area, font, colors and other GDI attributes and sends a handle to device context back to your program. You can then use the device context as a passport to painting on your client area.
在你能在客户区上绘制以些东西之前,你必须寻求windows的允许.你在也不能像在DOS中那样对屏幕拥有绝对的控制权. 你必须请求windows允许在你的客户区内绘画.widnows将确定你客户区的大小,字体,颜色和其他图形设备接口的属性并发送一设备描述表句柄给你的程序.你能用设备描述表来达到你在你的客户区绘画的目的.
What is a device context? It's just a data structure maintained internally by Windows. A device context is associated with a particular device, such as a printer or video display. For a video display, a device context is usually associated with a particular window on the display.
什么是描述表呢?它仅仅是windows内部用的一个数据结构.一个设备表和一个特定的设备相关联,例如 打印机或者是显示器.对于显示器而言,一个设备描述表通常和在显示器上的一个特定的窗口相关联.
Some of the values in the device context are graphic attributes such as colors, font etc. These are default values which you can change at will. They exist to help reduce the load from having to specify these attributes in every GDI function calls.
在设备描述表中的值通常是一些图形属性例如,颜色,字体等等.这些系统设定的值你可以任意的改变它. (它们的存在能减少装载,因为在每一个GDI函数引用时都必须指定的这些属性.) /* 这一句不知道翻译的对不对 */
You can think of a device context as a default environment prepared for you by Windows. You can override some default settings later if you so wish.
你可以认为一个设备环境就是windows为你准备的一个默认环境. 稍后,如果你需要,你能替换一些默认值.
When a program need to paint, it must obtain a handle to a device context. Normally, there are several ways to accomplish this.
当你的程序需要绘画时,它必须得到一个设备环境句柄.通常,这里有几种方式可以实现它.
call BeginPaint in response to WM_PAINT message.
在响应WM_PAINY消息的时候调用.
call GetDC in response to other messages.
在响应其他消息的时候调用.
call CreateDC to create your own device context
你可以创建你自己的设备环境.
One thing you must remember, after you're through with the device context handle, you must release it during the processing of a single message. Don't obtain the handle in response to one message and release it in response to another.
有一件事你必须记住,在你抛弃一个设备环境句柄的时候,你必须在处理一个单独的消息过程的时候释放它.在响应一个消息时获得的句柄不要在响应另一个消息的时候释放它.
Windows posts WM_PAINT messages to a window to notify that it's now time to repaint its client area. Windows does not save the content of client area of a window. Instead, when a situation occurs that warrants a repaint of client area (such as when a window was covered by another and is just uncovered), Windows puts WM_PAINT message in that window's message queue.
It's the responsibility of that window to repaint its own client area. You must gather all information about how to repaint your client area in the WM_PAINT section of your window procedure, so the window procudure can repaint the client area when WM_PAINT message arrives.
Windows传递WM_PAINT消息给一个窗口用来通知它现在重绘它的的客户区.widnows并不保存窗口客户区的内容. 它用的方法是“重绘”机制(譬如当客户区刚被另一个应用程序的客户区覆盖),Windows 会把 WM_PAINT 消息放入该窗口的消息队列.窗口的职责就是重绘它自己的客户区.你必须聚集所有的信息 ---关于如何在你的窗口过程的WM_PAINT代码区中重绘你的客户区.
Another concept you must come to terms with is the invalid rectangle. Windows defines an invalid rectangle as the smallest rectangular area in the client area that needs to be repainted. When Windows detects an invalid rectangle in the client area of a window , it posts WM_PAINT message to that window. In response to WM_PAINT message, the window can obtain a paintstruct structure which contains, among others, the coordinate of the invalid rectangle. You call BeginPaint in response to WM_PAINT message to validate the invalid rectangle. If you don't process WM_PAINT message, at the very least you must call DefWindowProc or ValidateRect to validate the invalid rectangle else Windows will repeatedly send you WM_PAINT message.
另外一个概念”无效区域”是你必须妥协的.
windows把在你的客户区中需要被重绘的最小矩形区域定义为一个无效区域.当windows在一个窗口中检测到一个无效区域的时候,它会发送WM_PAINT消息给那个窗口. 在响应WM_PAINT消息的时候,这个窗口能得到一个PAINTSTRUCT 结构,这个结构包含这个无效区的坐标,你可以在响应WM_PAINT的时候调用
BeginPaint 函数来使这个无效区域生效.如果你不想处理WM_PAINT消息,在最后你必须调用DefWindowProc 或者ValidateRect 函数来使无效区域生效.否则系统将重复发送WM_PAINT消息给你.
Below are the steps you should perform in response to a WM_PAINT message:
下面这些步骤你应该在WM_PAINT消息中完成.
Get a handle to device context with BeginPaint.
用BeginPaint获取一个设备环境句柄
Paint the client area.
绘制客户区面积
Release the handle to device context with EndPaint
用EndPaint函数释放设备环境句柄.
Note that you don't have to explicitly validate the invalid rectangle. It's automatically done by the BeginPaint call. Between BeginPaint-Endpaint pair, you can call any GDI functions to paint your client area. Nearly all of them require the handle to device context as a parameter.
注意,你不必要显然的让无效区有效. 它被BeginPaint函数调用自动完成。BeginPaint-Endpaint是一对的,你能调用任何GDI函数来绘制你的客户区。几乎所有的这些函数都需要用设备环境句柄作为参数。
Content:
内容:
We will write a program that displays a text string "Win32 assembly is great and easy!" in the center of the client area.
我们将写一个程序用来在客户区的中间显示一个字符串“win32 assembly is gereat and easy!”
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.DATA
ClassName db "SimpleWinClass",0
AppName db "Our First Window",0
OurText db "Win32 assembly is great and easy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.CODE
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
.IF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax, eax
ret
WndProc endp
end start
Analysis:
分析:
The majority of the code is the same as the example in tutorial 3. I'll explain only the important changes.
这里大多数代码和第三课的例子相同。我仅说明其中不同的地方。
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
These are local variables that are used by GDI functions in our WM_PAINT section. hdc is used to store the handle to device context returned from BeginPaint call. ps is a PAINTSTRUCT structure. Normally you don't use the values in ps. It's passed to BeginPaint function and Windows fills it with appropriate values. You then pass ps to EndPaint function when you finish painting the client area. rect is a RECT structure defined as follows:
这些局部变量被在WM_PAINT消息中的GDI函数调用。Hdc被用来存放调用BeginPaint函数中返回的设备环境句柄。Ps是一个PAINTSTRUCT的结构体。通常你不会用到在PS中的值。它被传递给BeginPaint函数并且由windows用适当的值填满它。当你在客户区中完成绘制时,再传递PS 结构给EndPaint函数。Rect是一个RECT结构,下面是它的定义:
RECT Struct
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
Left and top are the coordinates of the upper left corner of a rectangle Right and bottom are the coordinates of the lower right corner. One thing to remember: The origin of the x-y axes is at the upper left corner of the client area. So the point y=10 is BELOW the point y=0.
Left 和 top 是这个矩形的左上角坐标。Right 和 bottom 是这个矩形的右下角坐标。有一件事你要记住:x-y坐标的起点在客户区的左上角,这样y=10这个点就在y=0这个点的下面。
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
invoke GetClientRect,hWnd, ADDR rect
invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \
DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd, ADDR ps
In response to WM_PAINT message, you call BeginPaint with handle to the window you want to paint and an uninitialized PAINTSTRUCT structure as parameters. After successful call, eax contains the handle to device context. Next you call GetClientRect to retrieve the dimension of the client area. The dimension is returned in rect variable which you pass to DrawText as one of its parameters. DrawText's syntax is:
在响应WM_PAINT消息的时候,你调用BeginPaint函数,并将一个句柄和一个未初始化的PAINTSTRUCT结构传给那个你想绘制的窗口。在成功调用之后,在eax中返回以个设备环境句柄。下一步你调用GetClientRect来得到客户区的大小。客户区的大小被返回在rect变量中,并将rect作为一个参数传递给DrawText函数。Drawtext的句法是:
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText is a high-level text output API function. It handles some gory details such as word wrap, centering etc. so you could concentrate on the string you want to paint. Its low-level brother, TextOut, will be examined in the next tutorial. DrawText formats a text string to fit within the bounds of a rectangle. It uses the currently selected font,color and background (in the device context) to draw the text.Lines are wrapped to fit within the bounds of the rectangle. It returns the height of the output text in device units, in our case, pixels. Let's see its parameters:
DrawText是一个高层的文本输出函数。它能处理诸如文本换行,置于中心等等这些杂事。所以你只要把精力集中在你想绘制的文本上。它的低层调用兄弟,TextOut 将在下一课中讲解。DrawText 在矩形中格式化一个文本字符串。它通常用当前选择的字体,颜色,和背景(在设备环境中)来绘制文本。在这个矩形的内部文本以行为单位。它会返回以设备逻辑单位度量的文本的高度。在我们的例子中,我们的设备逻辑单位是像素 。让我们看看它的参数:
hdc handle to device context
设备环境句柄
lpString The pointer to the string you want to draw in the rectangle. The string must be null-terminated else you would have to specify its length in the next parameter, nCount.
指向你想在矩形中绘制的字符串指针。这个字符串必须以NULL结束,否则你将在下一个参数 nCount中指定它的长度.
nCount The number of characters to output. If the string is null-terminated, nCount must be -1. Otherwise nCount must contain the number of characters in the string you want to draw.
需要输出的字符数.如果这个字符窜时以NULL结尾的.nCount必须是-1 否则nCount必须包含你想绘制的字符串的字符数.
lpRect The pointer to the rectangle (a structure of type RECT) you want to draw the string in. Note that this rectangle is also a clipping rectangle, that is, you could not draw the string outside this rectangle.
指向一个你想在它内部绘制字符的矩形的指针( 一个RECT 的结构指针)。注意这个矩形是一个受限制的矩形。也就是,你不能将字符画在这个矩形外。
uFormat The value that specifies how the string is displayed in the rectangle. We use three values combined by "or" operator:
这个值指定字符串如何在显示在矩形中,我们可以用or操作来讲三个值组合在一起。
指定文本单行显示。
文本水平居中
文本垂直居中,必须和DT_SINGLELINE 一起使用 .
After you finish painting the client area, you must call EndPaint function to release the handle to device context.
在你完成客户区的绘制工作后,你必须调用EndPaint函数来释放这个设环境句柄.
That's it. We can summarize the salient points here:
我们在这里总结一下绘制文本重要的几点:
你在WM_PAINT消息中调用 BeginPaint-EndPaint这对函数来响应它。
在BeginPaint和EndPaint两个调用之间做任何你想在客户区中做的事。
如果你想在其它消息中充绘你的客户区,你必须注意两点:
你的绘制工作必须用在GetDC 和ReleaseDC这两个函数之间。
调用InvalidataRect 或者是UpdataWindow 来使整个客户区无效。强制windows放置一个消息在你窗口的消息队列中并在WM_PAINT消息中绘制你想绘制的任何东西。
This article come from Iczelion's asm page
风向改变翻译于