大家好,这篇文中介绍了libjpeg-turbo的简单使用,大部分是copy,有一点自己的心得
libjpeg是一款开源的jpeg压缩解压库,具体信息参见百度百科。libjpeg-turbo是它的升级版,性能有所 提升,源码去他的官网可以下载 https://libjpeg-turbo.org/ 我用的1.2.1 最新的已经到2.0
对于libjpeg-turbo的使用官方有详细的说明文档 :
/ ******************** JPEG DECOMPRESSION样本界面******************* /
/ *这个例子的这一半显示了如何从JPEG解压缩器读取数据。
*这比上面更精致一点,因为我们显示:
*(a)如何修改JPEG库的标准错误报告行为;
*(b)如何使用库的内存管理器分配工作区。
*
*为了使这个例子与第一个例子有点不同,我们会
*假设我们不打算将整个图像放入内存中
*缓冲区,但要将其逐行发送到其他地方。我们需要一个单一的,
* scanline-high JSAMPLE数组作为工作缓冲区,我们将让JPEG
内存管理器为我们分配它。这种方法实际上非常有用
*因为我们不需要记住单独释放缓冲区:它
*清除JPEG对象时将自动消失。
* /
/ *
*错误处理:
*
* JPEG库的标准错误处理程序(jerror.c)分为
*几个“方法”,你可以单独重写。这可以让你
*调整行为而不重复大量的代码,你可能会这样做
*必须更新每个未来版本。
*
*我们的例子展示了如何覆盖“error_exit”方法
*发生致命错误时,控制权返回给库的调用者,
*而不是像标准error_exit方法那样调用exit()。
*
*我们使用C的setjmp / longjmp工具来返回控制。这意味着
*调用JPEG库的例程必须先执行setjmp()调用
*建立返回点。我们想要更换error_exit来做一个
* longjmp()。但是我们需要让setjmp缓冲区可以访问
* error_exit例程。要做到这一点,我们做一个私人的延伸
*标准的JPEG错误处理程序对象。(如果我们使用C ++,我们会说我们
*正在制作常规错误处理程序的子类。)
*
*这是扩展的错误处理程序结构:
* /
struct my_error_mgr {
struct jpeg_error_mgr pub; / *“公共”字段* /
jmp_buf setjmp_buffer; / *返回给调用者* /
};
typedef struct my_error_mgr * my_error_ptr;
/ *
*以下是将替换标准error_exit方法的例程:
* /
METHODDEF(无效)
my_error_exit(j_common_ptr cinfo)
{
/ * cinfo-> err真的指向一个my_error_mgr结构,所以强制指针* /
my_error_ptr myerr =(my_error_ptr)cinfo-> err;
/ *总是显示消息。* /
/ *如果我们选择了,我们可以推迟到返回之后。* /
(* cinfo-> err-> output_message)(cinfo);
/ *将控制权返回给setjmp点* /
longjmp(myerr-> setjmp_buffer,1);
}
/ *
* JPEG解压缩的示例程序。我们假设源文件名
*传入。我们希望成功返回1,错误时return 0。
* /
GLOBAL(INT)
read_JPEG_file(char * filename)
{
/ *此结构包含JPEG解压缩参数和指针
*工作空间(根据JPEG库的需要分配)。
* /
struct jpeg_decompress_struct cinfo;
/ *我们使用我们的专用扩展名JPEG错误处理程序。
*注意,这个结构必须和主要的JPEG参数一样长
* struct,以避免悬挂指针问题。
* /
struct my_error_mgr jerr;
/* 更多东西 */
文件* infile; /* 源文件 */
JSAMPARRAY缓冲区; / *输出行缓冲区* /
int row_stride; / *输出缓冲区中的物理行宽度* /
/ *在这个例子中,我们希望在做其他事情之前打开输入文件,
*以便下面的setjmp()错误恢复可以假定文件已打开。
*非常重要:如果你在一台机器上,使用“b”选项来打开()
*需要它来读取二进制文件。
* /
if((infile = fopen(filename,“rb”))== NULL){
fprintf(stderr,“无法打开%s \ n”,文件名);
return 0;
}
/ *步骤1:分配并初始化JPEG解压缩对象* /
/ *我们设置正常的JPEG错误例程,然后覆盖error_exit。* /
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
/ *建立my_error_exit使用的setjmp返回上下文。* /
if(setjmp(jerr.setjmp_buffer)){
/ *如果我们到达这里,JPEG代码会发出错误信号。
*我们需要清理JPEG对象,关闭输入文件并返回。
* /
jpeg_destroy_decompress(&CINFO);
FCLOSE(infile中);
return 0;
}
/ *现在我们可以初始化JPEG解压缩对象。* /
jpeg_create_decompress(&CINFO);
/ *步骤2:指定数据源(例如文件)* /
jpeg_stdio_src(&cinfo,infile);
/ *第3步:用jpeg_read_header()读取文件参数* /
(void)jpeg_read_header(&cinfo,TRUE);
/ *我们可以忽略来自jpeg_read_header的返回值
*(a)使用stdio数据源无法暂停,并且
*(b)我们通过TRUE拒绝仅表格JPEG文件作为错误。
*有关更多信息,请参阅libjpeg.txt。
* /
/ *第4步:设置解压缩参数* /
/ *在这个例子中,我们不需要改变任何默认设置
* jpeg_read_header(),所以我们在这里什么都不做。
* /
/ *步骤5:开始解压缩* /
(void)jpeg_start_decompress(&cinfo);
/ *由于暂停不可能,我们可以忽略返回值
*与stdio数据源。
* /
/ *在阅读之前,我们可能需要在此处进行一些设置
* 数据。在jpeg_start_decompress()之后,我们有正确的缩放比例
*输出图像尺寸可用,以及输出颜色图
*如果我们要求颜色量化。
*在这个例子中,我们需要制作一个合适大小的输出工作缓冲区。
* /
/ *输出缓冲区中每行的JSAMPLEs * /
row_stride = cinfo.output_width * cinfo.output_components;
/ *制作一行高的样本数组,当完成图像时将消失* /
buffer =(* cinfo.mem-> alloc_sarray)
((j_common_ptr)&cinfo,JPOOL_IMAGE,row_stride,1);
/ *第6步:while(扫描行仍有待读取)* /
/ * jpeg_read_scanlines(...); * /
/ *这里我们使用库的状态变量cinfo.output_scanline作为
*循环计数器,以便我们不必跟踪自己。
* /
while(cinfo.output_scanline <cinfo.output_height){
/ * jpeg_read_scanlines需要一个指向扫描线的指针数组。
*这里的数组只有一个元素长,但你可以要求
*如果更方便,一次有多个扫描线。
* /
(void)jpeg_read_scanlines(&cinfo,buffer,1);
/ *假设put_scanline_someplace需要一个指针和样本数。* /
put_scanline_someplace(buffer [0],row_stride);
}
/ *第7步:完成解压缩* /
(void)jpeg_finish_decompress(&cinfo);
/ *由于暂停不可能,我们可以忽略返回值
*与stdio数据源。
* /
/ *第8步:释放JPEG解压缩对象* /
/ *这是一个重要的步骤,因为它会释放大量的内存。* /
jpeg_destroy_decompress(&CINFO);
/ * finish_decompress之后,我们可以关闭输入文件。
*在这里我们推迟它,直到没有更多的JPEG错误是可能的,
*以简化上面的setjmp错误逻辑。(其实,我没有
*认为jpeg_destroy可以做出错误退出,但为什么会假设任何事情......)
* /
FCLOSE(infile中);
/ *此时您可能需要检查是否有任何损坏数据
*发生警告(测试jerr.pub.num_warnings是否为非零)。
* /
/ *我们完成了!* /
return 1;
}
相信一个可以执行的程序更能直观的说明问题 :
/*
功能 : 在LCD上显示一副图片
*/
#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
#include <stdlib.h>
#define FB_DEVICE_NAME "/dev/fb0"
#define DBG_PRINTF printf
static int g_fd;
static struct fb_var_screeninfo g_tFBVar;
static struct fb_fix_screeninfo g_tFBFix;
static unsigned char *g_pucFBMem;
static unsigned int g_dwScreenSize;
static unsigned int g_dwLineWidth;
static unsigned int g_dwPixelWidth;
static int FBDeviceInit(void)
{
int ret;
g_fd = open(FB_DEVICE_NAME, O_RDWR);
if (0 > g_fd)
{
DBG_PRINTF("can't open %s\n", FB_DEVICE_NAME);
}
ret = ioctl(g_fd, FBIOGET_VSCREENINFO, &g_tFBVar);
if (ret < 0)
{
DBG_PRINTF("can't get fb's var\n");
return -1;
}
ret = ioctl(g_fd, FBIOGET_FSCREENINFO, &g_tFBFix);
if (ret < 0)
{
DBG_PRINTF("can't get fb's fix\n");
return -1;
}
g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;
g_pucFBMem = (unsigned char *)mmap(NULL , g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_fd, 0);
if (0 > g_pucFBMem)
{
DBG_PRINTF("can't mmap\n");
return -1;
}
g_dwLineWidth = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;
return 0;
}
static int FBShowPixel(int iX, int iY, unsigned int dwColor)
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int *pdwFB32bpp;
unsigned short wColor16bpp; /* 565 */
int iRed;
int iGreen;
int iBlue;
if ((iX >= g_tFBVar.xres) || (iY >= g_tFBVar.yres))
{
DBG_PRINTF("out of region\n");
return -1;
}
pucFB = g_pucFBMem + g_dwLineWidth * iY + g_dwPixelWidth * iX;
pwFB16bpp = (unsigned short *)pucFB;
pdwFB32bpp = (unsigned int *)pucFB;
switch (g_tFBVar.bits_per_pixel)
{
case 8:
{
*pucFB = (unsigned char)dwColor;
break;
}
case 16:
{
iRed = (dwColor >> (16+3)) & 0x1f;
iGreen = (dwColor >> (8+2)) & 0x3f;
iBlue = (dwColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
*pwFB16bpp = wColor16bpp;
break;
}
case 32:
{
*pdwFB32bpp = dwColor;
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
return -1;
}
}
return 0;
}
static int FBCleanScreen(unsigned int dwBackColor)
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int *pdwFB32bpp;
unsigned short wColor16bpp; /* 565 */
int iRed;
int iGreen;
int iBlue;
int i = 0;
pucFB = g_pucFBMem;
pwFB16bpp = (unsigned short *)pucFB;
pdwFB32bpp = (unsigned int *)pucFB;
switch (g_tFBVar.bits_per_pixel)
{
case 8:
{
memset(g_pucFBMem, dwBackColor, g_dwScreenSize);
break;
}
case 16:
{
iRed = (dwBackColor >> (16+3)) & 0x1f;
iGreen = (dwBackColor >> (8+2)) & 0x3f;
iBlue = (dwBackColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
while (i < g_dwScreenSize)
{
*pwFB16bpp = wColor16bpp;
pwFB16bpp++;
i += 2;
}
break;
}
case 32:
{
while (i < g_dwScreenSize)
{
*pdwFB32bpp = dwBackColor;
pdwFB32bpp++;
i += 4;
}
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
return -1;
}
}
return 0;
}
static int FBShowLine(int iXStart, int iXEnd, int iY, unsigned char *pucRGBArray)
{
int i = iXStart * 3;
int iX;
unsigned int dwColor;
if (iY >= g_tFBVar.yres)
return -1;
if (iXStart >= g_tFBVar.xres)
return -1;
if (iXEnd >= g_tFBVar.xres)
{
iXEnd = g_tFBVar.xres;
}
for (iX = iXStart; iX < iXEnd; iX++)
{
/* 0xRRGGBB */
dwColor = (pucRGBArray[i]<<16) + (pucRGBArray[i+1]<<8) + (pucRGBArray[i+2]<<0);
i += 3;
FBShowPixel(iX, iY, dwColor);
}
return 0;
}
/*
Allocate and initialize a JPEG decompression object // 分配和初始化一个decompression结构体
Specify the source of the compressed data (eg, a file) // 指定源文件
Call jpeg_read_header() to obtain image info // 用jpeg_read_header获得jpg信息
Set parameters for decompression // 设置解压参数,比如放大、缩小
jpeg_start_decompress(...); // 启动解压:jpeg_start_decompress
while (scan lines remain to be read)
jpeg_read_scanlines(...); // 循环调用jpeg_read_scanlines
jpeg_finish_decompress(...); // jpeg_finish_decompress
Release the JPEG decompression object // 释放decompression结构体
*/
/* Uage: jpg2rgb <jpg_file>
*/
int main(int argc, char **argv)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
int row_stride;
unsigned char *buffer;
if (argc != 2)
{
printf("Usage: \n");
printf("%s <jpg_file>\n", argv[0]);
return -1;
}
if (FBDeviceInit())
{
return -1;
}
FBCleanScreen(0);
// 分配和初始化一个decompression结构体
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
// 指定源文件
if ((infile = fopen(argv[1], "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return -1;
}
jpeg_stdio_src(&cinfo, infile);
// 用jpeg_read_header获得jpg信息
jpeg_read_header(&cinfo, TRUE);
/* 源信息 */
printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);
// 设置解压参数,比如放大、缩小
/* 无符号整数scale_num,scale_denom
通过scale_num / scale_denom分数缩放图像。默认值是
1/1,或不缩放。目前,唯一支持的缩放比率
是M / 8,所有M从1到16,或其任何缩小比例(例如
1/2,3/4等)(库设计允许任意
缩放比例,但是这是不太可能很快实施。)
较小的缩放比例允许显着更快的解码,因为
需要处理更少的像素并且可以使用更简单的IDCT方法 输入n/m*/
printf("enter scale M/N:\n");
scanf("%d/%d", &cinfo.scale_num, &cinfo.scale_denom);
printf("scale to : %d/%d\n", cinfo.scale_num, cinfo.scale_denom);
// 启动解压:jpeg_start_decompress
jpeg_start_decompress(&cinfo);
/* 输出的图象的信息 */
printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);
// 一行的数据长度
row_stride = cinfo.output_width * cinfo.output_components;
buffer = malloc(row_stride);
// 循环调用jpeg_read_scanlines来一行一行地获得解压的数据
while (cinfo.output_scanline < cinfo.output_height)
{
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);
// 写到LCD去
FBShowLine(0, cinfo.output_width, cinfo.output_scanline, buffer);
}
free(buffer);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return 0;
}
如果你编译报错,记得安装libjpeg-turbo
tar xzf libjpeg-turbo-1.2.1.tar.gz
cd libjpeg-turbo-1.2.1
./configure --prefix=/usr/local/libjpeg-turbo-1.2.1 --host=arm-linux
make
sudo su
make install
编译时记得指定头文件和库 :
arm-linux-gcc -o jpg2rgb jpg2rgb.c -I /mnt/hgfs/share/libjpeg-turbo-1.2.1/tmp/include -L /mnt/hgfs/share/libjpeg-turbo-1.2.1/tmp/lib -ljpeg /* 指定头文件和库 */
执行 : ./jpg2rgb 12.jpg
1/1