Відомо, що багатьох програмістів лякає складність використання графіки в Windows. Поняття дескрипторів, контекстів, шаблонів і т.п. речей відлякує багатьох. Але насправді, зрозумівши концепцію і принципи графічної підсистеми (GUI) ви самі побачите, що це нескладна річ.
Як і де малює Windows
У Windows є поняття дескрипторів (Handle) вікна, дескрипторів контексту пристрою і дескрипторів об'єкта, відповідно англійські терміни handle window (hWND), handle device context (hDC) і handle object (це загальна назва). Під hWND розуміються такі елементи як форма, кнопки, панелі і т.д. за допомогою цього дескриптора елементів передаються повідомлення і дані, а ось за допомогою hDC виробляються графічні операції з вмістом самого об'єкта.
Контекст пристрою - структура, яка визначає набір графічних об'єктів, їх властивостей і способів впливу на об'єкт. Графічні об'єкти включають перо (pen) для лінії, кисть (brush) для заповнення, побутова площину (bitmap) для копіювання або прокручування частин екрану, палітра (palette) для визначення набору доступних кольорів, регіон (region) для зрізання областей та інших дій.
Дескриптор об'єкта - це взагалі незалежна штука, вона може існувати окремо від hWND і hDC (ці ж працюють в парі). До дескрипторів об'єкта (далі hOBJ) відносяться пір'я системи малювання (Pen - колір пера, товщина і тип лінії), кисть, що заповнює область (Brush - колір кисті, стиль і вигляд шаблону заповнення) і т.д.
Для здійснення операцій ви завжди повинні, зажадати будь - якої дескриптор. Зажадавши hDC, ви зобов'язані повернути його системі, інакше по закінченню часу Windows припинить всі операція малювання і просто зависне.
Пояснимо на прикладі поняття дескрипторів. Аркуш паперу назвемо віконним елементом, тобто він має hWND, щоб що-небудь на ньому намалювати необхідно, отримати його hDC. У вас є три олівця різного кольору (червоний, чорний, зелений). Взявши олівець в руки, проводиться вибір об'єкта в контекст аркуша паперу. Потім, використовуючи методи переміщення олівця, ви малюєте по поверхні. Ось в принципі і все.
Загальний принцип роботи (спрощено) можна звести до наступного:- Отримати через hWND, дескриптор контексту пристрою hDC.
- Вибрати в контекст hDC потрібний об'єкт і зберегти попередній стан.
- Провести операцію.
- Повернути старий зміст контексту.
- Повернути hDC в систему.
УВАГА! Коли ви створили об'єкт і вибрали його в контекст, збережіть попередній об'єкт. Потім після всіх своїх операцій поверніть його в контекст і видаліть створений об'єкт. Це дуже важлива операція.
Починаємо проектування і програмування
Не будемо поспішати починати малювати, а посидимо і подумаємо. Найбільше часу займає операція виведення на екран і як наслідок мерехтіння. Отже, треба обійти ці обмеження.
Більшість комерційних програм (наприклад, крім ErWin) використовують принцип віртуального вікна, тобто вікна знаходиться в оперативній пам'яті з розмірами і характеристиками області виведення на екрані. Всі операції малювання виробляються з ним, а потім воно виводиться в потрібне місце екрану (хоча необов'язково на екран, але і на принтер), операцією копіювання бітових площин. Ефективність наявності.
А тепер від теорій перейдемо до практики. Прочитайте опис функцій і структур API графіки, щоб, далі зрозуміти роботу програми.
Структури і константи
Зберігає координати прямокутника.
Public Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Зберігає координати точки.
Public Type POINTAPI
X As Long
Y As Long
End Type
Зберігає параметри пера.
Public Type LOGPEN
lopnStyle As Long - стиль пера
lopnWidth As POINTAPI - товщина пера (це структура)
lopnColor As Long - колір пера
End Type
Зберігає параметри кисті.
Public Type LOGBRUSH
lbStyle As Long - стиль кисті
lbColor As Long - колір кисті
lbHatch As Long - шаблон заповнення
End Type
Константи наведено в повному обсязі, за повним списком зверніться в SDK графіки MSDN.
Повну декларацію опису функцій дивіться в початковому тексті. Де пропущено опис типу мається на увазі тип Long.
GetDC (hwnd) --- Отримує дескриптор контексту пристрою для зазначеного hWnd.
CreateCompatibleBitmap (HDC, nWidth, nHeight) --- Створює сумісну бітову карту з контексту HDC розмірами (nWidth, nHeight).
CreateCompatibleDC (HDC) --- Створює сумісний дескриптор контексту із зазначеного HDC.
ReleaseDC (hwnd, HDC) --- Видаляє дескриптор контексту HDC для дескриптора вікна hWnd.
SelectObject (HDC, hObject) --- Вибирає в контекст HDC об'єкт, що задається дескриптором hObject.
PatBlt (HDC, x, y, nWidth, nHeight, dwRop) --- Заповнює контекст HDC від координат (x, y, nWidth, hHeight) використовуючи поточну кисть контексту відповідно до операції dwRop.
DeleteObject (hObject) --- Видаляє дескриптор hObject.
DeleteDC (HDC) --- Видаляє контекст HDC.
BitBlt (hDestDC, x, y, nWidth, nHeight, hSrcDC, xSrc, ySrc, dwRop) --- Копіює бітову область, з контексту hSrcDC, починаючи з координат (xSrc, ySrc) відповідно до растрової операцією dwRop в бітову область контексту hDestDC в координати (x, y, nWidth, nHeight).
InvalidateRect (hwnd, pRect As RECT, bErase) --- Викликає подія відновлення поверхні вікна для дескриптора вікна hWnd в області прямокутника, що задається координатами pRect. Якщо bErase дорівнює істині, то проводиться очищення фону.
Rectangle (HDC, X1, Y1, X2, Y2) --- Малює прямокутник в контексті HDC за координатами (X1, Y1, X2, Y2).
Ellipse (HDC, X1, Y1, X2, Y2) --- Малює коло в контексті HDC за координатами (X1, Y1, X2, Y2).
Опис процедур програми
У формі Visual Basic властивість AutoRedraw встановіть в True, інакше при виведенні віртуального вікна буде відбуватися мерехтіння. Властивість ScaleMode встановіть в Pixels, тому що всі функції працюю з пікселями і для інших одиниць необхідно проводити перетворення.
Насамперед створюємо віртуальне вікно, процедурою CreateVirtualWindow. Ось її текст.
'В цієї змінної зберігається тимчасовий дескриптор контексту вікна
Dim CDC As Long
'Отримуємо дескриптор контексту полотна
CDC = GetDC (Me.hwnd)
'Створюємо сумісний дескриптор
MDC = CreateCompatibleDC (CDC)
'Створюємо сумісну бітову карту з картою бітів полотна
MBM = CreateCompatibleBitmap (CDC, RectForm.Right, RectForm.Bottom)
'Видаляємо дескриптор
ReleaseDC Me.hwnd, CDC
'Вибираємо бітову карту в тимчасовий контекст
SelectObject MDC, MBM
'Заливка області білим кольором (для простоти)
PatBlt MDC, 0, 0, RectForm.Right, RectForm.Bottom, WHITENESS
Пояснення. Використовуючи функцію GetDC, отримуємо дескриптор контексту форми через hWND об'єкта Me. Потім нам треба створити сумісний контекст пристрою для нашого віртуального вікна, тому що це повинно бути два різних об'єкта, але з ідентичними характеристиками. Все це проводиться функцій CreateCompatibleDC, отриманий hDC зберігаємо в глобальній змінній MDC типу Long. Так як віртуальне вікно, по суті бітова карта, то за допомогою функції CreateCompatibleBitmap створюємо бітову карту нашого вікна з розмірами зазначеними нами заздалегідь (дивіться вихідний текст в процедуру Form_Load) і отриманий дескриптор зберігаємо в глобальній змінній MBM типу Long. Потім, зверніть увагу, видаляємо тимчасовий дескриптор CDC через функцію ReleaseDC, це важлива операція. Наостанок вибираємо в наш контекст отриману бітову карту за допомогою функції SelectObject. Очищення області виробляємо за допомогою функції PatBlt.
Знищення віртуального вікна, проводиться процедурою DestroyVirtualWindow.
Sub DestroyVirtualWindow ()
'Видалити бітову карту
DeleteObject MBM
'Видалити дескриптор
DeleteDC MDC
End Sub
Пояснення. Після закінчення роботи програми віртуальне вікно повинно бути знищено, щоб звільнити ресурси. Першим видаляємо бітову карту функцією DeleteObject, потім знищуємо контекст функцією DeleteDC.
Висновок віртуального вікна на екран, процедурою InvalidateCanvas.
Пояснення. Щоб вивести наше вікно на екран, викликаємо функцію InvalidateRect, яка посилає повідомлення формі, що вона повинна перемалювати (тут криється одна тонкість, пов'язана з подією Paint форми і властивістю AutoRedraw, пропоную вам самим з цим розібратися, тоді ви точно зрозумієте тонкощі малювання). Далі знову очищаємо вікно білим кольором. Виводимо фігури в вікно через власну процедуру DrawFigures. І найголовніше, виводимо віртуальне вікно в форму через функцію битового копіювання BitBlt.
Відображення фігури на віртуальному вікні, процедурою DrawFigures.
Sub DrawFigures (HDC As Long, IndexFigure As Byte)
'Дескриптори пера
Dim NewPen, OldPen As Long
'Дескриптори кисті
Dim NewBrush, OldBrush As Long
'Створюємо перо зі структури заданої фігури
NewPen = CreatePenIndirect (Figures (IndexFigure) .Pen)
'Створюємо кисть зі структури заданої фігури
NewBrush = CreateBrushIndirect (Figures (IndexFigure) .Brush)
'Вибираємо створені дескриптори пера і пензля в контекст
OldPen = SelectObject (HDC, NewPen)
OldBrush = SelectObject (HDC, NewBrush)
Select Case Figures (IndexFigure) .Shape
Case SHAPE_RECT:
Call ShapeRect (HDC, Figures (IndexFigure) .Coord)
Case SHAPE_ELLIPSE:
Call ShapeEllipse (HDC, Figures (IndexFigure) .Coord)
End Select
'Вибираємо в контекст старі кисть і перо
'ЦЕ ДУЖЕ ВАЖЛИВА ОПЕРАЦІЯ
SelectObject HDC, OldBrush
SelectObject HDC, OldPen
'Видаляємо нові кисть і перо
'І ЦЕ ДУЖЕ ВАЖЛИВА ОПЕРАЦІЯ
DeleteObject (NewBrush)
DeleteObject (NewPen)
End Sub
Пояснення. В дану процедуру передається дескриптор контексту і індекс типу фігури, яку необхідно намалювати. Далі використовуючи функції CreatePenIndirect і CreateBrushIndirect, створюємо з структур нове перо і кисть. Зберігаємо отримані дескриптори об'єктів в локальних змінних (після закінчення процедури вони нам будуть не потрібні) NewPen і NewBrush типу Long. Вибираємо нове перо і кисть в контекст, зберігаючи при цьому попередні значення в змінних OldPen і OldBrush, функцією SelectObject. Малюємо фігури. Повертаємо попередні значення. Видаляємо дескриптори нових об'єктів. Все інше ви зрозумієте з вихідних текстів, які додаються до статті.