Робота з регіонами в Visual C + + h2>
Vander Nunes p>
У Win32 API є набір функцій для роботи з регіонами.
За допомогою регіонів Ви можете створювати різні поверхні, використовуючи тільки
стандартні геометричні фігури. Ну а далі все залежить від Вашої вигадки. P>
Отже, приступимо до визначень: p>
HRNG: p>
Це всього лише тип даних, що означає "Хендлі
регіону ". Такий Хендлі буде потрібно для кожного регіону, з яким Ви
захочете працювати. p>
Приклад використання: p>
HRGN hRegion = CreateRectRgn (x, y, x +128, y +128); p>
Після завершення роботи з регіоном, необхідно видалити
об'єкт, пов'язаний з регіоном за допомогою функції DeleteObject (). p>
CombineRgn,
CreateEllipticRgn, CreateEllipticRgnIndirect, CreatePolygonRgn,
CreatePolyPolygonRgn, CreateRectRgn, CreateRectRgnIndirect, CreateRoundRectRgn,
EqualRgn, ExtCreateRegion, FillRgn, FrameRgn, GetPolyFillMode, GetRegionData,
GetRgnBox, InvertRgn, OffsetRgn, PaintRgn, PtInRegion, RectInRegion,
SetPolyFillMode. P>
Це основні функції, які призначені для
створення та роботи з регіонами. Як видно з назв, регіони можна створювати
круглі, квадратні, а також будь-якої іншої форми. p>
SetWindowRgn p>
За допомогою цієї функції можна прикріпити регіон до
будь-якого вікна. Наприклад, після використання цієї функції, вікно може виглядати
наступним чином: p>
p>
Щоб виконати таке з вікном, знадобляться наступні
функції: p>
// ця функція створює цілий регіон. p>
HRGN CreateEllipticRgn ( p>
int nLeftRect,
//X-координата верхнього-лівого кута p>
int nTopRect,
//Y-координата верхнього-лівого кута p>
int nRightRect,
//X-координата нижнього-правого кута p>
int nBottomRect// y-координата нижнього-правого кута p>
); p>
// ця функція поєднує два регіони p>
int CombineRgn ( p>
HRGN hrgnDest,
//Хендлі кінцевого регіону p>
HRGN hrgnSrc1,
//Хендлі вихідного регіону p>
HRGN hrgnSrc2,
//Хендлі вихідного регіону p>
int fnCombineMode// режим сполучення регіонів p>
); p>
// ця функція прикріплює регіон до вікна. p>
// щоб прибрати регіон з вікна, треба замість Хендлі
регіону поставити NULL. p>
int SetWindowRgn ( p>
HWND hWnd,// Хендлі вікна, на яке буде
встановлений регіон p>
HRGN hRgn,// Хендлі регіону p>
BOOL bRedraw
//Прапор перемальовування вікна p>
); p>
Ну а тепер поглянемо на реальний код, який
демонструє створення вікна, зображення якого наведено вище: p>
// --------------------------------------------- ----- p>
// Створюємо круглий регіон. p>
// Використовуємо негативну початкову координату,
щоб наш еліпс p>
// захопив заголовок вікна. p>
// --------------------------------------------- ----- p>
HRGN hRegion1 = CreateEllipticRgn (20, -20,190,150); p>
// --------------------------------------------- ----- p>
// створюємо ще один круглий регіон в іншому місці. p>
// --------------------------------------------- ----- p>
HRGN hRegion2 = CreateEllipticRgn (140,100,300,240); p>
// --------------------------------------------- ----- p>
// склеюємо два регіони, щоб зробити новий регіон. p>
// підсумковий регіон буде поміщений в region1, p>
// подібно операції: p>
// p>
// hRegion1 = hRegion1 + hRegion2. p>
// p>
// у функції CombineRgn () можна використовувати набір
операцій RGN_. p>
//
-------------------------------------------------- p>
CombineRgn (hRegion1,
hRegion1, hRegion2, RGN_OR); p>
//
-------------------------------------------------- p>
// прикріплюємо регіон до вікна p>
//
-------------------------------------------------- p>
SetWindowRgn (hWnd,
hRegion1, true); p>
//
-------------------------------------------------- p>
// видаляємо об'єкти регіонів p>
//
-------------------------------------------------- p>
DeleteObject (hRegion1); p>
DeleteObject (hRegion2); p>
Щоб повернути вікно в нормальний стан (без
регіону), скористайтеся наступною функцією: p>
SetWindowRgn (hWnd,
NULL, true); p>
Завантажити зразок - 13Кб p>
СКІН p>
Тема скінів досить популярна в програмуванні. При
допомоги скінів ми можемо надати стандартного вікна привабливий вигляд: p>
p>
Для цього необхідний бітмапами, який заповнить все вікно.
На наведеній картинці використовується вікно розміром 320х240 і такого ж розміру
бітмапами. p>
Давайте створимо невелике демонстраційне
додаток. Нижче наведено кроки, які потрібно буде виконати: p>
1 - Завантажуємо бітмапами; p>
2 - Створюємо контекст пристрою для скіна і вибираємо в
ньому бітмапами; p>
3 - Створюємо перемикач між нормальним режимом і
скіном; p>
4 - приховує заголовок вікна і блокуємо зміна його
розмірів у режимі скіна; p>
5 - Показуємо заголовок вікна і розблокуємо його при
вихід з режиму скіна; p>
6 - обробляємо перемальовування скіна в повідомленні
WM_PAINT; p>
7 - обробляємо повідомлення WM_LBUTTON, щоб
користувач міг перетягувати вікно за будь-яку частину в режимі скіна; p>
А тепер кожен крок докладніше: p>
1 - Завантажуємо бітмапами: p>
//
-------------------------------------------------- --------------------- p>
// завантажуємо бітмапами скіна з ресурсу p>
//
-------------------------------------------------- --------------------- p>
hSkinBmp =
LoadBitmap (hInstance, MAKEINTRESOURCE (IDB_SKIN )); p>
if (! hSkinBmp) return -1; p>
Як видно з коду, нічого складного. Звичайно можна
завантажувати зображення інших форматів, але це тема іншої статті. p>
2 - Створюємо контекст пристрою для скіна і вибираємо в
ньому бітмапами: p>
//
-------------------------------------------------- --------------------- p>
// створюємо контекст пристрою для скіна p>
//
-------------------------------------------------- --------------------- p>
dcSkin = CreateCompatibleDC (0); p>
// --------------------------------------------- -------------------------- p>
// вибираємо бітмапами для скіна p>
//
-------------------------------------------------- --------------------- p>
hOldBmp =
(HBITMAP) SelectObject (dcSkin, hSkinBmp); p>
Не забудьте звільнити ці об'єкти перед тим як Ваше
додаток завершить свою роботу. p>
3 - Створюємо перемикач між нормальним режимом і
скіном: p>
case VK_SPACE: p>
( p>
if (! bRegioned) p>
RegionMe (); p>
else p>
UnRegionMe (); p>
break; p>
) p>
Цей фрагмент коду необходмимі помістити в головну
віконну процедуру в обробник повідомлення WM_KEYDOWN. Тут використовуються два
невеликі власні функції RegionMe () і UnregionMe () для перемикання
режиму. p>
4 - приховує заголовок вікна і блокуємо зміна його
розмірів у режимі скіна: p>
//
-------------------------------------------------- ---------------------- p>
// Створюємо основний регіон і встановлюємо його на
вікно. p>
//
-------------------------------------------------- ---------------------- p>
void RegionMe () p>
( p>
// --------------------------------------------- ----- p>
// створюємо цілий регіон і використовуємо негативну p>
// координату, щоб регіон захопив заголовок вікна. p>
//
-------------------------------------------------- p>
HRGN hRegion1 =
CreateEllipticRgn (20, -20,190,150); p>
OffsetRgn (hRegion1,
GetSystemMetrics (SM_CXBORDER) * 4, GetSystemMetrics (SM_CYCAPTION )); p>
// --------------------------------------------- ----- p>
// створюємо другий регіон в іншому місці. p>
//
-------------------------------------------------- p>
HRGN hRegion2 =
CreateEllipticRgn (140,100,300,240); p>
OffsetRgn (hRegion2,
GetSystemMetrics (SM_CXBORDER) * 4, GetSystemMetrics (SM_CYCAPTION )); p>
// --------------------------------------------- ----- p>
// поєднуємо два регіони за принципом: p>
// hRegion1 =
hRegion1 + hRegion2. p>
//
-------------------------------------------------- p>
CombineRgn (hRegion1,
hRegion1, hRegion2, RGN_OR); p>
// --------------------------------------------- ----- p>
// прикріплюємо підсумковий регіон до вікна p>
// --------------------------------------------- ----- p>
SetWindowRgn (hWnd, hRegion1, true); p>
// --------------------------------------------- ----- p>
// видаляємо об'єкти регіонів p>
// --------------------------------------------- ----- p>
DeleteObject (hRegion1); p>
DeleteObject (hRegion2); p>
// --------------------------------------------- ----- p>
// змінюємо стиль вікна ( "забрали заголовка) p>
//
-------------------------------------------------- p>
DWORD dwStyle =
GetWindowLong (hWnd, GWL_STYLE); p>
dwStyle & =
~ (WS_CAPTION | WS_SIZEBOX); p>
SetWindowLong (hWnd,
GWL_STYLE, dwStyle); p>
// --------------------------------------------- ----- p>
// перемальовує вікно, а так само прибираємо рамку вікна,
щоб p>
// щоб не можна було міняти його розмір p>
//
-------------------------------------------------- p>
InvalidateRect (hWnd,
NULL, TRUE); p>
SetWindowPos (hWnd,
NULL, 0,0,320,242, SWP_NOMOVE | SWP_NOZORDER); p>
// --------------------------------------------- ----- p>
// встановлюємо прапорець, тільки для того, щоб
додаток знало про зміну. p>
// --------------------------------------------- ----- p>
bRegioned = true; p>
) p>
5 - Показуємо заголовок вікна і розблокуємо його при
вихід з режиму скіна: p>
//
-------------------------------------------------- ---------------------- p>
// Прибираємо регіон з вікна p>
//
-------------------------------------------------- ---------------------- p>
void UnRegionMe () p>
( p>
// --------------------------------------------- ----- p>
// Прибираємо регіон з вікна p>
// --------------------------------------------- ----- p>
SetWindowRgn (hWnd, NULL, true); p>
// --------------------------------------------- ----- p>
// міняємо стиль вікна (знову показуємо заголовок) p>
//
-------------------------------------------------- p>
DWORD dwStyle =
GetWindowLong (hWnd, GWL_STYLE); p>
dwStyle | =
WS_CAPTION | WS_SIZEBOX; p>
SetWindowLong (hWnd,
GWL_STYLE, dwStyle); p>
// --------------------------------------------- ----- p>
// перемальовує вікно і повертаємо на місце рамку
вікна, щоб можна було p>
// змінювати його розмір p>
//
-------------------------------------------------- p>
InvalidateRect (hWnd,
NULL, TRUE); p>
SetWindowPos (hWnd,
NULL, 0,0,320,240, SWP_NOMOVE | SWP_NOZORDER); p>
// --------------------------------------------- ----- p>
// встановлюємо прапорець. p>
// --------------------------------------------- ----- p>
bRegioned = false; p>
) p>
6 - обробляємо перемальовування скіна в повідомленні
WM_PAINT: p>
case WM_PAINT: p>
( p>
PAINTSTRUCT ps; p>
BeginPaint (hWnd, & ps); p>
// малюємо скін на вікні p>
if (bRegioned)
SkinMe (ps.hdc); p>
// малюємо текст p>
SetBkMode (ps.hdc, TRANSPARENT); p>
SetTextColor (ps.hdc, RGB (255,0,0 )); p>
TextOut (ps.hdc,
115,90, "Press SPACE", 11); p>
EndPaint (hWnd, & ps); p>
break; p>
) p>
Функція SkinMe () викликається тільки в тому випадку, якщо
додаток знаходиться в режимі скіна (bRegioned). p>
Функція SkinMe () виглядає наступним чином: p>
void SkinMe (HDC dc) p>
( p>
BitBlt (dc,
0,0,320,240, dcSkin, 0,0, SRCCOPY); p>
) p>
7 - обробляємо повідомлення WM_LBUTTON, щоб
користувач міг перетягувати вікно за будь-яку частину в режимі скіна: p>
case WM_LBUTTONDOWN: p>
( p>
//
-------------------------------------------------- ------- p>
// Посилаємо повідомлення вікна, щоб воно думало, що
скликали на його заголовку. p>
//
-------------------------------------------------- ------- p>
if (bRegioned)
SendMessage (hWnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL); p>
break; p>
) p>
Естевственно, що це повідомлення надсилається тільки
коли вікно в режимі скіна. p>
Завантажити исходник - 52Кб p>
Складні регіони p>
Прості геометричні фігурки це звичайно добре. А
що робити, якщо треба створити регіон складної форми, наприклад у вигляді руки: p>
p>
Щоб зробити подібне, нам прийдеться наваяли
невелику функцію, яка буде сканувати бітмапами і створювати з нього
попіксельно регіон з прозорістю. Далі такий регіон буде достатньо
причепити до вікна. p>
//
-------------------------------------------------- -------------------- p>
// Функція сканує бітмапами і повертає необхідну
нам регіон. p>
// Звільнити об'єкт регіону потрібно буде
самостійно ... p>
//
-------------------------------------------------- -------------------- p>
HRGN
ScanRegion (HBITMAP pBitmap, BYTE jTranspR, BYTE jTranspG, BYTE jTranspB) p>
( p>
// ширина і висота бітмапами p>
WORD
wBmpWidth, wBmpHeight; p>
// кінцевий і тимчасовий регіони p>
HRGN hRgn, hTmpRgn; p>
// 24-бітові пікселі з бітмапами p>
BYTE * pPixels = Get24BitPixels (pBitmap,
& wBmpWidth, & wBmpHeight); p>
if (! pPixels)
return NULL; p>
// створюємо робочий регіон p>
hRgn =
CreateRectRgn (0,0, wBmpWidth, wBmpHeight); p>
if (! hRgn) (delete
pPixels; return NULL;) p>
//
-------------------------------------------------- ------- p>
// скануємо бітмапами p>
//
-------------------------------------------------- ------- p>
DWORD p = 0; p>
for (WORD y = 0;
y
( p>
for (WORD x = 0; x
( p>
BYTE jRed
= PPixels [p 2]; p>
BYTE jGreen = pPixels [p 1]; p>
BYTE jBlue
= PPixels [p 0]; p>
if (jRed == jTranspR & & jGreen ==
jTranspG & & jBlue == jTranspB) p>
( p>
// видаляємо
прозорий колір з регіону p>
hTmpRgn =
CreateRectRgn (x, y, x +1, y +1); p>
CombineRgn (hRgn,
hRgn, hTmpRgn, RGN_XOR); p>
DeleteObject (hTmpRgn); p>
) p>
// наступний піксель p>
p + = 3; p>
) p>
) p>
// звільняємо пікселі p>
delete pPixels; p>
// повертаємо регіон p>
return hRgn; p>
) p>
Як видно з коду, спершу створюється квадратний регіон
такого ж розміру як і бітмапами, переданий у функцію як параметр. Потім
відбувається сканування кожного пікселя і, якщо перебуває піксель такого ж
кольору як і зазначений, то цей піксель виключається з регіону. p>
Для демонстрації вищеописаного алгоритму була створена
утиліта RegionCreator, яка являє собою консольний застосунок
що працює з графічними файлами. p>
Завантажити RegionCreator - 19Кб p>
Запускається ця утилітка наступним чином: p>
regioncreator p>
bitmap.bmp: сам бітмапами p>
r, g, b: прозорий колір (в десятковому вигляді: 255 255
255) p>
Завантаження складних регіонів p>
Після того, як бітмапами буде оброблений утилітки, його
можна помістити в додаток як ресурс і завантажити в такий спосіб: p>
// шукаємо ресурс для нашого скіна. p>
HRSRC hrSkin =
FindResource (hInstance, MAKEINTRESOURCE (IDB_SKIN), "BINARY "); p>
if (! hrSkin) return
false; p>
// завантажуємо стандартний "BINARY"
ресурс. p>
LPRGNDATA pSkinData
= (LPRGNDATA) LoadResource (hInstance, hrSkin); p>
if (! pSkinData)
return false; p>
// створюємо регіон. p>
HRGN rgnSkin =
ExtCreateRegion (NULL, SizeofResource (NULL, hrSkin), pSkinData); p>
// звільняємо виділений ресурс p>
FreeResource (pSkinData); p>
Після цього, регіон досить буде причепити до вікна.
І не забудьте видалити регіон перед завершенням програми командою
DeleteObject (rgnSkin). P>
У висновку, непогано було б укласти весь
вищенаведений матеріал в клас, щоб код зручніше читався: p>
Удачи! p>
Список літератури h2>
Для підготовки даної роботи були використані
матеріали з сайту http://www.realcoding.net/
p>