20120525

Про рисование.

Сижу, разбираюсь с графикой. Ну, вы знаете - тема будущих уроков, всякие там wallhack и прочие радости.

Очень сложный материал, особенно для подачи, мне бы самому разобраться как следует. С чего начать - пока думаю, скорее всего, покодим немного самостоятельно - сначала порисуем под Windows при помощи GDI\GDI+ (почитать про это можно [тут]), затем будем осваивать [DirectX] и [OpenGL].

Если очень вкратце, то GDI (и его расширение, GDI+) - это интерфейс (набор функций, но если хочется подробностей - [пожалуйста]) для работы с двухмерной (плоской) графикой в винде. Грубо говоря всё, что вы видите на мониторе, рисуется при помощи функций этого интерфейса - окна, кнопки, буквы и так далее. Если почитать подробности в MSDN, можно найти примеры рисования линий, точек, геометрических фигур, работу с кистями и много чего интересного.

Одна проблема - работает эта штука довольно медленно, что не устраивает производителей игр, для которых придумали DirectX - набор интерфейсов для отрисовки графики, работы со звуком и прочими вещами, связанными с играми и OpenGL - ещё одну (полностью открытую) библиотеку для рисования графики. Фишка этих двоих в том, что они рисуют быстро - на порядки быстрее, чем GDI\GDI+, так что все более-менее требовательные к железу игры используют эти технологии.

Их-то нам и предстоит ковырять (или смотреть, как их ковыряю я, попивая чаёк с бутерами), но для ковыряния нужна какая-никакая подготовка.

Для начала, почитайте вот про [эту] функцию. Теперь смотрим на код:


format PE GUI 4.0 
entry start 
include 'win32ax.inc' 
section '.data' data readable writable
ps PAINTSTRUCT ?
hDC dd ? 
section '.text' code readable executable
  start:
        invoke  GetModuleHandle,0
        invoke  DialogBoxParam,eax,37,HWND_DESKTOP,DialogProc,0
        or      eax,eax
        jz      exit
  exit:
        invoke  ExitProcess,0
proc DialogProc hwnddlg,msg,wparam,lparam
        push    ebx esi edi
        cmp     [msg],WM_INITDIALOG
        je      .wminitdialog
        cmp     [msg],WM_COMMAND
        je      .wmcommand
        cmp     [msg],WM_CLOSE
        je      .wmclose
        cmp     [msg],WM_PAINT
        je      .wmpaint
        cmp     [msg],WM_LBUTTONDOWN
        je      .move
        cmp     [msg],WM_RBUTTONUP
        je      .wmclose
        xor     eax,eax
        jmp     .finish
  .move:
        invoke  SendMessage,[hwnddlg],WM_NCLBUTTONDOWN,2,0
        jmp     .processed
  .wmpaint:
        invoke  BeginPaint,[hwnddlg],ps
        mov     [hDC],eax
        invoke  Rectangle,[hDC],10,10,30,30
        invoke  EndPaint,[hwnddlg],ps
        jmp     .processed
  .wminitdialog:
        jmp     .processed
  .wmcommand:
        jmp     .processed
  .wmclose:
        invoke  EndDialog,[hwnddlg],0
  .processed:
        mov     eax,1
  .finish:
        pop     edi esi ebx
        ret
endp 
section '.idata' import data readable writeable
  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          gdi,'GDI32.DLL'
  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess';,\
  import user,\
         DialogBoxParam,'DialogBoxParamA',\
         SendMessage,'SendMessageA',\
         EndDialog,'EndDialog',\
         BeginPaint,'BeginPaint',\
         EndPaint,'EndPaint'
  import gdi,\
         Rectangle,'Rectangle'

section '.rsrc' resource data readable
  directory RT_DIALOG,dialogs
  resource dialogs,\
           37,LANG_ENGLISH+SUBLANG_DEFAULT,demonstration
  dialog demonstration,'Create message box',70,70,190,175,WS_POPUP
  enddialog

В процедуре работы диалогового окна (DialogProc) у нас появился обработчик нового события - [WM_PAINT]. Это крутое событие вызывается всякий раз, когда окно хочет, чтобы его нарисовали. Тем самым GDI, да-да. Следовательно, если у нас есть обработчик этого события -  обязательно надо там что-нибудь нарисовать (в голову лезут неприличные слова и забор)!

Что нам для этого нужно? Для начала, сообщить винде, что мы собрались рисовать. Та самая функция, про которую я предложил почитать чуть выше (BeginPaint), этим и занимается. Для работы ей нужно сообщить хэндл нашего окна и адрес специальной структуры (PAINTSTRUCT), которая хранит всю нужную информацию - размеры окна и т.д., но в принципе она нам не интересна - мы просто её объявили в секции данных и забыли.

Гораздо более интересен нам ответ, который приходит в регистр EAX после выполнения функции. Ответ этот - хэндл на штуковину под названием контекст устройства ([device context]), по сути - структура, описывающая выбранную область рисования, выбранную кисть, её цвет и прочие параметры.

Так, штука важная - сохраняем в отдельной переменной hDC, она нам ещё понадобится. А для чего? Смотрим на следующую строчку и видим вызов функции [Rectangle], которая, как не трудно догадаться из её названия, рисует прямоугольник. Что ей нужно сообщить в аргументах? А всего ничего - контекст устройства (который у нас уже есть) и координаты верхнего правого и нижнего левого угла. Про координаты сделаю небольшую картинку, даже парочку.

Вот как координаты считаются в обычном мире человеков:

Значит, две оси (монитор у нас, как известно, плоский) - абсцисс и ординат, X и Y. Нижний левый угол - ноль. Точка, которую я мастерски нарисовал, имеет координаты (10;10). Всё понятно, правда? На геометрии в школе было то же самое, бррр!

А теперь - как дела обстоят в мире компьютеров:


Всё наоборот, начало координат - верхний левый угол! Предлагаю в верхний правый угол монитора прикрепить бумажку цветную. В общем, привыкнуть придётся. Теперь вспоминаем нашу функцию, рисующую прямоугольник:

invoke  Rectangle,[hDC],10,10,30,30
Координаты верхнего левого угла - (10;10), нижнего правого - (30;30). Вот картинка работающей программы (крестики я нарисовал):

 Значит, два цветных крестика - как раз те самые (10;10) и (30;30), красный и синий, соответственно. Представили, да? Важно тут запомнить, что начало отсчёта (ноль) - верхний левый угол, а не нижний левый, как на геометрии.

После того, как прямоугольник (а в нашем случае - квадрат, но никто не мешает задать другие координаты) нарисовался, мы красноречиво сообщаем, что хватит это терпеть рисование закончено - вызываем [EndPaint], сообщая ей всё тот же хэндл окна и контекст устройства.

Воот, как-то так. На целую лекцию тянет! Уф. В общем, пока я разбираюсь дальше и привожу в порядок кашу в голове (т.к. сам изучаю эту тему практически с нуля), попрактикуйтесь - найдите в MSDN другие функции для рисования, почитайте про них, посмотрите примеры, попробуйте порисовать сами. Удачи, это ведь так интересно!



Комментариев нет:

Отправить комментарий

Не люблю мат и низкий уровень грамотности. Чем конкретнее поставите свой вопрос и чем лучше он будет выглядеть - тем большая вероятность на мой ответ. :)