Скіни, C / C + +, Програмування, статті

Тема скінів досить популярна в програмуванні. За допомогою скінів ми можемо
надати стандартному вікна привабливий вигляд:



Для цього необхідний бітмапи, який заповнить все вікно. На наведеній
зображенні використовується вікно розміром 320х240 і такого ж розміру бітмапи.


Давайте створимо невелике демонстраційне додаток. Нижче наведено кроки,
які потрібно буде виконати:



А тепер кожен крок докладніше:


1 – Завантажуємо бітмапи:

// ———————————————————————–
/ / Завантажуємо бітмапи скіна з ресурсу
// ———————————————————————–
hSkinBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_SKIN));
if (!hSkinBmp) return -1;

Як видно з коду, нічого складного. Звичайно можна завантажувати зображення інших
форматів, але це тема іншої статті.


2 – Створюємо контекст пристрою для скіна і вибираємо в ньому бітмапи:

// ———————————————————————–
/ / Створюємо контекст пристрою для скіна
// ———————————————————————–
dcSkin = CreateCompatibleDC(0);

// ———————————————————————–
/ / Вибираємо бітмапи для скіна
// ———————————————————————–
hOldBmp = (HBITMAP)SelectObject(dcSkin, hSkinBmp);


Не забудьте звільнити ці об’єкти перед тим як Ваше додаток завершить
свою роботу.


3 – Створюємо перемикач між нормальним режимом і скіном:

case VK_SPACE:
{
if (!bRegioned)
RegionMe();
else
UnRegionMe();

break;
}


Цей фрагмент коду необходмо помістити на головну віконну процедуру в
обробник повідомлення WM_KEYDOWN. Тут використовуються дві невеликі власні
функції RegionMe () і UnregionMe () для перемикання режиму.


4 – Приховуємо заголовок вікна і блокуємо зміна його розмірів у режимі
скіна:

// ————————————————————————
/ / Створюємо основний регіон і встановлюємо його на вікно.
// ————————————————————————
void RegionMe()
{
// ————————————————–
/ / Створюємо цілий регіон і використовуємо негативну
/ / Координату, щоб регіон захопив заголовок вікна.
// ————————————————–
HRGN hRegion1 = CreateEllipticRgn(20,-20,190,150);
OffsetRgn(hRegion1, GetSystemMetrics(SM_CXBORDER)*4, GetSystemMetrics(SM_CYCAPTION));

// ————————————————–
/ / Створюємо другий регіон в іншому місці.
// ————————————————–
HRGN hRegion2 = CreateEllipticRgn(140,100,300,240);
OffsetRgn(hRegion2, GetSystemMetrics(SM_CXBORDER)*4, GetSystemMetrics(SM_CYCAPTION));

// ————————————————–
/ / Поєднуємо два регіони за принципом:
// hRegion1 = hRegion1 + hRegion2.
// ————————————————–
CombineRgn(hRegion1, hRegion1, hRegion2, RGN_OR);

// ————————————————–
/ / Прикріплюємо підсумковий регіон до вікна
// ————————————————–
SetWindowRgn(hWnd, hRegion1, true);

// ————————————————–
/ / Видаляємо об’єкти регіонів
// ————————————————–
DeleteObject(hRegion1);
DeleteObject(hRegion2);

// ————————————————–
/ / Змінюємо стиль вікна (позбавляємося від заголовка)
// ————————————————–
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
dwStyle &= ~(WS_CAPTION|WS_SIZEBOX);
SetWindowLong(hWnd, GWL_STYLE, dwStyle);

// ————————————————–
/ / Перемальовували вікно, а так само прибираємо рамку вікна, щоб
/ / Щоб не можна було міняти його розмір
// ————————————————–
InvalidateRect(hWnd, NULL, TRUE);
SetWindowPos(hWnd, NULL, 0,0,320,242, SWP_NOMOVE|SWP_NOZORDER);

// ————————————————–
/ / Встановлюємо прапорець, тільки для того, щоб додаток знало про зміну.
// ————————————————–
bRegioned = true;
}


5 – Показуємо заголовок вікна і розблокуємо його при виході з режиму
скіна:

// ————————————————————————
/ / Прибираємо регіон з вікна
// ————————————————————————
void UnRegionMe()
{
// ————————————————–
/ / Прибираємо регіон з вікна
// ————————————————–
SetWindowRgn(hWnd, NULL, true);

// ————————————————–
/ / Змінюємо стиль вікна (знову показуємо заголовок)
// ————————————————–
DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
dwStyle |= WS_CAPTION|WS_SIZEBOX;
SetWindowLong(hWnd, GWL_STYLE, dwStyle);

// ————————————————–
/ / Перемальовували вікно і повертаємо на місце рамку вікна, щоб можна було
/ / Міняти його розмір
// ————————————————–
InvalidateRect(hWnd, NULL, TRUE);
SetWindowPos(hWnd, NULL, 0,0,320,240, SWP_NOMOVE|SWP_NOZORDER);

// ————————————————–
/ / Встановлюємо прапорець.
// ————————————————–
bRegioned = false;
}


6 – Обробляємо перемальовування скіна в повідомленні WM_PAINT:

case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(hWnd,&ps);

/ / Малюємо скін на вікні
if (bRegioned) SkinMe(ps.hdc);

/ / Малюємо текст
SetBkMode(ps.hdc,TRANSPARENT);
SetTextColor(ps.hdc,RGB(255,0,0));
TextOut(ps.hdc, 115,90, "Press SPACE", 11);

EndPaint(hWnd,&ps);

break;
}


Функція SkinMe () викликається тільки в тому випадку, якщо додаток знаходиться в
режимі скіна (bRegioned).


Функція SkinMe () виглядає наступним чином:

void SkinMe(HDC dc)
{
BitBlt(dc, 0,0,320,240, dcSkin, 0,0, SRCCOPY);
}

7 – Обробляємо повідомлення WM_LBUTTON, щоб користувач міг перетягувати
вікно за будь-яку частину в режимі скіна:

case WM_LBUTTONDOWN:
{
// ———————————————————
/ / Посилаємо повідомлення вікна, щоб воно думало, що скликали на його заголовку.
// ———————————————————
if (bRegioned) SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
break;
}

Естевственно, що це повідомлення надсилається тільки коли вікно в режимі
скіна.



 


Складні регіони


Прості геометричні фігурки це звичайно добре. А що робити, якщо треба
створити регіон складної форми, наприклад у вигляді руки:



Щоб проробити подібне, нам прийдется наваяли невелику функцію, яка
буде сканувати бітмапи і створювати з нього попіксельно регіон з прозорістю.
Далі такий регіон буде достатньо причепити до вікна.

// ———————————————————————-
/ / Функція сканує бітмапи і повертає необхідну нам регіон.
/ / Звільнити об’єкт регіону потрібно буде самостійно …
// ———————————————————————-
HRGN ScanRegion(HBITMAP pBitmap, BYTE jTranspR, BYTE jTranspG, BYTE jTranspB)
{
/ / Ширина і висота бітмапи
WORD wBmpWidth,wBmpHeight;

/ / Кінцевий і тимчасовий регіони
HRGN hRgn, hTmpRgn;

/ / 24-бітові пікселі з бітмапи
BYTE *pPixels = Get24BitPixels(pBitmap, &wBmpWidth, &wBmpHeight);
if (!pPixels) return NULL;

/ / Створюємо робочий регіон
hRgn = CreateRectRgn(0,0,wBmpWidth,wBmpHeight);
if (!hRgn) { delete pPixels; return NULL; }

// ———————————————————
/ / Скануємо бітмапи
// ———————————————————
DWORD p=0;
for (WORD y=0; y<wBmpHeight; y++)
{
for (WORD x=0; x<wBmpWidth; x++)
{
BYTE jRed = pPixels[p+2];
BYTE jGreen = pPixels[p+1];
BYTE jBlue = pPixels[p+0];

if (jRed == jTranspR && jGreen == jTranspG && jBlue == jTranspB)
{
/ / Видаляємо прозорий колір з регіону
hTmpRgn = CreateRectRgn(x,y,x+1,y+1);
CombineRgn(hRgn, hRgn, hTmpRgn, RGN_XOR);
DeleteObject(hTmpRgn);
}

/ / Наступний піксель
p+=3;
}
}

/ / Звільняємо пікселі
delete pPixels;

/ / Повертаємо регіон
return hRgn;
}


Як видно з коду, спершу створюється квадратний регіон такого ж розміру як і
бітмапи, переданий у функцію як параметр. Потім відбувається сканування кожного
пікселя і, якщо перебуває піксель такого ж кольору як і зазначений, то цей
піксель виключається з регіону.


Для демонстрації вищеописаного алгоритму була створена утиліта RegionCreator,
яка являє собою консольний додаток працює з графічними
файлами.



Запускається ця утилітка наступним чином:

regioncreator < bitmap.bmp > < r > < g > < b >
bitmap.bmp: сам бітмапи

r, g, b: прозорий колір (в десятковому вигляді: 255255255)


Завантаження складних регіонів


Після того, як бітмапи буде оброблений утилітка, його можна помістити в
додаток як ресурс і завантажити наступним чином:

 / / Шукаємо ресурс для нашого скіна.
HRSRC hrSkin = FindResource(hInstance, MAKEINTRESOURCE(IDB_SKIN),"BINARY");
if (!hrSkin) return false;

/ / Завантажуємо стандартний “BINARY” ресурс.
LPRGNDATA pSkinData = (LPRGNDATA)LoadResource(hInstance, hrSkin);
if (!pSkinData) return false;

/ / Створюємо регіон.
HRGN rgnSkin = ExtCreateRegion(NULL, SizeofResource(NULL,hrSkin), pSkinData);

/ / Звільняємо виділений ресурс
FreeResource(pSkinData);


Після цього, регіон досить буде причепити до вікна. І не забудьте видалити
регіон перед завершенням програми командою DeleteObject (rgnSkin).


У висновку, непогано було б укласти весь вищенаведений матеріал в
клас, щоб код зручніше читався:



Удачи!

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*