Отправляет email-рассылки с помощью сервиса Sendsay

Assembler - Просто и Эффективно.

  Все выпуски  

Assembler - Просто и Эффективно. Глава #11 - Графика GDI


Assembler

Глава #11 - Графика.

  Для отображения несложной графики используется GDI (graphics device interface). Особенности GDI - простота использования и низкая скорость, что делает его пригодным, в основном, для создания нестандартных элементов интерфейса и приложений со "слабой" графикой.
Для создания изображений часто применяются команды FPU и SIMD инструкции (MMX;XMM;3DNow!), поэтому изложенные материалы о GDI будут использоваться в следующих главах для наглядного изучения дополнительных инструкций процессора.
   -------------------------------

Общая информация.
Основное понятие GDI - это Device Context -DC. - Представляет из себя набор свойств среды рисования: изображение, используемые шрифты, цвета и т.д.
Для того, чтобы добавить/заменить в DC какой-либо элемент, нужно создать его, а после использовать ф-цию SelectObject. Рисование производится на объекте типа Bitmap.
Цвета, передаваемые функциям GDI - RGB: байт #0 определяет уровень красного цвета, #1 - зеленого, #2 - синего, и #3=0

С.м в MSDN: Graphics and Multimedia->Windows GDI
   -------------------------------

Рисование в окнах
Чаще всего GDI используется для отображения графики в окне.
При необходимости обновить часть изображения окна, Windows добавляет этот регион в т.н Update Region и, позже, посылает сообщение WM_PAINT. Обработчик сообщения обычно вызывает ф-цию BeginPaint, возвращающую хэндл DC клиент-части окна, с установленным clipping region'ом равным update region'у. Т.е рисование будет происходить только в части окна, необходимой для обновления. Это сделано для избежания мерцания при рисовании. Далее Update Region очищается и следующее сообщение WM_PAINT произойдет только при следующей необходимости прорисовки изображения. В случае надобности перерисовки всего клиент-содержимого окна кроме WM_PAINT посылается также WM_ERASEBKGND, а для обновления не клиент-части - WM_NCPAINT.

Для добавления части/всего окна в update region используются ф-ции InvalidateRect/ InvalidateRgn. Автоматическое добавление происходит при смене размера окна, удаления перекрытия другими окнами и т.д.
Можно получить DC окна функциями GetDC, GetWindowDC, и вывести изображение, но при появлении update region'а оно будет не действительным. Эти ф-ции не влияют и не зависят от Update Region'а
Некоторые свойства GDI зависят от стиля окна, стиля класса, хэндла кисти фона класса. Для избежания мигания изображения кисть фона обычно устанавливается в ноль при регистрации класса.
   -------------------------------

Изображение в памяти.
Можно создать DC, существующее только в памяти и не отображаемое на экране - Memory DC, вызвав ф-цию CreateCompatibleDC. Memory DC чаще всего используется для:
  • Избежания мерцания (Flicker) изображения при прорисовке.
    Мерцание происходит если пользователь видит процесс создания изображения, при прорисовке перекрывающихся элементов т.к графика, выводимая в DC окна, сразу же отображается. Для устранения мерцания можно использовать объекты типа Region, но чаще создается DC в памяти, на котором происходит рисование, а по завершению процесса создания изображения производится копирование из Memory DC в DC окна (обычно ф-цией BitBlt).
    Объект Bitmap в таких случаях можно создать ф-цией CreateCompatibleBitmap

  • Рисование изображения для последующих манипуляций с данными изображения. В таком случае объект Bitmap обычно создается ф-цией CreateDIBSection что позволяет иметь непосредственный досутп (чтение/запись) к массиву точек изображения. Ф-ции рисования в DC также влияют на массив точек изображения в памяти. Такое изображение можно легко сохранить в .bmp файл, записав сначала струкутру BITMAPFILEHEADER, за ней BITMAPINFOHEADER, далее палитру (если есть), и массив точек изображения (с выравниванием строк на dword)
       -------------------------------

  • Пример
    Данная программа предназначена для определения цвета. Исходник по размеру несколько больше чем обычно, зато обладает некой минимальной функциональностью.
    Напоминаю, что примеры в рассылке приводятся для обучения, и для усвоения/ развития изложенных материалов. Желательно чтобы вы изучали, модифицировали их, создавали на их основе тэстовые программы, тренируясь в использовании родственных ф-ций, не описанных в статье.
     .486
     .model flat,stdcall
    ;используем inc и lib файлы из пакета masm32 (с.м главу #8)
    include c:\masm32\include\windows.inc
    include c:\masm32\include\kernel32.inc
    includelib c:\masm32\lib\kernel32.lib
    include c:\masm32\include\user32.inc
    includelib c:\masm32\lib\user32.lib
    include c:\masm32\include\gdi32.inc
    includelib c:\masm32\lib\gdi32.lib
    
    RightSpace=25
    ImageX =256+RightSpace
    BottomSpace = 35
    ImageY=256+BottomSpace
    
     .data
    ProgramClassName db 'ColorPicker',0 ;класс основного окошка
    WinTitle db 'Color Picker',0
    ColorFormat db 'Color #%06lX',0
    WndRect RECT<0,0,ImageX,ImageY> ;Client Rect окна
    BitmapHeader BITMAPINFO <>
    BlueLevel dd 0   ;Уровень синего
    EdgeRect RECT <0,256,ImageX,ImageY>
    WClass WNDCLASSEX 
    DCFont LOGFONT <>
    MsgS MSG <>
    MemDC dd ?
    BitsPointer dd ?
     .code
    ;Процедура для заполнения объекта-изображения цветами.
    ;использует BitsPointer,BlueLevel
    ;работает непосредственно с памятью объекта Bitmap
    FillColorDC proc
     pusha
     mov edi,[BitsPointer]
     add edi,ImageX*4*BottomSpace
     mov eax,[BlueLevel]
     xor edx,edx
     cld
    DrawColorLine:
     stosd
     add eax,100h
     cmp ah,0
     jnz DrawColorLine
     mov ecx,RightSpace
     xchg eax,ebx
     mov eax,edx
     cmp bl,dl
     jnz NotCurrentBlueLevel
     mov eax,0FFFFFFh
    NotCurrentBlueLevel:
     rep stosd
     xchg eax,ebx
     inc dl
     jnz DrawColorLine
     popa
     ret
    FillColorDC endp
    
    ;Процедура для отображения текущего цвета
    ; - прорисовки нижней части изображения
    ;Вход: eax - номер цвета
    ;использует: BitsPointer,MemDC
    InitBottomColor proc
    local ColorText [16]:byte
     pusha
     invoke wsprintf,addr ColorText,offset ColorFormat,eax
    ;заполним нижнюю часть Memory DC выбранным цветом,
    ;  записывая данные непосредственно в память
     mov eax,dword ptr [esp+1Ch] ;eax in stack
     bswap eax
     shr eax,8
     mov edi,[BitsPointer]
     mov ecx,BottomSpace*ImageX
     cld
     rep stosd
    ;прямоугольник выбранного цвета нарисован.
    ;нарисуем рамку, используя ф-цию GDI
     invoke DrawEdge,[MemDC],offset EdgeRect,EDGE_SUNKEN,BF_RECT
    ;отобразим текст с номером цвета
     invoke DrawText,[MemDC],addr ColorText,-1,offset EdgeRect,\
      DT_CENTER or DT_VCENTER or DT_SINGLELINE
     popa
     ret
    InitBottomColor endp
    
    ;Точка входа.
    start:
    ;создадим Memory DC
     invoke CreateCompatibleDC,0
     mov MemDC,eax
    ;создадим объект Bitmap. Получим в BitsPointer адрес массива точек изображения
     invoke CreateDIBSection,0,offset BitmapHeader,\
      DIB_RGB_COLORS,offset BitsPointer,0,0
    ;добавим объект Bitmap в Memory DC
     invoke SelectObject,[MemDC],eax
    ;установим цвет текста и фона текста в DC
     invoke SetBkColor,[MemDC],0FF4040h
     invoke SetTextColor,[MemDC],0FFFFFFh
    
    ;получим хэндл стандартного шрифта
     invoke GetStockObject,DEFAULT_GUI_FONT
    ;получим структуру, описывающую объект - шрифт
     invoke GetObject,eax,size LOGFONT,offset DCFont
    ;создадим шрифт нужного размера на основе системного
     mov DCFont.lfHeight,BottomSpace-10
     invoke CreateFontIndirect,offset DCFont
    ;добавим созданный шрифт в DC
     invoke SelectObject,[MemDC],eax
    
    ;инициализируем Memory DC изначальным изображением
     call FillColorDC
     xor eax,eax
     call InitBottomColor
    
    ;До MainWindowProc  - стандартная часть
    ;зарегистрируем класс главного окна (см главу #7)
    
     mov edi,offset WClass
     assume edi:ptr WNDCLASSEX
    
     invoke GetModuleHandle,0
     mov [edi].hInstance,eax
     invoke LoadIcon,0,IDI_APPLICATION
     mov [edi].hIcon,eax
     mov [edi].hIconSm,eax
     invoke LoadCursor,0,IDC_ARROW
     mov [edi].hCursor,eax
     assume edi:nothing
    
     invoke RegisterClassEx,edi
    
    ;определим размер основного окна, на основе необходимого Client Rect'а
    MainWindowstyle="WS_CAPTION" or WS_DLGFRAME or WS_VISIBLE or WS_MINIMIZEBOX or WS_SYSMENU
     invoke AdjustWindowRect,offset WndRect,MainWindowStyle,0
     mov eax,WndRect.right
     sub eax,WndRect.left
     mov ecx,WndRect.bottom
     sub ecx,WndRect.top
    ;создадим главное окно
     invoke CreateWindowEx,0,offset ProgramClassName,offset WinTitle,\
      MainWindowStyle,CW_USEDEFAULT,CW_USEDEFAULT,eax,ecx,\
      0,0,[WClass.hInstance],0
    
    MsgLoop:
     invoke GetMessage,offset MsgS,0,0,0
     inc eax
     jz EndMsgLoop
     dec eax
     jz EndMsgLoop
     invoke TranslateMessage,offset MsgS
     invoke DispatchMessage,offset MsgS
     jmp MsgLoop
    EndMsgLoop:
     invoke ExitProcess,0
    
    MainWindowProc proc hwnd:dword,uMsg:dword,wParam:dword,lParam:dword
    local ps:PAINTSTRUCT
     cmp [uMsg],WM_PAINT
     jnz NotWmPaint
    ;обработчик WM_PAINT
    ;просто копирует Memory DC в DC окошка.
     invoke BeginPaint,[hwnd],addr ps
     invoke BitBlt,eax,0,0,ImageX,ImageY,[MemDC],0,0,SRCCOPY
     invoke EndPaint,[hwnd],addr ps
     xor eax,eax
     ret
    NotWmPaint:
     
     cmp [uMsg],WM_LBUTTONDOWN
     jz CheckMouseEvent
     cmp [uMsg],WM_MOUSEMOVE
     jnz NotWmMouseMove
    CheckMouseEvent:
     cmp [wParam],MK_LBUTTON
     jnz NotWmMouseMove
     movzx eax,word ptr [lParam]   ;mouse X
     movzx ecx,word ptr [lParam+2]  ;mouse Y
     cmp ecx,255
     ja CallDefProc
     cmp eax,255
     jbe NotBlueDrag
    ;пользователь изменил уровнь синего
     not cl
     mov [BlueLevel],ecx
     call FillColorDC
    ;объявим всю клиент-площадь недействительной.
     invoke InvalidateRect,[hwnd],0,0
     jmp CallDefProc
    NotBlueDrag:
    ;получим цвет точки под мышом
     invoke GetPixel,[MemDC],eax,ecx
    ;обновим информацию о текущем цвете (что находится внизу Memory DC)
     call InitBottomColor
    ;получим DC клиент-части окна
     invoke GetDC,[hwnd]
    ;обновим только нижнюю часть окна (для большего быстродействия)
     push eax
     invoke BitBlt,eax,0,256,ImageX,BottomSpace,[MemDC],0,256,SRCCOPY
     pop eax
     invoke ReleaseDC,[hwnd],eax
     jmp CallDefProc
    NotWmMouseMove:
    
    ;стандартная часть процедуры обработки сообщений с.м главу #7.
     cmp [uMsg],WM_DESTROY
     jnz NotWmDestroy
     invoke PostQuitMessage,0
     xor eax,eax
     ret
    NotWmDestroy:
    CallDefProc:
     invoke DefWindowProc,[hwnd],[uMsg],[wParam],[lParam]
     ret
    MainWindowProc endp
    end start
       -------------------------------

     Содержание дальнейших выпусков зависит от вас. Будут разъясняться те темы, которые непонятны большинству читателей. Присылайте свои вопросы и предложения по адресу: asm32@nm.ru .
       -------------------------------


    Автор рассылки Владимир Пронин. Любое коммерческое использование материалов рассылки без ведома и прямого согласия автора запрещено.
    Письма присланные автору рассылки могут быть опубликованы целиком или частично без предварительного уведомления. Если вы не хотите, чтобы ваше письмо было опубликовано укажите это в начале письма.
    Перепечатка материалов разрешена только с непосредственной ссылкой на asm32.nm.ru


    В избранное