图片播放器(六):JPG图片的显示函数

1.libjpeg介绍及开源库的使用方法

1.1.libjpeg介绍
(1)基于linux的开源软件
(2)C语言编写(gcc、Makefile管理)
(3)提供JPEG图片的编解码算法实现


1.2.libjpeg版本及下载资源
(1)经典版本v6b:https://sourceforge.net/projects/libjpeg/files/libjpeg/6b/
(2)最新版本v9b:http://www.ijg.org/


1.3.开源库的使用方法
(1)移植(源码下载、解压、配置、修改Makefile、编译或交叉编译)。移植的目的是由源码得到三个东西:动态库.so,静态库.a,头文件.h
(2)部署(部署动态库so、部署静态库.a和头文件.h)
动态库是运行时环境需要的,编译程序时不需要。
静态库是静态连接时才需要,动态链接时不需要。
头文件.h是在编译程序时使用的,运行时不需要的。
总结:静态库和头文件这两个东西,是在编译链接过程中需要的;而动态库是在运行时需要的。
所以动态库so文件是要放到开发板的文件系统中去的(放的过程就叫部署),把静态库.a文件和头文件.h文件放到ubuntu的文件系统中去。
(3)注意三个编译链接选项:-I  -l  -L
-I是编译选项(准确的是说是预处理选项CFLAGS或者CPPFLAGS中指定),用来指定预处理时查找头文件的范围的。
-l是链接选项(LDFLAGS中指定),用来指定链接额外的库(譬如我们用到了数学函数,就用-lm,链接器就会去链接libm.so;那么我们使用了libjpeg,对应的库名字就叫libjpeg.so,就需要用-ljpeg选项去链接)
-L是链接选项(LDFLAGS中指定),用来告诉链接器到哪个路径下面去找动态链接库。
总结:-l是告诉链接器要链接的动态库的名字,而-L是告诉链接器库的路径

 

2.libjpeg的移植实战1

2.1.移植
(1)源码下载、解压

(2)配置   

./configure --prefix=/open_source/JPG/jpg_v6b/libdecode --exec-prefix=/open_source/jpg_v6b/libdecode --enable-shared --enable-static -build=i386 -host=arm

(3)Makefile中有下面几句话,因此要到  /open_source/jpg_v6b/libdecode  创建 bin,lib,include

bindir = $(exec_prefix)/bin
libdir = $(exec_prefix)/lib
includedir = $(prefix)/include

(4)Makefile检查,主要查看交叉编译设置是否正确
CC=gcc         改为     CC=arm-linux-gcc
AR=ar rc     改为     AR=arm-linux-ar rc
AR2=ranlib     改为     AR2=arm-linux-ranlib
(5)编译    make

(5)安装 make install-lib        安装就是将编译生成的库文件、头文件、可执行文件分别装载到--prefix  --exec-prefix所指定的那些目录中去。

出错:

make install: ./libtool:命令未找到

参考我的博客:

https://blog.csdn.net/qq_40732350/article/details/84192247

6.22.2.部署
部署动态链接库一般有三个位置可以考虑:
第一个:/lib
第二个:/usr/lib
第三个:任意指定目录

第四个:找一个自己想放的文件夹,然后在开发板上输入命令:export LD_LIBRARY_PATH=/A/B/C:$LD_LIBRARY_PATH

/A/B/C:是自己放库的地方

 

重要结构体:

/* jpg 头信息 */
struct jpg_head_data
{
	unsigned short w;  /* 图片宽 */
	unsigned short h;  /* 图片高 */
	unsigned char  bpp;  /* BPP */
};



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);    
	fprintf(stderr, "my_error_exit\n");
	//longjmp(myerr->setjmp_buffer, 1);
}

全局变量和宏定义:

#define SHOW_MAX_W  1920
#define SHOW_MAX_H  1080

static unsigned char jpg_picture[SHOW_MAX_W * SHOW_MAX_H] = {0};  /* 当前播放器正在显示的的图片的有效数据数组 (最大值)*/

jpg 显示的核心函数:

/*
 * 函数功能: 解码jpg图片,并将解码出来的数据存储
 * 函数参数: pPIC,记录源jpg图片,解码出来的图片宽高、图片数据缓冲区等信息
 * 返回值  : 成功解码则返回0,失败则返回-1
 */
void fb_show_jpg (struct show_picture_set *set, unsigned int *pfb)
{
	struct jpg_head_data *st_pi = malloc(sizeof(struct jpg_head_data));
	const char *p_path = set->path;
	
	fb_file_to_jpg(p_path, st_pi);  /* 从jpg文件中读头信息 */

	fb_jpg_read_file(p_path, st_pi);


	switch (set->rotate){
	case ROTATE_FRONT:
		fb_jpg_front_show(set, st_pi, pfb);  /* bmp 图片正方向显示 */
		break;
	case ROTATE_180:
		fb_jpg_180_show(set, st_pi, pfb);  /* bmp 图片旋转180方向显示 */
		break;
	case ROTATE_MIRROR_180:
		fb_jpg_mirror_180_show(set, st_pi, pfb);
		break;
	case ROTATE_MIRROR:
		fb_jpg_mirror_show(set, st_pi, pfb);
		break;
	}

	free(st_pi);
}

判断是否为 jpg 文件:

char fb_is_jpg (const char *path)
{
	int ret;
	int fd = -1;
	char picture[2];

	if ((fd = open(path, O_RDWR)) < 0 ){
		DERROR("NO [%s] file\n", path);
		return 'O';
	}

	/* 读文件的头两个字节 */
	if ((ret = read(fd, picture, 2)) < 2){
		DERROR("read [%s] file error\n", path);
		close(fd);
		return 'R';
	}

	if((picture[0] != 0xff) || (picture[1] != 0xd8)){ /*判断能前两给字节是否是 BM */
		DERROR("It's not a BMP file\n");
		close(fd);
		return 'N';
	}

	lseek(fd, -2, SEEK_END);

	/* 读文件的最后两个字节 */
	if ((ret = read(fd, picture, 2)) < 2){
		DERROR("read [%s] file error\n", path);
		close(fd);
		return 'R';
	}

	if((picture[0] != 0xff) || (picture[1] != 0xd9)){ /*判断能前两给字节是否是 BM */
		DERROR("It's not a BMP file\n");
		close(fd);
		return 'N';
	}else{
		close(fd);
		return 'Y';
	}

}

从JPG文件中读头信息:

/* 从JPG文件中读头信息 */
int fb_file_to_jpg(const        char *path, void *st_pi)
{
	struct jpg_head_data *hand_data = st_pi;
	FILE * infile = NULL;		                 /* 指向fopen打开源jpg图片文件的指针 */
	struct jpeg_decompress_struct cinfo; /* cinfo贯穿整个解码过程的信息记录和传递的数据结构 */

	if ((infile = fopen(path, "rb")) == NULL) {
		DERROR("can't open %s\n", path);
		return -1;
	}

	struct my_error_mgr jerr;            /* 错误处理的 */

	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;

	jpeg_create_decompress(&cinfo); /* 给解码器做必要的内存分配和数据结构的初始化 */

	// 第2步: 将fopen打开的源jpg图片和解码器相关联
	jpeg_stdio_src(&cinfo, infile);

	// 第3步: 读jpg文件头
	jpeg_read_header(&cinfo, TRUE);

	// 第4步: 启动解码器
	jpeg_start_decompress(&cinfo);

	hand_data->w = cinfo.output_width;
	hand_data->h = cinfo.output_height;
	hand_data->bpp = cinfo.output_components;

	fclose(infile);
	return 0;
}

从文件中读有效数据到 now_picture 中:

/*  从文件中读有效数据到 now_picture 中*/
int fb_jpg_read_file(const         char *path, struct jpg_head_data *head_data)
{
	struct jpeg_decompress_struct cinfo; /* cinfo贯穿整个解码过程的信息记录和传递的数据结构 */
	struct my_error_mgr jerr;            /* 错误处理的 */

	FILE * infile;		                 /* 指向fopen打开源jpg图片文件的指针 */
	JSAMPARRAY buffer;		             /* 指向解码行数据的指针 */
	int row_stride;		                 /* 解码出来的一行图片信息的字节数 */
#if 0
typedef unsigned char JSAMPLE;
typedef JSAMPLE  *JSAMPROW;	/* ptr to one image row of pixel samples. */
typedef JSAMPROW *JSAMPARRAY;
#endif
	if ((infile = fopen(path, "rb")) == NULL) {
		DERROR("can't open %s\n", path);
		exit(-1);
	}
	
	/* 第1步: 错误处理函数部分的绑定 */
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	  /* Establish the setjmp return context for my_error_exit to use. */
	
	jpeg_create_decompress(&cinfo); /* 给解码器做必要的内存分配和数据结构的初始化 */

	// 第2步: 将fopen打开的源jpg图片和解码器相关联
	jpeg_stdio_src(&cinfo, infile);

	// 第3步: 读jpg文件头
	jpeg_read_header(&cinfo, TRUE);

	// 第4步: 启动解码器
	jpeg_start_decompress(&cinfo);
	

	DEBUG("image resolution: %d * %d, bpp/8=%d.\n", 
		cinfo.output_width, cinfo.output_height, cinfo.output_components);

	// 解码出来的数据一行的字节数
	row_stride = cinfo.output_width * cinfo.output_components;
	buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
	//buffer = (char *)malloc(row_stride);
	if (NULL == buffer)
	{
		DERROR("cinfo.mem->alloc_sarray error.\n");
		return -1;
	}

	// 第5步: 逐行解码,并将解码出的数据丢到事先准备好的缓冲区去
	while (cinfo.output_scanline < cinfo.output_height) 
	{	 
		// 解码一行信息,并且丢到buffer中
		//jpeg_read_scanlines(&cinfo, buffer, 1);
		jpeg_read_scanlines(&cinfo, buffer, 1);	 

		// 将buffer中这一行数据移走到别的地方去暂存或者使用,总之是要腾出buffer空间
		// 来给循环的下一次解码一行来使用
		memcpy(jpg_picture + (cinfo.output_scanline-1) * row_stride, *buffer, row_stride);
	}

	// 第6步: 解码完了,做各种清理工作
	jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	fclose(infile);

	return 0;
}

主要的显示函数:

/* jpg 图片正方向显示 */
void fb_jpg_front_show(struct show_picture_set *set, struct jpg_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont1 = 0, cont2 = 0;
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = 0; x < head_data->w; x++)
		{
			if((x + set->w) >= WIDTH){
				cont1 += 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((jpg_picture[cont1 + 0] << 16) | (jpg_picture[cont1 + 1] << 8) | (jpg_picture[cont1 + 2] << 0));
			cont1 += 3;
		}
	}
}

/* jpg 图片旋转180方向显示 (不是当前图片旋转 180,是绝对而不是相对)*/
void fb_jpg_180_show(struct show_picture_set *set, struct jpg_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = ((head_data->h * head_data->w * 3) - 3);
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = 0; x < head_data->w; x++)
		{
			if((x + set->w) >= WIDTH){
				cont1 -= 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((jpg_picture[cont1 + 0] << 16) | (jpg_picture[cont1 + 1] << 8) | (jpg_picture[cont1 + 2] << 0));
			cont1 -= 3;
		}
	}
}

/* jpg 图片 ( 镜像翻转 + 旋转180方向 )显示 (不是当前图片旋转 180,是绝对而不是相对)*/
void fb_jpg_mirror_180_show(struct show_picture_set *set, struct jpg_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = ((head_data->h * head_data->w * 3) - 3);
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = head_data->w -1 ; x >= 0; x--)
		{
			if((x + set->w) >= WIDTH){
				cont1 -= 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((jpg_picture[cont1 + 0] << 16) | (jpg_picture[cont1 + 1] << 8) | (jpg_picture[cont1 + 2] << 0));
			cont1 -= 3;
		}
	}
}

/* jpg 图片 ( 镜像翻转 )显示 (不是当前图片翻转,是绝对而不是相对)*/
void fb_jpg_mirror_show(struct show_picture_set *set, struct jpg_head_data *head_data, unsigned int *pfb)
{
	int x, y;
	unsigned int cont2 = 0, cont1 = 0;
	unsigned int *p_data = pfb;

	for(y = 0; y < head_data->h; y++)		
	{
		if((y + set->h) >= HEIGHT)
			break;
		for(x = head_data->w -1 ; x >= 0; x--)
		{
			if((x + set->w) >= WIDTH){
				cont1 += 3;
				continue;
			}
			
			cont2 = (y + set->h) * WIDTH + x + set->w;

			*(p_data + cont2) = ((jpg_picture[cont1 + 0] << 16) | (jpg_picture[cont1 + 1] << 8) | (jpg_picture[cont1 + 2] << 0));
			cont1 += 3;
		}
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(项目学习)