Как узнать, где начинается блок изображений в изображениях GIF?
Источник информации - http://www.onicos.com/staff/iz/formats/gif.html
В изображениях GIF фактический размер изображения (ширина, высота) сохраняется в блоке изображений. Насколько я понимаю, Image Block - самый первый блок, включенный в заголовок. Перед началом фактических блоков выделяется память, называемая Глобальной таблицей цветов(0..255 x 3 байта)(отныне GCT). Если я могу узнать количество байтов, зарезервированных для GCT, я могу извлечь байты 5-9 из блока изображений и получить фактический размер изображения.
Вопрос: Как я могу узнать / узнать, каков размер GCT?
ИЛИ ЖЕ
Где заканчивается GCT?
ИЛИ ЖЕ
Где начинается блок изображения?
ИЛИ ЖЕ
Где заканчивается блок изображения?
1 ответ
Все, что вам нужно для GIF Enc/ Dec вы найдете здесь 3MF Project GIF
GCT
этот блок является необязательным и не всегда присутствует в файле GIF. Размер определяется количеством цветов и шириной бита из заголовка GIF. Я декодирую / загружаю это так:
struct _hdr { // Header BYTE Signature[3]; /* Header Signature (always "GIF") */ BYTE Version[3]; /* GIF format version("87a" or "89a") */ // Logical Screen Descriptor WORD xs; WORD ys; BYTE Packed; /* Screen and Color Map Information */ BYTE BackgroundColor; /* Background Color Index */ BYTE AspectRatio; /* Pixel Aspect Ratio */ } hdr; gcolor_bits= (hdr.Packed &7)+1; // global pallete scolor_bits=((hdr.Packed>>4)&7)+1; // screen _gcolor_sorted =hdr.Packed&8; _gcolor_table =hdr.Packed&128; scolors=1<<scolor_bits; gcolors=1<<gcolor_bits;
- если
_gcolor_table
верно тогда GCT присутствует - РазмерGCT
3*gcolors
[Байт] хранится в порядкеR,G,B
- если
Начало изображения
Это немного сложно, потому что файлы GIF89a могут содержать много дополнительных блоков. Вам необходимо выполнить цикл декодирования, определяющий тип блока и декодирующий / пропускающий его в соответствии с его назначением. Я делаю это так:
struct _gfxext { BYTE Introducer; /* Extension Introducer (always 21h) */ BYTE Label; /* Graphic Control Label (always F9h) */ BYTE BlockSize; /* Size of remaining fields (always 04h) */ BYTE Packed; /* Method of graphics disposal to use */ WORD DelayTime; /* Hundredths of seconds to wait */ BYTE ColorIndex; /* Transparent Color Index */ BYTE Terminator; /* Block Terminator (always 0) */ } gfx; struct _txtext { BYTE Introducer; /* Extension Introducer (always 21h) */ BYTE Label; /* Extension Label (always 01h) */ BYTE BlockSize; /* Size of Extension Block (always 0Ch) */ WORD TextGridLeft; /* X position of text grid in pixels */ WORD TextGridTop; /* Y position of text grid in pixels */ WORD TextGridWidth; /* Width of the text grid in pixels */ WORD TextGridHeight; /* Height of the text grid in pixels */ BYTE CellWidth; /* Width of a grid cell in pixels */ BYTE CellHeight; /* Height of a grid cell in pixels */ BYTE TextFgColorIndex; /* Text foreground color index value */ BYTE TextBgColorIndex; /* Text background color index value */ // BYTE *PlainTextData; /* The Plain Text data */ // BYTE Terminator; /* Block Terminator (always 0) */ }; struct _remext { BYTE Introducer; /* Extension Introducer (always 21h) */ BYTE Label; /* Comment Label (always FEh) */ // BYTE *CommentData; /* Pointer to Comment Data sub-blocks */ // BYTE Terminator; /* Block Terminator (always 0) */ }; struct _appext { BYTE Introducer; /* Extension Introducer (always 21h) */ BYTE Label; /* Extension Label (always FFh) */ BYTE BlockSize; /* Size of Extension Block (always 0Bh) */ CHAR Identifier[8]; /* Application Identifier */ BYTE AuthentCode[3]; /* Application Authentication Code */ // BYTE *ApplicationData; /* Point to Application Data sub-blocks */ // BYTE Terminator; /* Block Terminator (always 0) */ }; // handle 89a extensions blocks _gfxext gfxext; gfxext.Introducer=0; _txtext txtext; txtext.Introducer=0; _remext remext; remext.Introducer=0; _appext appext; appext.Introducer=0; if((hdr.Version[0]=='8') &&(hdr.Version[1]=='9') &&(hdr.Version[2]=='a')) _89a=true; else _89a=false; if (_89a) for (;!f.eof;) { f.peek((BYTE*)&dw,2); if (dw==0xF921) { f.read((BYTE*)&gfxext,sizeof(_gfxext)); } else if (dw==0x0121) { f.read((BYTE*)&txtext,sizeof(_txtext)); for (;!f.eof;) { f.read(&db,1); if (!db) break; f.read(dat,DWORD(db)); } } else if (dw==0xFE21) { f.read((BYTE*)&remext,sizeof(_remext)); for (;!f.eof;) { f.read(&db,1); if (!db) break; f.read(dat,DWORD(db)); } } else if (dw==0xFF21) { f.read((BYTE*)&appext,sizeof(_appext)); for (;!f.eof;) { f.read(&db,1); if (!db) break; f.read(dat,DWORD(db)); } } else if ((dw&0x00FF)==0x0021) return; // corrupted file else break; // no extension found }
db
переменная BYTEdw
переменная WORDf
мой класс файлового кэша, члены говорят сами за себя, я надеюсь, в любом случае:f.read(&data,size)
читатьsize
БАЙТЫ вdata
f.peek(&data,size)
сделать то же самое, но не обновлять позицию в файлеf.eof
указывает на конец файла достигнут
это должно быть сделано для каждого кадра после начала всего этого заголовка изображения.
Конец изображения
Блок изображения заканчивается терминатором. Все куски изображения начинаются с
BYTE
сосчитать. Если этоzero
это блок терминатора. Обычно после изображения малоBYTES
данные LZW не используются, поэтому после заполнения всей области изображения пропустите все блоки до тех пор, пока не достигнете блока нулевого размера, а затем остановите его в конце изображения. ЕслиBYTE
после этого0x3B
hex вы достигли конца файла GIF
[заметки]
Не забудьте инкапсулировать структуру #pragma pack(1)
а также #pragma pack()
или вручную установить выравнивание на 1 BYTE
, Остерегайтесь проблем со знаковыми типами данных (данныеLZW не подписаны), поэтому используйте слишком много типов, чтобы избежать проблем или использовать только неподписанные переменные (с достаточной шириной в битах) для декодирования.