图像解码之三:giflib解码gif图片
来源:互联网 发布:mac capslock灯不亮了 编辑:程序博客网 时间:2024/05/16 07:55
目录(?)[-]
- gif文件格式简单介绍
- giflib中数据类型
- 初始化giflib
- 初始化屏幕
- 解码gif数据
- 处理图像数据
- 处理扩展块
- 总结
前面已经介绍过了libjpeg解码jpeg图片和libpng解码png图片,本文将会介绍怎样用giflib解码gif图片。giflib可以在这里下载。
gif文件格式简单介绍
在解码jpeg图片和png图片的时候我们不需要对jpeg和png文件格式有了解就可以解码了(了解jpeg和png当然更好),但是在使用giflib解码gif的时候,我们必须要对gif文件有很简单的了解。
gif文件中可以存放一帧或者多帧图像数据,并且可以存放图像控制信息,因此可以存储动画图片。
gif文件由文件头开头,文件尾结尾,中间是一些连续的数据块(block)。这些数据块又分为图像数据块和扩展数据块(extension),图像数据块可以理解成存放一帧的图像数据。扩展数据块存放的是一些辅助信息,比如指示怎样显示图像数据等等。
gif文件中的图像基于调色板的,因此一张gif文件中的图像最多只能有255中颜色,因此gif文件只能存储比较简单的图像。gif文件中有两种调色板 ——全局调色板和图像局部调色板。当一帧图像有局部调色板时,则以局部调色板来解码该帧图像,如果该帧图像没有局部调色板则用全局调色板来解码该图像。
更详细的信息可以查阅giflib的文档中的gif89.txt文件,或者在网络搜索相关的信息。
giflib中数据类型
在giflib中最重要的数据类型为GifFileType,定义如下:
- typedef struct GifFileType {
- int SWidth, SHeight, /* Screen dimensions. */
- SColorResolution, /* How many colors can we generate? */
- SBackGroundColor; /* I hope you understand this one... */
- ColorMapObject *SColorMap; /* NULL if not exists. */
- int ImageCount; /* Number of current image */
- GifImageDesc Image; /* Block describing current image */
- struct SavedImage *SavedImages; /* Use this to accumulate file state */
- VoidPtr Private; /* The regular user should not mess with this one! */
- } GifFileType
以上代码来自gif_lib.h文件,使用giflib库解码gif文件都需要包含这个文件。
1、以S开头的变量标识屏幕(Screen)。SaveImages变量用来存储已经读取过得图像数据。
2、Private变量用来保存giflib私有数据,用户不应该访问该变量。
3、其他变量都标识当前图像。
初始化giflib
初始化giflib比较简单,只需要打开相应的gif数据就可以了。giflib和libpng一样提供了两种打开源数据的方式,一种是以文件流方式打开gif文件,另外一种用户可以自定义输入回调函数给giflib来完成初始化。
文件流初始化giflib的代码如下:
- if ((GifFile = DGifOpenFileName(*FileName)) == NULL) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- if ((GifFile = DGifOpenFileHandle(0)) == NULL) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- if ((GifFile = DGifOpen(&gif, gif_input_cb)) == NULL) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
gif_input_cb为自定义输入回调函数,该函数负责giflib的数据输入。
我们另外需要注意以上三个函数都返回一个GifFileType类型的指针,该指针以后在调用giflib的函数时,用作第一个参数传入。
初始化屏幕
所有的gif图像共享一个屏幕(Screen),这个屏幕和我们的电脑屏幕不同,只是一个逻辑概念。所有的图像都会绘制到屏幕上面。
首先我们需要给屏幕分配内存:
- if ((ScreenBuffer = (GifRowType *)
- malloc(GifFile->SHeight * sizeof(GifRowType *))) == NULL)
- GIF_EXIT("Failed to allocate memory required, aborted.");
另外我们需要以背景颜色(GifFile->SBackGroundColor)初始化屏幕buffer。
- Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
- if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
- GIF_EXIT("Failed to allocate memory required, aborted.");
- for (i = 0; i < GifFile->SWidth; i++) /* Set its color to BackGround. */
- ScreenBuffer[0][i] = GifFile->SBackGroundColor;
- for (i = 1; i < GifFile->SHeight; i++) {
- /* Allocate the other rows, and set their color to background too: */
- if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
- GIF_EXIT("Failed to allocate memory required, aborted.");
- memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
- }
解码gif数据
我们上面已经提到gif数据是以顺序存放的块来存储的,DGifGetRecordType函数用来获取下一块数据的类型。因此解码gif数据的代码组织如下:
- do {
- if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- switch (RecordType) {
- case IMAGE_DESC_RECORD_TYPE:
- break;
- case EXTENSION_RECORD_TYPE:
- break;
- case TERMINATE_RECORD_TYPE:
- break;
- default: /* Should be traps by DGifGetRecordType. */
- break;
- }
- while (RecordType != TERMINATE_RECORD_TYPE);
循环解析gif数据,并根据不同的类型进行不同的处理。
处理图像数据
首先先介绍怎样处理IMAGE_DESC_RECORD_TYPE类型的数据。这代表这是一个图像数据块,这个图像数据需要绘制到前面提到的屏幕buffer上面,相应的代码如下:
- if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- Row = GifFile->Image.Top; /* Image Position relative to Screen. */
- Col = GifFile->Image.Left;
- Width = GifFile->Image.Width;
- Height = GifFile->Image.Height;
- GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ",
- PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
- if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
- GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
- fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
- exit(EXIT_FAILURE);
- }
- if (GifFile->Image.Interlace) {
- /* Need to perform 4 passes on the images: */
- for (Count = i = 0; i < 4; i++)
- for (j = Row + InterlacedOffset[i]; j < Row + Height;
- j += InterlacedJumps[i]) {
- GifQprintf("\b\b\b\b%-4d", Count++);
- if (DGifGetLine(GifFile, &ScreenBuffer[j][Col], Width) == GIF_ERROR) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- }
- }
- else {
- for (i = 0; i < Height; i++) {
- GifQprintf("\b\b\b\b%-4d", i);
- if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
- Width) == GIF_ERROR) {
- PrintGifError();
- exit(EXIT_FAILURE);
- }
- }
- }
- /* Get the color map */
- ColorMap = (GifFile->Image.ColorMap
- ? GifFile->Image.ColorMap
- : GifFile->SColorMap);
- if (ColorMap == NULL) {
- fprintf(stderr, "Gif Image does not have a colormap\n");
- exit(EXIT_FAILURE);
- }
- DumpScreen2RGB(OutFileName, OneFileFlag,
- ScreenBuffer, GifFile->SWidth, GifFile->SHeight);
这里面有几点需要注意的是:
1、gif数据交织处理,参照上面的代码。
2、另外注意调色板的选择,如果当前图像数据中有局部调色板就用局部调色板来解码数据,否则用全局调色板来解码数据。
3、屏幕数据的解码,根据你的显示要求选择输出格式。
解析屏幕数据,假设需要把数据转换成ARGB8888的格式,代码如下:
- static void DumpScreen2RGBA(UINT8* grb_buffer, GifRowType *ScreenBuffer, int ScreenWidth, int ScreenHeight)
- {
- int i, j;
- GifRowType GifRow;
- static GifColorType *ColorMapEntry;
- unsigned char *BufferP;
- for (i = 0; i < ScreenHeight; i++) {
- GifRow = ScreenBuffer[i];
- BufferP = grb_buffer + i * (ScreenWidth * 4);
- for (j = 0; j < ScreenWidth; j++) {
- ColorMapEntry = &ColorMap->Colors[GifRow[j]];
- *BufferP++ = ColorMapEntry->Blue;
- *BufferP++ = ColorMapEntry->Green;
- *BufferP++ = ColorMapEntry->Red;
- *BufferP++ = 0xff;
- }
- }
- }
解析屏幕数据的时候需要住处理透明色。这里先埋一个伏笔,等介绍完扩展块,再来重新实现这个函数。
处理扩展块
上面提到扩展块主要实现一些辅助功能,扩展块影响其后的图像数据解码。这里面比较重要的扩展块是图像控制扩展块(Graphic control extension)。可以参考giflib文档中的gif89.txt文件了解图像控制扩展块的详细内容。这个扩展块中有两个内容我们比较关心:
延时时间(delay time)——后面的图像延时多长时间再显示,如果解码线程不是主线程的话,可以在这里延时一下再处理后面的数据。
透明色(transparent color)——在后面的图像解码时,遇到同样的颜色值,则跳过不解码,继续处理后续的点。
如下是一种可供参考的处理方式:
- UINT32 delay = 0;
- if( ExtCode == GIF_CONTROL_EXT_CODE
- && Extension[0] == GIF_CONTROL_EXT_SIZE) {
- delay = (Extension[3] << 8 | Extension[2]) * 10;
- /* Can sleep here */
- }
- /* handle transparent color */
- if( (Extension[1] & 1) == 1 ) {
- trans_color = Extension[4];
- }
- else
- trans_color = -1;
这里GIF_CONTROL_EXT_CODE为0xF9表明该扩展块是一个图像控制扩展块,GIF_CONTROL_EXT_SIZE为4,图像控制扩 展块的大小。我们可以看到解析出delay信息之后,就地delay。解析出透明颜色值之后,则标识透明色,否则标识为-1。解析图片的时候可以根据透明 色的值进行相应的处理,参考如下解析图像的函数:
- static void DumpScreen2RGBA(UINT8* grb_buffer, GifRowType *ScreenBuffer, int ScreenWidth, int ScreenHeight)
- {
- int i, j;
- GifRowType GifRow;
- static GifColorType *ColorMapEntry;
- unsigned char *BufferP;
- for (i = 0; i < ScreenHeight; i++) {
- GifRow = ScreenBuffer[i];
- BufferP = grb_buffer + i * (ScreenWidth * 4);
- for (j = 0; j < ScreenWidth; j++) {
- if( trans_color != -1 && trans_color == GifRow[j] ) {
- BufferP += 4;
- continue;
- }
- ColorMapEntry = &ColorMap->Colors[GifRow[j]];
- *BufferP++ = ColorMapEntry->Blue;
- *BufferP++ = ColorMapEntry->Green;
- *BufferP++ = ColorMapEntry->Red;
- *BufferP++ = 0xff;
- }
- }
- }
注意我们这里假设grb_buffer已经正确的初始化,如果他是垃圾数据,那么得到结果肯定是错误的。
至此gif文件解析完成了,gif图片和gif动画都可以正确的解析,并显示了。
总结
从上面的情况我们看出如果想使用giflib还是需要对gif格式有一个简单的了解,这点要求比libjpeg和libpng要求要高了一些。至此图像处理系列已经完成,欢迎大家批评指正。
- 图像解码之三——giflib解码gif图片
- 图像解码之三:giflib解码gif图片
- 图像解码之三:giflib解码gif图片
- 图像解码之三——giflib解码gif图片
- giflib解码gif图片
- Android 解码播放GIF图像
- 图像解码之二:使用libpng解码png图片
- 图像解码之二:使用libpng解码png图片
- Android gif图片的解码与合成
- 图像解码之二——使用libpng解码png图片
- 图像解码之二——使用libpng解码png图片
- 图像解码之二——使用libpng解码png图片
- 图像解码之一:使用libjpeg解码jpeg图片
- 图像解码之一——使用libjpeg解码jpeg图片
- 图像解码之一——使用libjpeg解码jpeg图片
- 图像解码之一——使用libjpeg解码jpeg图片
- 图像解码之一——使用libjpeg解码jpeg图片
- 利用CxImage实现编解码Gif图像代码举例
- 第六篇 实战RMAN备份--转自君三思
- 修改linux swap空间的swappiness,降低对硬盘的缓存
- Couldn't register com.lcworld.iphone4itouch with the bootstrap server.
- PowerDesigner中设置取消name与code的联动的方法
- hdoj 1520 Anniversary party(树形dp)
- 图像解码之三:giflib解码gif图片
- 十大领域管理软件供应商入选厂商如下(排名不分先后)
- CSDN博客发帖越来越难操控
- #小练习 类与继承
- 第七篇 RMAN基础知识补充 二 --转自君三思
- SQL语句
- linux模块编程(四)——消息的使者list
- 中控 X638考勤机编程(delphi)
- 新手如何从 iOS App 市场分一杯羹?