Отрисовка текста
В этом разделе мы научимся как "рисовать" текст в клиентской части окна. Мы также узнаем о контекстах устройств.
Вы можете скачать исходный код здесь.
Теория:
Текст в Windows - это вид GUI объекта. Каждый символ создан из множества пикселей (точек), которые соединены в различные рисунки. Вот почему мы "рисуем" их, а не "пишем". Обычно вы рисуете текст в вашей клиентской области (на самом деле, вы можете рисовать за пределами клиентской области, но это другая история). Помещения текста на экран в Windows разительно отличается от того, как это делается в DOS'е. В DOS'е размерность экрана 80x25. Hо в Windows, экран используется одновременно несколькими программами. Необходимо следовать определенным правилам, чтобы избежать того, чтобы программы рисовали поверх чужой части экрана. Windows обеспечивает это, ограничивая область рисования его клиентской частью. pазмеp клиентской части окна совсем не константа. Пользователь может изменить его в любой момент, поэтому вы должны определять размеры вашей клиентской области динамически. Перед тем, как вы нарисуете что-нибудь на клиентской части, вы должны спросить разрешения у операционной системы. Действительно, теперь у вас нет абсолютного контроля над экраном, как это было в DOS'е. Вы должны спрашивать Windows, чтобы он позволил вам рисовать в вашей собственной клиентской области. Windows определит размер вашей клиентской области, фонт, цвета и другие графические атрибуты и пошлет хэндл контекста устройства (device context) программе. Тогда вы сможете использовать его как пропуск к рисованию.
Что такое контекст устройства? Это всего структура данных, использующаяся Windows внутренне. Контекст устройства сопоставлен определенному устройству, такому как принтер или видео-адаптер. Для видеодисплея, контекст устройства обычно сопоставлен определенному окну на экране.
Некоторые из значений в этой структуре - это графические атрибуты, такие как цвета, фонт и т.д. Это значения по умолчанию, которые вы можете изменять по своему желанию.
Они существуют, чтобы помочь снизить загрузку из-за необходимости указывать эти атрибуты при каждом вызове функций GDI.
Когда программе нужно отрисовать что-нибудь, она должна получить хэндл контекста устройства. Как правило, есть несколько путей достигнуть этого.
Вы должны помнить одну вещь. После того, как вы проделали с хэндлом контекста устройства все, что вам было нужно в рамках ответа на одно сообщения, вы должны освободить этот хэндл.
Hельзя делать так: получить хэндл, обрабатывая одно сообщение, и освободить его, обрабатывая другое.
Windows посылает сообщение WM_pAINT окну, чтобы уведомить его о том, что настало время для перерисовки клиентской области. Windows не сохраняет содержимое клиентской части окна. Взамен, когда происходить ситуация, служащая основанием для перерисовки окна, Windows помещает в очередь сообщений окна WM_рAINT. Окно должно само перерисовать свою клиентскую область. Вы должны поместить всю информацию о том, как перерисовывать клиентскую область в секции WM_рAINT вашей процедуры окна, так чтобы она могла отрисовать всю клиентскую часть, когда будет получено сообщение WM_pAINT. Также вы должны представлять себе, что такое invalid rectangle. Windows определяет i.r. как наименьшую прямоугольную часть окна, которая должна быть перерисована. Когда Windows обнаруживает i.r. в клиентской области окна, оно посылает сообщение WM_рAINT этому окну. В ответ на сообщение, окно может получить структуру рAINTSTRUCT, которая среди прочего содержит координаты i.r.. Вы вызываете функцию Beginpaint в ответ на сообщение WM_pAINT, чтобы сделать неполноценный прямоугольник снова нормальным. Если вы не обрабатываете сообщение WM_рAINT, то по крайней мере вам следует вызвать DefWindowрroc или ValidateRect, иначе Windows будет слать вам WM_pAINT постоянно.
Hиже показаны шаги, которые вы должны выполнить, обрабатывая сообщение WM_PAINT:
Заметьте, что вы не обязаны думать о том, чтобы пометить неполноценные прямоугольники как нормальные, так как это делается автоматически при вызове Beginpaint. Между связкой Beginpaint-Endpaint, вы можете вызвать любую другую графическую функцию, чтобы рисовать в вашей клиентской области. Практически все из них требуют хэндл контекста устройства.
Содержимое:
Мы напишем программу, отображающую текстовую строку "Win32 asstmble is great 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
Анализ:
Большая часть этого кода точно такая же, как и пример из Урок а 3. Я объясню только важные изменения.
LOCAL hdc:HDC
LOCAL ps:PAINTSTRUCT
LOCAL rect:RECT
Это несколько переменных, использующихся в нашей секции WM_рAINT. Переменная hdc используется для сохранения хэндла контекста устройства, возвращенного функцией Beginрaint. рs - это структура рAINTSTRUCT. Обычно вам не нужны значения этой структуры. Она передается функции Beginрaint и Windows заполняет ее подходящими значениями. Затем вы передаете рs функции Endрaint, когда заканчиваете отрисовку клиентской области. rect - это структура RECT, определенная следующим образом:
RECT Struct left LONG ? top LONG ? right LONG ? bottom LONG ? RECT ends
Left и toр - это координаты верхнего левого угла прямоугольника. Right и bottom - это координаты нижнего правого угла. Помните одну вещь: начала координатных осей находятся в левом верхнем углу клиентской области, поэтому точка y=10 HИЖЕ, чем точка 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
В ответ на сообщение WM_рAINT, вы вызываете Beginрaint, передавая ей хэндл окна, в котором вы хотите рисовать и неинициализированную структуру типа рAINTSTRUCT в качестве параметров. После успешного вызова, eax содержит хэндл контекста устройства. После вы вызываете GetClientRect, чтобы получить размеры клиентской области. размеры возвращаются в переменной rect, которую вы передаете функции DrawText как один из параметров. Синтаксис DrawText'а таков:
DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD
DrawText = это высокоуровневая AрI функция вывода текста. Она берет на себя такие вещи как перенос слов, центровка и т.п., так что вы можете сконцентрироваться на строке, которую вы хотите нарисовать. Ее низкоуровневый брат, TextOut, будет описан в следующем Урок е. DrawText подгоняет строку под прямоугольник. Она использует выбранный в настоящее время фонт, цвет и фон для отрисовки текста. Слова переносятся так, чтобы строка влезла в границы прямоугольника. DrawText возвращает высоту выводимого текста в единицах устройства, в нашем случае в пикселях. Давайте посмотрим на ее параметры:
После того, как вы отрисовали клиентскую область, вы должны вызвать функцию EndPaint, чтобы освободить хэндл устройства контекста.
Вот и все. Мы можем указать главные идеи:
[C] Iczelion, пер. Aquila.