На этом шаге мы рассмотрим общие принципы работы с графикой.
Этот шаг посвящается графике. Впрочем, основы графики в Windows, в принципе, достаточно тривиальны, поэтому мы рассмотрим один простой пример - на вывод графических образов. Но в начале мы изложим некоторые опорные моменты:
После всего сказанного пора продемонстрировать программу. Программа достаточно проста, но в ней заложены основы работы с графикой. По щелчку левой кнопки мыши сначала появляется горизонтальная линия, по второму щелчку - наклонная линия, по третьему щелчку - заполненный прямоугольник. Результат работы программы приведен на рисунке 1.
Рис.1. Результат работы приложения
;Константы. ;Сообщение приходит при закрытии окна. WM_DESTROY equ 2 ;Сообщение приходит при создании окна. WM_CREATE equ 1 ;Сообщение при щелчке левой кнопкой мыши в области окна. WM_LBUTTONDOWN equ 201h ;Сообщение приходит при перерисовке окна. WM_PAINT equ 0Fh ;Свойства окна. CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000h stylcl equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS DX0 equ 600 DY0 equ 400 ;Компоненты цветов. RGBW equ (50 or (50 shl 8)) or (255 shl 16);Цвет окна. RGBR equ 150 ;Цвет региона. RGBL equ 0 ;Цвет линии. RGBP equ 255 or (100 shl 8);Цвет точки. ;Идентификатор стандартной пиктограммы. IDI_APPLICATION equ 32512 ;Идентификатор курсора. IDC_CROSS equ 32515 ;Режим показа окна - нормальный. SW_SHOWNORMAL equ 1 ;Прототипы внешних процедур. EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN BeginPaint@8:NEAR EXTERN EndPaint@8:NEAR EXTERN GetStockObject@4:NEAR EXTERN CreateSolidBrush@4:NEAR EXTERN GetSystemMetrics@4:NEAR EXTERN GetDC@4:NEAR EXTERN CreateCompatibleDC@4:NEAR EXTERN SelectObject@8:NEAR EXTERN CreateCompatibleBitmap@12:NEAR EXTERN PatBlt@24:NEAR EXTERN BitBlt@36:NEAR EXTERN ReleaseDC@8:NEAR EXTERN DeleteObject@4:NEAR EXTERN InvalidateRect@12:NEAR EXTERN DeleteDC@4:NEAR EXTERN CreatePen@12:NEAR EXTERN SetPixel@16:NEAR EXTERN LineTo@12:NEAR EXTERN MoveToEx@16:NEAR EXTERN Rectangle@20:NEAR ;Структуры ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS ;----------------------- WNDCLASS STRUC CLSSTYLE DD ? ;Стиль окна. CLWNDPROC DD ? ;Указатель на процедуру окна. CLSCBCLSEX DD ? ;Информация о доп. байтах для данной структуры. CLSCBWNDEX DD ? ;Информация о доп. байтах для окна. CLSHINST DD ? ;Дескриптор приложения. CLSHICON DD ? ;Идентификатор иконки окна. CLSHCURSOR DD ? ;Идентификатор курсора окна. CLBKGROUND DD ? ;Идентификатор кисти окна. CLMENNAME DD ? ;Имя-идентификатор меню. CLNAME DD ? ;Специфицирует имя класса окон. WNDCLASS ENDS ;-------------------------------- PAINTSTR STRUC hdc DWORD 0 fErase DWORD 0 left DWORD 0 top DWORD 0 right DWORD 0 bottom DWORD 0 fRes DWORD 0 fIncUp DWORD 0 Reserv DB 32 dup(0) PAINTSTR ENDS ;-------------------------- RECT STRUC L DWORD ? ;X - левого верхнего угла. T DWORD ? ;Y - левого верхнего угла. R DWORD ? ;Х - правого нижнего угла. B DWORD ? ;Y - правого нижнего угла. RECT ENDS
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr22_1.asm ;Директивы компоновщику для подключения библиотек. includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ;------------------------------------------------ ;Сегмент данных. _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> PNT PAINTSTR <?> HINST DD 0 ;Здесь хранится дескриптор приложения. TITLENAME BYTE 'Графика в окне',0 NAM BYTE 'CLASS32',0 XT DWORD 30 YT DWORD 30 XM DWORD ? YM DWORD ? HDC DWORD ? MEMDC DWORD ? HPEN DWORD ? HBRUSH DWORD ? P DWORD 0 ;Признак вывода. XP DWORD ? YP DWORD ? _DATA ENDS ;Сегмент кода. _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ;Получить дескриптор приложения. PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ;Заполнить структуру окна. ;Стиль. MOV [WC.CLSSTYLE],stylcl ;Процедура обработки сообщений. MOV [WC.CLWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEX], 0 MOV [WC.CLSCBWNDEX], 0 MOV EAX, [HINST] MOV [WC.CLSHINST], EAX ;------------ пиктограмма окна PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;------------ курсор окна PUSH IDC_CROSS PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;------------ PUSH RGBW ;Цвет кисти. CALL CreateSolidBrush@4;Создать кисть. MOV [WC.CLBKGROUND],EAX MOV DWORD PTR [WC.CLMENNAME],0 MOV DWORD PTR [WC.CLNAME], OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ;Создать окно зарегистрированного класса. PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DY0 - высота окна. PUSH DX0 ; DX0 - ширина окна. PUSH 100 ; Координата Y. PUSH 100 ; Координата X. PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ;Имя окна. PUSH OFFSET NAM ;Имя класса. PUSH 0 CALL CreateWindowExA@48 ;Проверка на ошибку. CMP EAX,0 JZ _ERR MOV [NEWHWND], EAX ;Дескриптор окна. ;------------------------------------ PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ;Показать созданное окно. ;------------------------------------ PUSH [NEWHWND] CALL UpdateWindow@4 ;Команда перерисовать видимую ;часть окна, сообщение WM_PAINT. ;Цикл обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP EAX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ;Выход из программы (закрыть процесс). PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;----------------------------------------- ;Процедура окна. ;Расположение параметров в стеке: ;[ЕВР+14Н] LPARAM ;[ЕВР+10Н] WPARAM ;[ЕВР+0СН] MES ;[ЕВР+08H] HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH], WM_PAINT JE WMPAINT CMP DWORD PTR [EBP+0CH], WM_LBUTTONDOWN JE LBUTTON JMP DEFWNDPROC LBUTTON: CMP P,0 ;Какой щелчок левой кнопки мыши? JNE F1 ;Линия точками (горизонтальная). MOV YP,50 ;Y MOV XP,10 ;X MOV ECX,200 ;Длина линии. LL: PUSH ECX PUSH RGBP PUSH YP PUSH XP PUSH MEMDC CALL SetPixel@16 INC XP POP ECX LOOP LL INC P JMP F3 F1: CMP P,1 JNE F2 ;Ввначале установим текущие координаты на конец предыдущей линии. PUSH 0 PUSH YP PUSH XP PUSH MEMDC CALL MoveToEx@16 ;Линия пером. PUSH 300 ;Конечные координаты. PUSH 550 PUSH MEMDC CALL LineTo@12 INC P JMP F3 F2: CMP P,2 JNE FIN ;Замкнутая фигура - прямоугольник. ;Вначале выбрать кисть для заполнения области. PUSH HBRUSH PUSH MEMDC CALL SelectObject@8 ;Теперь рисуем заполненный прямоугольник. ;Если не выбирать кисть, то будет нарисован незаполненный прямоугольник. PUSH 350 PUSH 400 PUSH 200 PUSH 200 PUSH MEMDC CALL Rectangle@20 INC P F3: ;Дать команду перерисовать окно. PUSH 0 PUSH OFFSET RECT PUSH DWORD PTR [EBP+08H] CALL InvalidateRect@12 FIN: MOV EAX, 0 JMP FINISH WMPAINT: ;-------------------------- PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL BeginPaint@8 MOV HDC,EAX ;Сохранить контекст (дескриптор) ;Скопировать виртуальное окно на реальное. PUSH 0CC0020h ;SRCCOPY=Изображение как есть PUSH 0 ;у источника. PUSH 0 ;х источника. PUSH MEMDC ;Контекст источника. PUSH YM ;Высота куда. PUSH XM ;Ширина куда. PUSH 0 ;у куда. PUSH 0 ;х куда. PUSH HDC ;Контекст куда. CALL BitBlt@36 ;---------------------- закрыть контекст окна PUSH OFFSET PNT PUSH DWORD PTR [EBP+08H] CALL EndPaint@8 MOV EAX, 0 JMP FINISH WMCREATE: ;Размеры экрана. PUSH 0 ;X CALL GetSystemMetrics@4 MOV XM,EAX PUSH 1 ;Y CALL GetSystemMetrics@4 MOV YM,EAX ;Открыть контекст окна. PUSH DWORD PTR [EBP+08H] CALL GetDC@4 MOV HDC,EAX ;Создать совместимый с данным окном контекст. PUSH EAX CALL CreateCompatibleDC@4 MOV MEMDC,EAX ;Создать в памяти растровое изображение, совместимое с hdc. PUSH YM PUSH XM PUSH HDC CALL CreateCompatibleBitmap@12 ;Выбрать растровое изображение в данном контексте. PUSH EAX PUSH MEMDC CALL SelectObject@8 ;Цвет кисти PUSH RGBW CALL CreateSolidBrush@4 ;Создать кисть. ;Выбрать кисть в данном контексте. PUSH EAX PUSH MEMDC CALL SelectObject@8 ;Заполнить данную прямоугольную область. PUSH 0F00021h ;PATCOPY=заполнить данным цветом. PUSH YM PUSH XM PUSH 0 PUSH 0 PUSH MEMDC CALL PatBlt@24 ;Создать кисть и перо для рисования. PUSH RGBR CALL CreateSolidBrush@4 ;Создать кисть. MOV HBRUSH,EAX ;Задать перо. PUSH RGBR ;Цвет. PUSH 0 ;Толщина=1 PUSH 0 ;Сплошная линия. CALL CreatePen@12 MOV HPEN,EAX ;Удалить контекст. PUSH HDC PUSH DWORD PTR [EBP+08H] CALL ReleaseDC@8 MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: ;Удалить перо. PUSH HPEN CALL DeleteDC@4 ;Удалить кисть. PUSH HBRUSH CALL DeleteDC@4 ;Удалить виртуальное окно. PUSH MEMDC CALL DeleteDC@4 ;Выход. PUSH 0 ;MB_OK CALL PostQuitMessage@4 ;Сообщение WM_QUIT. MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
Рассказ о графическом выводе будет неполным, если не коснуться вопроса о манипулировании растровыми изображениями. Рассмотрим последовательность действий, которые необходимо выполнить для вывода растрового изображения, примеры вывода будут даны в последующих шагах.
С растровыми изображениями удобно работать при помощи ресурсов, но их можно создавать непосредственно в программе или считывать из файла. Но об этом позднее.
Со следующего шага мы начнем рассматривать консольные приложения.