BMP图片的显示,本文章侧重于图片的内部数据结构

在做基于Linux的BMP图片显示的时候不单单需要了解LCD屏的操作,
更需要对BMP图片的数据格式有深入的理解,特别是那三个结构体,
这正是本文章的侧重点,具体细节会在代码中提到。


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include




/*图片都有类似这样的三个结构体,header,info,quad,
bmp是未经过压缩的,所以结构体quad里的值全部为缺省值-1
若输入一个*.bmp 图片,则结构体的值会是这样的,你可以
得到其中一些数据的联系吗?



//结构体里面的数据显示,打印出来的结果
the struct header size        : 14.......header结构体的大小
the struct info size             : 40.......info结构体的大小
the struct quad size           : 4........quad结构体的大小
the picture size                  : 1152054..图片总的大小
the picture is jpg orjpeg?   : 0........是否压缩 
the struct  header type      : 19778....图片类型的标号
the struct  header offbits   : 54.......图片数据的偏移量
the struct  info bit_count    : 24.......色深
the struct  info size_img    : 1152000..图片数据的大小
the struct  info X_pel         : 0--------以下8个目前还不是很清楚
the struct  info Y_pel         : 0--------但是不影响显示图片
the struct  info clrused       : 0--------可以自行深究
the struct  info clrImportant : 0--------
the struct  quad bule         : -1-------
the struct  quad green      : -1-------
the struct  quad red          : -1
the struct  quad reserved : -1
*/


#define RED   2
#define GREEN 1
#define BLUE  0


struct header
{
    int16_t type; //图片类型,这里使用一个整数来表示的
    int32_t size; // 图像文件大小
    int16_t reserved1;//未使用
    int16_t reserved2;//未使用
    int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));


struct info
{
    int32_t size;        // 本结构大小    
    int32_t width;       // 图像宽度(单位像素)
    int32_t height;      // 图像高度(单位像素)
    int16_t planes;      // 总为零


    int16_t bit_count;   // 色深(类似24,32等等,也就是每个像素点含有多少位)
    int32_t compression; // 是否压缩
    int32_t size_img;    //图片数据大小,需要读取的大小
    int32_t X_pel;       //等于0
    int32_t Y_pel;       //等于0 
    int32_t clrused;     //等于0
    int32_t clrImportant;//等于0
}__attribute__((packed));


struct quad
{
    int8_t blue;        //等于 -1
    int8_t green;       //等于 -1
    int8_t red;         //等于 -1
    int8_t reserved;    //等于 -1
}__attribute__((packed));




struct BMP_point
{
struct header linkhead;
struct info   linkinfo;
struct quad   linkquad;
}__attribute__((packed));




//为了更好地的到以上三个结构体的信息
//这里新建了一个结构体,这样的话只需要读一次就OK了
struct image_info
{
int  width;
int  height;
int  bpp;
int  offbits;
int  size_img;
};






//获取LCD屏的映象区
char *init_lcd(struct fb_var_screeninfo *pvinfo)
{
int lcd = open("/dev/fb0", O_RDWR);
if(lcd == -1)
{
perror("open /dev/fb0 failed");
exit(0);
}

bzero(pvinfo, sizeof(struct fb_var_screeninfo));
ioctl(lcd, FBIOGET_VSCREENINFO, pvinfo);

char *fbmem = mmap(NULL, pvinfo->xres * pvinfo->yres * pvinfo->bits_per_pixel/8, PROT_READ | PROT_WRITE, MAP_SHARED, lcd, 0);
if(fbmem == MAP_FAILED)
{
perror("mmap() failed");
exit(0);
}


return fbmem;
}


//获取图片的结构体信息
struct image_info valud_data(int fd)
{


struct image_info bmpdata;
//bzero = (&bmpdata, sizeof(struct image_info));


struct BMP_point *bmppoint  = malloc(sizeof(struct BMP_point));
read(fd, bmppoint, sizeof(struct BMP_point));


bmpdata.width   = bmppoint-> linkinfo.width;
bmpdata.height  = bmppoint-> linkinfo.height;
bmpdata.bpp     = bmppoint-> linkinfo.bit_count;
bmpdata.offbits = bmppoint-> linkhead.offbits;
bmpdata.size_img= bmppoint-> linkinfo.size_img;



//打印出结构体的全部信息

printf("the struct BMP_point size     : %d\n", sizeof(struct BMP_point));
printf("the struct header size        : %d\n", sizeof(struct header));
printf("the struct info size          : %d\n", sizeof(struct info));
printf("the struct quad size          : %d\n", sizeof(struct quad));
printf("the picture size              : %d\n", bmppoint-> linkhead.size);
printf("the struct  size              : %d\n", bmppoint-> linkinfo.size);
printf("the picture is jpg orjpeg?    : %d\n", bmppoint-> linkinfo.compression);
printf("the struct  header type       : %d\n", bmppoint-> linkhead.type);
printf("the struct  header offbits    : %d\n", bmppoint-> linkhead.offbits);
printf("the struct  info bit_count    : %d\n", bmppoint-> linkinfo.bit_count);
printf("the struct  info size_img     : %d\n", bmppoint-> linkinfo.size_img);
printf("the struct  info X_pel        : %d\n", bmppoint-> linkinfo.X_pel);
printf("the struct  info Y_pel        : %d\n", bmppoint-> linkinfo.Y_pel);
printf("the struct  info clrused      : %d\n", bmppoint-> linkinfo.clrused);
printf("the struct  info clrImportant : %d\n", bmppoint-> linkinfo.clrImportant);
printf("the struct  quad bule         : %d\n", bmppoint-> linkquad.blue);
printf("the struct  quad green        : %d\n", bmppoint-> linkquad.green);
printf("the struct  quad red          : %d\n", bmppoint-> linkquad.red);
printf("the struct  quad reserved     : %d\n", bmppoint-> linkquad.reserved);



return bmpdata;
}




//获取图片的颜色信息,这才是我们真正信息
char *load_bmp(int fd, struct image_info *bmpdata)
{
char *bmpfile = malloc(bmpdata->size_img);
char *tmp = bmpfile;
int offset = 0 , need_read = bmpdata->size_img;
printf(".......picture size.....%d\n",need_read);
printf(".......offset...........%d\n",bmpdata->offbits);


//读取图片数据,注意得先偏移到图片数据初始位置
while(need_read > 0)
{
       lseek(fd, bmpdata->offbits + offset, SEEK_SET);
offset = read(fd, bmpfile, need_read);
need_read -= offset;
}
return bmpfile;
}


/*  BMP图片比较特殊,当你要显示他的时候,请注意以下几点
1. 图片每一行的字节殊比需是4的倍数(int pad  = (4 - (bmpdata->width * bmpdata->bpp/8) % 4) %4;)
2. 读取BMP图片最数据是反过来读的,这样的后果是图片第一行的内容会映射到图片buffer的最后一行,
   图片的第二行内容会映射到图片buffer的倒数第二行,以此类推,图片数据就是这样读的,
   所以,相应地,将图片数据映射到LCD屏的时候,是将图片映象区的最后一行数据映射到LCD屏的第一行,
   图片映象区的倒数第二行数据映射到LCD屏的第二行,以此类推。
*/
void display_bmp(char *fbmem, char *bmpfile,
struct fb_var_screeninfo *pvinfo,
struct image_info *bmpdata,
int xoffset, int yoffset)
{
int pad  = (4 - (bmpdata->width * bmpdata->bpp/8) % 4) %4;


fbmem   += (yoffset * pvinfo->xres + xoffset) *
pvinfo->bits_per_pixel/8;
 
bmpfile += (bmpdata->width * bmpdata->bpp/8 + pad) * 
      (bmpdata->height -1);


int i, j;
for(j=0; j < bmpdata->height && j < pvinfo->yres-yoffset; j++)
{
   
    for(i=0; i < bmpdata->width && i < pvinfo->xres-xoffset; i++)
{

long fb_offset, ig_offset;

fb_offset = i * pvinfo->bits_per_pixel/8 +
j*pvinfo->xres * pvinfo->bits_per_pixel/8;

ig_offset = i * bmpdata->bpp/8;


//将图片数据一股脑写到LCD屏上,不分像素点中的颜色字节排列
//memcpy(fbmem+fb_offset, bmpfile+ig_offset, 
//bmpdata->bpp/8);

// 将图片的一个像素里面的3个字节,按照RGB的顺序,分别复制
// 图片中一个像素的内部细节: 高地址 <--   |B|G|R| --> 低地址
// LCD中一个像素的内部细节:  高地址 <-- |A|R|G|B| --> 低地址
memcpy(fbmem+fb_offset + pvinfo->red.offset/8,  bmpfile+ig_offset + RED,   1); // R
memcpy(fbmem+fb_offset + pvinfo->green.offset/8,bmpfile+ig_offset + GREEN, 1); // G
memcpy(fbmem+fb_offset + pvinfo->blue.offset/8, bmpfile+ig_offset + BLUE,  1); // B

}         
//将图片的最后一行数据显示完,往前偏移一行,显示倒数第二行,以此类推
bmpfile -= bmpdata->width * bmpdata->bpp/8 + pad; 

}

//用完记得释放内存,虽然这里显示一张图片没有多大关系,但是多了就不行了
//Linux的内存管理员会禁止你肆意的浪费内存,因为你每显示一张图片就申请一
//一块堆内存又不及时还给系统,系统就很不开心.....
    free(bmpfile);
free(fbmem);
}




int main(int argc, char **argv)
{
int bmp_fd = open(argv[1], O_RDWR);
if(bmp_fd == -1)
{
perror("open() failed");
exit(0);
}
struct fb_var_screeninfo vinfo;
char *fbmem = init_lcd(&vinfo);


struct image_info bmpdata = valud_data(bmp_fd);


char *bmpfile = load_bmp(bmp_fd, &bmpdata);


display_bmp(fbmem, bmpfile, &vinfo, &bmpdata, 0, 0 );

close(bmp_fd);//关闭图片
close(lcd);//关闭显示屏

return 0;
}






























你可能感兴趣的:(linux,IO编程)