1、bitmap的文件格式
bitmap包含了54个字节的文件头。分别是14个字节的文件信息头,和40个字节的位图信息头
bit_lib.h
#ifndef __TINY6410_BMP_LIB_H__
#define __TINY6410_BMP_LIB_H__
#include <string.h>
/* bitmap 格式的位图文件会带有 54 字节的信息头,这些信息是固定不变的,可以通过read 来读取 */
/* 文件信息头(14字节) */
typedef struct {
char type[2]; /* 文件类型,必须为"BMP" (0X4D42) */
char size[4]; /* 文件的大小(字节) */
char reserved[4]; /* 保留,必须为0 */
char off[4]; /* 位图阵列相对于文件头的偏移量(字节) */
} bmp_file_header_t;
/* 位图信息头(40字节) */
typedef struct {
char size[4]; /* 说明BITMAPINFOHEADER 结构所需要的字数 */
char width[4]; /* 位图宽度(像素) */
char height[4]; /* 位图高度(像素),如果该值是一个正数,说明图像是倒像的,大多数BMP 文件都是倒像的 */
char planes[2]; /* 目标设备的位平面数,必须置为1 */
char bitcount[2]; /* 每个像素的位数,1, 4, 8, 16, 24, 32 */
char compress[4]; /* 位图阵列的压缩方法,0表示不压缩 */
char img_size[4]; /* 图像大小(字节) */
char xpel[4]; /* 说明水平分辨率,用 像素/米 表示 */
char ypel[4]; /* 垂直 */
char clr_used[4]; /* 位图实际使用的颜色表的颜色数 */
char clr_important[4]; /* 重要颜色索引的个数 */
} bmp_info_header_t;
/* 这里应用程序可以兼容24 位和32位的位图,其中red,green,blue这三个颜色所占的位置应该倒置 */
typedef struct {
char blue;
char green;
char red;
char reserved;
} rgb_32_t;
/* 对位图操作进行封装 */
typedef struct {
int fd; /* 图像文件描述符 */
rgb_32_t *curp; /* 指向当前的像素点 */
int width; /* 图像宽度 */
int height; /* 图像高度 */
int bitcount; /* 图像每个像素的位数 */
int size; /* 图像大小 */
void *data; /* 图像有效数据指针 */
} bmp_t;
/* 由于开发板帧缓冲在驱动中被设置为16 位数据表示一个像素点(16bpp),这里需要对24 位或者32 位的位图进行转化 */
static short transfer_to_16bit(char red,char green,char blue)
{
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
/* 获取一个被转化为16 位的像素值 */
static short bmp_get_pixel_16bit(bmp_t *bmp)
{
return transfer_to_16bit(bmp->curp->red, bmp->curp->green, bmp->curp->blue);
}
/* 字符串变成整形的函数 */
static long char_to_int(char *str)
{
return *((int *)str);
}
/* 使bmp->curp 指向下一个像素点 */
static void bmp_next_pixel(bmp_t *bmp)
{
if (24 == bmp->bitcount)
bmp->curp = (rgb_32_t*)((int)bmp->curp + 3);
else if (32 == bmp->bitcount)
bmp->curp = (rgb_32_t*)((int)bmp->curp + 4);
}
/* 使bmp->curp 指向图像有效数据的初始位置 */
static void bmp_reset_pixel(bmp_t *bmp)
{
bmp->curp = (rgb_32_t*)bmp->data;
}
extern int bmp_open(bmp_t *bmp, char *bmpn);
extern void bmp_close(bmp_t *bmp);
#endif
a.这里主要讲位图封装成一个结构bmp_t,里面包含了图像文件的描述符、指向当前像素的指针、图像宽度、图像高度、图像每个像素的位数、整个图像大小、图像有效数据的指针。
b.其中图像文件描述符由open函数返回
c.指向当前像素指针最开始由有效数据指针获得,然后随着像素的移动而移动( void bmp_next_pixel(bmp_t *bmp));
d.图像宽度和图像高度还有图像每个像素的位数由读取位图信息头后从中取得
e.整个图像的字节由高度宽度和图像位数计算而出
f.
一个像素封装成一个结构体rgb_32_t
typedef struct {
char blue;
char green;
char red;
char reserved;
} rgb_32_t;
这里可以兼容24位和32位的位图,当为24位位图时,指向下一个像素时跳3个字节;当32位时,跳4个字节。
void bmp_next_pixel(bmp_t *bmp)
{
if (24 == bmp->bitcount)
bmp->curp = (rgb_32_t*)((int)bmp->curp + 3);
else if (32 == bmp->bitcount)
bmp->curp = (rgb_32_t*)((int)bmp->curp + 4);
}
g.由于LCD这里设置的是16位显示,所以需要将24或者32位的位图转化
/* 由于开发板帧缓冲在驱动中被设置为16 位数据表示一个像素点(16bpp),这里需要对24 位或者32 位的位图进行转化 */
static short transfer_to_16bit(char red,char green,char blue)
{
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3);
}
/* 获取一个被转化为16 位的像素值 */
static short bmp_get_pixel_16bit(bmp_t *bmp)
{
return transfer_to_16bit(bmp->curp->red, bmp->curp->green, bmp->curp->blue);
}
bit_lib.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include "tiny6410_bmp_lib.h"
int bmp_open(bmp_t *bmp, char *bmpn)
{
int ret;
bmp_file_header_t fhr;
bmp_info_header_t ihr;
printf("[Call bmp_open.]\n");
if (-1 == (bmp->fd=open(bmpn, O_RDONLY))) {
printf("Error: cannot open framebuffer device.\n");
_exit(EXIT_FAILURE);
}
// printf("Open done!\n");
read(bmp->fd, &fhr, sizeof(bmp_file_header_t)); /* 读取文件信息头 */
read(bmp->fd, &ihr, sizeof(bmp_info_header_t)); /* 读取位图信息头 */
bmp->width = char_to_int(ihr.width);
bmp->height = char_to_int(ihr.height);
bmp->bitcount = char_to_int(ihr.bitcount);
bmp->size = (bmp->width * bmp->height * bmp->bitcount) / 8;
printf("bmp->width = %d\n", bmp->width);
printf("bmp->height = %d\n", bmp->height);
printf("bmp->bitcount = %d\n", bmp->bitcount);
printf("bmp->siz = %d\n", bmp->size);
bmp->data = malloc(bmp->size);
ret = read(bmp->fd, bmp->data, bmp->size); /* 读取实际的图像数据 */
bmp->curp = (rgb_32_t*)bmp->data; /* 指向当前图像有效数据,即指向图像的起始数据 */
return 0;
}
void bmp_close(bmp_t *bmp)
{
close(bmp->fd);
free(bmp->data);}
这里主要将位图打开,将位图信息头中的宽度和高度还有图像位数读取出来,存在bmp_t的结构体中,并将文件头后面的有效数据读取出来,把当前像素指针指向有效数据。
bit_app.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include "tiny6410_bmp_lib.h"
#define FB_DEVICE_NAME "/dev/fb0"
#define RED_COLOR565 0X0F100
#define GREEN_COLOR565 0X007E0
#define BLUE_COLOR565 0X0001F
typedef struct fb_dev {
int fd; /* 帧缓冲设备硬件描述符 */
void *pfb; /* 指向帧缓冲映射到用户空间的首地址 */
int xres; /* 一帧图像的宽度 */
int yres; /* 一帧图像的高度 */
int size; /* 一帧图像的大小 */
int bits_per_pixel; /* 每个像素的大小 */
} fb_dev_t;
int fb_open(fb_dev_t *fbd, char *fbn)
{
struct fb_var_screeninfo vinfo;
if((fbd->fd = open(fbn, O_RDWR)) == -1) {
printf("Error: Cannot open framebuffer device.\n");
_exit(EXIT_FAILURE);
}
/* 获取LCD 的可变参数 */
ioctl(fbd->fd, FBIOGET_VSCREENINFO, &vinfo);
fbd->xres = vinfo.xres;
fbd->yres = vinfo.yres;
fbd->bits_per_pixel = vinfo.bits_per_pixel;
/* 计算一帧图像的大小 */
fbd->size = fbd->xres * fbd->yres * fbd->bits_per_pixel / 8;
printf("%d * %d,%d bits_per_pixel,screensize = %d.\n",fbd->xres,fbd->yres,fbd->bits_per_pixel,fbd->size);
/* 将帧映射到内存 */
/* mmap的应用 */
/* mmap可以把文件内容映射到一段内存中,准确说是虚拟内存,通过对这段内存的读取和修改,实现对文件的读取和修改。 */
/* addr:指定映射的起始地址,通常为NULL,由系统指定 */
/* length:将文件的多大长度映射到内存 */
/* prot:映射区的保护方式,可以是可被执行(PROT_EXEC),可被写入(PROT_WRITE),可被读取(PROT_READ),映射区不能存取(PROT_NONE) */
/* flags:映射区的特性,对映射区的写入数据会复制回文件,且允许其他映射文件的进城共享(MAP_SHARED),对映射区的写入操作会产生一个映射的复制,对此区域所做的修改不会写会源文件(MAP_PRIVATE) */
/* fd:由open返回的文件描述符,代表要映射的文件 */
/* offset:以文件开始出的偏移,必须是分页大小的整数倍,通常为0,表示从头开始映射 */
/* 注意:在修改映射文件时,只能在原长度上修改,不能增加文件长度,因为内存是已经分配好的 */
fbd->pfb = mmap(NULL, fbd->size, PROT_READ | PROT_WRITE, MAP_SHARED, fbd->fd, 0);
if((int)fbd->pfb == -1) {
printf("Error: Failed to map frambuffer device to memory!\n");
_exit(EXIT_FAILURE);
}
return 0;
}
void fb_close(fb_dev_t *fbd)
{
/* 解除映射 */
munmap(fbd->pfb,fbd->size);
/* 关闭设备文件 */
close(fbd->fd);
}
int fb_drawrect(fb_dev_t *fbd, int x0, int y0, int w, int h, int color)
{
int x,y;
for(y=y0; y<y0+h; y++) {
for(x=x0; x<x0+w; x++) {
*((short *)(fbd->pfb) + y*fbd->xres +x) = color;
}
}
return 0;
}
/* 用于屏幕显示图片函数 */
int fb_drawbmp(fb_dev_t *fbd, int x0, int y0, char *bmpn)
{
int x, y, x1, y1;
bmp_t bmpt; /* <span style="color:#ff0000;">就是这里,我如果定义一个指针型的变量就会出现我一开始说的问题,当然我传递的时候是个地址,但是还是不行!</span> */
bmp_open(&bmpt, bmpn);
x1 = x0 + bmpt.width;
y1 = y0 + bmpt.height;
for(y=y1; y>y0; y--) {
for(x=x0; x<x1; x++) {
if(x1>fbd->xres || y1>fbd->yres) {
bmp_next_pixel(&bmpt);
continue;
}
*((short *)(fbd->pfb) + y*fbd->xres + x) = bmp_get_pixel_16bit(&bmpt);
bmp_next_pixel(&bmpt);
}
}
bmp_close(&bmpt);
return 0;
}
int main(int argc, char **argv)
{
fb_dev_t *fbd;
fbd = (fb_dev_t *)malloc(sizeof(fb_dev_t));
fb_open(fbd, FB_DEVICE_NAME);
if(fbd->bits_per_pixel == 16) {
printf("Red/Green/Blue Screen And a picture!\n");
fb_drawrect(fbd, 0, 0, fbd->xres, fbd->yres/3, RED_COLOR565);
fb_drawrect(fbd, 0, fbd->yres/3, fbd->xres, fbd->yres/3, GREEN_COLOR565);
fb_drawrect(fbd, 0, fbd->yres*2/3, fbd->xres, fbd->yres/3, BLUE_COLOR565);
fb_drawbmp(fbd, 0, 0, argv[1]);
}
else
printf("16 bits only!");
fb_close(fbd);
free((void *)fbd);
return 0;
}
这里封装了一个fb_dev的结构体
typedef struct fb_dev {
int fd; /* 帧缓冲设备硬件描述符 */
void *pfb; /* 指向帧缓冲映射到用户空间的首地址 */
int xres; /* 一帧图像的宽度 */
int yres; /* 一帧图像的高度 */
int size; /* 一帧图像的大小 */
int bits_per_pixel; /* 每个像素的大小 */
} fb_dev_t;
a.fb由open打开设备返回
这里open函数封装在了fb_open中
int fb_open(fb_dev_t *fbd, char *fbn);
b.结构体中的xres.yres,size,bits_per_pixel、由fb_info设备结构体中的 struct fb_var_screeninfo var; /* Current var */
中的xres,yres ,bits_per_pixel获得
然后将fb映射到内存中,以后直接将数据写入到此段内存中就只相当将数据写入到framebuffer显示
/* 将帧映射到内存 */
/* mmap的应用 */
/* mmap可以把文件内容映射到一段内存中,准确说是虚拟内存,通过对这段内存的读取和修改,实现对文件的读取和修改。 */
/* addr:指定映射的起始地址,通常为NULL,由系统指定 */
/* length:将文件的多大长度映射到内存 */
/* prot:映射区的保护方式,可以是可被执行(PROT_EXEC),可被写入(PROT_WRITE),可被读取(PROT_READ),映射区不能存取(PROT_NONE) */
/* flags:映射区的特性,对映射区的写入数据会复制回文件,且允许其他映射文件的进城共享(MAP_SHARED),对映射区的写入操作会产生一个映射的复制,对此区域所做的修改不会写会源文件(MAP_PRIVATE) */
/* fd:由open返回的文件描述符,代表要映射的文件 */
/* offset:以文件开始出的偏移,必须是分页大小的整数倍,通常为0,表示从头开始映射 */
/* 注意:在修改映射文件时,只能在原长度上修改,不能增加文件长度,因为内存是已经分配好的 */
fbd->pfb = mmap(NULL, fbd->size, PROT_READ | PROT_WRITE, MAP_SHARED, fbd->fd, 0);
if((int)fbd->pfb == -1) {
printf("Error: Failed to map frambuffer device to memory!\n");
_exit(EXIT_FAILURE);
}
在主函数中先静态分配一个fb_dev_t的结构体
然后打开设备fb_dev
然后直接向fb_dev_t的结构体中pfb 中写入图像数据就可以显示颜色了
画方块的函数如下
int fb_drawrect(fb_dev_t *fbd, int x0, int y0, int w, int h, int color)
{
int x,y;
for(y=y0; y<y0+h; y++) {
for(x=x0; x<x0+w; x++) {
*((short *)(fbd->pfb) + y*fbd->xres +x) = color;
}
}
return 0;
}
显示位图的函数如下:
int fb_drawbmp(fb_dev_t *fbd, int x0, int y0, char *bmpn)
{
int x, y, x1, y1;
bmp_t bmpt;
bmp_open(&bmpt, bmpn);
x1 = x0 + bmpt.width;
y1 = y0 + bmpt.height;
for(y=y1; y>y0; y--) {
for(x=x0; x<x1; x++) {
if(x1>fbd->xres || y1>fbd->yres) {
bmp_next_pixel(&bmpt);
continue;
}
*((short *)(fbd->pfb) + y*fbd->xres + x) = bmp_get_pixel_16bit(&bmpt);
bmp_next_pixel(&bmpt);
}
}
bmp_close(&bmpt);
return 0;
}
正好前几天和基友讨论一个显示碰碰球的问题,恰好在这里一并实现了代码如下:
void fb_drawcircle(fb_dev_t *fbd, unsigned int x0,unsigned int y0,unsigned int r)
{
int a,b;
int di;
// printf("in drawcircle!\n");
a=0;b=r;
di=3-(r<<1);
while(a<=b)
{
fb_drawpoint(fbd,x0-b,y0-a,BLUE_COLOR565); //3
fb_drawpoint(fbd,x0+b,y0-a,BLUE_COLOR565); //0
fb_drawpoint(fbd,x0-a,y0+b,BLUE_COLOR565); //1
fb_drawpoint(fbd,x0-b,y0-a,BLUE_COLOR565); //7
fb_drawpoint(fbd,x0-a,y0-b,BLUE_COLOR565); //2
fb_drawpoint(fbd,x0+b,y0+a,BLUE_COLOR565); //4
fb_drawpoint(fbd,x0+a,y0-b,BLUE_COLOR565); //5
fb_drawpoint(fbd,x0+a,y0+b,BLUE_COLOR565); //6
fb_drawpoint(fbd,x0-b,y0+a,BLUE_COLOR565);
a++;
if(di<0)di +=4*a+6;
else
{
di+=10+4*(a-b);
b--;
}
fb_drawpoint(fbd,x0+a,y0+b,BLUE_COLOR565);
}
}
int fun_ball(fb_dev_t *fbd, int x0,int y0,float vx0,float vy0,int r,int a)
{
float x,y,vx,vy,dt=0.01;
x=x0;
y=y0;
vy=vy0;
vx=vx0;
while(1)
{
fb_drawrect(fbd, x-r-1, y-r-1, 2*r+2, 2*r+2,!( BLUE_COLOR565 | RED_COLOR565 | GREEN_COLOR565));
y=y+vy*dt+0.5*a*dt*dt;
x=x+vx*dt;//+0.5*a*dt*dt;
vx=vx;//+a*dt;
vy=vy+a*dt;
// fb_drawrect(fbd, 0, 0, 480, 272, BLUE_COLOR565 | RED_COLOR565 | GREEN_COLOR565);
fb_drawcircle(fbd,x,y,r);
usleep(5);
if(y< (2*r) || y> (272-2*r))
vy=-vy;
if(x< (2*r) || x>(480-2*r))
vx=-vx;
}
}