嵌入式 在开发板显示bmp图片、jpeg图片
一、简述
记--在GEC6818开发板(800W*480H)显示24位的bmp图片、使用开源的jpeg库显示jpeg图片。
代码:链接: https://pan.baidu.com/s/1G3jzvdncocDMRbwCvsmSlg 密码: gz6m
二、效果
执行开始显示bmp图片,回车后显示jpg图片。
三、工程结构
四、源代码
display_bmp.c文件
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "lcd.h"
#include "freetype.h"
#include "to_wchar.h"
#include "display_bmp.h"
/*display_bmp:显示bmp图片
*pathname:图片的路径名
*lcd_ptr:指向LCD映射首地址
*x_s,y_s:左上角起始点
*/
int display_bmp(const char *pathname, unsigned int *lcd_ptr,unsigned int x_s, unsigned int y_s)
{
unsigned int bmp_width,bmp_height;
unsigned int x, y, color;
unsigned char *pic_buf_ptr, *ptr_save;
if( get_bmp_width_and_height( pathname, &bmp_width, &bmp_height) != 0 )//获取图片的宽高
{
fprintf(stderr, "get bmp width and height failed!\n");
return -1;
}
if( get_pic_data_to_buffer(pathname, &pic_buf_ptr) == -1 )//获取图片的像素数据
{
fprintf(stderr, "get picture data error\n");
return -1;
}
ptr_save = pic_buf_ptr;
for(y=y_s; y<y_s+bmp_height && y< LCD_HEIGHT; y++)
{
for(x=x_s; x< x_s+bmp_width ; x++)
{
if(x>=0 && x< LCD_WIDTH)
{
//将RGB的3个字节颜色数据装到4字节 RGB:红绿蓝
color = (*(pic_buf_ptr+2)<<16) | (*(pic_buf_ptr+1)<<8) | (*pic_buf_ptr);
lcd_draw_point(x, LCD_HEIGHT-y-1, color, lcd_ptr);//LCD_HEIGHT-y-1:镜像翻转
}
pic_buf_ptr += 3;//三个字节一个颜色像素点
}
}
//显示图片路径名
wchar_t *w_pathname = NULL;
if( mchars_to_wchars(pathname, &w_pathname) == 0)//将多字节转换为宽字符(汉字未能转换成功)
{
//显示字体
Lcd_Show_FreeType(w_pathname,32,0x00,x_s+10,y_s+50, lcd_ptr);
free(w_pathname);
}
free( ptr_save );
return 0;
}
/*get_pic_data_to_buffer:获取图片的像素数据
*pathname:图片的路径名
*pic_buf_ptr:用来指向 存放图片的像素数据缓冲区
*/
int get_pic_data_to_buffer(const char *pathname, unsigned char **pic_buf_ptr)
{
FILE *pic_fp;
off_t file_size;
struct stat file_info;
pic_fp = fopen(pathname, "r");
if(pic_fp == NULL)
{
perror("open %s error\n");
return -1;
}
if( stat(pathname, &file_info) == -1)
{
perror("get file info error\n");
return -1;
}
file_size = file_info.st_size;//获取图片大小
*pic_buf_ptr = (unsigned char *)malloc(file_size-54);
fseek(pic_fp, 54,SEEK_SET);//跳过文件头(存在内存对齐,可能是56字节)
fread(*pic_buf_ptr, file_size-54, 1, pic_fp);//读取图片颜色数据
if(ferror(pic_fp))
{
perror("read picture data error\n");
return -1;
}
return fclose(pic_fp);
}
/*get_bmp_width_and_height:获取bmp图片的宽高信息
*pathname:图片路径名
*width:图片的宽度
*height:图片的高度
*/
int get_bmp_width_and_height(const char *pathname, unsigned int *width, unsigned int *height)
{
FILE *pic_fp;
unsigned char file_head[54];
pic_fp = fopen(pathname, "r");
if(pic_fp == NULL)
{
perror("open %s error\n");
return -1;
}
fread( file_head, 54, 1, pic_fp);
if( ferror( pic_fp ) )
{
perror("read picture head error\n");
return -1;
}
fclose(pic_fp);
*width = *((int*)&file_head[18]);//宽:18~21共4个字节
*height = file_head[22] | file_head[23]<<8;//高:22~23共两个字节
return 0;
}
display_jpeg.c文件
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <jpeglib.h>
#include <jerror.h>
#include "lcd.h"
#include "to_wchar.h"
extern JSAMPLE * image_buffer; /* Points to large array of R,G,B-order data */
extern int image_height; /* Number of rows in image */
extern int image_width;
struct my_error_mgr
{
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
/*display_jpeg:显示jpeg图片
*filename:图片的路径名
*lcd_ptr:指向LCD映射首地址
*x_s,y_s:左上角起始点
*/
int display_jpeg (char * filename, unsigned int *lcd_ptr,unsigned int x_s, unsigned int y_s)
{
struct my_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
int row_stride; /* physical row width in output buffer */
FILE * infile; /* source file */
if((infile = fopen(filename, "rb")) == NULL)
{
fprintf(stderr, "can't open %s\n", filename);
return -1;
}
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if(setjmp(jerr.setjmp_buffer))
{
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return -1;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
char *buffer;
buffer = (char*)(*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
unsigned int x, y;
unsigned int color;
char * ptr_save = buffer;
y = y_s;
while(cinfo.output_scanline < cinfo.output_height && y < cinfo.output_height)
{
buffer = ptr_save;
jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&buffer, 1);
for(x=x_s; x< x_s+cinfo.output_width && x<LCD_WIDTH; x++)
{
color = buffer[2] | buffer[1]<<8 | buffer[0]<<16;
lcd_draw_point(x, y, color, lcd_ptr);
buffer += 3;
}
y++;
}
//显示图片路径
wchar_t *w_pathname = NULL;
if( mchars_to_wchars(filename, &w_pathname) == 0)
{
//显示图片路径
Lcd_Show_FreeType(w_pathname,32,0x00,x_s+10,y_s+50, lcd_ptr);
free(w_pathname);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return 0;
}
freetype.c文件
#include <sys/mman.h>
#include <stdio.h>
#include <linux/fb.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <wchar.h>
#include <string.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <lcd.h>
#define SIM_TTY_PATH "font/simsun.ttc"
/*
功能:根据得到的RGB24数据,进行图像任意位置显示
(主要是迎合上面的各种转换函数的显示测试)
x:显示的x轴起点
y:显示的y轴起点
w:显示图像宽度。
h:显示图像高度。
bit_dept:要显示的图像位深 24/32
pbmp_data:要进行显示的RGB24_buf
*/
int Show_FreeType_Bitmap(FT_Bitmap* bitmap,int start_x,int start_y,int color, unsigned int *lcd_buf_ptr)
{
int buff_x, buff_y; //遍历bitmap时使用
int x,y; //循环遍历使用
int end_x = start_x + bitmap->width; //图像宽度
int end_y = start_y + bitmap->rows; //图像高度
for ( x = start_x, buff_x = 0; x < end_x; buff_x++, x++ ) //y表示起点y,x表示起点x
{
for ( y = start_y, buff_y = 0; y < end_y; buff_y++, y++ ) //y表示起点y,x表示起点x
{
//LCD边界处理
if ( x < 0 || y < 0 || x >=800 || y >= 480 )
continue;
if(bitmap->buffer[buff_y * bitmap->width + buff_x] ) //判断该位上是不是为1,1则表明需要描点
lcd_draw_point(start_x+buff_x , start_y+buff_y , color, lcd_buf_ptr); //在当前x位置加上p的偏移量(p表示buff中列的移动)
//在当前y位置加上q的偏移量(q表示buff中行的移动)
}
}
}
void Lcd_Show_FreeType(wchar_t *wtext, int size, int color, int start_x, int start_y, unsigned int *lcd_buf_ptr)
{
//1.定义库所需要的变量
FT_Library library;
FT_Face face;
FT_GlyphSlot slot; //用于指向face中的glyph
FT_Vector pen;
FT_Error error;
int n;
//2.初始化库
error = FT_Init_FreeType( &library );
//3.打开一个字体文件,并加载face对象:
error = FT_New_Face( library, SIM_TTY_PATH, 0, &face );
slot = face->glyph;
//4.设置字体大小
error = FT_Set_Pixel_Sizes(face, size, 0);
//x起点位置:start_x。需要*64
pen.x = start_x * 64;
//y起点位置:LCD高度 - start_y。需要*64
pen.y = ( 480 - start_y ) * 64;
//每次取出显示字符串中的一个字
for ( n = 0; n < wcslen( wtext ); n++ )
{
//5.设置显示位置和旋转角度,0为不旋转,pen为提前设置好的坐标
FT_Set_Transform( face, 0, &pen );
//将字形槽的字形图像,转为位图,并存到 face->glyph->bitmap->buffer[],并且更新bitmap下的其他成员,
// face->glyph->bitmap.rows:图像总高度
// face->glyph->bitmap.width:图像总宽度
// face->glyph->bitmap.pitch:每一行的字节数
// face->glyph->bitmap.pixel_mode:像素模式(1单色 8反走样灰度)
// face->glyph->bitmap.buffer:点阵位图的数据缓冲区
error = FT_Load_Char( face, wtext[n], FT_LOAD_RENDER );
//FT_LOAD_RENDER表示转换RGB888的位图像素数据
//出错判断
if ( error )
continue;
Show_FreeType_Bitmap(&slot->bitmap, slot->bitmap_left, 480 - slot->bitmap_top, color, lcd_buf_ptr);
//增加笔位
pen.x += slot->advance.x;
}
FT_Done_Face(face);
FT_Done_FreeType(library);
}
lcd.c文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "lcd.h"
/*lcd_draw_point:在lcd上画一个点
*x:x轴坐标 (左上角为原点,x轴分别向右,y轴下递增)
*y:y轴坐标
*color:点的颜色
*lcd_ptr:LCD的操作指针
*/
void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color, unsigned int *lcd_ptr)
{
if( x>=0 && x<LCD_WIDTH && y>=0 && y<LCD_HEIGHT )
{
*(lcd_ptr+LCD_WIDTH*y+x) = color;
}
}
/*lcd_draw_full_srceen_single_color:用一种颜色画满整个屏幕
*color:点的颜色
*lcd_ptr:LCD的操作指针
*/
void lcd_draw_full_srceen_single_color(unsigned int color, unsigned int *lcd_ptr)
{
int x, y;
for(y=0;y<LCD_HEIGHT;++y)
{
for(x=0;x<LCD_WIDTH;++x)
{
lcd_draw_point(x, y, color, lcd_ptr);
}
}
}
/*open_lcd_device:打开LCD
*lcd_ptr:LCD的内存映射首地址
*返回值:lcd_fd:LCD的描述符
*/
int open_lcd_device(unsigned int **lcd_ptr)
{
int lcd_fd;
lcd_fd = open("/dev/fb0", O_RDWR);//打开LCD设备
if(lcd_fd == -1)
{
perror("open lcd device failed\n");
return -1;
}
//内存映射
*lcd_ptr = mmap( NULL, LCD_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(lcd_ptr == MAP_FAILED)
{
perror("map lcd_fb error\n");
return -1;
}
return lcd_fd;
}
/*close_lcd_device:关闭LCD
*lcd_fd:LCD的描述符
*lcd_ptr:LCD的内存映射首地址
*/
int close_lcd_device(int lcd_fd, unsigned int *lcd_ptr)
{
munmap(lcd_ptr, LCD_SIZE);
return close(lcd_fd);
}
main.c文件
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"
#include "freetype.h"
#include "display_bmp.h"
#include "display_jpeg.h"
int main(int argc, char *argv[])
{
int lcd_fd;
int dis_ret;
unsigned int *lcd_ptr;
//打开LCD
lcd_fd = open_lcd_device(&lcd_ptr);
if(lcd_fd == -1)
{
return -1;
}
//显示bmp图片
dis_ret = display_bmp("pic/test.bmp", lcd_ptr, 0, 0);
if(dis_ret != 0)
{
printf("display bmp error\n");
}
printf("please enter any key continue!\n");
getchar();
//显示jpeg图片
dis_ret = display_jpeg("pic/bird.jpg", lcd_ptr, 0, 0);
if(dis_ret != 0)
{
printf("display jpeg error\n");
}
//关闭LCD
close_lcd_device(lcd_fd, lcd_ptr);
return 0;
}
to_wchar.c文件
#include <wctype.h>
#include <locale.h>
#include <wchar.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*mchars_to_wchars:多字节转宽字符
*mbs_char:多字节字符串
*w_char:宽字符
*/
int mchars_to_wchars(const char *mbs_char, wchar_t **w_char)
{
int mbslen = mbstowcs(NULL, mbs_char, 0);
if (mbslen == (size_t) -1)
{
perror("mbstowcs");
return -1;
}
*w_char = NULL;
/* if (setlocale(LC_CTYPE, "zh_CN.utf8") == NULL)
{
perror("setlocale");
return -1;
}
*/
*w_char = calloc(mbslen + 1, sizeof(wchar_t));
if(*w_char == NULL )
{
perror("calloc\n");
return -1;
}
if (mbstowcs(*w_char, mbs_char, mbslen + 1) == (size_t) -1)
{
perror("mbstowcs");
free(*w_char);
return -1;
}
//printf("Wide character string is: %ls (%zu characters)\n", *w_char, mbslen);
return 0;
}
Makefile文件
make:
arm-linux-gcc src/*.c -o lcd -I include -I include/freetype2 -L lib -ljpeg -lfreetype
五、待添加功能
1、图片根据显示屏的大小自适应(缩放操作:"偷工减料"; 复制周边)
2、百叶窗式显示图片
3、从左到右滚动
4、扇形显示图片
=================================以下 回复 qq_37261494 ==================
百叶窗式显示图片测试
主要思路:将图片数据分为几个部分显示,每个部分交由子线程同时显示/隐藏。
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#define LCD_HEIGHT 480
#define LCD_WIDTH 800
#define LCD_SIZE ((LCD_WIDTH)*(LCD_HEIGHT)*4)
#define ON 1
#define OFF 0
#define BLIND 5
int open_lcd_device(unsigned int **lcd_ptr);
void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color, unsigned int *lcd_ptr);
int get_pic_data_to_buffer(const char *pathname, unsigned char **pic_buffer);
void blind_window(int lcd, unsigned char *image, int flag);
//打包数据给子线程使用
struct argument
{
unsigned int *lcd_ptr;//LCD显示屏的显示内存空间指针
unsigned char *pic_buf_ptr;;//图片数据
int offset;//每叶的像素数据的偏移量(距离全部数据的起始位置)起始就是用来记录每叶的起始行
int flag;//用来控制类似开窗关窗
};
int main(int argc, char *argv[])
{
int lcd_fd;
int ret;
unsigned int *lcd_ptr;
if(argc !=2)
{
printf("argc !=2\n");
return -1;
}
//打开LCD
lcd_fd = open_lcd_device(&lcd_ptr);
if(lcd_fd == -1)
{
return -1;
}
//获取图片的像素数据
unsigned char *pic_ptr;
ret = get_pic_data_to_buffer(argv[1], &pic_ptr);
if(ret == -1)
{
printf("get_pic_data_to_buffer erro\n");
}
//百叶窗显示
blind_window(lcd_fd, pic_ptr, ON);//关窗
blind_window(lcd_fd, pic_ptr, OFF);//开窗
munmap(lcd_ptr, LCD_SIZE);//接触映射
close(lcd_fd);//关闭LCD设备
return 0;
}
void *routine(void *_arg)//子线程执行的动作---显示1叶图片数据
{
struct argument *arg = (struct argument *)_arg;
unsigned int color;
int x,y;
if(arg->flag == ON)
{
arg->pic_buf_ptr += (4*(LCD_HEIGHT/BLIND)-arg->offset) * LCD_WIDTH * 3;
for(y=arg->offset+LCD_HEIGHT/BLIND-1; y>=arg->offset; y--)//显示每叶数据-从下往上,有HEIGHT/BLIN行
{
for(x=0; x<LCD_WIDTH; x++)
{
color = (arg->pic_buf_ptr[2]<<16) | (arg->pic_buf_ptr[1]<<8) | arg->pic_buf_ptr[0];
lcd_draw_point(x, y, color, arg->lcd_ptr);
arg->pic_buf_ptr += 3;
}
usleep(10000);
}
}
else if(arg->flag == OFF)//清除每叶数据-从上往下,有HEIGHT/BLIN行
{
for(y=arg->offset; y<arg->offset+LCD_HEIGHT/BLIND; y++)
{
for(x=0; x<LCD_WIDTH; x++)
{
lcd_draw_point(x, y, 0, arg->lcd_ptr);
}
usleep(10000);
}
}
sleep(1);
pthread_exit(NULL);
}
//百叶窗式显示图片
void blind_window(int lcd_fd, unsigned char *image, int flag)
{
unsigned int *p = mmap( NULL, LCD_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);//内存映射,将LCD显示屏显示空间映射为内存空间,方便操作,这样给内存赋值为像素数据就是显示图片了
int i;
pthread_t tid[BLIND];
for(i=0; i<BLIND; i++)//每1叶交由一条子线程显示
{
struct argument *arg = malloc(sizeof(struct argument));
arg->lcd_ptr = p;
arg->pic_buf_ptr = image;
arg->offset = i*(LCD_HEIGHT/BLIND);//设置偏移量
arg->flag = flag;
//printf("offset=%d\n", arg->offset);
pthread_create(&tid[i], NULL, routine, (void *)arg);//创建线程
}
for(i=0; i<BLIND; i++)
{
pthread_join(tid[i], NULL);//等待子线程结束
}
}
/*lcd_draw_point:在lcd上画一个点
*x:x轴坐标 (左上角为原点,x轴分别向右,y轴下递增)
*y:y轴坐标
*color:点的颜色
*lcd_ptr:LCD的操作指针
*/
void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color, unsigned int *lcd_ptr)
{
if( x>=0 && x<LCD_WIDTH && y>=0 && y<LCD_HEIGHT )
{
*(lcd_ptr+LCD_WIDTH*y+x) = color;
}
}
/*open_lcd_device:打开LCD
*lcd_ptr:LCD的内存映射首地址
*返回值:lcd_fd:LCD的描述符
*/
int open_lcd_device(unsigned int **lcd_ptr)
{
int lcd_fd;
lcd_fd = open("/dev/fb0", O_RDWR);//打开LCD设备
if(lcd_fd == -1)
{
perror("open lcd device failed\n");
return -1;
}
//内存映射
*lcd_ptr = mmap( NULL, LCD_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
if(lcd_ptr == MAP_FAILED)
{
perror("map lcd_fb error\n");
return -1;
}
return lcd_fd;
}
/*get_pic_data_to_buffer:获取图片的像素数据
*pathname:图片的路径名
*pic_buf_ptr:用来指向 存放图片的像素数据缓冲区
*/
int get_pic_data_to_buffer(const char *pathname, unsigned char **pic_buf_ptr)
{
FILE *pic_fp;
off_t file_size;
struct stat file_info;
pic_fp = fopen(pathname, "r");
if(pic_fp == NULL)
{
perror("open %s error\n");
return -1;
}
if( stat(pathname, &file_info) == -1)
{
perror("get file info error\n");
return -1;
}
file_size = file_info.st_size;//获取图片大小
*pic_buf_ptr = (unsigned char *)malloc(file_size-54);
fseek(pic_fp, 54,SEEK_SET);//跳过文件头(存在内存对齐,可能是56字节)
fread(*pic_buf_ptr, file_size-54, 1, pic_fp);//读取图片颜色数据
if(ferror(pic_fp))
{
perror("read picture data error\n");
return -1;
}
return fclose(pic_fp);
}
测试结果: