关于bmp图片倾斜的解决方法
小白实践遇到的小问题,请大佬轻锤。
在开发板上想显示一张bmp图片,可以使用LCD设备文件,往LCD中写入一段像素点数据,进行内存映射。
示例如下
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
int lcd_fd,bmp_fd;
unsigned int* lcd_map;
int Lcd_Init() //初始化LCD
{
//1,打开lcd, 打开图片文件
lcd_fd = open("/dev/fb0", O_RDWR);
if(-1 == lcd_fd)
{
perror("open lcd failed");
return -1;
}
//3,映射LCD到内存
lcd_map = (unsigned int* )mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(lcd_map == MAP_FAILED)
{
perror("mmap failed");
return -1;
}
}
void Lcd_Uninit() //关闭LCD
{
//5,关闭lcd文件,关闭图片文件,释放映射内存
munmap(lcd_map, 800*480*4);
close(lcd_fd);
close(bmp_fd);
}
//居中显示图片位置
void Show_bmp(const char *picname) //显示一张800*480的bmp图片,参数为 图片路径名,显示高度为 high 宽度为weigh
{
bmp_fd = open(picname, O_RDWR);
if(-1 == bmp_fd)
{
perror("open bmp failed");
return;
}
//2,读取图片中的像素点数据 --》 跳过54字节头信息 800*480*3
unsigned char head[54];
int weigh,high;
read(bmp_fd, head , 54); //head[18],head[19],head[20],head[21],==>宽度信息 ; head[22],head[23],head[24],head25] ==》高度信息
weigh = head[21]<<24 | head[20]<<16 | head[19]<<8 | head[18];
high = head[25]<<24 | head[24]<<16 | head[23]<<8 | head[22];
printf("weigh=%d, high=%d\n", weigh, high);
//lseek(bmp_fd, 54, SEEK_SET); //bmp图片54字节头信息中含有图片高度,宽度信息
unsigned char bmp_buf[weigh*high*3];
unsigned int buf[weigh*high];
read(bmp_fd, bmp_buf, sizeof(bmp_buf));
/处理像素点数据 RGB --》 ARGB//
unsigned char A, R, G, B;
int i,j;
for(i=0; i<weigh*high; i++)
{
A = 0x00;
B = bmp_buf[3*i];
G = bmp_buf[3*i + 1];
R = bmp_buf[3*i + 2]; //BGR存储像素点信息
// ARGB = A R G B ==> 1234 == 1*1000 + 2*100 + 3*10 + 4*1
buf[i] = A<<24 | R<<16 | G<<8 | B;
}
//图片居中
int x_s = (800-weigh)/2;
int y_s = (480-high)/2;
///
//4,把读取的bmp图片数据写入到映射内存空间中 memcpy --》把一片内存中的数据拷贝到另一片内存 //修改显示位置,使得图片不再倒立
for(i=y_s; i<high+y_s && i<480; i++)
{
for(j=x_s; j<weigh+x_s && j<800; j++) //(j,i)点 ==》 buf[800*i+j] ==>
{
//LCD映射内存上坐标为(j,i)// 图片上坐标为(j,i)的点
lcd_map[800*i+j] = buf[weigh*(high-1- i + y_s)+j - x_s];
}
}
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("%s <pic_name>\n",argv[0]);
exit(1);
}
Lcd_Init();
Show_bmp(argv[1]);
Lcd_Uninit();
return 0;
}
但此时会出现一个问题,当行数据不能被4整除时,会出现图片倾斜的问题
如示例图中一张750*450的bmp图片,每一行的数据应该是750x3 = 2250字节,但2250不能被4整除,所以系统会自动在每一行后面补齐2个字节数据,达到2252字节,这样图片的长就被拉长了,就会出现倾斜现象。
所以为了解决这个问题,我们应该判断图片的行数据字节是否能被4整除,如果不行,我们将系统补齐的多余的字节剔除掉
if((weigh*3)%4 != 0)
{
new_weigh = weigh*3 + 4 -(weigh*3)%4;
}
unsigned char bmp_buf[new_weigh*high];
这样图片显示出来无论大小多少只要不超过屏幕的大小都可以正常显示出来。
如图所示刚才倾斜的图片就成功恢复原样了,本次实践是小白本人发现问题后,参考网上一些大神的教学进行代码的修改,请大家多多包涵。
完整代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
int lcd_fd,bmp_fd;
unsigned int* lcd_map;
int Lcd_Init() //初始化LCD
{
//1,打开lcd, 打开图片文件 "test.bmp"
lcd_fd = open("/dev/fb0", O_RDWR);
if(-1 == lcd_fd)
{
perror("open lcd failed");
return -1;
}
//3,映射LCD到内存
lcd_map = (unsigned int* )mmap(NULL, 800*480*4, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(lcd_map == MAP_FAILED)
{
perror("mmap failed");
return -1;
}
}
void Lcd_Uninit() //关闭LCD
{
//5,关闭lcd文件,关闭图片文件,释放映射内存
munmap(lcd_map, 800*480*4);
close(lcd_fd);
close(bmp_fd);
}
//居中显示图片位置
void Show_bmp(const char *picname) //显示一张800*480的bmp图片,参数为 图片路径名,显示高度为 high 宽度为weigh
{
bmp_fd = open(picname, O_RDWR);
if(-1 == bmp_fd)
{
perror("open bmp failed");
return;
}
//2,读取图片中的像素点数据 --》 跳过54字节头信息 800*480*3
unsigned char head[54];
int weigh,high;
int n; //n是每一行最后多出来的字节数
read(bmp_fd, head , 54); //head[18],head[19],head[20],head[21],==>宽度信息 ; head[22],head[23],head[24],head25] ==》高度信息
weigh = head[21]<<24 | head[20]<<16 | head[19]<<8 | head[18];
high = head[25]<<24 | head[24]<<16 | head[23]<<8 | head[22];
printf("weigh=%d, high=%d\n", weigh, high);
n = ((weigh*3)%4==0) ? 0 : 4 -(weigh*3)%4; //如果每一行字节数能被4整除,n为0,否则是多出来的字节数
unsigned char bmp_buf[(weigh*3 + n)*high]; //实际的图片数据大小
//lseek(bmp_fd, 54, SEEK_SET); //bmp图片54字节头信息中含有图片高度,宽度信息
unsigned int buf[weigh*high];
read(bmp_fd, bmp_buf, sizeof(bmp_buf));
/处理像素点数据 RGB --》 ARGB,去掉多余的字节//
unsigned char A, R, G, B;
int i,j;
for(i=0; i<high; i++)
{
for(j=0; j<weigh; j++)
{
A = 0x00;
B = bmp_buf[3*(weigh*i + j) + i*n]; //只取每一行的前weigh个点的像素点数据
G = bmp_buf[3*(weigh*i + j) + 1 + i*n];
R = bmp_buf[3*(weigh*i + j) + 2 + i*n]; //BGR存储像素点信息
// ARGB = A R G B ==> 1234 == 1*1000 + 2*100 + 3*10 + 4*1
buf[weigh*i+j] = A<<24 | R<<16 | G<<8 | B;
}
}
//图片居中
int x_s = (800-weigh)/2;
int y_s = (480-high)/2;
///
//4,把读取的bmp图片数据写入到映射内存空间中 memcpy --》把一片内存中的数据拷贝到另一片内存 //修改显示位置,使得图片不再倒立
for(i=y_s; i<high+y_s && i<480; i++)
{
for(j=x_s; j<weigh+x_s && j<800; j++) //(j,i)点 ==》 buf[800*i+j] ==>
{
//LCD映射内存上坐标为(j,i)// 图片上坐标为(j,i)的点
lcd_map[800*i+j] = buf[weigh*(high-1- i + y_s)+j - x_s];
}
}
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("%s <pic_name>\n",argv[0]);
exit(1);
}
Lcd_Init();
Show_bmp(argv[1]);
Lcd_Uninit();
return 0;
}