micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片

继续折腾ST7789v液晶屏幕,这次我们要在屏幕上显示BMP文件图片。

一、BMP图片文件格式

BMP图片文件格式问题,网上有很多文章能找到,我也找了很多资料来学习。关于文件头的问题,网上的文章大概都讲得很透彻。我只是拿这个ST7789v液晶来玩玩,没那么多时间去研究和兼容每种格式,所以我只打算锚定其中一种比较简单的24位真彩色格式来显示在我的液晶上,其他格式可以在电脑上转换成24位真彩色格式后再传给液晶。

所以,在文章的开头,我还是得啰嗦一下24位真彩色格式的BMP文件结构。

BMP文件包含四个部分:
1.位图文件头(BITMAPFILEHEADER)(14字节)
2.位图信息头(BITMAPINFOHEADER)(40字节)
3.颜色表*(RGBQUAD[])(不一定存在)
4.像素阵列(Pixels[][])(图像数据)

其中1和2是固定的大小;3在24位真彩色格式中是没有的,可以忽略;4就是图像数据了。

1.文件头

文件头的结构定义如下:

typedef struct tagBITMAPFILEHEADER{
        WORD    bfType;                  // 位图文件的类型,必须为BMP (2个字节)
        DWORD    bfSize;                  // 位图文件的大小,以字节为单位 (4个字节)
        WORD    bfReserved1;             // 位图文件保留字,必须为0 (2个字节)
        WORD    bfReserved2;             // 位图文件保留字,必须为0 (2个字节)
        DWORD    bfOffBits;               // 位图数据的起始位置,以相对于位图 (4个字节)
    } BITMAPFILEHEADER;

其中文件的大小bfSize稍微有点用,可以用以下代码读取出来:

f.seek(2)#文件大小
buff=f.read(4)
fileSize=buff[3]*1024+buff[2]*512+buff[1]*256+buff[0]
print("BMP文件大小:",fileSize,"字节")

下图是本文所用的一张图片用二进制编辑器打开后的文件大小数值。

micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片_第1张图片

 注意,低位在后,高位在前。图中文件大小是十六进制的0x00016926,十进制是92454字节。

后续占用多个字节的数值都是用这种低位在后,高位在前的规则。

2.信息头

信息头的结构体定义如下:
typedef struct tagBITMAPINFOHEADER{
        DWORD biSize;             // 本结构所占用字节数  (4个字节)
        LONG biWidth;              // 位图的宽度,以像素为单位(4个字节)
        LONG biHeight;             // 位图的高度,以像素为单位(4个字节)
        WORD biPlanes;            // 目标设备的级别,必须为1(2个字节)
        WORD biBitCount;         // 每个像素所需的位数,必须是1(双色)、// 4(16色)、8(256色)、
                                //24(真彩色)或32(增强真彩色)之一 (2个字节)
        DWORD biCompression;     // 位图压缩类型,必须是 0(不压缩)、 1(BI_RLE8 
                                // 压缩类型)或2(BI_RLE4压缩类型)之一 ) (4个字节)
        DWORD biSizeImage;         // 位图的大小,以字节为单位(4个字节)
        LONG biXPelsPerMeter;      // 位图水平分辨率,每米像素数(4个字节)
        LONG biYPelsPerMeter;   // 位图垂直分辨率,每米像素数(4个字节)
        DWORD biClrUsed;        // 位图实际使用的颜色表中的颜色数(4个字节)
        DWORD biClrImportant;   // 位图显示过程中重要的颜色数(4个字节)
    } BITMAPINFOHEADER;

其中有几个信息是必须要取出来的。

biWidth;              // 位图的宽度,以像素为单位(4个字节),--即图片横向的像素宽度。

biHeight;             // 位图的高度,以像素为单位(4个字节),--即图片纵向的像素高度。

biBitCount;         // 每个像素所需的位数,必须是1(双色)、// 4(16色)、8(256色)、
                                //24(真彩色)或32(增强真彩色)之一 (2个字节)。---判断图片格式是不是24位真彩色

3.颜色表

24位真彩色格式没有颜色表,直接忽略。

4.像素阵列

很多文章里没有详细介绍这部分的格式,我也走了写弯路,所以我必须把这部分详细讲一下。

24位真彩色文件格式里,像素阵列是从第55个字节开始的(文件头14+信息头40),从0开始的话是54。

1个像素占3个字节,顺序分别是B、G、R(注意顺序不是RGB!!!)。

但是的但是,你要注意了!第55字节并不是图片左上第一个像素点!!!

数据的规律是这样的。

首先,由于某些原因(DWORD),像素阵列对图片每一行数据的字节数进行了约束,必须是4的整数倍。比如,图像横向时5个像素点,每像素3字节,3*5=15字节;为了是4的倍数,这时就会填充以0x00填充1个字节。这时图片一行所占用的字节数是16,而不是15,这个知识点网上大多数文章都提到了。

然后下面我要讲的这个知识点网上那些文章就没有告诉你了。

用个例子说明:

假设一张图片是横向5像素,纵向4像素。前面讲了,横向每行占用的字节数是16字节。一共4行,占用的字节数就应该是16*4=64字节。

这64字节在BMP文件中的存放时,行顺序是倒着像下图这样存放的(图中蓝色箭头的顺序):

micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片_第2张图片micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片_第3张图片

即:首先是最后一行,然后倒数第二行。。。。最后是第一行。

如果不知道这个规律,你的图像显示可能变成这个样子(倒着显示):

micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片_第4张图片

二、源代码

好了,知识点到此就讲完了,剩下的就是源代码了。

'''
    本程序只处理24位真彩色bmp文件。24位真彩色即RGB888,分别用8位(1个字节)
来表达一个像素点的R、G、B色彩信息。
    BMP文件包含四个部分:
    1.位图文件头(BITMAPFILEHEADER)
    2.位图信息头(BITMAPINFOHEADER)
    3.颜色表*(RGBQUAD[])----不一定存在
    4.像素阵列(Pixels[][])

各个部分的结构:
    1.文件头(14字节)
    typedef struct tagBITMAPFILEHEADER{
        WORD    bfType;                  // 位图文件的类型,必须为BMP (2个字节)
        DWORD    bfSize;                  // 位图文件的大小,以字节为单位 (4个字节)
        WORD    bfReserved1;             // 位图文件保留字,必须为0 (2个字节)
        WORD    bfReserved2;             // 位图文件保留字,必须为0 (2个字节)
        DWORD    bfOffBits;               // 位图数据的起始位置,以相对于位图 (4个字节)
    } BITMAPFILEHEADER;
    
    2.信息头(40字节),用于描述大小等信息
    typedef struct tagBITMAPINFOHEADER{
        DWORD biSize;             // 本结构所占用字节数  (4个字节)
        LONG biWidth;              // 位图的宽度,以像素为单位(4个字节)
        LONG biHeight;             // 位图的高度,以像素为单位(4个字节)
        WORD biPlanes;            // 目标设备的级别,必须为1(2个字节)
        WORD biBitCount;         // 每个像素所需的位数,必须是1(双色)、// 4(16色)、8(256色)、
                                //24(真彩色)或32(增强真彩色)之一 (2个字节)
        DWORD biCompression;     // 位图压缩类型,必须是 0(不压缩)、 1(BI_RLE8 
                                // 压缩类型)或2(BI_RLE4压缩类型)之一 ) (4个字节)
        DWORD biSizeImage;         // 位图的大小,以字节为单位(4个字节)
        LONG biXPelsPerMeter;      // 位图水平分辨率,每米像素数(4个字节)
        LONG biYPelsPerMeter;   // 位图垂直分辨率,每米像素数(4个字节)
        DWORD biClrUsed;        // 位图实际使用的颜色表中的颜色数(4个字节)
        DWORD biClrImportant;   // 位图显示过程中重要的颜色数(4个字节)
    } BITMAPINFOHEADER;

    3.颜色表
    typedef struct tagRGBQUAD 
    {
      BYTE rgbBlue;          // 蓝色的亮度(值范围为0-255)
      BYTE rgbGreen;         // 绿色的亮度(值范围为0-255)
      BYTE rgbRed;           // 红色的亮度(值范围为0-255)
      BYTE rgbReserved;      // 保留,必须为0
    } RGBQUAD;

    可以看到一个RGB表项为4个字节。
    颜色表中RGBQUAD结构数据的个数由位图信息头中的biBitCount来确定:
    当biBitCount=1, 4, 8时,分别有2, 16,256个表项
    !!!!!!!!当biBitCount=24时,没有颜色表项!!!!!!!!!

    4.像素阵列
    记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数如下:
    当biBitCount=1时,8个像素占1个字节;
    当biBitCount=4时,2个像素占1个字节;
    当biBitCount=8时,1个像素占1个字节;
    当biBitCount=24时,1个像素占3个字节,分别是R、G、B;
    Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充。

'''


f=open('csdn.bmp',"rb")
buff=f.read(1)
cnt=0

f.seek(2)#文件大小
buff=f.read(4)
fileSize=buff[3]*1024+buff[2]*512+buff[1]*256+buff[0]
print("BMP文件大小:",fileSize,"字节")


f.seek(18)#水平分辨率
buff=f.read(4)
HResolution=buff[3]*1024+buff[2]*512+buff[1]*256+buff[0]
print("图片水平分辨率:",HResolution,"像素")

f.seek(22)#垂直分辨率
buff=f.read(4)
VResolution=buff[3]*1024+buff[2]*512+buff[1]*256+buff[0]
print("图片垂直分辨率:",VResolution,"像素")


f.seek(28)#每个像素所需的位数
buff=f.read(2)
colorMode=buff[1]*256+buff[0]
print("颜色模式:",colorMode)


BMPBuffer=bytearray(0) #准备传给液晶的buffer

#计算图片每行占用的字节数(向上往4的整数倍靠)
if HResolution*3%4==0:
    bytesPerRow=(HResolution*3//4)*4
else:
    bytesPerRow=(HResolution*3//4+1)*4

rgb565=bytearray(2)

for rowCnt in range(VResolution-1,-1,-1):#行号要倒着来
    f.seek(bytesPerRow*rowCnt+54)#注意偏移量54,因为图像数据是从54字节开始的
    for cntInARow in range(int(bytesPerRow/3)):#读取行中的有效数据,填充的0x00将被忽略
        buff=f.read(3)#RGB888格式,一次读取3字节
        #从24位真彩色RGB888转换为RGB565格式
        blue=buff[0]&0xf8#注意第一字节为蓝色blue数据
        green=buff[1]&0xfc
        red=buff[2]&0xf8
        rgb565[0]=red | (green & 0xe0)>>5
        rgb565[1]=(blue>>3) | ((green & 0x1b) <<3)
        BMPBuffer.append(rgb565[0])
        BMPBuffer.append(rgb565[1])

f.close()


tft.blit_buffer(BMPBuffer, x=80, y=40, width=HResolution, height=VResolution)

显示效果:

micropython驱动ST7789v液晶屏幕显示24位真彩BMP文件图片_第5张图片

你可能感兴趣的:(开发语言,python,嵌入式硬件,驱动开发)