前些天看到一幅gif图片,大致是一个程序员通过绘图工具绘出一幅bmp图片,然后用文本文件打开,可以看出其实他是写的C++版的helloworld程序。当真正了解了bmp图片的编码规则时,就可以很清楚知道这是如何实现的。

helloworld

       bmp(bitmap)一种没有进行压缩的图片格式,它是一种二进制的格式。可以通过如下的数据结构struct bmp_data来了解该格式中的各个数据位表示的具体含义。首先是两个字节bmp_type,bmp格式这两个字节固定为0x4248,它其实也是"BM"这两个字符的ASII码;其次是bmp_head,表示bmp的头部,其中有整个bmp文件的大小、保留位以偏移量;接着是bmp_info,表示该bmp图片相关的一些信息,如宽度和高度、bpp等信息;最后是data,即具体的图像完整的编码数据,对于常用的24位bmp格式,就是通过RGB格式表示每个像素点的颜色。

/*
**    这里的long型表示32位整数即4字节,short和char表示8位整数即1字节
*/
struct bmp_head {
        unsigned long size;
        unsigned long reserved;
        unsigned long offset;
};

struct bmp_info {
        unsigned long size;
        long width;
        long height;
        unsigned short planes;
        unsigned short bpp;
        unsigned long compression;
        unsigned long image_size;
        long x_ppm;
        long y_ppm;
        unsigned long used;
        unsigned long important;
};

struct bmp_type {
        char type[2];
};

struct bmp_data {
        struct bmp_type type;
        struct bmp_head head;
        struct bmp_info info;
        void *data;
};

       当明白以上的数据结构,就不难理解gif图片中是如何实现的:首先写出helloworld,然后将其按照对应ASII码转化为相应的RGB绘制出来,之后只要通过文本方式打开就可以得到gif中的效果。如下图,是我自己实现的gif中的效果。当然逆向制作会非常简单,首先用绘图软件任意绘制一幅大小为4X10像素的bmp图片(可任意选择大小)并保存;然后使用二进制查看工具UE打开,并通过UE将helloworld程序复制到该图片的bmp_data中的data域,除了helloworld程序代码外,其它用0x0D0A代替(即回车符与换行符);最后保存好数据,用绘图工具重新打开该文件,就可以看到helloworld版本的图像(注意:要保证该图经过修改后数据长度一致,即bmp_size中的size大小与整个文件大小一致)。

helloworld