这篇博文对BMP图像数据的基本数据结构做了简单的文字记录,方便供后期自己工作项目中进行查找,也希望能够帮助到其他阅读本文的读者。
图像文件的背后
不管是开发工作还是生活中,我们都会应用到图像文件。比如手机中的照片、UI开发过程当中的贴图素材等等。不知道各位小伙伴们是不是会好奇图像到底是怎样在Windows中显示的。博主在读书的时候就很好奇两件事:
- WIndows是怎么知道我的图像尺寸的?我同一个手机拍的照片像素尺寸一样可文件大小不一样啊?
- Windows怎么知道我的图像格式的?是BMP、PNG还是JPEG?如果我改了文件后缀名,为什么图片查看器还是能显示呢?
本文将以BMP格式的图像文件为例,对Windows图像查看器是如何解析和查看图像的进行记录和介绍。
准备一张BMP图像

查看属性
我们通过windows的文件浏览器查看图像的详细信息,可以看到BMP文件有256x128个像素,色深是24位。

修改文件拓展名再打开
为了确定属性格式到底是怎么确认的,我们尝试修改BMP文件的后缀名成为PNG,然后再上传到属性查看工具网站,发现通过工具解析出来的信息依旧是BMP。
那么此时问题就很明显了,图像的格式是根据图像的内容来识别的。由此就有了另一个问题,我给这个图像后面追加一些内容会怎么样呢?
在图像文件末尾追加一段数据
通过C#的代码对测试图片追加字符串信息,看图像尺寸是否会有变化
class Program
{
static readonly string assetsName = "sample_256_126.bmp";
static readonly string copyName = "sample_256_126_append.bmp";
static void Main(string[] args)
{
Console.WriteLine("Append Hello World to bmp data!");
try
{
if (File.Exists(copyName))
{
File.Delete(copyName);
}
File.Copy(assetsName, copyName);
StreamWriter streamWriter = File.AppendText(copyName);
for (int i = 0; i < 1000; ++i)
streamWriter.WriteLine("Hello World!");
streamWriter.Close();
} catch (Exception e)
{
Console.WriteLine(e.StackTrace);
Console.WriteLine(e.Message);
}
}
}

追加数据后,我们发现图片的文件大小变大了,但是像素尺寸并没有任何改变。
上述两个实验得出结论,BMP的图像信息是以一定格式存在图像文件当中,接下来我们就探秘以下BMP文件是如何存储的。
BMP图像的数据结构
BMP文件基础的数据结构如下图所示,由BMP文件头,BMP信息头和具体的像素信息所组成。本节将结合代码对数据格式进行进一步的介绍数据格式。

BMP 文件头
定义如下BMP文件头结构体:
[StructLayout(LayoutKind.Sequential, Pack =1)]
public struct BMPHeader
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] ID; // BMP Identification code "BM"
public uint FileSize; // the size of bmp file
public ushort AppSpec1; // reserved commonly zero
public ushort AppSpec2; // reserved commonly zero
public uint PixelOffset; // offset of pixel data
public override string ToString()
{
return string.Format("Identification: {0}\n" +
"File Size: {1}\n" +
"App Spec Reserved1: {2}\n" +
"App Spec Reserved2: {3}\n" +
"Pixel Offset: {4}\n",
Encoding.UTF8.GetString(ID),
FileSize,
AppSpec1,
AppSpec2,
PixelOffset
);
}
}
通过代码读取我们的测试图片信息输出如下,可以看到文件大小为96K与预想的一致
Hello Read BMP Header!
Identification: BM
File Size: 98358
App Spec Reserved1: 0
App Spec Reserved2: 0
Pixel Offset: 54
新增图像信息数据的结构体如下,并通过控制台进行读取
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BMPInfoHeader
{
public uint Size; // Size of the info header
public int Width; // width of bitmap in pixels
public int Height; // height of bitmap in pixels
public ushort Planes; // No. of planes for the target device, always 1
public ushort BitCount; // No. of bits per pixels
public uint Compression;
public uint SizeImage; // size of the image data
public int PixelsPerMeterInX; // ppm in x
public int PixelsPerMeterInY; // ppm in y
public uint ColorsUsed; // No. color indexes in the color table. use 0 for max number
public uint ColorsImportant; // No. of colors used for displaying the bitmap
public override string ToString()
{
return string.Format("Header Size: {0}\nPixel Width: {1}\nPixel Height: {2}\n" +
"Planes: {3}\nBitCount: {4}\nCompression: {5}\n" +
"Size Image: {6}\nPixelsPerMeterInX: {7}\nPixelsPerMeterInX: {8}\n" +
"ColorsUsed: {8}\nColorsImportant: {9}",
Size,
Width,
Height,
Planes,
BitCount,
Compression,
SizeImage,
PixelsPerMeterInX,
PixelsPerMeterInY,
ColorsUsed,
ColorsImportant
);
}
}
输出结果如下:
Hello Read BMP Header!
Identification: BM
File Size: 98358
App Spec Reserved1: 0
App Spec Reserved2: 0
Pixel Offset: 54Header Size: 40
Pixel Width: 256
Pixel Height: 128
Planes: 1
BitCount: 24
Compression: 0
Size Image: 98304
PixelsPerMeterInX: 0
PixelsPerMeterInX: 0
ColorsUsed: 0
ColorsImportant: 0
与我们预期的结果一致,像素高度,像素宽度均与图片查看器得到的结果一致
实验代码请见github