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. OpenCASCADE7.3.0 is available for download

    OpenCASCADE7.3.0 is available for download OPEN CASCADE is pleased to announce a new public release ...

  2. 比MD5 和HMAC还要安全的加密 - MD5 加时间戳

    //1.给一个字符串进行MD5加密 NSString *passKey = @"myapp"; passKey = [passKey md5String]; //2.对第一步中得到 ...

  3. Linq聚合函数使用

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  4. 分享一个表格插入和删除编辑功能用jQuery实现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Codefroces Educational Round 27 845G Shortest Path Problem?

    Shortest Path Problem? You are given an undirected graph with weighted edges. The length of some pat ...

  6. batch---系统不繁忙时执行任务

    batch:不需要指定时间,自动在系统空闲的时候执行指定的任务 [root@xiaolizi ~]# batch at> echo 1234at> <EOT>job 5 at ...

  7. Highcharts柱形范围图使用示例

    功能需求:统计三种不同的状态在一天的时间段里面所占的范围 第一步:引入highcharts.js和highcharts-more.js文件 引入文件文件源码:下载https://img.hcharts ...

  8. 【2017 Multi-University Training Contest - Team 5】Rikka with Graph

    [Link]:http://acm.hdu.edu.cn/showproblem.php?pid=6090 [Description] 给你n个点; 让你在这n个点上最多连m条无向边; 使得 ∑ni= ...

  9. Android自己定义圆角ImageView 支持网络图片

    先看下效果图 我们再来看一张CSDN的圆角图片 从布局能够看出csdn app 的头像也是圆角的Image,但能够看到.有明显的毛刺感.不知道是csdn 程序猿的疏忽还是 我手机的问题,本人手机(小米 ...

  10. .Net Standard和各平台关系

    .NET Standard      1.0      1.1      1.2      1.3      1.4 1.5 1.6 2.0 .NET 核心 1.0 1.0 1.0 1.0 1.0 1 ...