03-基于GEC6818开发板实现加载一张图片
实现基于GEC6818开发板实现加载一张BMP文件。其中详细解析了一张BMP格式图的内容。
其他相关GEC6818开发板的内容可以参考
01-基于粤嵌GEC6818实现屏幕的显示固定颜色进行自动切换
02-基于GEC6818开发板的画正方形、画圆的操作——使用mmap映射提高效率
文章目录
- 03-基于GEC6818开发板实现加载一张图片
- 一、 bmp图片的格式内容
- 1.1 BITMAP文件头
- 1.2 DIB头
- 1.3 调色板(颜色数值组)
- 1.4 像素数组
- 二、 实例练习
- 2.1加载一张图片实现代码
- 2.2 进阶:对于图片大小不是800*480的图片进行轮播
一、 bmp图片的格式内容
我们要加载一张图片就必须知道这张图片的一些特定格式,并获取其中的像素数组。
那么BMP文件主要由四部分组成:BITMAP文件头,DIB头,调色板(颜色数值组),像素数组。
1.1 BITMAP文件头
文件头时表征这个文件是什么格式,例如我们下面代码中会提及的,判断这个图片是不是真的BMP文件,则可以
//判断是否为真的BMP文件
unsigned char buf[2];
read(fd,buf,2);
if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码
{
printf("NOT BMP\n");
return;
}
1.2 DIB头
这一部分也非常重要,因为其中包含了很多图片的基础信息,比如图片的宽度,高度,色深以及图片的大小等,这些都有助于我们后续对这个文件进行进一步的操作。
//读取数据
int width,height,depth;
//读取宽度,将偏移量偏移到宽度
lseek(fd,0x12,SEEK_SET);
read(fd,&width,4);//读取四个字节
read(fd,&height,4);//高度
lseek(fd,0x1c,SEEK_SET);
read(fd,&depth,4);
1.3 调色板(颜色数值组)
1.4 像素数组
二、 实例练习
2.1加载一张图片实现代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <math.h>
int lcd_fd = -1; // 全局的lcd描述符
unsigned int* plcd = NULL;
void lcdinit() {
lcd_fd = open("/dev/fb0", O_RDWR);
if (-1 == lcd_fd) {
perror("open fb0 error");
exit(1);
}
plcd = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if (plcd == MAP_FAILED) {
perror("mmap error");
return;
}
}
void lcd_destory() {
munmap(plcd, 800 * 480 * 4);
close(lcd_fd);
}
void point(int x, int y, unsigned int color) {
if (x >= 0 && x < 800 && y >= 0 && y < 480) {
*(plcd + y * 800 + x) = color;
}
}
void display_sql(int w, int h, int x0, int y0, int color) {
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
point(x + x0, y + y0, color);
}
}
}
//添加背景颜色-color
void display_bgm(int color) {
int w =800,h=480;
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
point(x , y , color);
}
}
}
void display_bmp(const char* filename,int x0,int y0)
{
//打开文件
int fd = open(filename, O_RDONLY);
if(-1 == fd)
{
perror("open bmp error");
return;
}
//判断是否为真的BMP文件
unsigned char buf[2];
read(fd,buf,2);
if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码
{
printf("NOT BMP\n");
return;
}
//读取数据
int width,height,depth;
//读取宽度,将偏移量偏移到宽度
lseek(fd,0x12,SEEK_SET);
read(fd,&width,4);//读取四个字节
read(fd,&height,4);//高度
lseek(fd,0x1c,SEEK_SET);
read(fd,&depth,4);
//只支持色深24和32
if(!(depth == 24 || depth == 32))
{
printf("NOT Support!\n");
return;
}
printf("width = %d height = %d depth = %d ", width,height,depth);
//4.获取像素数组
int line_valid_bytes = abs(width)*depth/8;//一行有效字节数
int line_bytes;//一行总字节数=有效字节数+赖子数
int laizi = 0;
if(line_valid_bytes%4)
{
laizi = 4-line_valid_bytes%4;
}
line_bytes = line_valid_bytes + laizi;
int total_bytes = line_bytes*abs(height);//整个像素数组的大小
//开辟一块动态内存
unsigned char *piexl = (unsigned char *)malloc(total_bytes); //用完后需要释放内存
lseek(fd,54,SEEK_SET);
read(fd,piexl,total_bytes);
unsigned char a,r,g,b;
int color;
int i = 0;
int x,y;
for(y=0;y<abs(height);y++)
{
for(x=0;x<abs(width);x++)
{
//a r g b 0xargb 小端模式 b g r a
b = piexl[i++];
g = piexl[i++];
r = piexl[i++];
if(depth == 32)
{
a = piexl[i++];
}
else
{
a = 0;//不透明
}
color=(a<<24)|(r<<16)|(g<<8)|(b);
//在屏幕对应的位置显示
point(width>0?x0+x:x0+abs(width)-x-1,
height>0?y0+abs(height)-y-1:y0+y,
color);
}
//每一行的末尾 有可能填充几个赖子
i += laizi;
}
//释放内存
free(piexl);
//关闭文件
close(fd);
}
int main() {
lcdinit();
display_bgm(0x000000);
display_bmp("picture.bmp",0,0);
lcd_destory();
return 0;
}
上面代码中存在一段需要好好理解的一段
int line_valid_bytes = abs(width)*depth/8;//一行有效字节数
int line_bytes;//一行总字节数=有效字节数+赖子数
int laizi = 0;
if(line_valid_bytes%4)
{
laizi = 4-line_valid_bytes%4;
}
line_bytes = line_valid_bytes + laizi;
int total_bytes = line_bytes*abs(height);
这段代码计算了与BMP图像相关的字节信息。具体来说,它涉及到了BMP图像中每一行的数据存储方式,以及为什么需要计算和使用这些值。
以下是代码中各部分的解释:
-
line_valid_bytes = abs(width) * depth / 8;
width
表示图像的宽度。depth
表示每个像素的位深度,通常为24(表示RGB,每个颜色通道8位)或32(带有额外的透明度通道)。- 这一行计算了每一行的有效字节数。例如,如果图像的宽度是800像素,深度是24位,则每一行需要
800 * 3 = 2400
字节来存储数据。
-
laizi = 0;
- 这是一个中文词汇“赖子”的拼音,通常用于描述不完全的部分或余数。
-
if(line_valid_bytes % 4)
- 这里检查有效字节数是否可以被4整除。在BMP文件格式中,每一行的数据存储通常会在每行结束时填充到4字节的倍数。如果不能整除,就需要在每一行的末尾添加一些额外的字节(通常为0)使其达到4字节的倍数。
-
laizi = 4 - line_valid_bytes % 4;
- 如果
line_valid_bytes
不能被4整除,laizi
会被设置为使得总字节数达到4字节倍数所需的字节数。
- 如果
-
line_bytes = line_valid_bytes + laizi;
- 计算了每一行总共所需的字节数。
-
int total_bytes = line_bytes * abs(height);
- 这里计算了整个BMP图像所需的总字节数。它是每一行总字节数乘以图像的高度。
总的来说,这段代码的目的是为了正确地计算BMP图像在内存中的字节布局
。由于BMP图像要求每一行的数据存储都必须是4字节的倍数
,所以需要进行这样的计算来确保数据的正确存储和访问。
2.2 进阶:对于图片大小不是800*480的图片进行轮播
因为我们的LCD的大小是800*480,但是我们的图片的分辨率不是完全满足这个分辨率,那么就需要对图片进行居中显示,那么就只需要设置一定的偏移量就可以了.可以直接加入下面一段代码进去即可。
//处理居中的情况
if(width<800||height<480)
{
x0 = (int)(800-width)/2;
y0 = (int)(480-height)/2;
}
具体实现代码。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <math.h>
int lcd_fd = -1; // 全局的lcd描述符
unsigned int* plcd = NULL;
void lcdinit() {
lcd_fd = open("/dev/fb0", O_RDWR);
if (-1 == lcd_fd) {
perror("open fb0 error");
exit(1);
}
plcd = mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if (plcd == MAP_FAILED) {
perror("mmap error");
return;
}
}
void lcd_destory() {
munmap(plcd, 800 * 480 * 4);
close(lcd_fd);
}
void point(int x, int y, unsigned int color) {
if (x >= 0 && x < 800 && y >= 0 && y < 480) {
*(plcd + y * 800 + x) = color;
}
}
//添加背景颜色-color
void display_bgm(int color) {
int w =800,h=480;
int x, y;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
point(x , y , color);
}
}
}
void display_mid(const char* filename)
{
int x0,y0;
//打开文件
int fd = open(filename, O_RDONLY);
if(-1 == fd)
{
perror("open bmp error");
return;
}
//判断是否为真的BMP文件
unsigned char buf[2];
read(fd,buf,2);
if(buf[0]!= 0x42 || buf[1]!= 0x4d)//若果不是B M 的ASCII码
{
printf("NOT BMP\n");
return;
}
//读取数据
int width,height,depth;
//读取宽度,将偏移量偏移到宽度
lseek(fd,0x12,SEEK_SET);
read(fd,&width,4);//读取四个字节
read(fd,&height,4);//高度
lseek(fd,0x1c,SEEK_SET);
read(fd,&depth,4);
//只支持色深24和32
if(!(depth == 24 || depth == 32))
{
printf("NOT Support!\n");
return;
}
printf("width = %d height = %d depth = %d ", width,height,depth);
//处理居中的情况
if(width<800||height<480)
{
x0 = (int)(800-width)/2;
y0 = (int)(480-height)/2;
}
//4.获取像素数组
int line_valid_bytes = abs(width)*depth/8;//一行有效字节数
int line_bytes;//一行总字节数=有效字节数+赖子数
int laizi = 0;
if(line_valid_bytes%4)
{
laizi = 4-line_valid_bytes%4;
}
line_bytes = line_valid_bytes + laizi;
int total_bytes = line_bytes*abs(height);//整个像素数组的大小
//开辟一块动态内存
unsigned char *piexl = (unsigned char *)malloc(total_bytes); //用完后需要释放内存
lseek(fd,54,SEEK_SET);
read(fd,piexl,total_bytes);
unsigned char a,r,g,b;
int color;
int i = 0;
int x,y;
for(y=0;y<abs(height);y++)
{
for(x=0;x<abs(width);x++)
{
//a r g b 0xargb 小端模式 b g r a
b = piexl[i++];
g = piexl[i++];
r = piexl[i++];
if(depth == 32)//32 色的有透明度,但是对24位的来说无所谓这个a的都无效
{
a = piexl[i++];
}
else
{
a = 0;//不透明
}
color=(a<<24)|(r<<16)|(g<<8)|(b);
//在屏幕对应的位置显示
point(width>0?x0+x:x0+abs(width)-x-1,
height>0?y0+abs(height)-y-1:y0+y,
color);
}
//每一行的末尾 有可能填充几个赖子
i += laizi;
}
//释放内存
free(piexl);
//关闭文件
close(fd);
}
int main() {
lcdinit();
//display_bgm(0x000000);
const char* images[] = {"1.bmp",
"2.bmp",
"3.bmp"};
int num_images = sizeof(images) / sizeof(images[0]);
int current_image_index = 0;
while(1)
{
display_mid(images[current_image_index]);
sleep(2);
display_bgm(0xFFFFFF);
current_image_index = (current_image_index + 1) % num_images; // 切换到下一张图片
}
lcd_destory();
return 0;
}
居中图片的显示