如何找到Image Block在GIF图像中的起始位置?

时间:2015-09-02 09:16:49

标签: image gif decoding file-format

信息来源 - http://www.onicos.com/staff/iz/formats/gif.html#header

GIF 图像中,实际图像尺寸(宽度,高度)存储在图像块中。据我所知,图像块是标题中包含的第一个块。 在实际块开始之前,有一个称为全局颜色表(0..255 x 3字节)的内存分配(从现在开始 GCT )。如果我知道为 GCT 保留的字节数,我可以从图像块中提取字节5-9,并具有实际图像大小。

问题: 我如何知道/了解 GCT 的大小是什么?

GCT 在哪里结束?

图像阻止从哪里开始?

图像阻止在哪里结束?

1 个答案:

答案 0 :(得分:2)

您在gif enc / dec所需的一切都可以在这里找到3MF Project GIF

  1. <强> 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
    • 的顺序存储
  2. 图片开头

    这个有点棘手,因为 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是BYTE变量
    • dw是WORD变量
    • f是我的文件缓存类,成员不言自明,我希望无论如何:
    • f.read(&data,size)size BYTES读入data
    • f.peek(&data,size)执行相同但不更新文件中的位置
    • f.eof表示已达到文件结尾

    在所有此图像标题开始后,必须为每个帧完成此操作。

  3. 图像结束

    图像块以终止符结束。所有图像块都以BYTE计数开头。如果是zero,则它是终结符块。通常在图像之后, LZW 数据很少使用BYTES,因此在填充整个图像区域后跳过所有块直到达到零大小的块然后停止图像结束。如果BYTE之后0x3B#pragma pack(1),则您已到达 GIF 文件的末尾

  4. <强> [注释]

    不要忘记按#pragma pack()1 BYTE封装结构,或手动将对齐设置为chrome.tabs.onUpdated.addListener(function(tabId, info, tab) { var url = info.url || tab.url; if(url && url.indexOf('example.com') > -1) chrome.pageAction.show(tabId); else chrome.pageAction.hide(tabId); }); 。注意有符号数据类型的问题( LZW 数据是无符号的),所以在你可以避免问题或只使用无符号变量(具有足够的位宽)进行解码时进行改写