GEC6818学习记录-颜色图片的显示

116 阅读6分钟

1、开发板显示颜色

开发板大小为800*480,采用32位色深,Argb真彩色。,要显示颜色,只需要将每个像素点填充颜色。首先显示屏设备目录是:"/dev/fb0",先用函数open打开设备文件,然后将数据写到LCD。

int lcd_fd = open("/dev/fb0", O_RDWR);
if(lcd_fd == -1)
{
    perror("lcd_fd");
    return -1;
}
unsigned int red = 0xff0000;
for (int y = 0; y < 480 ; ++y)
{
    for (int x = 0; x < 800; ++x)
    {
        write(lcd_fd, &red, 4);
    }    
}
close(lcd_fd);

这种方式要调用write 800*480次,屏幕显示也会有延时。所以我们需采用映射的方式,mmap函数可以将LCD映射到一个虚拟内存,这样我们对这个内存空间操作将会实时显示到LCD。

#include <sys/mman.h> 
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 
参数列表: addr:内存映射起始的地址,若为NULL:系统自动分配 。
length:要映射的内存大小。 
prot:PROT_READ:可读 PROT_WRITE:可写 ......
flags:多个进程在访问的时候,是否让其他进程可见,MAP_SHARED,允许其他进程可见,MAP_PRIVATE,跟MAP_SHARED相反......
fd:LCD屏的文件描述符 
offset:偏移量 
返回值: 成功:映射内存的起始地址 失败:(void *)-1 
解除内存映射 
int munmap(void *addr, size_t length); 
addr:解除内存映射的地址 
length:内存映射的大小 

练习:在自定义位置显示一个红色的长方形

#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
int main(int argc, char const *argv[])
{
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if(lcd_fd == -1)
    {
        perror("lcd_fd");
        return -1;
    }
    unsigned int white = 0xffffff;
    unsigned int red = 0xff0000;
    int *p = mmap(NULL, 800*480*4, PROT_WRITE | PROT_READ, MAP_SHARED, lcd_fd, 0);
    if(p == (void *)-1)
    {
        perror("mmap");
        return -1;
    }
    //先将屏幕全部填充为白色背景
    for (int y = 0; y < 480 ; ++y)
    {
        for (int x = 0; x < 800; ++x)
        {
            *(p+x+y*800) = white;
        }    
    }
    //在坐标(500,100)位置填充红色
    for (int y = 100; y <= 150; ++y)
    {
        for (int x = 500; x <= 600 ; ++x)
        {
            *(p+x+y*800) = red;
        }
    }
    close(lcd_fd);
    munmap(p,800*480*4);

效果图:

image.png

2、开发板显示bmp格式图片

图片格式有bmp,jpg,png...... jpg和png格式的图片都经过了压缩,需要解压。而bmp没有压缩。可以直接读取数据。bmp图片文件头占14字节,位图信息头占40字节,之后的才是颜色数据。所以在读取图片信息时要跳过前面54字节。

bmp图像采用24位色深bgr,而LCD存储方式是32位rgb,则需要将数据转换成RGB在进行填充。由于显示屏是从上往下显示,而bmp图片数据是从下往上存储。所以我们在填充数据时要反向填充。

练习:显示图片

#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if(lcd_fd == -1)
    {
        perror("lcd_fd");
        return -1;
    }
    int *p = mmap(NULL, 800*480*4, PROT_WRITE | PROT_READ, MAP_SHARED, lcd_fd, 0);
    if(p == (void *)-1)
    {
        perror("mmap");
        return -1;
    }
    //打开图片
    int bmp_fd = open("./1.bmp", O_RDWR);
    if(bmp_fd == -1)
    {
        perror("bmp_fd");
        return -1;
    }
     
    char buf1[800*480*3];
    int buf[800*480];
    read(lcd_bmp, buf1, 800*480*3);  //读取数据
    for (int i = 0; i < 800*480; ++i)
    {
        //         b            g            r
        buf[i] = buf1[3*i] | buf1[3*i+1] <<8  | buf1[3*i+2] << 16 | 0xff << 24;  //拼接成rgb
    }
    for (int y = 0; y < 480; ++y)
    {
        for (int x = 0; x < 800; ++x)
        {
            p[x+(479-y)*800] = buf[y*800+x];   //反向填充
        }

    close(lcd_fd);
    munmap(p,800*480*4);
    return 0;
}

效果图: image.png

如果图片尺寸小于800*480,我们需要获取图片宽度和高度如何去计算坐标,显示在任意位置。由于计算机读取数据是按固定字长如32位CPU一次读4字节。图片宽的字节数必须为4的倍数。不满足4的倍数计算机会自动补齐空白字节。

如下图 401240的bmp图片存储大小是401240*3+240(补齐的空白字节)

image.png

image.png

所以在读取是需要跳过空白字节

switch(width%4)
{
    case 1:
        for (int i = 0; i < height; ++i)
        {
            read(bmp_fd, buf+i*width*3, width*3);// 401*3= 1203  *   240
            lseek(bmp_fd, 1, SEEK_CUR);
        }break;
    case 2:
        for (int i = 0; i < height; ++i)
        {
            read(bmp_fd, buf+i*width*3, width*3);//402
            lseek(bmp_fd, 2, SEEK_CUR);
        }break;
    case 3:
        for (int i = 0; i < height; ++i)
        {
            read(bmp_fd, buf+i*width*3, width*3);//403
            lseek(bmp_fd, 3, SEEK_CUR);
        }break;
    case 0:read(bmp_fd, buf, width*height*3);  //若为0满足是4的倍数,不需要偏移,可直接读取。
}

练习:在中间显示图片400*240

首先读取宽度高度,可以定义一个结构体,也可以读取54字节然后取出宽和高对应位的数据。

1、读取54字节
char head[54];
read(bmp_fd, head, 54);
int width = head[18]| head[19]<<8| head[20]<<16| head[21]<<24;
int height = head[22]| head[23]<<8| head[24]<<16| head[25]<<24;
printf("width = %d\n", width);
printf("hight = %d\n", height);

2、定义结构体
typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
#pragma pack(1)             /* 取消字节对齐 */

typedef struct {            /* bmp图片文件头信息封装 */
    /* 位图文件头-14Byte */
    u8  bit_file_type[2];   /* 位图文件类型:'BM'->0x4d42 */
    u32 file_size;          /* 整个文件大小 */
    u16 reserved1;          /* 保留字1 */
    u16 reserved2;          /* 保留字2 */
    u32 offset;             /* 文件头到位图数据之间的偏移量 */
    
    /* 位图信息头-40Byte */
    u32 head_size;          /* 位图信息头长度 */
    u32 width;              /* 位图宽度 */  //19~22
    u32 height;             /* 位图高度 */  //23~26
    u16 bit_planes;         /* 位图位面数 */
    u16 bits_per_pixel;     /* 每个像素的位数 */
    u32 compression;        /* 压缩说明 */
    u32 image_size;         /* 位图数据大小 */
    u32 h_res;              /* 水平分辨率 */
    u32 v_res;              /* 垂直分辨率 */
    u32 color_palette;      /* 位图使用的颜色索引数 */
    u32 vip_color;          /* 重要的颜色索引数目 */

}bmp_head;

#pragma pack()  /* 恢复字节对齐 */

bmp_head head;  //定义一个结构体
read(lcd_bmp, &head, 54);
printf("宽:%d\n", head.width);
printf("高:%d\n", head.height);

显示图片:

#include <sys/mman.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //采用传参方式
    if(argc != 2) 
    {
        printf("./bmp  <filename>\n");  
        return 0;
    }
    int lcd_fd = open("/dev/fb0", O_RDWR);
    if(lcd_fd == -1)
    {
        perror("lcd_fd");
        return -1;
    }
    int *p = mmap(NULL, 800*480*4, PROT_WRITE | PROT_READ, MAP_SHARED, lcd_fd, 0);
    if(p == (void *)-1)
    {
        perror("mmap");
        return -1;
    }
    int bmp_fd = open(argv[1], O_RDWR);
    if(bmp_fd == -1)
    {
        perror("lcd_bmp");
        return -1;
    }
    
    unsigned char b, g, r;

    char head[54];
    read(bmp_fd, head, 54);
    int width = head[18]| head[19]<<8| head[20]<<16| head[21]<<24;
    int height = head[22]| head[23]<<8| head[24]<<16| head[25]<<24;
    char buf1[width*height*3];
    int st_x = (800-width) / 2;
    int st_y = (480-height) / 2;
    read(bmp_fd, buf1, width*height*3);
    int i = 0;
    for (int y = 0; y < height; ++y)
    {
        for (int x = 0; x < width; ++x)
        {
            b = buf1[i++];
            g = buf1[i++]; 
            r = buf1[i++];  
            p[st_x+x+(479-st_y-y)*800] = b | g <<8 | r <<16;   //倒转填充
        }
    }

    close(lcd_fd);
    munmap(p,800*480*4);
    return 0;
}

效果图:

image.png