O сохранении иконок 32х32 в 256-цветном формате

=== O сохранении иконок 32х32 в 256-цветном формате ===
Высылаю Вам информацию для FAQ о сохранении иконок 32х32 в 256-цветном
формате. Распоряжайтесь ею как Вам нужно. Размер кода большой, думаю для FAQ
не годится, скорее для раздела "Обмен опытом", но Вам видней. И сообщите,
пожалуйста, где эта информация будет размещена, чтоб не посылать каждому,
кто обратится, все описание, а просто дать ссылку.
С уважением, Владимир, г.Иркутск, 'nsvi@hotbox.ru'
Суть вопроса: я столкнулся с проблемой сохранения полноцветных иконок, когда
понадобилось немного изменить имеющиеся у меня для своих программ. Ни родной
Image Editor от Delphi6, ни другие редакторы не смогли мне помочь. Могли это
делать платные редакторы, но они не для нас. Начав разбираться, я обнаружил,
что созданная функцией CreateIconIndirect иконка нормально выглядит, если
после создания ее кинуть на форму, однако после записи Icon.SaveToFile
иконка обезображивается. Это происходит на стадии записи иконки. Поискав
информацию, облазив форумы, я понял, что либо этой проблемой никто не
занимался, либо с ней все мирятся и заниматься не хотят, хотя интересующиеся
имеются.
Став заниматься проблемой вплотную, я решил использовать нетипизированные
файлы, которым все равно, что в них записывают. Эти файлы хорошо подходят
для работы с массивами байт и, если создать массив полностью соответствующий
структуре иконки, то записав массив в нетипизированный файл, получим искомую
иконку.
Пришлось изучить структуру файла ICO и вот результат.
Для использования процедуры нет необходимости изучать файл ICO, если только
вы не хотите ничего изменять, например, размеры иконки. В принципе, можно
сделать универсальную процедуру, которая бы определяла размер иконки и, в
соответствии с размерами, создавала иконку. Все адреса и размеры, указанные
в процедуре, являются абсолютными и изменению не подлежат, так как именно от
них зависит, будет ли соответствовать создаваемый массив файлу ICO.
В процедуру введено автоопределение количества цветов палитры, от этого
зависит структура файла. Вам нет нужды указывать количество используемых
цветов.
Кроме того, введено определение размера рисунка и если он не 32х32, то
процедура прекратит работу. Это произойдет и в случае если кому-то захочется
использовать более 256 цветов. Тем, кто хочет узнать о структуре файлов ICO,
читать после кода процедуры.
Используя этот метод можно работать с любыми файлами, например, для открытия
и побайтного изменения EXE-файлов.
Для работы этой процедуры нужен рисунок 32х32 на канве (у меня Image2),
диалоговое окно SaveDialog1 (но не SavePictureDialog1)
и все. Если нужно, измените имя канвы, оно встречается в двух строчках кода,
и имя диалогового окна. Процедура с комментариями ниже:
// Записываем картинку как иконку (файл .ico)
{ Так как стандартные функции не дают возможности создавать 256 цветные
иконки, мы пойдем другим путем, хоть он и медленнее и длиннее.
Создадим массив байт, который запишем как файл, это и будет
искомая иконка. Для этого создадим промежуточные массивы, иначе процедура
станет слишком навороченной, затем проверим количество используемых цветов
и, в зависимости от этого, создадим окончательный массив. }
procedure TForm1.ToolButton6Click(Sender: TObject);
var
 MAnd, MXor, MOsn, MCol: array of Byte;
 MPix, MOrg: array of TColor;
 a, b, c, n, m: integer;
 Bt, Bu, Indicator: Byte;
 p: TColor;
 k: boolean;
 F: file;
 LenOsn, LenXor, LenCol: integer;
begin
 if (Image2.Picture.Height <> 32) or (Image2.Picture.Width <> 32) then
 begin
 ShowMessage('Исходный рисунок не 32х32');
 exit;
 end;
{ Создание массивов: MAnd - массив маски AND, MXor - массив маски Xor,
основного массива MOsn - куда будет собираться вся информация,
массив MPix - массив пикселов рисунка начиная с левого нижнего угла,
MCol - массив таблицы цветов - палитры, MOrg - массив оригинальных цветов }
{ Заполнение массивов MPix и MAnd }
{ Активизация массивов }
 SetLength(MPix, 1024);
 SetLength(MAnd, 128);
{ Установка счетчиков }
 b := 0; // Счетчик битов
 a := 0; // Счетчик пикселов картинки
 c := 0; // Счетчик байтов маски MAnd
 Bt := 0; // Формируемый байт маски MAnd
{ Преобразование картинки в удобный формат, с началом от 0,
и перенос ее в таком виде в массив MPix, меняя по пути белый -
будущий прозрачный цвет - на черный, заодно заполняем массив MAnd }
 for m := 31 downto 0 do
 begin
 for n := 0 to 31 do
 begin
 MPix[a] := Image2.Canvas.Pixels[n, m];
 p := MPix[a];
 if p = RGB(255, 255, 255) then
 begin
 MPix[a] := RGB(0, 0, 0);
 if b = 0 then
 Bt := Bt + 128;
 if b = 1 then
 Bt := Bt + 64;
 if b = 2 then
 Bt := Bt + 32;
 if b = 3 then
 Bt := Bt + 16;
 if b = 4 then
 Bt := Bt + 8;
 if b = 5 then
 Bt := Bt + 4;
 if b = 6 then
 Bt := Bt + 2;
 if b = 7 then
 Bt := Bt + 1;
 end;
 b := b + 1;
 if b = 8 then
 begin
 MAnd[c] := Bt;
 Bt := 0;
 c := c + 1;
 b := 0;
 end;
 a := a + 1;
 end;
 end;
{ Заполнение маски MOrg нулями, предполагая 256 цветов }
 SetLength(MOrg, 256);
 for n := 0 to 255 do
 MOrg[n] := RGB(255, 255, 255);
{ Оцениваем количество цветов и от результата переделываем MOrg }
 a := 0; // Счетчик количества цветов
 for n := 0 to 1023 do
 begin
 p := MPix[n];
 k := false;
 for m := 0 to 255 do
 begin
 if k = false then
 if p = MOrg[m] then
 k := true;
 end;
 if k = false then
 begin
 if a > 255 then
 begin
 ShowMessage('Ваш рисунок имеет более 256 цветов');
 MOrg := nil;
 MPix := nil;
 MAnd := nil;
 exit;
 end;
 MOrg[a] := p;
 a := a + 1;
 end;
 end;
 LenOsn := 2238;
 LenXor := 1024;
 LenCol := 1024;
 Indicator := 1;
 if a < 15 then
 begin
 LenOsn := 766;
 LenXor := 512;
 Indicator := 0;
 end;
{ Заполняем маски нулями }
 SetLength(MXor, LenXor);
 SetLength(MCol, LenCol);
 for n := 0 to LenXor - 1 do
 begin
 MXor[n] := 0;
 end;
 for n := 0 to LenCol - 1 do
 begin
 MCol[n] := 0;
 end;
{ Заполнение массива MXor согласно массива MPix }
 Bu := 0;
 b := 0;
 c := 0;
 for n := 0 to 1023 do
 begin
 p := MPix[n];
 k := false;
 for Bt := 0 to 255 do
 begin
 if k = false then
 begin
 if p = MOrg[Bt] then
 begin
 k := true;
 if indicator = 1 then
 MXor[n] := Bt
 else if b = 1 then
 begin
 Bu := 16 * Bu + Bt;
 MXor[c] := Bu;
 c := c + 1;
 b := 0;
 end
 else
 begin
 Bu := Bt;
 b := 1;
 end;
 end;
 end;
 end;
 end;
{ Заполняем массив MCol согласно массива MOrg }
 begin
 a := 0;
 for n := 0 to 255 do
 begin
 p := MOrg[n];
 MCol[a] := GetBValue(p);
 MCol[a + 1] := GetGValue(p);
 MCol[a + 2] := GetRValue(p);
 a := a + 4;
 end;
 end;
{ Заполняем MOsn нулями }
 SetLength(MOsn, LenOsn);
 for n := 0 to LenOsn - 1 do
 MOsn[n] := 0;
{ Заполняем массив MOsn для 256 цветной иконки }
 if indicator = 1 then
 begin
 MOsn[2] := 1;
 MOsn[4] := 1;
 MOsn[6] := 32;
 MOsn[7] := 32;
 MOsn[14] := $A8;
 MOsn[15] := 8;
 MOsn[18] := $16;
 MOsn[22] := $28;
 MOsn[26] := 32;
 MOsn[30] := $40;
 MOsn[34] := 1;
 MOsn[36] := 8;
 MOsn[42] := $80;
 MOsn[43] := 4;
 MOsn[55] := 1;
 m := 0;
 for n := 62 to 1085 do
 begin
 MOsn[n] := MCol[m];
 m := m + 1;
 end;
 m := 0;
 for n := 1086 to 2109 do
 begin
 MOsn[n] := MXor[m];
 m := m + 1;
 end;
 m := 0;
 for n := 2110 to 2237 do
 begin
 MOsn[n] := MAnd[m];
 m := m + 1;
 end;
 end;
{ Заполняем массив MOsn для 16-цветной }
 if indicator = 0 then
 begin
 MOsn[2] := 1;
 MOsn[4] := 1;
 MOsn[6] := 32;
 MOsn[7] := 32;
 MOsn[8] := 4;
 MOsn[14] := $E8;
 MOsn[15] := 2;
 MOsn[18] := $16;
 MOsn[22] := $28;
 MOsn[26] := 32;
 MOsn[30] := 64;
 MOsn[34] := 1;
 MOsn[36] := 4;
 MOsn[43] := 2;
 MOsn[54] := 16;
 m := 0;
 for n := 62 to 125 do
 begin
 MOsn[n] := MCol[m];
 m := m + 1;
 end;
 m := 0;
 for n := 126 to 637 do
 begin
 MOsn[n] := MXor[m];
 m := m + 1;
 end;
 m := 0;
 for n := 638 to 765 do
 begin
 MOsn[n] := MAnd[m];
 m := m + 1;
 end;
 end;
{ Закрываем все массивы, кроме MOsn }
 MAnd := nil;
 MXor := nil;
 MOrg := nil;
 MCol := nil;
 MPix := nil;
{ Записываем массив MOsn в файл, для этого создаем нетипизированный файл,
активизируем его и побайтно пишем в него данные из массива MOsn }
 if SaveDialog1.Execute then
 begin
 AssignFile(f, SaveDialog1.FileName + '.ico');
 Rewrite(f, 1);
 for n := 0 to LenOsn - 1 do
 begin
 Bt := MOsn[n];
 BlockWrite(f, Bt, 1);
 end;
 CloseFile(f);
 MOsn := nil;
 end;
end;
Существуют иконки более сложной структуры. Они могут содержать несколько
рисунков. Я с ними не разбирался.
Может быть кто-то из профессионалов скажет - примитивщина, нагородил. Ну что
ж, может быть есть пути проще и лучше, но когда я спрашивал на форумах никто
не смог помочь. Главное, работает без проблем.
Я всего месяц как познакомился с Delphi и Паскалем, думаю
можно кое-что и простить.
О структуре иконки. Информации немного в интернете, но кое-что я раскопал.
Пришлось просмотреть коды разных иконок, чтобы иметь о них представление.
Файл ICO очень похож на BMP:
в начале файла заголовок BITMAPFILEHEADER, размером обычно 22 байта. В нем
содержится информация о файле. За ним идет BITMAPINFOHEADER, 40 байт,
содержит информацию о ширине и высоте иконки в пикселах, количество бит на
пиксел в структуре картинки, занимаемое место таблицей цветов и другая
информация. Для примера оба заголовка для 16-цветной иконки.
В квадратных скобках номер байта в файле, далее его значение.
 [2] := 1; // Всегда 1
 [4] := 1; // Всегда 1
 [6] := 32; // Ширина и
 [7] := 32; // высота в пикселах?
 [8] := 4 // Не знаю
 [14] := $E8; // Младший байт размера файла минус 22(размер BITMAPFILEHEADER)
 [15] := 2; // Старший байт размера файла
 [18] := 22; // Размер BITMAPFILEHEADER
 [22] := 40; // Размер заголовка BITMAPINFOHEADER
 [26] := 32; // Ширина в пикселах
 [30] := 64; // Высота в пикселах обоих масок в сумме
 [34] := 1; // Число плоскостей
 [36] := 4; // бит/пиксел для таблицы пикселов
 [43] := 2; // Старший байт размера таблицы пикселов (т.е.$200=512)
 [54] := 16; // Число используемых цветов (не обязательно)
Остальные не указанные байты и до адреса 62 все 0.
После заголовков находится таблица цветов.
Каждый цвет занимает 4 байта. По порядку - синий, зеленый, красный и нулевой
байты. Первые 3 байта образуют цвет пиксела. То есть, каждый пиксел на
иконке может иметь 256х256х256 оттенков. При 16 цветной иконке таблица
цветов занимает 16х4=64 байта, при 256 цветной - 1024 байта. По идее, при
записи иконки любая программа должна просканировать иконку и все найденные
цвета записать в таблицу.Почему этого не происходит я не знаю. Windows эти
цвета при записи игнорирует и устанавливает свои.
Если же при 16 цветной палитре используются только, допустим, 6 цветов, то
остальные 10 цветов не нужны, однако место для них остается, но оно не
используется.
Следующим блоком в файле идет таблица пикселов. Первый пиксел это самый
нижний в левом углу. И переписываются они в таблицу построчно. Основной
параметр, по которому таблица организуется, это количество бит на пиксел.
Каждый элемент этой таблицы - это номер цвета в таблице цветов. При выводе
на экран иконки берется очередной элемент, то есть номер цвета в таблице
цветов, берет по номеру цвет из таблицы цветов и этот цвет выводится на
экран. Так как для определения номера в таблице из 16 цветов достаточно 4
бит, то каждый байт таблицы пикселов определяет 2 пиксела. Причем, младшие 4
разряда относятся к первому, а старшие - к следующему пикселам.
Для 256 цветов необходим полный байт. Поэтому, таблица пикселов занимает
соответственно 512 и 1024 байтов.
Таблица пикселов это образ маски XOR иконки, цвет, который должен быть
прозрачным, заменен черным, в остальном же это рисунок, который вы создали в
редакторе.
И последним идет таблица маски AND. Это двухцветная маска вашего рисунка,
имеет только белый цвет (бит включен) и черный цвет (бит равен 0). Белый
цвет находится там, где будет прозрачный цвет, черный скрывает все
остальное. При наложении двух масок друг на друга тот пиксел, у которого на
маске AND белый, а на маске XOR черный цвет, будет невидим.
Так как маска AND монохромная, то один байт содержит информацию о 8
пикселах, а размер маски будет 1024/8=128 байт.
Имеем:
для 16-цветной иконки размер 22+40+64+512+128=766 байт,
для 256 - 22+40+1024+1024+128=2238 байт.
Создав массив из соответствующим образом пребразованного рисунка, мы можем
записать его байты в нетипизированный файл, которому все равно, что в нем
находится, добавив к имени расширение .ico, мы получим то, что было нужно.
Конечно, есть разные варианты размеров иконок, у них могут меняться размеры
заголовков, использоваться другая информация, но структуры эти должны
присутствовать. Существуют иконки гораздо большие размером, но я с такими не
разбирался.
Source: Взято с Vingrad.ru http://forum.vingrad.ru
ID: 02342