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和32if(!(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 ab = 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和32if(!(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 ab = 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;
}
居中图片的显示