Прозорість - це просто

Windows 98 і бібліотека msimg32.dll

Перш ніж почати, переконаємося, що розуміємо один одного:
  • Растрове зображення, растр (bitmap) - прямокутна картинка, що складається з пікселів.
  • Піксель - мінімальний елемент зображення, точка на екрані або в пам'яті растра.
  • Прозорість - властивість деяких пікселів не відображатись на пристрої виведення, залишаючи оригінальне зображення незмінним.
  • Напівпрозорість - така взаємодія пікселів, при якому видно як пікселі виведеного растра, так і фонове зображення.
  • Спрайт - растрове зображення з прозорими і напівпрозорими ділянками.

Навіщо потрібні растрові зображення з прозорістю або полупрозрачностью окремих ділянок? Це важливі елементи графічного інтерфейсу Windows, які ви можете спостерігати кожен раз, коли включаєте комп'ютер. Іконки на робочому столі мають прозорі ділянки, що дозволяє бачити "крізь" них. Коли ви, працюючи в папці Windows 98, перетаскуєте який-небудь об'єкт, його значок стає напівпрозорим, дозволяючи бачити, що в даний момент знаходиться під ним. І, нарешті, не можна забувати про такі "графікоемкіх" програмах, як гри. Важко собі уявити, щоб в ретельно спроектованої плоскою космічної "стрелялке" всі кораблі мали прямокутну форму. А при відображенні вибухів бажано робити їх напівпрозорими, наближаючи картинку до реальності.

Взагалі - то на цю тему писали досить багато. Для розуміння основних механізмів отримання ефектів прозорості рекомендую прочитати статтю Рона Джері "Bitmaps with transparency" (її можна знайти в MSDN в розділі Technical Articles-> Multimedia-> GDI). Також рекомендую вивчити знаходяться там статті Дейла Роджерсона ( "Sprites Make the World Go Round") і Германа РОДЕНТ ( "Animation in Win32").

Windows 95 і списки зображень

З виходом Windows 95 в розпорядженні програмістів виявилася зручна бібліотека Common Controls. В її складі були не тільки нові (тепер уже відомі всім) елементи управління, а й невізуальний компонент - список зображення (Image List control). Його основне призначення - містити набір картинок однакового розміру. Це зручно для застосування в різноманітних елементах - наприклад, в панелях інструментів (toolbar):

Нас же більше цікавить інша цікава можливість - зберігати в списках зображень інформацію про прозорість. Це досягається одним з двох способів:

  • Завантаженням растрового зображення із зазначенням, пікселі якого кольору вважати прозорими;
  • Підготовкою спеціальної маски прозорості - чорно-білого растра, в якому пікселі чорного кольору означають прозорість для відповідних точок основного растра. При цьому маска прозорості повинна мати такі ж розміри, що і основний растр.

Потрібно розуміти, що в обох випадках список зображень буде містити маску прозорості, просто при першому способі вона буде створена за вас. Який спосіб вибрати - справа смаків кожного програміста. Я зазвичай знаходжу в палітрі який-небудь непотрібний колір і призначаю його прозорим. У більшості випадків не щастить яскраво-бузкового кольору (, RGB = (255,0,255)), але в цей раз для демонстрації ефекту прозорості я вибрав білий:


Створення списків прозорих зображень

Створити список зображення і завантажити в нього растр з прозорістю можна так:

У наведених прикладах передбачатиметься, що растрові зображення знаходяться в ресурсах програми і мають глибину кольору 24 біти (16 млн. Квітів). При створенні списку необхідно вказати розміри завантажується растра (cx, cy), його колірної формат (ILC_COLOR24, 16 мільйонів відтінків) і ознака наявності маски (ILC_MASK). Останні два параметри Create () визначають число збережених в списку зображень і величину приросту списку при нестачі місця. Макрос RGB зручний для вказівки кольору в 24-бітовому діапазоні, в даному випадку - кольору прозорості.

В принципі, завантажити картинку можна і одним викликом, але цей метод не підтримує повнокольорові зображення (як в нашому прикладі):

За замовчуванням, такий растр обмежений 16 кольорами.

Малювання за допомогою Image List

Для чого списки зображень дійсно корисні, так це для полегшення роботи з малювання. Ви згадайте навскидку число і порядок параметрів у BitBlt ()? А її незручність через необхідність підготувати додатковий контекст пристрою в пам'яті? Все це здатне збентежити не тільки новачка, але і більш досвідченого програміста.

Ситуація ще більш ускладнюється, коли ми маємо справу з прозорістю. Необхідно виводити картинку кілька разів з різними параметрами растрових операцій (Raster operation, ROP). Кожен раз, коли ми збираємося вивести на екран невелике зображення космічного корабля, необхідно виконати безліч роботи.

На щастя, програмісти Microsoft вже зробили її за нас. Можна намалювати растр, що міститься в списку зображень, просто викликавши функцію ImageList_Draw (). З використанням MFC цей виклик виглядає, наприклад, так:

Тут pDC - покажчик на контекст пристрою (CDC), 0 - порядковий номер виводиться із списку зображення, m_drawPoint - координати початку області виведення. Прапор ILD_TRANSPARENT вказує, що висновок потрібно здійснювати з урахуванням маски прозорості.

Для самих допитливих повідомлю, що реалізація ефекту прозорості при цьому досягається методом, який Рон Джері називає Black Source Method, тобто "Метод чорного джерела". Він дозволяє виводити зображення з прозорими ділянками за два виклики BitBlt () замість трьох, але вимагає попередньо замінити пікселі, які є прозорими, чорним кольором. Тому, завантажуючи растр в список зображень, ви міняєте його.

В результаті виклику вийде приблизно ось що:

Windows 98 і бібліотека msimg32.dll

Windows 98 принесла новий простий спосіб виведення прозорих зображень. Вхідна в її склад бібліотека msimg32.dll містить нові функції для отримання спокусливих графічних ефектів. Для її використання потрібно підключити до проекту при складанні файл msimg32.lib.

Тепер растр з прозорістю можна вивести за один прийом за допомогою функції TransparentBlt, вказавши прозорий колір в останньому параметрі функції:

"Нічого собі - за один прийом!" скажете ви і. матимете рацію. Знову з'являється необхідність в сумісному контексті пристрою, в якому потрібно вибрати виведений растр. Знову - довгий (і не інтуїтивний) список параметрів. Але зате з'явилися можливості по управлінню висновком. В даному прикладі x, y, x1, y1 - координати початкової точки растра в приймальнику і джерелі відповідно. Параметри dstX і dstY - розміри області виведення, а srcX і srcY - ширина і висота прямокутника, відображуваного з растра. Що якщо задати для них різні значення? Результат наведено тут:

Як бачимо, ця функція містить можливості зі стиснення / розтягування растрових зображень. Тільки не перестарайтеся і не передайте в якості розмірів негативні значення - дзеркального відображення TransparentBlt () створювати не вміє.

Додамо, що функція TransparentBlt () при виведенні спирається на можливості DirectX даного пристрою, що може дати приріст продуктивності в порівнянні з традиційними методами.

Порада
Не забудьте після використання контексту пристрою знову вибрати в ньому початковий растр (temp в нашому прикладі). В іншому випадку відбудеться витік графічних ресурсів системи.

AlphaBlend (): "Полу" - не обов'язково 1/2

Всі вищеописані методи працюють тільки з однією моделлю прозорості, званої в комп'ютерній графіці Chroma Key. Це означає, що прозорим призначається певний колір. Інша, більш розвинена, модель називається Alpha Blending. При її використанні для опису характеристик пікселів крім колірних компонент (R, G, B) застосовується прозорість (Alpha). Ступінь прозорості визначається зворотною величиною цього параметра.

Призначення її параметрів, в-общем, ясно з прототипу. Особливий інтерес представляє останній параметр - BLENDFUNCTION. Він являє собою структуру (всього з чотирьох байт), що визначає режим виведення.

Що це таке? Справа в тому, що AlphaBlend () може працювати в двох різних режимах.

Простий режим (загальна прозорість)

Перший (і найбільш простий у використанні) режим роботи AlphaBlend () передбачає, що значення Alpha задано для всієї картинки. В такому випадку, воно застосовується до всіх пикселам без винятку:

Цей результат був отриманий при такому форматі BLENDFUNCTION:

Для поля BlendOp в даний момент визначено тільки одне допустиме значення - AC_SRC_OVER.
Поле BlendFlags повинно містити 0.
Погано документований параметр AlphaFormat визначає взаємодію пікселів джерела і приймача, про що ми ще поговоримо далі.
Параметр SourceConstantAlpha визначає ступінь непрозорості. Задавши для цього поля 0, ви не побачите свій растр взагалі. Максимальне значення, уміщається в тип BYTE, так само 255. При цьому виводиться растр повністю перекриє область призначення. Але навіщо вам, в такому випадку, AlphaBlend ()? І це значення використовується, в-основному, для другого режиму.

Режим з альфа-каналом

Він вимагає деякої додаткової підготовки. В цьому режимі растр, підготовлений для виведення, повинен містити інформацію про ступінь прозорості кожного пікселя. Це досягається, наприклад, застосуванням формату 32 біта на піксель (по одному байту на кожен колірний компонент і одному - на альфа-канал).

Складності виникнуть ось із чим. Мені невідомий жоден графічний пакет, що дозволяє зберігати растри з альфа-каналом в форматі, придатному для AlphaBlend (). При створенні такого растра програмно ми можемо зберегти його на диск (в форматі Windows Bitmap 32-bit). Він прочитується популярними програмами типу ACDSee, але функції LoadBitmap () і LoadImage () відмовляються його завантажувати. При спробі помістити його в ресурс rc.exe у мене вивалився з повідомленням Internal error.

Але сумувати не варто. Ми можемо підготувати растр в пам'яті, скомбінувавши, наприклад, два растра - із зображенням об'єкта і його тіні. На щастя, функція AlphaBlend () може працювати з растрами, створеними за допомогою CreateDIBSection ():

Тут hdc - контекст, сумісний з пристроєм виведення, pbmi - покажчик на структуру BITMAPINFO з інформацією про розміри і колірної глибині створюваного растра. Параметр iUsage визначає, що буде міститися в буфері растра - індекси в палітрі кольорів (DIB_PAL_COLORS) або прямі їх значення (DIB_RGB_COLORS). Очевидно, що друге - для режиму TrueColor палітра не потрібна.
Покажчик на створений буфер при успішному виклику буде повернений в параметрі ppvBits.
В параметрах hSection і dwOffset при роботі з растром в пам'яті необхідно вказувати 0.

Примітка
Все вищесказане не означає, що спрайт з альфа-каналом не можна зберігати в ресурсах програми. Ви можете включити їх як custom resource, завантажити за допомогою LoadResource () і заповнити отриманими даними буфер, створений за допомогою CreateDIBSection (). Але не забувайте, що це значно збільшить розмір вашого виконуваного модуля. Крім того, можна розглянути варіант подгрузки попередньо розрахованих в подібній програмі растрів з зовнішніх файлів - засобами бібліотеки виконання або використовуючи параметри hSection і dwOffset при виклику CreateDIBSection ().

Використання CreateDIBSection () полегшує доступ до бітам зображення: в іншому випадку такі картинки можна було б вивести тільки в режимі екрана True Color 32 bit. Для звернення з таким форматом даних ідеально підходить структура RGBQUAD:

Альфа-канал можна зберігати в поле rgbReserved, хоча воно для цього і не призначалося :) Крім того, залишається ще одна (недокументированная) можливість - скористатися функцією AlphaDIBBlend (), яку ми розглядати не будемо.

Результат наведено тут:

Деталі роботи з бітами растров ми опустимо (все це можна знайти в доданому проекті). Зазначу тільки, що для виведення використовувався такий формат BLENDFUNCTION:

Порада
При використанні альфа-каналу у вас все одно залишається можливість без перерахунку бітів растра змінювати прозорість всієї картинки - SourceConstantAlpha працює і в цьому випадку.

І на завершення нагадаю, що AlphaBlend () також вимагає включення в проект при складанні бібліотеки імпорту msimg32.lib, яка відсутня в Windows 95.

Схожі статті