Использование TrueColor иконок в программе

Написанный ниже текст предназначен только для Windows XP (и ее продолжений)

Строго говоря, в этой статье описываются приемы работы с обновленным элементом библиотеки common controls 6.0 ImageList для обеспечения XP-интерфейса с использованием 32-битных битмапов.

Q: Что такое 32-битные картинки?

A: Для обеспечения нового интерфейса, в Windows XP были введены 32-битные изображения. По структуре они аналогичны битмапам типа TrueColor с 24-битным цветом. Однако, в них "базовая тройка" цветов каждой четырехбайтовой структуры RGBr (R - красный, G - зеленый, B - синий, r - резервный байт), описывающей точку в 24-х битных битмапах, заменена "базовой четверкой" RGBA, где резервный байт заменен действующим байтом, обозначающим т.н. "альфа-канал", т.е. этот байт обозначает прозрачность выбранной точки. При этом, значение 0x00 означает полностью прозрачную точку, а 0xFF - полностью непрозрачную точку.

Для обеспечения этой самой прозрачности в элемент ImageList библиотеки common controls Windows были внесены изменения. Однако эти самые изменения были внесены только в последнюю, шестую версию этой библиотеки. Для ее подключения к программе в Windows XP используется манифест.

- Как же использовать все эти нововведения в своей программе, - спросите вы.

Для начала нужно создать этот самый имаджлист.

Создание имаджлиста

При использовании Windows API нужно использовать функцию ImageList_Create
ImageList_Create(x, y, Константа_ILC_*, 0, 0);
при использовании MFC это же самое создание будет выглядеть так:
CImageList m_ImageList;
m_ImageList.Create(x, y, Константа_ILC_*, 0, 0);
В данных примерах используется странный параметр - "Константа ILC_*" он означает, что здесь, в зависимости от нужд программы (и программиста) используются разные значения. А нужд у нас с вами может быть (аж!) целых два варианта.

Вариант 1. Мы используем в качестве изображения 32-х битную иконку.

В этом случае все просто - можно смело ставить в качестве параметов "ILC_COLOR32 | ILC_MASK", т.к. иконки всегда используют для отрисовки изображения маску.

Т.е. в этом случае можно написать (для иконок формата 16x16):
ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 0, 0);
Вариант 2. Мы используем в качестве изображения 32-х битный битмап.

Этот случай немного сложнее. Однако, этого достаточно, чтобы его использовал в своих программах только Microsoft. На момент написания статьи (август 2002 года) я не видел ни одной программы, использующей 32-битные битмапы. Везде, изображения используются только в виде иконок.

Небольшое отступление. Обычно, битмапы при использовании в качестве изображений с использованием в качестве маски какого-либо цвета, которым закрашивается фон картинки. Обычно используется цвет Magenta - RGB(255, 0, 255), хотя можно использовать и любой другой.

В случае использования 32-х битных битмапов, маска абсолютно не нужна. Однако, вследствие того, что существуют режимы экрана, отличные от TrueColor, придется использовать два вида битмапов: с использованием цветовой маски и без нее. Поэтому, необходимо ввести функцию, проверяющую количество цветов экрана и, заодно, версию операционной системы. Вот они:
BOOL IsWinXP()
{
// Проверка операционной системы
  
DWORD dwVersion = GetVersion();

// Get major and minor version numbers of Windows
  
DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
  
DWORD dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));

// Check for Windows XP
if ((dwVersion < 0x80000000) &&                 // The OS is a NT family
  
(dwWindowsMajorVersion >= 5) &&
  (
dwWindowsMinorVersion >= 1))             // Windows NT 5.1 is an Windows XP version
  
return TRUE;

return FALSE;
}

int ScreenBPP()
{
// Возвращает количество битов на точку в данном режиме
   
int iRet;
   
HDC hdc = GetDC(NULL);
   
if (hdc != NULL)
   {
       
iRet = GetDeviceCaps(hdc, BITSPIXEL);
       
ReleaseDC(NULL, hdc);
   }
   
return iRet;
}
И, наконец третья функция, объединяющая первые две:
BOOL Set32BPP()
{
   
return (IsWinXP() & (ScreenBPP() >= 32));
}
Соответственно, если мы работаем в режиме TrueColor и в Windows XP мы используем 32-х битный битмап, а если нет - то, скажем, 256-цветный аналог.

Таким образом, создание имаджлиста выливается в следующий код:
ImageList_Create(16, 16, (Set32BPP())? 0 : ILC_MASK) | ILC_COLOR32, 0, 0);
Добавление изображения в созданный имаджлист.

Теперь, после того, как мы создали имаджлист с нужными параметрами, надо добавить в него эти самые изображения. Продолжим разбиение на два варианта (какие это варианты, см. выше)

Вариант 1: Вот здесь-то и проявляется неудобство этого варианта для использования иконок в качестве, скажем, изображений для панели инструментов (toolbar), содержащей порой до нескольких десятков кнопок: каждую иконку надо загружать в имаджлист отдельной командой!

Приведу пример для загрузки трех иконок. В нем использована MFC. Для использования этого кода в чистом API надо заменить "m_ImageList.Add" на "ImageList_AddIcon":
HICON m_hImage;
int n_Index;

// Добавляем иконки в имаджлист
m_hImage = LoadIcon(NULL, IDI_ICON1);
n_Index = m_ImageList.Add(m_hImage); // Иконка №0

m_hImage = LoadIcon(NULL, IDI_ICON2);
n_Index = m_ImageList.Add(m_hImage); // Иконка №1

m_hImage = LoadIcon(NULL, IDI_ICON3);
n_Index = m_ImageList.Add(m_hImage); // Иконка №2


Заметьте, что в этом случае иконки получают номера от нуля до двух.

Вариант 2: Несмотря на навороченное создание имаджлиста для этого варианта, все изображения сразу загружаются одной командой. Однако, при этом надо иметь как минимум два битмапа: один для варианта TrueColor с 32-х битным цветом и еще один - для варианта 256 и HiColor - цветных режимов.
// Загружаем битмапы
CBitmap m_Bitmap;

if (Set32BPP())
   
m_Bitmap.LoadBitmap(IDB_BITMAPXP32);
else
   
m_Bitmap.LoadBitmap(IDB_BITMAP256);
В этом случае, вы загружаете сразу столько изображений, сколько вам позволят ограничения системы.

При добавлении изображения в имаджлист необходимо помнить, что в случае 32-х битных изображений у нас в битмапе отсутствует маска и надо загрузить его соответствующим образом:
if (Set32BPP())
   
m_ImageList.Add(&m_ImageList, &m_ImageList);
else
   
m_ImageList.Add(&m_ImageList, RGB(255, 0, 255));
Опять-таки, в случае написания программы на чистом API код видоизменяется. Приведу его без разрыва на комментарий:
// Загружаем битмапы

HBITMAP hBmp;
if (Set32BPP())
{
   
hBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAPXP32));
   
ImageList_Add(hImageList, hBmp, hBmp);
}
else
{
   
hBmp = LoadBitmap(hInsatnce, MAKEINTERESOURCE(IDB_BITMAP256));
   
ImageList_AddMasked(hImageList, hBmp, RGB(255, 0, 255));
}
DeleteObject(hBmp);
Теперь, для воспроизведения изображения нужно либо самим воспользоваться функциями прорисовки изображения, входящими в имаджлист, либо передать указатель (на класс имаджлиста в случае MFC) или хендл на сам имаджлист (API) в контрол, который сам воспользуется этими же функциями отрисовки.

Ну, вот в общем-то и все. Надеюсь, что я прояснил еще один вопрос, возникающий при программировании для Windows XP. Удачного программирования.