Графика в Delphi - примеры задач на построение графиков, а также компоненты для графики

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

Суть вопроса: я столкнулся с проблемой сохранения полноцветных иконок, когда понадобилось немного изменить имеющиеся у меня для своих программ. Ни родной 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
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;
{Заполнение маски 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
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;
{Заполняем массив 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, мы получим то, что было нужно. Конечно, есть разные варианты размеров иконок, у них могут меняться размеры заголовков, использоваться другая информация, но структуры эти должны присутствовать. Существуют иконки гораздо большие размером, но я с такими не разбирался.

Powered WP Ъ скачать delphi, delphi 7, скачать delphi 7, delphi файлы, delphi, компоненты, delphi 2009, delphi программы, delphi бесплатно, delphi скачать, бесплатно работа delphi, delphi создание, delphi строки, программирования delphi, borland delphi, delphi формы