Step to UEFI (160)UEFI 下的 GIF 解码

现在最常见的 GIF 就是各种动图了,因为它足够小,无需支付版权费用和不用安装额外的插件,使得它成为分享动图的首选。
这次介绍来自https://github.com/lecram/gifdec 的 GIF 解码库,可以用来解码静态和动态GIF格式。
需要注意的是,这个库有下面2个局限(GIF 最多支持256色,这个限制是来自他的调色板只有256色。通常情况下,一幅有很多帧的动图都会共享同一个调色板。特殊情况下,每一帧都有一个调色板。这个库不支持后面那种情况):
* no support for GIF files that don't have a global color table
* no direct support for the plain text extension (rarely used)

实例程序如下,我在 IA32 和 X64 NT32环境下测试过,都是可以正常工作的:

#include <stdlib.h>
#include <stdio.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include "gifdec.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
  { \
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
  }
  
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;

int
main (
  IN int Argc,
  IN char *Argv[]
  )
{
    EFI_STATUS    Status;
   EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
    int i,j;

    UINT8* RGB32;
    UINT8* frame;

    if (Argc<2) {
            printf("Please input file name\n");
            return 0;
    }

    gd_GIF* gif = gd_open_gif(Argv[1]);
    if (gif == NULL) {
        printf("Open file Error\n");
        return -1;
    }

    printf("width %d height %d\n",gif->width,gif->height);
    printf("number of colors: %d\n", gif->palette->size);
    printf("number of frames: %d\n", gif->loop_count);

    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
                GraphicsOutput = NULL;
                printf("Loading Graphics_Output_Protocol error!\n");
                return EFI_SUCCESS;}

                
    frame=(UINT8*)AllocatePool(gif->width*gif->height*3);
    RGB32 = (UINT8*)AllocatePool(gif->width*gif->height*4); 

       while (1)
       {
        int retx = gd_get_frame(gif);
        if (retx == -1) 
            break;
        gd_render_frame(gif, frame);
                    for (j=0;j<gif->height;j++) {
                            for (i=0;i<gif->width;i++) {
                                RGB32[(j*gif->width+i)*4]  = frame[(j*gif->width+i)*3+2]; //Blue   
                                RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green 
                                RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red  
                                RGB32[(j*gif->width+i)*4+3]=0;
                            }
                    GraphicsOutput->Blt(
                                GraphicsOutput, 
                                (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
                                EfiBltBufferToVideo,
                                0, 0, 
                                0, 0, 
                                gif->width, gif->height, 0);    
                              }                          

        if (retx == 0) 
                break;
            //gd_rewind(gif);
   }

   free(RGB32);  
    free(frame);

    gd_close_gif(gif);    

    return 0;
}

 

工作截图:

完整代码下载:
giftest

简单说一下工作流程,首先gd_open_gif() 函数打开 GIF,这步可以获得尺寸信息,但是从我实验来看,无法取得帧数信息(loop_count),但是readme 中说是可以的;接下来使用gd_get_frame() 函数检查当前GIF播放的位置,返回1表示还有下一帧,0表示达到尾部,-1表示出现错误;然后,gd_render_frame()函数取出来解码后的图像信息,经过格式变换即可通过 BLT显示出来;最后,检查是否播放完毕,完毕之后退出。
代码写的比较简单,效率不高,在处理较大 GIF 的时候,还是能够感受到刷新的。有兴趣的朋友可以琢磨一下如何提速,我猜测瓶颈应该是在处理解码后的数据RGB顺序的那里。

=======================================================================================
2018年11月23日 感谢 Cai 3023772977@qq.com 在评论中之处问题,代码中 GraphicsOutput() 函数,写在循环内了,这是导致播放缓慢的原因,将它移动到循环外面就可以正常工作了。

for (j=0;jheight;j++) {
for (i=0;iwidth;i++) {
RGB32[(j*gif->width+i)*4] = frame[(j*gif->width+i)*3+2]; //Blue
RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green
RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red
RGB32[(j*gif->width+i)*4+3]=0;
}

GraphicsOutput->Blt(
GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
EfiBltBufferToVideo,
0, 0,
0, 0,
gif->width, gif->height, 0);

}

《Step to UEFI (160)UEFI 下的 GIF 解码》有4个想法

  1. Hi Sir
    loop_count 是 圖片要迴圈幾次, 0 好像代表無窮
    我在 https://ezgif.com/loop-count 把你提供的圖片 改loop_count 是可以讀到 loop_count
    不過你的 printf("number of frames: %d\n", gif->loop_count);
    在 gd_get_frame(gif); 之前
    所以還是顯示 0

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注