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.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文件中读头信息 */
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 中*/
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;
}
}
}