• VC调用giflib(1):VC编译giflib


    作者:马健
    邮箱:stronghorse_mj@hotmail.com
    主页:http://www.comicer.com/stronghorse
    发布:2020.03.14

    以前我只与静态GIF文件打交道,用CxImage就够了。最近需要编、解码动画GIF,才发现CxImage不够用。刚好处理webp动画的libwebp库提供了用giflib解码动画GIF的源代码,所以就试着在我的VC代码中调用giflib,结果发现实在麻烦——这个库的官方文档基本没有,官方例子代码也绕来绕去,网上的说明和例子不仅不多,还到处是坑,说起来都是泪。好在经过一番折腾,我想要的功能最终还是实现了,虽然过程曲折了一点。为了不在同样的坑里踩两遍,于是写下了这一系列笔记。

    本系列笔记所谈的giflib均针对5.2.1版本,不再逐一说明。

    ==========================================================================

    要在VC里调用giflib,第一步当然是用VC编译giflib。考虑到复用性,以编译成静态库为宜。网上有文章介绍这个编译过程,但其中可能藏坑。这里先列出我已经填过坑的编译过程:

    1、新建一个空项目,名称为giflib,类型为静态库(Static library),注意务必把预编译头(Precompiled header)选项去掉。

    2、把创建出来的项目文件剪切到giflib-5.2.1文件夹。

    3、将h和c文件添加到项目中,包括:
    dgif_lib.c
    egif_lib.c
    gif_err.c
    gif_hash.c
    gifalloc.c
    openbsd-reallocarray.c
    qprintf.c
    gif_hash.h
    gif_lib.h
    gif_lib_private.h

    其中:
    1)加入qprintf.c是因为我要调用giftext.c中的代码。如果不调用,可以不加入。
    2)如果要使用giflib的调色板量化功能(用法参见例子gif2rgb.c),还需要加入quantize.c。我嫌它这个中位切分(median cut)算法太low就没加,改用FreeImage提供的其他调色板量化算法。
    3)、如果要编译giflib的命令行,还需要从github开源项目
    https://github.com/bjornblissing/osg-3rdparty-cmake.git
    的giflib文件夹下载这两个命令行解析代码,加入工程中:
    getopt.c
    getopt.h
    我不打算编译生成命令行工具,所以就没加。

    4、设置Code Generation、Output,创建x64编译配置。

    5、开始编译。编译报错时先把#include <stdint.h>、#include<unistd.h>代码全部注释掉。VC没有这两个头文件,也不需要。

    6、将gif_lib.h中
    #include <stdbool.h>
    这一行,改成

    //#include 
    #ifndef __cplusplus
    typedef char bool;
    #define false 0
    #define true 1
    #endif
    typedef unsigned __int32 uint32_t;
    

    即注释掉对stdbool.h的引用,在C下定义bool量为单字节类型,同时增加uint32_t类型定义。注意网上的一些giflib编译说明,包括上面那个github开源项目提供的stdbool.h中一般是
    typedef int bool;
    即把bool说明为4字节的int类型,而不是我上面说的单字节char类型。这就是我要填的第一个坑,本文后面再详谈。

    7、对giflib源代码进行清理,以消除一些警告错误:
    1)egif_lib.c中的EGifPutPixel、EGifCompressLine、EGifCompressOutput函数原型的参数类型与实现不一致,按照实现改原型。
    2)EGifOpenFileName、EGifOpenFileHandle、DGifOpenFileName、DGifOpenFileHandle等函数会报一大堆4996警告,在gif_lib.h的
    #ifdef __cplusplus
    前面加一行
    #pragma warning( disable : 4996 )
    世界就清净了。
    3)编译x64版本还有几个类型转换警告,强制转换即可,不转也无所谓。
    4)如果需要处理Unicode文件名,把EGifOpenFileName、DGifOpenFileName函数复制一份并更名为EGifOpenFileNameW、DGifOpenFileNameW,把函数原型中FileName参数的类型从char*改成wchar_t*,函数体中的open改成_wopen即可。当然也可以不这么麻烦,而是用EGifOpen、DGifOpen解决Unicode文件名的问题,下一篇笔记会专门讲这两个函数的使用。

    经过以上修改,不出意外就可以成功编译出giflib静态库。至于giflib源代码中的内存泄漏(memory leak)等坑,等后面讲到的时候再详谈。

    这里先说详细谈一下前面提到的第一个坑,这个坑可以细化为以下三个问题:

    1. 为什么giflib要单独定义bool类型?
    2. bool类型究竟是1字节还是4字节?
    3. 字节数不同对于giflib的调用究竟有什么影响?

    先回答第1个问题:bool是C++中规定的内建(built-in)数据类型,在C中则不是。所以纯C代码或跨平台库,通常要么就不使用bool类型,要么就自己定义一个,最著名的就是Windows SDK里单独定义的BOOL类型(typedef int BOOL),freetype-2.8里则定义了FT_Bool数据类型(typedef unsigned char FT_Bool),jpeg_v6b中定义了boolean(typedef unsigned char boolean),djvulibre-3.5.28的定义和我的差不多:

    /* - BOOL */
    #if !defined(HAVE_BOOL) && !defined(bool)
    #define bool  char
    #define true  1
    #define false 0
    #endif    

    giflib是纯C代码,所以就用了C99标准库stdbool.h,对bool类型进行定义。VC对C99标准只是部分实现,就没有这个库,所以需要自己搞。

    再回答第2个问题:在VC 2010帮助文档的bool keyword[C++]词条里,前面引用的都是C99标准说明文字,后面来了一段神转折:

    Microsoft Specific

    In Visual C++4.2, the Standard C++ header files contained a typedef that equated bool with int. In Visual C++ 5.0 and later, bool is implemented as a built-in type with a size of 1 byte. That means that for Visual C++ 4.2, a call of sizeof(bool) yields 4, while in Visual C++ 5.0 and later, the same call yields 1. This can cause memory corruption problems if you have defined structure members of type bool in Visual C++ 4.2 and are mixing object files (OBJ) and/or DLLs built with the 4.2 and 5.0 or later compilers.

    也就是说,从VC 5.0开始,微软就认定用一个字节表示bool类型足够了。在MSDN在线文档
    Built-in types (C++) | Microsoft Docs
    中,也说明bool就是一个字节,而不是像网上一些代码所定义的是int类型,即4个字节。

    结合以上两个问题的答案就可以回答第3个问题:

    1. giflib是纯C代码,用它编译出来的静态库如果只被纯C代码调用,大家使用相同的stdbool.h定义,对bool类型的理解一致,那么P事没有,一切正常。
    2. 如果是从VC写的C++代码(CPP)中调用giflib,就会出现数据类型不匹配。

    具体而言,由于VC在C++中认定bool只有一个字节,则在调用giflib函数时,对于bool类型就只会传递1个字节的值,但giflib如果认定bool有4个字节,那么接收bool类型参数时就按4字节接收,最终所收到的bool类型参数只有最低1个字节是有效的,其余高位3字节具体是这么值,完全看临近的变量是什么值。

    要验证这个问题,其实很简单:

    1. 就按网上一般说的,采用上面那个github开源项目提供的stdbool.h,把bool定义为int类型,编译giflib静态库。
    2. 用VC写一段CPP代码,在调用EGifOpenFileName创建一个GIF文件后,就调用函数
      void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
      对于gif89参数传递一个false,然后跟踪进去,实际看一下giflib收到的是不是0。

    除了这个接口外,最最关键的是GifImageDesc结构体的Interlace成员也有这个问题。我也是在发现无论我怎么设置,创建出来的都是交错GIF文件时,才发现双方在bool类型上是鸡同鸭讲。

  • 相关阅读:
    网站调整为黑白的方法
    滚动条样式优化
    js点击页面其他地方如何隐藏div元素菜单
    微信分享网页时自定义标题、描述和图片
    纯CSS3美化单选按钮radio
    纯CSS3实现圆形进度条动画
    解决checkbox的attr(checked)一直为undefined问题
    jQuery – 鼠标经过(hover)事件的延时处理
    PC版模块滚动不显示滚动条效果
    上传文件样式美化
  • 原文地址:https://www.cnblogs.com/stronghorse/p/16002872.html
Copyright © 2020-2023  润新知