libjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。这个库由独立JPEG工作组维护。最新版本号是6b,于1998年发布。可以参考维基百科关于libjpeg的介绍http://zh.wikipedia.org/wiki/Libjpeg

libjpeg库的数据结构

    用libjpeg库解码jpeg数据的时候,最重要的数据类型为struct jpeg_decompress_struct,一般变量定义成cinfo变量,该变量保存着jpeg数据的详细信息,也保存着解码之后输出数据的详细信息。一般情况下,每次调用libjpeg库API的时候都需要把这个变量作为第一个参数传入。另外用户也可以通过修改该变量来修改libjpeg行为,比如输出数据格式,libjpeg库可用的最大内存等等。

libjpeg库的使用

1、设置出错处理函数

    “天有不测风云”,我们使用libjpeg库的时候难免会产生错误,所以我们在使用libjpeg解码之前,首先要做好错误处理。在libjpeg库中,实现了默认错误处理函数,当错误发生时,比如如果内存不足(非常可能发生,后面会介绍)等,则默认错误处理函数将会调用exit函数结束整个进程,详细内容可以参考jerror.c文件。这个对于很多用户来说这样的特性是不合适的,不过libjpeg提供了接口让我们注册自定义错误处理函数。

在C语言中没有C++的异常处理机制,但是提供了setjmp和longjmp机制来实现类似的功能,如果你对这个机制不熟悉的话,请查阅C语言手册。

  1. <span style="font-size: medium;">struct my_error_mgr {
  2. struct jpeg_error_mgr pub;    /* "public" fields */
  3. jmp_buf setjmp_buffer;    /* for return to caller */
  4. };
  5. typedef struct my_error_mgr * my_error_ptr;
  6. cinfo.err = jpeg_std_error(&jerr.pub);
  7. jerr.pub.error_exit = my_error_exit;
  8. if (setjmp(jerr.setjmp_buffer)) {
  9. jpeg_destroy_decompress(&cinfo);
  10. fclose(infile);
  11. return -1;
  12. }
  13. </span>

上述代码中的重点在于我们向libjpeg注册了一个my_error_exit回调函数,当发生错误的时候,该回调函数将会被调用。然后我们调用setjmp函数,设置一个返回点。这样我们在my_error_exit回调函数处理完错误信息之后,就可以调用longjmp函数返回到这里,在这个返回点进行资源的释放(非常重要,否则将会内存泄漏)。我们再看看my_error_exit回调函数的实现:

  1. <span style="font-size: medium;">METHODDEF(void) my_error_exit (j_common_ptr cinfo)
  2. {
  3. my_error_ptr myerr = (my_error_ptr) cinfo->err;
  4. (*cinfo->err->output_message) (cinfo);
  5. longjmp(myerr->setjmp_buffer, 1);
  6. }
  7. </span>

可以通过检查cinfo->err->msg_code的值来判断错误类型,进行相应的处理。本例中只是简单的打印一个错误信息。最后调用longjmp跳转到setjmp调用的地方。

2、初始化解码对象

    要使用libjpeg解码jpeg数据,这步是必须要做的。

  1. <span style="font-size: medium;">FILE * infile;
  2. if ((infile = fopen(filename, "rb")) == NULL) {
  3. fprintf(stderr, "can't open %s\n", filename);
  4. return -1;
  5. }
  6. jpeg_create_decompress(&cinfo);
  7. </span>

这步之后,如果结束解码或者出错之后,需要调用jpeg_destroy_decompress销毁解码对象,否则将会内存泄漏。

3、初始化源数据

    在libjpeg库中仅仅提供了文件作为输入数据的接口,在example.c中代码如下:

jpeg_stdio_src(&cinfo, infile);

    这个设计我个人觉得非常不合理,我觉得一个友好的库,需要能够接受各式各样的输入(内存数据,网络数据等等)。比较友好的做法是提供几种常用的输入数据支持(在libjpeg中如:文件输入等)。然后还要提供一个用户注册自定义输入函数(回调函数)的接口,这样库就可以适配现实生活中各式各样的输入数据类型了。Simon也在以前的博文中写过怎样修改libjpeg库,使之能够解码内存buffer中的jpeg数据,请参考《LibJpeg解码内存中的Jpeg数据》http://my.unix-center.net/~Simon_fu/?p=565。当然Simon没有扩展libjpeg库让其支持用户注册自定义输入函数(回调函数),有兴趣的朋友可以自行实现。

4、读取jpeg文件的头信息

    这个和初始化解码对象一样,是必须要调用的,是约定,没什么好说的。

  1. <span style="font-size: medium;">jpeg_read_header(&cinfo, TRUE);
  2. </span>

5、设置解码参数

    很多情况下,这步非常重要。比如设置输出格式,设置scale(缩放)等等功能都是在这一步设置。参数设置通过修改上步得到cinfo的值来实现。这里简单介绍一下一些常用的字段。out_color_space:输出的颜色格式,libjpeg定义如下:

  1. <span style="font-size: medium;">typedef enum {
  2. JCS_UNKNOWN,        /* error/unspecified */
  3. JCS_GRAYSCALE,        /* monochrome */
  4. JCS_RGB,        /* red/green/blue */
  5. JCS_YCbCr,        /* Y/Cb/Cr (also known as YUV) */
  6. JCS_CMYK,        /* C/M/Y/K */
  7. JCS_YCCK,        /* Y/Cb/Cr/K */
  8. #ifdef ANDROID_RGB
  9. JCS_RGBA_8888,  /* red/green/blue/alpha */
  10. JCS_RGB_565     /* red/green/blue in 565 format */
  11. #endif
  12. } J_COLOR_SPACE;
  13. </span>

我们可以看出谷歌在Android扩展了几种输出格式,那么如果你需要的颜色格式输出格式libjpeg不支持(比如:YUYV等颜色格式),那么请参考Android对libjpeg的扩展自行修改,不用担心复杂性,实现起来比较easy。请重点研究jdcolor.c文件中的jinit_color_deconverter函数。

scale_num,scale_denom:因为实际的显示设备千变万化,我们可能需要根据实际情况对输出数据进行一些缩放才能够显示。libjpeg支持对输出数据进行缩放(scale),这个变量就是用来设置缩放的参数。目前libjpeg支持1/2,1/4,1/8三种缩放。

mem:可以指定内存管理相关的内容,比如分配和释放内存,指定libjpeg可以使用的最大内存。默认情况下不同的平台下面都有一个libjpeg默认最大可用内存值,比如Android平台上面该值为10000000L(10M),请参考jmemxxxx.c文件中的DEFAULT_MAX_MEM,了解不同平台的默认最大内存值。通过修改mem->pub.max_memory_to_use的值,库的使用者可以自定义libjpeg可以使用的最大内存值。

6、开始解码

    经过前面的参数设置,我们可以开始解码了,没有什么好说的。

jpeg_start_decompress(&cinfo);

7、读取解码数据(我们打印到终端看看)

  1. <span style="font-size: medium;">    buffer = (unsigned char *) malloc(cinfo.output_width * cinfo.output_components);
  2. while (cinfo.output_scanline < cinfo.output_height) {
  3. jpeg_read_scanlines(&cinfo, &buffer, 1);
  4. for (i=0; i<cinfo.output_width; i++) {
  5. printf("%02X%02X%02X ", buffer[i*3], buffer[i*3+1], buffer[i*3+2]);
  6. if (0 == ((i+1) % 10) || i == cinfo.output_width-1) {
  7. printf("\n");
  8. }
  9. }
  10. printf("least:%d\n\n", cinfo.output_height-cinfo.output_scanline);
  11. }</span>

请注意虽然函数jpeg_read_scanlines可以指定一次读多少行,但是目前该函数还是只能支持一次只读1行。

8、结束解码

  1. <span style="font-size: medium;">jpeg_finish_decompress(&cinfo);
  2. </span>

9、释放解码对象

  1. <span style="font-size: medium;">jpeg_destroy_decompress(&cinfo);
  2. </span>

至此一个jpeg数据已经解析完成了。虽然步骤不少但是对于常规的使用还是比较简单的。

总结

    libjpeg对于baseline的jpeg数据解码比较好,但是解码progressive的jpeg数据的时候,对内存的需求比较大(我测试过的progressive的图片曾经发现过消耗70M内存)。如果你的硬件能够有硬件解码jpeg的能力,请尽可能使用硬件解码jpeg数据。

    简单的说baseline   jpeg要全部下载后才能观看;progressive   jpeg采用了类似fgs的技术,可以先传个质量粗糙的,然后逐渐精细,直至全部传完。举个例子,浏览有些网页时,看到图片一行行的出现,但都很清晰,一般都是baseline的,而如果开始就是一大块,但就像有好多马赛克似的,看不清楚,然后慢慢就好了,这种一般就是progressive的了

备注:附件里test.rar里面是test.c文件,包含了示例代码,我测试通过了。内存不用设置的时候,也能处理1.68MB的图片,更大的没试过。jpegsrc.v6b.tar.gz是从官网http://sourceforge.net/projects/libjpeg下载的linux上编译通过了的库源码,如果在linux上用,请不要下载官网的.zip文件,那个在linux下面编译会出错。官网下载地址:http://sourceforge.net/projects/libjpeg/files/libjpeg/6b/jpegsrc.v6b.tar.gz/download 打开是个倒计时下载的。

转自http://canlynet.iteye.com/blog/1433259

libjpeg用法的更多相关文章

  1. [Linux] yum和apt-get用法及区别

    一般来说著名的linux系统基本上分两大类: 1.RedHat系列:Redhat.Centos.Fedora等 2.Debian系列:Debian.Ubuntu等 RedHat 系列 1 常见的安装包 ...

  2. Linux中yum和apt-get用法及区别

    Linux中yum和apt-get用法及区别   一般来说著名的linux系统基本上分两大类:   1.RedHat系列:Redhat.Centos.Fedora等   2.Debian系列:Debi ...

  3. libjpeg安装和使用

    转自: http://blog.csdn.net/ice__snow/article/details/52563944 ,有几处做了一部分修改 一. 编译 下载地址 http://www.ijg.or ...

  4. yum和apt-get 软件包管理器的用法及区别

    yum( Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器. 一般来说著名的linux系统基本上分两大类: 1.R ...

  5. 基础(三):yum(RedHat系列)和apt-get(Debian系列 )用法及区别

    文章转载来自:http://blog.csdn.net/chengly0129/article/details/70169760 一般来说著名的linux系统基本上分两大类:1.RedHat系列:Re ...

  6. imread()用法|| root权限

    1.ushort用法? USHORT is a macro which is not part of the official C++ language (it's probably defined ...

  7. EditText 基本用法

    title: EditText 基本用法 tags: EditText,编辑框,输入框 --- EditText介绍: EditText 在开发中也是经常用到的控件,也是一个比较必要的组件,可以说它是 ...

  8. jquery插件的用法之cookie 插件

    一.使用cookie 插件 插件官方网站下载地址:http://plugins.jquery.com/cookie/ cookie 插件的用法比较简单,直接粘贴下面代码示例: //生成一个cookie ...

  9. Java中的Socket的用法

                                   Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的 ...

随机推荐

  1. linux c statfs系统调用

    statfs 系统调用原型: int statfs(const char *path, struct statfs *buf); 參数说明: path : 位于须要查询信息的文件系统的路径名(不是设备 ...

  2. Extjs在HtmlEditor的工具栏上插入自定义按钮

    Ext.ns('Ext.ux.form.HtmlEditor');Ext.ux.form.HtmlEditor.HR =Ext.extend(Ext.util.Observable,{ init:fu ...

  3. 6.Maven之(六)setting.xml配置文件详解

    转自:https://blog.csdn.net/u012152619/article/details/51485152

  4. deep-in-es6(七)

    Symbols对象 JavaScript的第七种原始类型 以前的数据类型: Undefined 未定义 Null 空值 Boolean 布尔类型 Number 数字类型 String 字符串类型 Ob ...

  5. visualSVN+花生壳实现外网访问局域网内SVN

    使用SubVersion+TortoiseSVN局域网内访问SVN成功后,想从外网访问SVN,使用花生壳绑定路由器动态DNS,但是折腾半天没搞定,突然发现一个帖子http://hi.baidu.com ...

  6. windows删除多余启动引导项

    方法1: 按快捷键win+r,打开运行界面,输入msconfig. 点击确定,进入系统配置,选择引导选项卡,如图: 选中你不需要的启动项,点击下面的删除按钮即可.删除完成之后点击确定,重启计算机就可以 ...

  7. POJ 2481 Cows (线段树)

    Cows 题目:http://poj.org/problem?id=2481 题意:有N头牛,每仅仅牛有一个值[S,E],假设对于牛i和牛j来说,它们的值满足以下的条件则证明牛i比牛j强壮:Si &l ...

  8. 6. oracle学习入门系列之六 模式

    oracle学习入门系列之六 模式 上篇咱们学习记录了ORACLE数据库中的数据库结构.内存结构和进程等.篇幅 蛤蟆感觉偏多了.这次要休整下,每次笔记不宜太多,不然与书籍有何差别. 我们要保证的是每次 ...

  9. Python - 字典(dict)删除元素

    字典(dict)删除元素, 能够选择两种方式, dict.pop(key)和del dict[key]. 代码 # -*- coding: utf-8 -*- def remove_key(d, ke ...

  10. JavaScript作用域闭包(你不知道的JavaScript)

    JavaScript闭包.是JS开发project师必须深入了解的知识. 3月份自己曾撰写博客<JavaScript闭包>.博客中仅仅是简单阐述了闭包的工作过程和列举了几个演示样例,并没有 ...