图像格式简单解析(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格式的图像文件中,如果一个文件中包含多个图像,图像数据区将依次重复数据块序列。
-
结束标志区的作用主要是标记整个数据流的结束。
Header
在文件头中总包含着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);
}
}