不以物喜,不以己悲

图像格式简单解析(BMP,WebP,JPEG,PNG,GIF)

Bitmap位图

介绍

位图(Bitmap),又称栅格图或点阵图,是使用像素阵列来表示的图像。根据位深度,可将位图分为1、4、8、16、24及32位图像等。每个像素使用的信息位数越多,可用的颜色就越多,颜色表现就越逼真,相应的数据量越大。

BMP文件是微软公司所开发的一种交换和存储数据的方法。BMP格式的缺点是,要占用较大的存储空间,文件尺寸太大。

BMP文件结构

BMP文件存储结构的格式可以在Windows中的WINGDI.h文件中找到定义。

BMP文件总体上由4部分组成,分别是位图文件头、位图信息头、调色板和图像数据。

位图文件头
位图信息头
彩色表/调色板
位图数据

解析

BITMAPFILEHEADER文件头

用于描述 Windows 位图(Bitmap)文件的文件头信息,包含了图像类型、图像大小、图像数据存放地址和两个保留未使用的字段,其定义如下

typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

bfType:这是一个 16 位无符号整数,用于标识文件类型。对于位图文件,这个值通常是0x4D42,其对应的 ASCII 码是BM。通过检查这个值,程序可以快速判断一个文件是否可能是位图文件

bfSize:这是一个 32 位无符号整数,表示位图文件的大小,单位是字节。这个值包含了文件头、信息头、颜色表(如果有)和像素数据的全部大小。例如,如果一个位图文件的bfSize值为1024字节,那么从文件开始到结束总共的数据量是 1024 字节。

bfOffBits:这是一个 32 位无符号整数,表示从文件头的起始位置到实际像素数据的偏移量,单位是字节。这个偏移量包括了文件头和信息头(以及可能存在的颜色表)的大小。

BITMAPINFOHEADER位图信息头

BITMAPINFOHEADER是 Windows 图形编程中用于描述位图(Bitmap)详细信息的一个关键结构体。它紧跟在BITMAPFILEHEADER之后,为程序提供了足够的信息来理解和处理位图的像素数据。

typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
字段名 大小(单位:字节) 描述
biSize 4 本结构的大小,在windows中,此字段的值总为28h字节=40字节
biWidth 4 BMP图像的宽度,单位像素
biHeight 4
biPlanes 2 总为0
biBitCount 2 BMP图像的色深,即一个像素用多少位表示,常见的有1、4、8、16、24和32,分别对应单色、16色、256色、16位高彩色、24位真彩色和32位增强型真彩色
biCompression 4 压缩方式,0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定
biSizeImage 4 BMP图像数据大小,必须是4的倍数,图像数据大小不是4的倍数时用0填充补足
biXPelsPerMeter 4 水平分辨率,单位像素/m
biYPelsPerMeter 4 垂直分辨率,单位像素/m
biClrUsed 4 BMP图像使用的颜色,0表示使用全部颜色,对于256色位图来说,此值为100h=256
biClrImportant 4 重要的颜色数,此值为0时所有颜色都重要,对于使用调色板的BMP图像来说,当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色

彩色表/调色板(color table)

彩色表/调色板是是单色、16色和256色图像文件所特有的,相对应的调色板大小是2、16和256,调色板以4字节为单位,每4个字节存放一个颜色值,图像的数据是指向调色板的索引。

可以将调色板想象成一个数组,每个数组元素的大小为4字节,调色板的数据结构定义:

typedef struct tagRGBQUAD {
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
} RGBQUAD;

WebP

定义

WebP是由Google开发的一种现代图像格式,旨在提供更小的文件大小,同时保持较高的图像质量。它支持有损压缩和无损压缩,还支持透明背景(类似PNG)和动画(类似GIF)。

特点

  • 压缩效率高

    • 有损压缩比JPG文件小约25%~34%

    • 无损压缩比PNG文件小约26%

  • 支持透明背景(Alpha通道):无损和有损压缩都支持透明

  • 支持动画:可以替代GIF格式

  • 支持渐进加载(Progressive Decoding):提供更快的加载体验。

  • 广泛支持:现代浏览器(如Chrome、Edge、Firefox、Safari等)都支持WebP

优点

  • 高效压缩:相较JPG和PNG文件体积更小,节省带宽

  • 多功能性:同时支持透明(类似PNG)、动画(类似GIF)和高质量压缩(类似JPG)。

  • 提示网站性能:减少图像大小,提升页面加载速度。

下载链接

https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.4.0-windows-x64.zip

参考资料

https://developers.google.cn/speed/webp?hl=zh-cn

格式解析

RIFF文件格式

WebP文件格式基于RIFF(资源交换文件格式)文档格式

RIFF文件的基本元素是数据块。它由以下部分组成:

+++++++++++++++++++++

Chunk FourCC

+++++++++++++++++++++

Chunk Size

+++++++++++++++++++++

Chunk Payload

+++++++++++++++++++++

Chunk FourCC:32位

用于标识分块的四位ASCII代码

分块大小:32位

分块的大小,不包括此字段、区块标识符或填充

分块载荷:分块大小(字节)

数据载荷。如果Chunk Size(数据块大小)为奇数,则会添加一个填充字节(必须为0,以符合RIFF规范)。

注意:RIFF有一个惯例,即全大写的块FourCC是适用于任何RIFF文件格式的标准块,而特定于文件格式的FourCC全部采用小写形式。WebP不遵循此惯例。

WebP文件头

RIFF文件大小WEBP,统称为WebP文件头:WebP header,4个字节(RIFF),4个字节的文件大小,4个字节的标识(WEBP),总计12个字节。

https://developers.google.cn/speed/webp/docs/riff_container?hl=zh-cn

“RIFF”:32位

ASCII字符“R"”I"“F"”F"

文件大小:32位(uint32)

文件的大小(以字节为单位),从偏移量8开始。此字段的最大值为2^32减10字节,因此整个文件的大小不得超过4GiB减2字节。

“WEBP”:32位

ASCII字符“W"”E“”B""P"

WebP文件必须以包含FourCC"WEBP"的RIFF标头开头。标头中的文件大小是后续数据块的总大小加上“WEBP”FourCC的4字节。文件不应该在文件大小指定的数据后面包含任何数据。读取器可以解析此类文件,并忽略尾随数据。由于任何分块的大小都是偶数,因此RIFF标头给出的大小也是偶数。

JPEG

JPEG(Joint Photographic Experts Group)是同名专家小组开发的图像压缩技术标准,该标准由国际标准化组织(ISO)制订,是面向连续色调静止图像的一种压缩标准。JPEG格式是最常用的图像文件格式,后缀名为.jpg或.jpeg。其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的存储空间,一定程度上会造成图像数据的损伤。

二进制文件

格式解析

JPEG文件头2字节 FF D8

FF D8标识JPEG文件头

APP0图像识别信息 FF E0

名称 字节数 说明
段标识 1 FF
段类型 1 E0
段长度 2 0010 如果有缩略图=16+3n
以下为段内容
交换格式 5 4A46494600 JFIF的ASCII码
主版本号 1
次版本号 1
密度单位 1 0=无单位;1=点数/英寸;2=点数/厘米
X像素密度 2 水平方向的密度
Y像素密码 2 垂直方向的密度
缩略图X像素 1 缩略图水平像素数目
缩略图Y像素 1 缩略图垂直像素数目
如果“缩略图X像素”和“缩略图Y像素”的值均>0,才有以下的数据
RGB缩略图 3xN n=缩略图像素总数=缩略图X像素x缩略图Y像素

DQT定义量化表 FF DB

名称 字节数 说明
段标识 1 FF
段类型 1 DB
段长度 2 43 其值=3+n(当只有一个QT时)
以下为段内容
QT信息 1 0-3位 QT号
4-7位 QT精度(0=8bit,1字节;否则=16bit,2字节)
QT n n=64xQT精度的字节数

SOF0图像基本信息(帧图像起始块)FF C0

名称 字节数 说明
段标识 1 FF
段类型 1 C0
段长度 2 其值=8+组件数量x3
以下为段内容
样本精度 1 8 每个样本位数(大多数软件不支持12和16)
图片高度 2
图片宽度 2
组件数量 1 3 1=灰度图,3=YCbCr/YIQ彩色图,4=CMYK彩色图
组件ID 1 1=Y,2=Cb,3=Cr,4=I,5=Q
采样系数 1 0-3位:垂直采样系数

4-7位:水平采样系数
量化表号 1

DHT定义huffman表 FF C4

名称 字节数 说明
段标识 1 FF
段类型 1 C4
段长度 2 其值=19+n(当只有一个HT表时)
HT信息 1 0-3位:HT号

4位:HT类型,0=DC表,1=AC表

  5-7位:必须=0
HT位表 16 这16个数的和应该<=256
HT值表 n n=表头16个数的和

SOS扫描行开始 FF DA

名称 字节数 说明
段标识 1 FF
段类型 1 DA
段长度 2 000C 其值=6+2x扫描行内组件数量
扫描行内组件数量 1 3 必须>=1,<=4否则错误,通常=3
组件ID 1 1=Y,2=Cb,3=Cr,4=I,5=Q
Huffman表号 1 0-3位:AC表号(其值=0...3)

4-7位:DC表号(其值=0...3)
剩余3个字节 3 忽略

EOI文件尾 FF D9

名称 字节数
段标识 1 FF
段类型 1 D9

PNG

PNG介绍

png是图像文件存储格式,其目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。流式网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG‘s Not GIF",是一种位图文件存储格式。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法。

PNG文件结构

PNG图像格式文件由一个8字节的PNG文件署名域和按照特定结构组织的3个以上的数据块(chunk)组成。

PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。

PNG文件署名域

在PNG文件前8字节的PNG文件署名域用来识别该文件是不是PNG文件。该域的值是:

89 50 4e 47 0d 0a 1a 0a

PNG数据块

PNG文件中的每个数据块都由表1所示的4个域组成。

名称 字节数 说明
Length(长度) 4字节 指定数据块中数据域的长度
Chunk Type Code(数据块类型码) 4字节 数据块类型码由ASCII字母(A-Z和a-z)组成
Chunk Data(数据块数据) 可变长度 存储按照Chunk Type Code指定的数据
CRC(循环冗余检测) 4字节 存储用来检测是否有错误的循环冗余码

PNG文件中的关键数据块的4个标准数据块分别是文件数据块、调色板数据块、图像数据块、图像结束数据块。

数据块符号 数据块名称 多数据块 可选 位置限制
IHDR 文件头数据块 第一块
cHRM 基色和白色点数据块 在PLTE和IDAT之前
gAMA 图像Y数据块 在PLTE和IDAT之前
sBIT 样本有效位数据块 在PLTE和IDAT之前
PLTE 调色板数据块 在IDAT之前
bKGD 背景颜色数据块 在PLTE之后IDAT之前
hIST 图像直方图数据块 在PLTE之后IDAT之前
tRNS 图像透明数据块 在PLTE之后IDAT之前
oFFs 专用公共数据块 在IDAT之前
pHYs 物理像素尺寸数据块 在IDAT之前
sCAL 专用公共数据块 在IDAT之前
IDAT 图像数据块 与其他IDAT连续
tIME 图像最后修改时间数据块 无限制
tEXt 文本信息数据块 无限制
zTXt 压缩文本数据块 无限制
fRAc 专用公共数据块 无限制
glFg 专用公共数据块 无限制
glFt 专用公共数据块 无限制
glFx 专用公共数据块 无限制
IEND 图像结束数据 最后一个数据块

文件头数据块

文件头数据块的数据块类型码为IHDR,它包含有PNG文件中存储的图像数据的基本信息,并作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。文件头数据块由13个字节组成,格式如下:

域的名称 字节数 说明
Width 4 图像宽度,以像素为单位
Height 4 图像高度,以像素为单位
Bit depth 1 图像深度:

索引彩色图像:1,2,4或8

灰度图像:1,2,4,8或16

真彩色图像:8或16
ColorType 1 颜色类型:

0:灰度图像

2:真彩色图像

3:索引彩色图像

4:带α通道数据的灰度图像

6:带α通道数据的真彩色图像
Compression method 1 压缩方法(LZ77派生算法)
Filter method 1 滤波器方法
Interlace method 1 隔行扫描方法:

0:非隔行扫描

1:Adam7

GIF

介绍

GIF的全称是Graphics Interchange Format,可译为图形交换格式,用于以超文本标志语言方式显示索引彩色图像。

文件格式

  • 文件头是一个带有识别GIF格式数据流的数据块,用以区分早期版本和新版本

  • 逻辑屏幕描述区定义了与图像数据相关的图像平面尺寸、彩色深度,并指明后面的调色板数据区属于全局调色板还是局部调色板。若使用的是全局调色板,则生成一个24bit的RGB全局调色板,其中一个基色占用一个字节。

  • 调色板数据区。分通用调色板和局部调色板。其中通用调色板适用于文件中所有图像,局部调色板只适用于某一个图像。

  • 图像数据区的内容有两类,一类是纯粹的图像数据,一类是用于特殊目的的数据块(包含专用应用程序代码和不可打印的注释信息)。在GIF89a格式的图像文件中,如果一个文件中包含多个图像,图像数据区将依次重复数据块序列。

  • 结束标志区的作用主要是标记整个数据流的结束。

在文件头中总包含着magic number数据,标识当前的文件类型,对于gif而言,它的前三个字节总是 0x47,0x49,0x46,而后三个字节标识当前gif的版本,可能是87a也可能是89a,这是header中包含的全部信息。

LogicScreenDescriptor

紧随Header之后的是LogicScreenDescriptor,这里包含了整个gif文件的全局信息,固定7个字节。

名称 字节个数 说明
2 小端形式存储
2 同上
压缩字节 1 1位:全局颜色标识,如果存在则全局颜色表紧跟在LogicScreenDescriptor后面出现。

2-4位:色表中每个颜色通道的位深,111即表示每个颜色通道允许的最大值为255,也就是7+1位所能表示的最大值。

5位:排序标识,标识色表中的颜色是否经过排序,排序按颜色出现的次数从高至低,也就是说越重要的颜色排在更前面。

6-8位:全局色表的个数,记为n,则颜色个数为2(n+1)
索引 1 背景色在全局表中的索引,全局色表存在时有效,当图像不能完全占满gif的实际大小时,应显示背景色
宽高比 1 一般不设置

Global Color Table

全局颜色表,只有当全局颜色标识为1时才存在;gif中颜色以rgb格式排序,也就是说每个颜色三字节,128个颜色则占用384字节。

Extension

在gif中还存在着很多扩展块,这些块的格式都是固定的。

[0x21][flag][info]?[subdata]?[0x00]

  • 固定值[0x21]标识这是一个扩展块

  • [flag]标识当前扩展块的类型

  • [info]是可选的,携带了所需的额外信息,它的第一个字节表示后续有多少个字节的数据

  • [subdata]同样是可选的,这是携带的实际数据,它的第一个字节表示后续有多少个字节的数据,如果跳过这些字节后遇到的字节值不是0x00,那么这个字节表示的依然是后续的数据子块大小

  • 固定值[0x00]标识块结束

Comment Extension

评论扩展,存储额外的文字数据。这些数据不能被感知,也就是与图像渲染无关,大部分解析器会跳过它。该扩展块以0x21,0xfe开始,其后跟数据子块(subdata);GIF只支持ASCII表中的字符,也就是一字节表示一字符,如果遇到不能打印的ASCII字符时,gif规范建议用空格(0x20)代替。

该扩展块建议始终在文件头或文件尾,不打扰到更重要的数据(图像或文本)的解析。

Graphic Control Extension

图形控制扩展,顾名思义包含了可以控制图形展示的信息,它的作用范围是此块出现后的第一个Image或Plain Text Extension,固定7字节,其中0x21,0xf9占用两个字节。

Image Descriptor

图像描述符,描述了当前图像的信息。遇到此块时则表示着一个Image的开始,固定10个字节,其中第一个字节始终是0x2c。

Local Color Table

本地色表,同全局色表,在本地色表标识为1时存在,如果不存在则使用全局色表。如果想要gif重现真彩色,可以利用本地色表的特性,将一个完整的图片切割的足够小,并对它们应用不同的色表,但这在大部分时候没有必要,因为这会增加整个gif的体积。

Image Data

图像数据紧跟在本地色表(不存在则跟随Image Descriptor)后,实际是经过压缩后的颜色索引。在gif编码中将图像中出现的所有颜色经过算法压缩至256色内,然后将每个颜色替换为在色表中最接近的颜色的索引,最后通过lzw算法将这些索引字节压缩。

Plain Text Extension

纯文本扩展,存放ASCII文本数据,这些文本需要被渲染到gif中,虽然大部分解析器都忽略它。此块包含15字节的固定信息,其后是可变长度的数据子块,用于存放文本数据。

Trailer

文件结尾,包含固定值0x3b

示例

获取图片宽高

#include<Windows.h>
#include <iostream>
#include "webp/decode.h"
//下载地址https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.4.0-windows-x64.zip
#pragma comment(lib, "libwebp.lib")
using namespace std;
struct LogicScreenDescriptor
{
        short width;
        short height;
        UCHAR yasuo;
        char index;
        char widthAndHeight;
};
void printResolution(UINT32 width, UINT32 height)
{
        cout << "分辨率为:" << width << "x" << height << endl;
}
void printResolutionLeve(UINT32 x, UINT32 y)
{
        cout << "水平分辨率为:" << x << ";垂直分辨率为:" << y << endl;
}
void readGif(PCHAR buffer, size_t len)
{
        cout << "当前是GIF文件" << endl;
        LogicScreenDescriptor* lsd = (LogicScreenDescriptor*)buffer;
        printResolution(lsd->width, lsd->height);
}
uint32_t bigToLittleEndian(uint32_t bigEndianValue) {
        return ((bigEndianValue & 0xff) << 24) |
                ((bigEndianValue & 0xff00) << 8) |
                ((bigEndianValue & 0xff0000) >> 8) |
                ((bigEndianValue & 0xff000000) >> 24);
}
void readPNG(PCHAR buffer, size_t len)
{
        cout << "当前是PNG文件" << endl;
        uint32_t length = bigToLittleEndian(*(uint32_t*)buffer);
        const char* chunkTypeCode = (const char*)(buffer + 4);
        char* data = (char*)malloc(length);
        if (data == nullptr)
                return;
        memcpy(data, buffer + 8, length);
        uint32_t width = bigToLittleEndian(*(uint32_t*)data);
        uint32_t height = bigToLittleEndian(*(uint32_t*)(data + 4));
        printResolution(width, height);
        free(data);
}
short swapEndian_short(short value) {
        return ((value & 0xff) << 8) | ((value & 0xff00) >> 8);
}
void readJPG(PCHAR buffer, int len)
{
        cout << "当前是JPG文件" << endl;
        while (len > 0)
        {
                char seg[2] = {};
                memcpy(seg, buffer, 2);
                short length = swapEndian_short(*(short*)(buffer + 2));
                
                char app0[] = { 0xFF, 0xE0 };
                char sof0[] = { 0xff, 0xc0 };
                if (memcmp(buffer, app0, 2) == 0)
                {
                        short length = (*(short*)(buffer + 2));
                        const char* type = (const char*)(buffer + 4);
                        char* mversion = (buffer + 9);
                        char* sversion = (buffer + 10);
                        char* danwei = (buffer + 11);
                        short width = swapEndian_short((*(short*)(buffer + 12)));
                        short height = swapEndian_short((*(short*)(buffer + 14)));
                        printResolutionLeve(width, height);
                }
                else if (memcmp(buffer, sof0, 2) == 0)
                {
                        short heigth = swapEndian_short((*(short*)(buffer + 5)));
                        short width = swapEndian_short((*(short*)(buffer + 7)));
                        printResolution(width, heigth);
                }
                len -= length;
                buffer = buffer + length + 2;
        }
}
void readBMP(PCHAR buffer, int len)
{
        cout << "当前是BMP文件" << endl;
        BITMAPFILEHEADER* bitmapFileHeader = (PBITMAPFILEHEADER)buffer;
        PBITMAPINFOHEADER bitmapInfoHeader = (PBITMAPINFOHEADER)(buffer + sizeof(BITMAPFILEHEADER));
        printResolution(bitmapInfoHeader->biWidth, bitmapInfoHeader->biHeight);
}
void readWebP(const uint8_t* buffer, int len)
{
        cout << "当前是WebP文件" << endl;
        int width, height;
        WebPGetInfo(buffer, len, &width, &height);
        printResolution(width, height);
}
int main()
{
        std::string fileName = R"(C:\Users\stdio\AppData\Roaming\Tencent\WeMeet\Global\Data\DynamicResource\3e1fa3ae1923bb4bacdabc661bc53ec5\remote_control_privilege_elevate_guide\remote_control_privilege_elevate_guide_zh-cn.gif)";
        fileName = R"(C:\Users\stdio\AppData\Roaming\kingsoft\office6\wpsassist\onlinetemplates\online\54b472eceb9de061adafdfa33b11780c_ds.png)";
        fileName = R"(C:\Users\stdio\Documents\WXWork\1688854706571334\Cache\Image\2024-11\海报.png)";
        fileName = R"(C:\Users\stdio\Documents\WXWork\1688854706571334\Cache\Image\2024-12\2.1每日一句 (1)(5).jpg)";
        fileName = R"(C:\Users\stdio\Documents\WXWork\1688854706571334\Avator\2024-12\d8142a10d6690d8f96a979eee418051c.jpg)";
        fileName = R"(C:\Users\stdio\AppData\Local\Packages\Microsoft.Windows.Search_cw5n1h2txyewy\AC\INetCache\LUF4768K\th0PBG8C9P.jpg)";
        fileName = R"(C:\Users\stdio\AppData\Local\Packages\Microsoft.Windows.Search_cw5n1h2txyewy\AC\INetCache\DY0N0B04\thQOHNB4RX.jpg)";
        fileName = R"(C:\Users\stdio\Documents\WXWork\1688854706571334\Cache\Image\2024-12\企业微信截图_17338795688595.jpg)";
        fileName = R"(D:\Github\TrafficMonitor-master\TrafficMonitor\skins\皮肤06\background.bmp)";
        fileName = R"(D:\Downloads\CppGuide-master\articles\imgs\zhyz7.webp)";
        fileName = R"(D:\Downloads\CppGuide-master\articles\imgs\zhyz25.webp)";
        FILE* file = nullptr;
        fopen_s(&file, fileName.c_str(), "rb");
        if (file)
        {
                cout << "当前文件:" << fileName << endl;
                fseek(file, 0, SEEK_END);
                long len = ftell(file);
                fseek(file, 0, SEEK_SET);
                PCHAR buffer = (PCHAR)malloc(len);
                if (buffer == nullptr)
                        return 1;
                size_t rlen = fread(buffer, 1, len, file);
                fclose(file);
                char gif89a[] = "GIF89a";
                size_t gif89aCount = sizeof(gif89a);
                char pngFileHead[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
                size_t pngFileHeadCount = sizeof(pngFileHead);
                char jpgFileHead[] = { 0xff, 0xd8};
                size_t jpgFileHeadCount = sizeof(jpgFileHead);
                char bmpFileHead[] = { 0x42, 0x4d };
                size_t bmpFileHeadCount = sizeof(bmpFileHead);
                char webpFileHead[] = { 'R', 'I', 'F', 'F' };
                size_t webpFileHeadCount = sizeof(webpFileHead);
                if (memcmp(buffer, gif89a, gif89aCount) == 0)
                {
                        readGif(buffer + gif89aCount, len - gif89aCount);
                }
                else if (memcmp(buffer, pngFileHead, pngFileHeadCount) == 0)
                {
                        readPNG(buffer + pngFileHeadCount, len - pngFileHeadCount);
                }
                else if (memcmp(buffer, jpgFileHead, jpgFileHeadCount) == 0)
                {
                        readJPG(buffer + jpgFileHeadCount, len - jpgFileHeadCount);
                }
                else if (memcmp(buffer, bmpFileHead, bmpFileHeadCount) == 0)
                {
                        readBMP(buffer, len);
                }
                else if (memcmp(buffer, webpFileHead, webpFileHeadCount) == 0)
                {
                        readWebP((const uint8_t*)buffer, len);
                }
                else
                {
                        cout << "未知文件" << endl;
                }
                free(buffer);
        }
}
posted @ 2024-12-18 16:22  这种人  阅读(28)  评论(0编辑  收藏  举报