Я уже давно не писал про форматы графических файлов, хотя эта тема многих интересует. Сегодня я вернулся к этой теме с очередным форматом, который интересует многих программистов - Adobe Photoshop. Этот формат очень сильно распространён, поэтому мне кажется, что эта статья будет самой интересной для тех, кто хочет заниматься графикой профессионально.
Самое главное преимущество формата Photoshop это то, что он является платформенно-независимым. Он поддерживается не только в PC-совместимых, но и в яблоках от Apple.
Файл PSD состоит из пяти основных частей - заголовок, блок данных цветового режима, блок ресурсов изображения, блок информации о слоях и на конец данные изображения. Все блоки, кроме заголовка имеют переменную длину. Только заголовок состоит из фиксированных 26 байтов. Я покажу тебе его в виде структуры для Delphi и С++:
Для Delphi
TPSDHeader = record
Signature: array[0..3] of Char;
Version: Word;
Reserved: array[0..5] of Byte;
Channels: Word;
Rows, Columns: Cardinal;
Depth: Word;
Mode: Word;
end;
Для С++
struct _PSD_HEADER
{
BYTE Signature[4];
WORD Version;
BYTE Reserved[6];
WORD Channels;
LONG Rows, Columns;
WORD Depth, Mode;
};
Теперь рассмотрим каждый параметр в отдельности:
Signature - идентификатор файла. Он должен быть всегда "8BPS";
Version - номер версии, всегда равен единице (1);
Reserved - резерв, он и в Африке резерв. Он должен быть обнулён.
Channels - количество цветовых каналов, включая альфа-канал. Может принимать значения от 1 до 24.
Rows и Columns - высота и ширина изображения. Изменяется от 1 до 30000.
Depth - глубина цвета или количество бит на канал. Может быть 1, 8 или 16.
Mode - цветовой режим.
Цветовой режим (Mode) может принимать значения:
0 - растровое изображение
1 - шкала серого
2 - индексированный цвет (используется палитра)
3 - в виде цветов RGB
4 - в виде цветов CMYK
5 - многоканальный цвет
6 - полутоновое изображение (дуплекс)
7 - цветовая модель Lab.
Когда ты прочитаешь заголовок, не торопись его использовать. Сначала нужно обратить своё внимание на то, что PSD файл - межплатформенный, поэтому его данные записаны не в формате PC. А именно, старший и младший бай поменяны местами, поэтому прежде чем использовать параметры их надо перевернуть. Функция для переворота целого есть в Delphi и называется она как:
function Swap(X);
Для переворота длинного целого числа я не видел функции, поэтому придётся это делать самостоятельно:
function SwapLong(Value: Cardinal): Cardinal; overload;
asm
BSWAP EAX
end;
Итак, все параметры, которые хранятся в виде WORD нужно переворачивать с помощью Swap, а все параметры в виде LONG нужно переворачивать с помощью SwapLong.Это правило действует не только для заголовка, но и для всех последующих данных.
После заголовка идет блок данных цветового режима. Длина этого блока записана сразу после заголовка PSD файла в виде длинного целого числа. Прочитав это число, ты "сможешь" узнать длину блока данных цветового режима. Я подчёркиваю, что ты "сможешь", но сразу не узнаешь, потому что это число тоже перевёрнуто. Не забудь его перевернуть с помощью SwapLong.
После того, как ты узнал длину блока данных цветового режима, можно приступать к его чтению. Хранящиеся в этом блоке данные зависят от параметра mode (цветовой режим). Если mode равно 2 (индексированный цвет), то последующие данные состоят из 768 байтов и содержат 256-ю палитру. Если mode = 6 (полутоновое изображение), то последующие данные хранят в себе специфическую информацию об экране. При других значениях mode длинна блока должна быть равна нулю и после 4-х байтового параметра указывающего его длину будет идти блок ресурсов изображения.
Блок ресурсов изображения - находится сразу же за блоком данных цветового режима. В нём также, в самом начале находятся четыре байта (число типа LONG), указывающее на длину блока. Весь блок - это структура, хотя и переменной длинны:
Для Delphi
TColorModeDataBlock = record
Type: array[0..3] of Char;
ID: Word;
Byte: string;
Size: Cardinal;
Data: array [0..Size] of Char;
end;
Для С++
struct _ColorModeDataBlock
{
BYTE Type[4];
WORD ID;
BYTE Name[ ];
LONG Size;
BYTE Data[ ];
};
Теперь снова о каждом параметре в отдельности:
Type - Первый параметр похож на сигнатуру и всегда 8BIM. По этой последовательности ты можешь проверить, в том ли ты находишься месте? Если нет, то можно поднимать панику. Это может означать испорченность данных.
ID - тип. В зависимости от значения этого параметра будет изменяться содержимое параметра Data. Немного позже я покажу возможные здесь варианты.
Name здесь находится простое имя, которое можно игнорировать. Самое интересное, что эта строка символов хранится в формате Паскаль.
Size - Длинна поля Data.
Data - непосредственно данные. Они могут быть длинной Size и их содержимое зависит от значения параметра ID.
Теперь о возможных значениях параметра ID. Я знаю далеко не все возможные значения, потому что фирма Adobe не хочет делиться своими секретами. Но то, что я смог выяснить я расскажу. Для этого я создал специальную таблицу:
ID
Тип
Значение в поле Data
03e8
array[0..4] of Char
Строки, столбцы, режим, глубина, каналы.
03eb
Таблица индексированных цветов.
03ed
Структура
Инфа о разрешении.
03f0
string
Заголовок, строка в формате Паскаль.
03f4
Инфа о формате пикселя в шкале серого и полутонового изображения.
03f5
Инфа о формате пикселя полутонового изображения.
03f6
Инфа о формате пикселя дуплексного полутонового изображения.
03f8
Функции передачи для цветных изображений.
03f9
Функции передачи для дуплексных изображений.
03f7
Функции передачи для "серых" изображений.
0401
Рабочий контур.
0406
Качество JPEG.
Это самые интересные идентификаторы, которые я знаю. Конечно же это не все, потому что большинство идентификаторов, которые я знаю просто устарели используются для изображений с голимым качеством. В принципе, весь этот блок можно смело игнорировать, но я всё же рассказал тебе о нём. Это чтобы ты имел хоть маленькое представление об инфе хранящейся здесь.
Прежде чем окончательно покончить с этим блоком я немного остановлюсь на идентификаторе 03ed. Как ты можешь видеть в моей таблице, данные будут содержать структуру. Возможно, ты захочешь прочитать её и воспользоваться хранящимися в ней данными. Вот как она выглядит:
Для Delphi
TPSDResInfo = record
HRes: Cardinal;
hResUnit, WidthUnit:Word;
vRes: Cardinal;
vResUnit, HeightUnit:Word;
end;
Для С++
struct _PSD_ResInfo
{
LONG hRes;
WORD hResUnit, WidthUnit;
LONG vRes;
WORD vResUnit, HeightUnit;
};
А теперь о содержимом:
hRes - горизонтальное количество пикселей на дюйм.
hResUnit - горизонтальная единица измерения. Если 1 - дюймы. 2 - сантиметры.
WidthUnit - горизантальная единица измерения ширины. Если 1 - дюймы. 2 - сантиметры, 3- точек, 4 - кегль, 5 - столбцы.
vRes - вертикальное количество пикселей на дюйм.
vResUnit - вертикальная единица измерения. Если 1 - дюймы. 2 - сантиметры.
HeightUnit - вертикальная единица измерения ширины. Если 1 - дюймы. 2 - сантиметры, 3- точек, 4 - кегль, 5 - столбцы.
Ну всё. На сегодня мне кажется хватит. В следующий раз я познакомлю тебя с остальными блоками. Если будет время, то я напишу маленький пример на Delphi для чтения PSD файла.