Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


32-битные иконки: закат солнца вручную

Поиск:
Статья написана мной чёрт знает в каком году. В ней есть несколько неточностей, однако в целом она мне показалась интересной. Решил запостить, мало ли - пригодиться.

32-битные иконки: закат солнца вручную


С каждой новой версией операционная система Microsoft Windows® становится всё нарядней и нарядней. Как только не ухитряются сотрудники известной компании, какие только игрушки не навешивают на неё, забывая порой, что скорость и надёжность работы программ ценится куда выше, чем внешний вид... Но оставим философствования и упреки тем, кто по профессии призван заниматься ими; у нас же слишком мало на то времени...
Итак, с выходом Windows XP, столь широко распространённые значки стали просто неописуемы! - в них, наконец, добавили функции прозрачности. Но не заметили ли вы чего-нибудь странного? Почему эти полупрозрачные значки не открываются нашими стандартными функциями? большинство программ по их редактированию вообще не хотят читать их, а те немногие, что шагнули вместе с системой, как правило, требуют немалых денег для использования. Но ладно, сделаем вид, что не заметили.. В новой версии программы радостно пишем код, загружающий иконки со стандартных Windows-приложений, запускаем... дулю! WinAPI только ехидно посмеётся над вами и в лучшем случае нарисует нечто отдалённо похожее на оригинал, но окружённое чёрными пятнами.. не узнаёте ситуацию?
Что ж это получается - монополия? Неужели корпорация майкрософт, не без пафоса будь сказано, таким образом думает оказаться заранее на более высокой планке, чем все сторонние разработчики? Смешно! Реакция этой гиганской софтверной компании похожа на реакцию маленького ребёнка, который старается поломать песочный домик приятеля, только чтобы казаться окружающим единственным и неповторимым!.. Страшно сказать, но эта детская логика оказывается действенна - не раз я сам из двух более-менее схожих по возможностям программ выбирал именно ту, в которой красивее нарисованы кнопочки, менюшки, иконки... Иногда так и хочется задать вопрос компании: может вам вообще сторонние программисты не нужны? Глядя на цены Dev-пакетов + внезапно возросший интерес компании к языкам "сверхвысокого" уровня, идея эта перестаёт почему-то казаться бредовой: на .NET оси не напишешь!..
Но бог с ней - с политикой. Нам иконки нужны, по-красивей и побольше! Спокойствие, если нам не дают возможность отображать в наших программах полупрозрачные иконки... мы возьмём её сами!..

Формат ICO (как и CUR) не представляет из себя ничего особенного. Всё представление можно разбить на пару структур, вложенных друг в друга. Спускаясь как бы сверху вниз, разберём строение файла и способ его рисования. Итак, на самом "высоком" уровне находятся только две структуры:

Код

struct ICON {
ICONHEADER ich;
ICONIMAGE* icm;
};


Число элементов ICONIMAGE равно числу пиктограмм, находящихся в файле. Структура ICONHEADER представляет собой нечто вроде каталога пиктограмм:

Код

struct ICONHEADER {
WORD ihReserved;
WORD ihType;
WORD ihCount;

ICONDIRENTRY* ihEntries;
};


ihReserved - не понимаю, зачем нужны подобные "резервы"; данный байт по идее должен быть равен нулю, но не советую полагаться на это...
ihType - тип файла; 1 - иконка, 0 - курсор.
ihCount - количество пиктограм, находящихся в файле.
ihEntries - массив описаний пиктограмм; как не трудно догадаться, число элементов массива равно ihCount.

ICONDIRENTRY содержит в себе размеры изображения, число цветов и ещё пару переменных, которые включены в формать только для удобства:

Код

struct ICONDIRENTRY {
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wReserved1;
WORD wReserved2;
DWORD dwBytesInRes;
DWORD dwImageOffset;
};


bWidth и dHeight содержат длину и ширину файла; надо заметить, это не единственные размерные величины, но самые верные - если они не соответствуют действительному размеру, файл испорчен.
bColorCount указывает на количество цветов пиктограммы; бесполезнейшая штука, так как в 24-32 разрядных режимах количество всех доступных цветов в один байт не уместится - да и надо ли оно?!..
dwBytesInRes, следуя книге 95-го года, содержит размер в байтах ресурса пиктограммы, который состоит из всех пиктограмм файла минус ICONHEADER; логичней было бы сделать просто размер данной пиктограммы в байтах...
dwImageOffset - байтовое смещение до начала изображения пиктограммы, начиная от начала файла.

Теперь пришло время описать структуру самого изображения пиктограммы:

Код

struct ICONIMAGE {
BITMAPINFOHEADER icHeader;// 40 байт
RGBQUAD* icColors;
BYTE* icXOR;
BYTE* icAND;
};


icHeader содержит в себе информацию о данной пиктограмме; как и RGBQUAD, структура определена в стандартных windows-заголовочных файлах; вот, что она собой представляет:

Код

struct BITMAPINFOHEADER {
DWORD biSize;// обязан содержать 0x28 - размер структуры
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
};


Не поленюсь и объясню все переменные структуры, хотя большинство из них совершенно бесполезны.
biWidth - формально ширина изображения в пикселях; на практике может быть что угодно, лучше не пользоваться...
biHeight - формально высота изображения; см. выше...
biPlanes - должен быть равен единице; толкового объяснения зачем это слово нужно так и не нашёл...
biBitCount - число битов на пиксел. Вот эту переменную и нужно использовать при определении разрешения пиктограммы! Возможные значения: все степени двойки. На практике сейчас используются в основном 4, 8, 24, 32.
biCompression - показывает, хранится ли данное растровое изображение в сжатом виде, а также метод его упаковки. BI_RGB - изображение не сжато, BI_RLE8 - восьмиразрядное групповое кодирование, BI_RLE4 - четырёхразрядное групповое кодирование. Лично я сжатых иконок не встречал, а вы?..
biSizeImage - содержит размер растрового изображения в байтах. Может быть нулевым, если изображение не сжато.. Короче: не используйте эту переменную.
biXPelsPerMeter - предпочтительное разрешение по горизонтали в пикселах на метр. Чушь, не встречал использования ни разу...
biYPelsPerMeter - предпочтительное разрешение по вертикали в пикселах на метр; см. выше...
biClsUsed - обычно содержит число цветов, используемое в изображении и определяемое массивом iсColors типа RGBQUAD. Если равно нулю, в изображении используется максимальное количество цветов, определяемое переменной biBitCount.
biClrImportant - содержит число важных цветов изображения... тьфу, ставим нуль - все цвета важные, и нечего голову ломать!..

RGBQUAD структура содержит в себе три байтовых переменные - Red, Green, Blue. Чтобы массив таких структур было удобней читать, добавлена ещё одна "резервная" байтовая переменная. Массив icColors содержит 24-битные определения цветов изображения; если biBitCount больше или равно 24 - массива icColors не существует. Соответственно, при biBitCount=8, icColors содержит 2^8=256 элементов.

Теперь пришло время пояснить один момент: каким именно образом принято рисовать пиктограммы. Алгоритм следующий.
Рисование изображения сильно зависит от значения переменной biBitCount. Но в любом случае рисование происходит от левого края к правому, от нижней строки, к верхней. Восьмибитное изображение в одном байте содержит 8 пикселей, расположенных на одной строке (ВНИМАНИЕ! пикселы идут от старшего разряда к младшему - момент, который пропускают почти все документации!); 24 разрядный пиксель требует 3 байт. Само изображение находится в массиве icXOR, но есть маленькая хитрость!..
Наверное не стоит объяснять, что пиктограммы имеют свойство быть как бы наклеенными на задний план: иначе говоря рисуется не весь прямоугольник, а только нужная нам картинка. Как это получается? Очень просто! Вы уже должны были обратить внимание на массив icAND, стоящий сразу за массивом пикселей изображения. Это, так называемая, маска пиктограмы. Она представляет собой чёрно-белое изображение той же длины и ширины, что и icXOR, но используется несколько иначе: единица в маске указывает на то, что изображение в данной точке не рисуется, ноль - соответственно рисуется. В 32-разрядном режиме на один пиксель приходится 4 байта, причём один из них - коэфициент прозрачности (0 - абсолютно прозрачно, 255 - абсолютно непрозрачно). Все эти хитрости легко понять на конкретном примере. Давайте попробуем написать функции, которые будут загружать иконку в оперативную память и отображать на контексте устройства (DC).

Сначала определим ещё раз структуры:

Код

struct ICONDIRENTRY {
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wReserved1;
WORD wReserved2;
DWORD dwBytesInRes;
DWORD dwImageOffset;
};

struct ICONHEADER {
WORD ihReserved;
WORD ihType;
WORD ihCount;

ICONDIRENTRY *ihEntries;
};

struct ICONIMAGE {
BITMAPINFOHEADER icHeader;
RGBQUAD *icColors;
BYTE *icXOR;
BYTE *icAND;
};

struct ICON {
ICONHEADER ich;
ICONIMAGE *icm;
};


Теперь пишем функцию для загрузки из файла:

Код

bool LoadIconFromFile(LPCSTR FileName, ICON &icon)
{
FILE* file = fopen(FileName, "rb");
if(!file) return FALSE;

fread(&icon.ich, 1, 6, file);

if(icon.ich.ihReserved || icon.ich.ihType != 1) return FALSE;
if(!icon.ich.ihCount) return FALSE;

icon.ich.ihEntries = (ICONDIRENTRY*)malloc(sizeof(ICONDIRENTRY)*
icon.ich.ihCount);
icon.icm = (ICONIMAGE*)malloc(sizeof(ICONIMAGE)*icon.ich.ihCount);

fread(icon.ich.ihEntries, 1, sizeof(ICONDIRENTRY)*icon.ich.ihCount, file);

for(int i = 0; i < icon.ich.ihCount; i++)
{
if(!icon.ich.ihEntries[i].bWidth || !icon.ich.ihEntries[i].bHeight)
return FALSE;

fread(&icon.icm[i].icHeader, 1, sizeof(BITMAPINFOHEADER), file);

t = icon.ich.ihEntries[i].bWidth*icon.ich.ihEntries[i].bHeight;
switch(icon.icm[i].icHeader.biBitCount) {
case 1:
icon.icm[i].icColors = (RGBQUAD*)malloc(sizeof(RGBQUAD)*2);
fread(icon.icm[i].icColors, 1, sizeof(RGBQUAD)*2, file);
icon.icm[i].icXOR = (BYTE*)malloc(t/8);
fread(icon.icm[i].icXOR, 1, t/8, file);
icon.icm[i].icAND = (BYTE*)malloc(t/8);
fread(icon.icm[i].icAND, 1, t/8, file);
break;
case 4:
icon.icm[i].icColors = (RGBQUAD*)malloc(sizeof(RGBQUAD)*16);
fread(icon.icm[i].icColors, 1, sizeof(RGBQUAD)*16, file);
icon.icm[i].icXOR = (BYTE*)malloc(t/2);
fread(icon.icm[i].icXOR, 1, t/2, file);
icon.icm[i].icAND = (BYTE*)malloc(t/8);
fread(icon.icm[i].icAND, 1, t/8, file);
break;
case 8:
icon.icm[i].icColors = (RGBQUAD*)malloc(sizeof(RGBQUAD)*256);
fread(icon.icm[i].icColors, 1, sizeof(RGBQUAD)*256, file);
icon.icm[i].icXOR = (BYTE*)malloc(t);
fread(icon.icm[i].icXOR, 1, t, file);
icon.icm[i].icAND = (BYTE*)malloc(t/8);
fread(icon.icm[i].icAND, 1, t/8, file);
break;
case 24:
icon.icm[i].icColors = NULL;
icon.icm[i].icXOR = (BYTE*)malloc(t*3);
fread(icon.icm[i].icXOR, 1, t*3, file);
icon.icm[i].icAND = (BYTE*)malloc(t/8);
fread(icon.icm[i].icAND, 1, t/8, file);
break;
case 32:
icon.icm[i].icColors = NULL;
icon.icm[i].icXOR = (BYTE*)malloc(t*4);
fread(icon.icm[i].icXOR, 1, t*4, file);
icon.icm[i].icAND = (BYTE*)malloc(t/8);
fread(icon.icm[i].icAND, 1, t/8, file);
break;
}
}

fclose(file);

return TRUE;
}


Оговорюсь: я не ставлю перед собой цель дать вам готовый код, совсем нет! Я лишь объясняю логику и для большей понятности прибегаю иногда даже к неэффективности!.. Так что, оптимизация остаётся на ваших плечах...

Теперь функция рисования:

Код

void ExDrawIcon(HDC hdc, ICON ic, BYTE nImage, WORD PosX, WORD PosY)
{
if(nImage >= ic.ich.ihCount) return;

WORD wd = ic.ich.ihEntries[nImage].bWidth;
WORD hg = ic.ich.ihEntries[nImage].bHeight;
BYTE bt = ic.icm[nImage].icHeader.biBitCount;

BYTE* xr = icon.icm[nImage].icXOR;
BYTE* an = icon.icm[nImage].icAND;
RGBQUAD* clr = icon.icm[nImage].icColors;

WORD y = hg, x = 0, i;
BYTE b[3];
bool bTh = true;

for(i = 0; i < wd*hg; i++) {
switch(bt)
{
case 4:
b[0] = xr[i/2];
if(!bTh) b[0] &= 0xF;
else b[0] >>= 4;
if(!(an[i/8]&(0x01<<(7-i%8))))
SetPixel(hdc, PosX+x, PosY+y, RGB(clr[b[0]].rgbRed,
clr[b[0]].rgbGreen, clr[b[0]].rgbBlue));

bTh = !bTh;
break;

case 8:
b[0] = xr[i];
if(!(an[i/8]&(0x01<<(7-i%8))))
SetPixel(hdc, PosX+x, PosY+y, RGB(clr[b[0]].rgbRed,
clr[b[0]].rgbGreen, clr[b[0]].rgbBlue));

bTh = !bTh;
break;

case 24:
b[0] = xr[i*3+2];
b[1] = xr[i*3+1];
b[2] = xr[i*3];
if(!(an[i/8]&(0x01<<(7-i%8))))
SetPixel(hdc, PosX+x, PosY+y, RGB(b[0], b[1], b[2]));

bTh = !bTh;
break;

case 32:
b[0] = xr[i*4+2];
b[1] = xr[i*4+1];
b[2] = xr[i*4];
b[3] = xr[i*4+3];
if(!(an[i/8]&(0x01<<(7-i%8)))) {
COLORREF pix = GetPixel(hdc, PosX+x, PosY+y);
BYTE R = GetRValue(pix), G = GetGValue(pix), B = GetBValue(pix);
b[0] = (b[0]*b[3] + R*(255-b[3]))/255;
b[1] = (b[1]*b[3] + G*(255-b[3]))/255;
b[2] = (b[2]*b[3] + B*(255-b[3]))/255;
SetPixel(hdc, PosX+x, PosY+y, RGB(b[0], b[1], b[2]));
}

bTh = !bTh;
break;
}
if(++x == wd) { x = 0; y--; }
}
}


Вот, кажется, и всё. Можете скомпилировать код, проверить - все 32-битные иконки будут отлично отображаться на любом фоне!.. "Где же их взять?" - скажете вы? "Нарисовать!" - отвечу я! Раз вы теперь знаете формат пиктограмм, почему бы не написать вам программку, в которой можно было бы рисовать замечательные прозрачные иконки?..

Удачного пиктограммирования!..

© 2006 Левенков Артём Алексеевич [ManiaK]
Автор: ManiaK
Сайт: http://sch2.krasno.ru/






Просмотров: 3427

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную