多媒体应用在现在电子产品中的地位越来越重要,尤其是在嵌入式设备中。本系列文章将会介绍利用libjpeg解码jpeg文件,libpng解码png文件,libgif解码gif文件。本文为第一篇,介绍使用libjpeg解码jpeg文件。 libjpeg简介libjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。这个库由独立JPEG工作组维护。最新版本号是6b,于1998年发布。可以参考维基百科关于libjpeg的介绍。 libjpeg库的数据结构 用libjpeg库解码jpeg数据的时候,最重要的数据类型为struct libjpeg库的使用1、设置出错处理函数 在C语言中没有C++的异常处理机制,但是提供了setjmp和longjmp机制来实现类似的功能,如果你对这个机制不熟悉的话,请查阅C语言手册。本文下面的代码片段都是出自libjpeg的example.c文件,可以查阅之。 1: /* We set up the normal JPEG error routines, then override error_exit. */ 2: cinfo.err = jpeg_std_error(&jerr.pub); 3: jerr.pub.error_exit = my_error_exit; 4: /* Establish the setjmp return context for my_error_exit to use. */ 5: if (setjmp(jerr.setjmp_buffer)) { 6: /* If we get here, the JPEG code has signaled an error. 7: * We need to clean up the JPEG object, close the input file, and return. 8: */ 9: jpeg_destroy_decompress(&cinfo); 10: fclose(infile); 11: return 0; 12: } 1: /* 2: * Here's the routine that will replace the standard error_exit method: 3: */ 4: METHODDEF(void) 5: my_error_exit (j_common_ptr cinfo) 6: { 7: /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ 8: my_error_ptr myerr = (my_error_ptr) cinfo->err; 9: 10: /* Always display the message. */ 11: /* We could postpone this until after returning, if we chose. */ 12: (*cinfo->err->output_message) (cinfo); 13: 14: /* Return control to the setjmp point */ 15: longjmp(myerr->setjmp_buffer, 1); 16: } 可以通过检查cinfo->err->msg_code的值来判断错误类型,进行相应的处理。本例中只是简单的打印一个错误信息。最后调用longjmp跳转到setjmp调用的地方。 2、初始化解码对象要使用libjpeg解码jpeg数据,这步是必须要做的。 1: /* Now we can initialize the JPEG decompression object. */ 2: jpeg_create_decompress(&cinfo); 这步之后,如果结束解码或者出错之后,需要调用jpeg_destroy_decompress销毁解码对象,否则将会内存泄漏。 3、初始化源数据在libjpeg库中仅仅提供了文件作为输入数据的接口,在example.c中代码如下: 1: /* Step 2: specify data source (eg, a file) */ 2: jpeg_stdio_src(&cinfo, infile); 4、读取jpeg文件的头信息这个和初始化解码对象一样,是必须要调用的,是约定,没什么好说的。 1: /* Step 3: read file parameters with jpeg_read_header() */ 2: (void) jpeg_read_header(&cinfo, TRUE); 5、设置解码参数很多情况下,这步非常重要。比如设置输出格式,设置scale(缩放)等等功能都是在这一步设置。参数设置通过修改上步得到cinfo的值来实现。这里简单介绍一下一些常用的字段。 out_color_space:输出的颜色格式,libjpeg定义如下: 1: /* Known color spaces. */ 2: typedef enum { 3: JCS_UNKNOWN, /* error/unspecified */ 4: JCS_GRAYSCALE, /* monochrome */ 5: JCS_RGB, /* red/green/blue */ 6: JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ 7: JCS_CMYK, /* C/M/Y/K */ 8: JCS_YCCK, /* Y/Cb/Cr/K */ 9: #ifdef ANDROID_RGB 10: JCS_RGBA_8888, /* red/green/blue/alpha */ 11: JCS_RGB_565 /* red/green/blue in 565 format */ 12: #endif 13: } J_COLOR_SPACE; 我们可以看出谷歌在Android扩展了几种输出格式,那么如果你需要的颜色格式输出格式libjpeg不支持(比 scale_num,scale_denom:因为实际的显示设备千变万化,我们可能需要根据实际情况对输出数据进行一些缩放才能够显示。libjpeg支持对输出数据进行缩放(scale),这个变量就是用来设置缩放的参数。目前libjpeg支持1/2,1/4,1/8三种缩放。 mem:可以指定内存管理相关的内容,比如分配和释放内存,指定libjpeg 6、开始解码经过前面的参数设置,我们可以开始解码了,没有什么好说的。 1: /* Step 5: Start decompressor */ 2: (void) jpeg_start_decompress(&cinfo); 7、读取解码数据1: /* Here we use the library's state variable cinfo.output_scanline as the 2: * loop counter, so that we don't have to keep track ourselves. 3: */ 4: while (cinfo.output_scanline < cinfo.output_height) { 5: /* jpeg_read_scanlines expects an array of pointers to scanlines. 6: * Here the array is only one element long, but you could ask for 7: * more than one scanline at a time if that's more convenient. 8: */ 9: (void) jpeg_read_scanlines(&cinfo, buffer, 1); 10: /* Assume put_scanline_someplace wants a pointer and sample count. */ 11: put_scanline_someplace(buffer[0], row_stride); 12: } 请注意虽然函数jpeg_read_scanlines可以指定一次读多少行,但是目前该函数还是只能支持一次只读1行。 8、结束解码1: /* Step 7: Finish decompression */ 2: (void) jpeg_finish_decompress(&cinfo); 9、释放解码对象1: /* Step 8: Release JPEG decompression object */ 2: 3: /* This is an important step since it will release a good deal of memory. */ 4: jpeg_destroy_decompress(&cinfo); 至此一个jpeg数据已经解析完成了。虽然步骤不少但是对于常规的使用还是比较简单的。 总结 |
|
来自: SamBookshelf > 《libjpeg》