音视频入门-19-使用giflib处理GIF图片

* 音视频入门文章目录 *

GIFLIB

The GIFLIB project

上一篇 【手动生成一张GIF图片】, 自己生成了一张 GIF 动态图 rainbow.gif

example-rainbow.gif

下面,使用 GIFLIB 分离出 GIF 每一帧的 RGB ,然后将分离出的 RGB 再合成 GIF。

GIF to RGB

GIFLIB 项目里的 gif2rgb.c 已经实现了解码 GIF -> RGB。不过 gif2rgb.c 只保存了最后一帧图片的 RGB,这里需要改造。

gif2rgb.c

gif2rgb.c 在 GIF2RGB 方法最后才调用 DumpScreen2RGB 保存 RGB。

......

static void DumpScreen2RGB(char *FileName,
                           ColorMapObject *ColorMap,
                           GifRowType *ScreenBuffer,
                           int ScreenWidth, int ScreenHeight)
{
......
}

static void GIF2RGB(int NumFiles, char *FileName, 
		    bool OneFileFlag, 
		    char *OutFileName)
{
    ......
    
    /* Scan the content of the GIF file and load the image(s) in: */
    do {
	......
    } while (RecordType != TERMINATE_RECORD_TYPE);
    
    /* Lets dump it - set the global variables required and do it: */
    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);
    }

    /* check that the background color isn't garbage (SF bug #87) */
    if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
        fprintf(stderr, "Background color out of range for colormap\n");
        exit(EXIT_FAILURE);
    }

    DumpScreen2RGB(OutFileName, OneFileFlag,
		   ColorMap,
		   ScreenBuffer, 
		   GifFile->SWidth, GifFile->SHeight);

    (void)free(ScreenBuffer);

    if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
	PrintGifError(Error);
	exit(EXIT_FAILURE);
    }

}

gif-to-rgb-library.c

需要把调用 DumpScreen2RGB 的位置移到循环体内 case IMAGE_DESC_RECORD_TYPE: 位置。

......

static void DumpScreen2RGB(char *FileName,
                           ColorMapObject *ColorMap,
                           GifRowType *ScreenBuffer,
                           int ScreenWidth, int ScreenHeight)
{
......
}

static void GIF2RGB( char *FileName, char *OutFileNamePattern)
{
    ......
    do {
        ......
        switch (RecordType) {
            case IMAGE_DESC_RECORD_TYPE:
                ......

                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);
                }

                /* check that the background color isn't garbage (SF bug #87) */
                if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
                    fprintf(stderr, "Background color out of range for colormap\n");
                    exit(EXIT_FAILURE);
                }

                char *name = malloc(255*sizeof(char));
                sprintf(name, OutFileNamePattern,  screenIndex++);
                printf("Final File Name: %s\n", name);

                DumpScreen2RGB(name,
                               ColorMap,
                               ScreenBuffer,
                               GifFile->SWidth, GifFile->SHeight);
                break;
            case EXTENSION_RECORD_TYPE:
            ......
        }
    } while (RecordType != TERMINATE_RECORD_TYPE);
   ......
}

int main(int argc, char **argv) {
    GIF2RGB( "/Users/staff/Desktop/rainbow.gif", "/Users/staff/Desktop/rainbow-%d.rgb");
    return 0;
}

查看 GIF -> RGB 结果

根据 【手动生成一张GIF图片】 生成的 GIF rainbow.gif 含有 7 个图像,所以会得到 7 个 .RGB 文件。

ffplay 查看 RGB 文件:

ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-0.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-1.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-2.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-3.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-4.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-5.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-6.rgb

decode-gif-to-rgb.jpg

RGB to GIF

上面,从 rainbow.gif 中提取出了 7 个 RGB 文件。
下面,读取这 7 个 RGB 文件,使用 GIFLIB 编码成 GIF 动态图。

// LoadRGB 是 gif2rgb.c 文件中的方法
static void LoadRGB(char *FileName,
                    GifByteType **RedBuffer,
                    GifByteType **GreenBuffer,
                    GifByteType **BlueBuffer,
                    int Width, int Height)
{
   ......
}

// gif2rgb.c 文件中 RGB2GIF 只实现了 RGB to GIF 静态图功能
// 这里做了修改,可以生成动态 GIF
static void RGB2GIF(char **RGBFileNames, int NumOfRGBFile, char *GIFFileName,
                    int ExpNumOfColors, int Width, int Height)
{
    int ColorMapSize;
    GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, *OutputBuffer = NULL;
    ColorMapObject *OutputColorMap = NULL;

    // 打开输出的 GIF 文件
    int Error;
    GifFileType *GifFile;
    if ((GifFile = EGifOpenFileName(GIFFileName, false, &Error)) == NULL) {
        PrintGifError(Error);
        exit(EXIT_FAILURE);
    }

    GifFile->SWidth = Width;
    GifFile->SHeight = Height;
    GifFile->SColorResolution = 1;
    GifFile->SBackGroundColor = 0;
    GifFile->SColorMap = NULL;

    for(int i = 0; i < NumOfRGBFile; i++) {
        ColorMapSize = 1 << ExpNumOfColors;
        printf("读取 RGB 文件:%d\n", i);
        LoadRGB(RGBFileNames[i], &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
        if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
            (OutputBuffer = (GifByteType *) malloc(Width * Height *
                                                   sizeof(GifByteType))) == NULL)
        GIF_EXIT("Failed to allocate memory required, aborted.");

        if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
                              RedBuffer, GreenBuffer, BlueBuffer,
                              OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
            exit(EXIT_FAILURE);
        free((char *) RedBuffer);
        free((char *) GreenBuffer);
        free((char *) BlueBuffer);

        printf("MakeSavedImage:%d\n", i);
        SavedImage *image = GifMakeSavedImage(GifFile, NULL);

        GifImageDesc *imageDesc = (GifImageDesc *) malloc(sizeof(GifImageDesc));
        imageDesc->Left = 0;
        imageDesc->Top = 0;
        imageDesc->Width = Width;
        imageDesc->Height = Height;
        imageDesc->Interlace = false;
        imageDesc->ColorMap = OutputColorMap;

        image->ImageDesc = *imageDesc;
        image->RasterBits = OutputBuffer;

        GraphicsControlBlock *GCB = (GraphicsControlBlock *) malloc(sizeof(GraphicsControlBlock));
        GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
        GCB->DelayTime = 50;
        GCB->UserInputFlag = false;
        GCB->TransparentColor = NO_TRANSPARENT_COLOR;

        printf("GCBToSaved:%d\n", i);
        EGifGCBToSavedExtension(GCB, GifFile, i);
    }

    printf("输出文件。");
    // 输出文件
    EGifSpew(GifFile);

}

int main(int argc, char **argv) {
    char *rgbFiles[] = {
            "/Users/staff/Desktop/rainbow-0.rgb",
            "/Users/staff/Desktop/rainbow-1.rgb",
            "/Users/staff/Desktop/rainbow-2.rgb",
            "/Users/staff/Desktop/rainbow-3.rgb",
            "/Users/staff/Desktop/rainbow-4.rgb",
            "/Users/staff/Desktop/rainbow-5.rgb",
            "/Users/staff/Desktop/rainbow-6.rgb",
    };

    RGB2GIF(rgbFiles, 7, "/Users/staff/Desktop/rainbow0.gif", 3, 700, 700);
    return 0;
}

查看 RGB -> GIF 结果

成功实现了 GIF -> RGB(s) -> GIF。

gif-rgb-gif.jpg

encode-rgb-to-gif.jpg


代码:
audio-video-blog-demos

参考资料:

图像解码之三——giflib解码gif图片

How do I get the RGB colour data from a GIFLIB SavedImage structure