嵌入式 在开发板显示bmp图片、jpeg图片

一、简述

        记--在GEC6818开发板(800W*480H)显示24位的bmp图片、使用开源的jpeg库显示jpeg图片。

        代码:链接: https://pan.baidu.com/s/1G3jzvdncocDMRbwCvsmSlg 密码: gz6m

二、效果

        执行开始显示bmp图片,回车后显示jpg图片。

嵌入式开发板如何运行java程序 嵌入式开发板显示图片_#include

三、工程结构

        

嵌入式开发板如何运行java程序 嵌入式开发板显示图片_#include_02

        

嵌入式开发板如何运行java程序 嵌入式开发板显示图片_#include_03

四、源代码

        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);
    
}

测试结果:

嵌入式开发板如何运行java程序 嵌入式开发板显示图片_ci_04