前言
在开始之前,我跟大家声明一下,这个教程不是在开发板上面显示png格式的图片,而是打开的是bmp格式的图片文件,显示成为png格式图片的效果。
思路
假设你们已经学会了如何在开发板上面显示bmp格式的图片了,那么我们知道的png格式图片在显示的时候是不会把背景显示出来的,所以我的猜想是png格式的图片,除了要显示的区域外,其他像素点都是存白色的,RGB值为0xFFFFFF;一个像素点3个字节,记住这个点,很重要。
实现思路:就是在映射图片像素点在屏幕上的时候,遇到像素点的值为0x00FFFFFF的时候就跳过,不要这些存白的像素点映射在屏幕上面即可。
原理
我们熟知的图片都是有一个个像素点集合而成的,每个像素点的颜色都是各自的RGB值组合而成,RGB是什么,是我们熟悉的三原色 红-R ,绿-G ,蓝-B ,他们可以组合形成各种各样的颜色,我们bmp格式的图片文件是不经过压缩的,所以里面存储的像素点的数据是最原始的RGB,这也是我们选bmp格式图片的缘由。
注意一点的是,bmp在存储图片RGB数据的时候,以BGR的顺序存储的,而且上下是颠倒的,所以我们在映射的顺序也要颠倒才行;
我们开发板的屏幕的像素点不仅仅是由RGB组成,在其前面还有一个A,也就是我们屏幕像素点是ARGB,一般情况下我们都不会去理会前面的那个A,一个像素点ARGB占4个字节大小,所以在把图片显示在屏幕上有几个步骤;
1、把bmp图片文件读取到系统bmp_buf[ ](即一个数组里面);
2、把bmp_buf里面BGR 数据,转换成屏幕像素点的格式ARGB,放到 buf 数组里面;
3、将 buf 里面的ARGB 像素点一个个的映射在屏幕的像素点上面,记得颠倒过来,
最关键的步骤;
遇到存白色的像素点利用 continue 语句跳开,
for(y=start_y,j=0; y<(start_y+high),j<high; y++,j++)
{
for(x=start_x,i=0; x<(start_x+width),i<width; x++,i++)
{
if(buff[(high-1-j)*width+i] == 0x00ffffff)
continue;
*(mem_p+y*800+x) = buff[(high-1-j)*width+i];
}
}
源码
show_bmp.c
#include <stdio.h>
#include <dlfcn.h> // 动态加载动态库的头文件:dlopen()、dlsym()
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/mman.h>
struct bitmap_header
{
int16_t type;
int32_t size; // 图像文件大小
int16_t reserved1;
int16_t reserved2;
int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
struct bitmap_info
{
int32_t size; // 本结构大小
int32_t width; // 图像宽
int32_t height; // 图像高
int16_t planes;
int16_t bit_count; // 色深
int32_t compression;
int32_t size_img; // bmp数据大小,必须是4的整数倍
int32_t X_pel;
int32_t Y_pel;
int32_t clrused;
int32_t clrImportant;
}__attribute__((packed));
// 以下结构体不一定存在于BMP文件中,除非:
// bitmap_info.compression为真
struct rgb_quad
{
int8_t blue;
int8_t green;
int8_t red;
int8_t reserved;
}__attribute__((packed));
#define FB_FILE "/dev/fb0"
unsigned int *mem_p;
int lcd_fd;
// BMP格式头规范
int lcd_init(void);
int lcd_uninit(void);
int show_bmp_png(const char *pathname,int start_x, int start_y);
int lcd_init(void)
{
//打开file.txt文件, 文件不存在,则打开失败,如果使用O_CREAT,那必须要添加文件权限。
lcd_fd = open(FB_FILE, O_RDWR);
if(lcd_fd == -1)
{
printf("open a.txt fail\n");
return -1;
}
//屏幕映射
mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(mem_p == MAP_FAILED)
{
printf("mmap fail\n");
}
return 0;
}
int lcd_uninit(void)
{
// 解除映射
munmap(mem_p, 800*480*4);
close(lcd_fd);
}
/*
参数说明:*pathname-----图片路径
start_x--------在屏幕的哪个位置显示-x轴
start_y--------在屏幕的哪个位置显示-y轴
*/
int show_bmp_png(const char *pathname,int start_x, int start_y)
{
int width, high; //图片亮度与高度
int i, j, x, y;
// 读取BMP格式头,获取图片信息
struct bitmap_header header;
struct bitmap_info info;
struct rgb_quad quad;
int bmp_fd = open(pathname, O_RDONLY);
if(bmp_fd == -1)
{
printf("open bmp fail\n");
return -1;
}
//跳过54个字节头
// 第一、二个结构体是必有信息
read(bmp_fd, &header, sizeof(header));
read(bmp_fd, &info, sizeof(info));
width = info.width;
high = info.height;
if(start_x+width > 800 || start_y+high > 480)
{
printf("%s图片显示溢出!\n",pathname);
close(bmp_fd);
return -1;
}
//变长数组
unsigned char bmpbuff[width*high*3];
unsigned int buff[width*high];
unsigned int tmpbuff[width*high];
read(bmp_fd, bmpbuff, sizeof(bmpbuff));
for(i=0; i<width*high; i++)
{
buff[i] = bmpbuff[3*i+0] | bmpbuff[3*i+1]<<8 | bmpbuff[3*i+2]<<16;
}
//正真显示图片
for(y=start_y,j=0; y<(start_y+high),j<high; y++,j++)
{
for(x=start_x,i=0; x<(start_x+width),i<width; x++,i++)
{
if(buff[(high-1-j)*width+i] == 0x00ffffff)
continue;
*(mem_p+y*800+x) = buff[(high-1-j)*width+i];
}
}
close(bmp_fd);
return 0;
}
int main()
{
show_bmp_png("1.bmp",0,0);
return 0 ;
}
里面有专门读取图片长度宽度大小信息的结构体,可以得知图片的大小。