分类: 嵌入式
2014-06-27 23:09:57
原文地址:jpeg库使用及源码分析 作者:超级忍者龟
注意下载了JPEG的源码后,先看看源码里面自带的libjpeg.doc文件
先来研究一下JPEG的压缩操作过程:
The rough outline of a JPEG compression operation is:
Allocate and initialize a JPEG compression object
Specify the destination for the compressed data (eg, a file)
Set parameters for compression, including image size & colorspace
jpeg_start_compress(...);
while (scan lines remain to be written)
jpeg_write_scanlines(...);
jpeg_finish_compress(...);
Release the JPEG compression object
结合代码:
/*
* Sample routine for JPEG compression. We assume that the target file name
* and a compression quality factor are passed in.
*/
GLOBAL(void)
write_JPEG_file (char * filename, int quality)
{
/* This struct contains the JPEG compression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
* It is possible to have several such structures, representing multiple
* compression/decompression processes, in existence at once. We refer
* to any one struct (and its associated working data) as a "JPEG object".
*/ struct jpeg_compress_struct cinfo;
这个结构体是JPEG库需要使用的,它保存了JPEG的压缩参数和JPEG库的工作状态,这个结构可以有多个,多个就表示有多个压缩和解压缩要处理。当需要对一系列的image进行compress操作时,这个结构可以重复使用。
/* This struct represents a JPEG error handler. It is declared separately
* because applications often want to supply a specialized error handler
* (see the second half of this file for an example). But here we just
* take the easy way out and use the standard error handler, which will
* print a message on stderr and call exit() if compression fails.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct jpeg_error_mgr jerr;
/* More stuff */
FILE * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
第一步:分配并初始化一个JPEG的压缩对象
/* Step 1: allocate and initialize JPEG compression object */
/* We have to set up the error handler first, in case the initialization
* step fails. (Unlikely, but it could happen if you are out
of memory.)
* This routine fills in the contents of struct jerr, and returns
jerr's
* address which we place into the link field in cinfo.
*/
cinfo.err = jpeg_std_error(&jerr);
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
第二步:指定转换后数据的目的地
/* Step 2: specify data destination (eg, a file) */
/* Note: steps 2 and 3 can be done in either order.第二步和第三步顺序无所谓,可以颠倒 */
/* Here we use the library-supplied code to send compressed data to a
* stdio stream. You can also write your own code to do
something else.
* VERY IMPORTANT: use "b" option to fopen() if you are
on a machine that
* requires it in order to write binary files.
*/
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);关联JPEG的压缩对象跟输入文件
第三步:设置压缩参数
/* Step 3: set parameters for compression */
/* First we supply a description of the input image.
* Four fields of the cinfo struct must be filled in:
*/
cinfo.image_width = image_width; /* image width and
height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /* # of
color components per pixel */
cinfo.in_color_space = JCS_RGB;
/* colorspace of input image */
/* Now use the library's routine to set default compression
parameters.
* (You must set at least
cinfo.in_color_space before calling this,
* since the defaults depend on the source color space.)
*/
jpeg_set_defaults(&cinfo);使用库函数设置默认的压缩参数
/* Now you can set any non-default parameters you wish to.
* Here we just illustrate the use of quality (quantization table)
scaling:
*/
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG
values */);
第四步:开始压缩
/* Step 4: Start compressor */
/* TRUE ensures that we will write a complete interchange-JPEG file.
* Pass TRUE unless you are very sure of what you're doing.
*/
jpeg_start_compress(&cinfo, TRUE);
第五步:这里的循环参数是库的一个状态变量cinfo.next_scanline
/* Step 5: while (scan lines remain to be written) */
/*
jpeg_write_scanlines(...); */
/* Here we use the library's state variable cinfo.next_scanline as the
* loop counter, so that we don't have to keep track ourselves.
* To keep things simple, we pass one scanline per call; you can
pass
* more if you wish, though.
*/
row_stride = image_width * 3; /* JSAMPLEs per row in
image_buffer */
当前扫描的行数小于图片的总行数就继续
while (cinfo.next_scanline < cinfo.image_height) {
/* jpeg_write_scanlines expects an array of pointers to
scanlines.
* Here the array is only one element long, but you
could pass
* more than one scanline at a time if that's more
convenient.
*/
row_pointer[0] = & image_buffer[cinfo.next_scanline *
row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
第六步:完成压缩
/* Step 6: Finish compression */
jpeg_finish_compress(&cinfo);
/* After finish_compress, we can close the output file. */
fclose(outfile);关闭输出文件
第七步:释放JPEG压缩对象,之后这个cinfo可以用作另外一个image
/* Step 7: release JPEG compression object */
/* This is an important step since it will release a good deal of
memory. */
jpeg_destroy_compress(&cinfo);
/* And we're done! */
}
下面研究JPEG的解压缩过程:
the rough outline of a JPEG decompression operation is:
Allocate and initialize a JPEG decompression object
Specify the source of the compressed data (eg, a file)
Call jpeg_read_header() to obtain image info
Set parameters for decompression
jpeg_start_decompress(...);
while (scan lines remain to be read)
jpeg_read_scanlines(...);
jpeg_finish_decompress(...);
Release the JPEG decompression object
这个过程与压缩过程差不多,就是多了读数据流头这一步。这个头很有用,包含了image的大小,颜色空间,当咱们自己的应用程序选择解压缩参数时候要用到这些信息。例如,应用程序可以选择一个比例因子生成一个适合大小的图片。
结合代码:
/*
* Sample routine for JPEG decompression. We assume that the source file name
* is passed in. We want to return 1 on success, 0 on error.
*/
GLOBAL(int)
read_JPEG_file (char * filename)
{
/* This struct contains the JPEG decompression parameters and pointers to
* working space (which is allocated as needed by the JPEG library).
*/
struct jpeg_decompress_struct cinfo;
/* We use our private extension JPEG error handler.
* Note that this struct must live as long as the main JPEG parameter
* struct, to avoid dangling-pointer problems.
*/
struct my_error_mgr jerr;
/* More stuff */
FILE * infile; /* source file */
JSAMPARRAY buffer; /* Output row buffer */
int row_stride; /* physical row width in output buffer */
/* In this example we want to open the input file before doing anything else,
* so that the setjmp() error recovery below can assume the file is open.
* VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
* requires it in order to read binary files.
*/
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
return 0;
}
/* Step 1: allocate and initialize JPEG decompression object */
/* We set up the normal JPEG error routines, then override error_exit. */
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. */
if (setjmp(jerr.setjmp_buffer)) {
/* If we get here, the JPEG code has signaled an error.
* We need to clean up the JPEG object, close the input file, and return.
*/
jpeg_destroy_decompress(&cinfo);
fclose(infile);
return 0;
}
/* Now we can initialize the JPEG decompression object. */
jpeg_create_decompress(&cinfo);
/* Step 2: specify data source (eg, a file) */
jpeg_stdio_src(&cinfo, infile);
/* Step 3: read file parameters with jpeg_read_header() */
(void) jpeg_read_header(&cinfo, TRUE);
/* We can ignore the return value from jpeg_read_header since
* (a) suspension is not possible with the stdio data source, and
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
* See libjpeg.doc for more info.
*/
/* Step 4: set parameters for decompression */
/* In this example, we don't need to change any of the defaults set by
* jpeg_read_header(), so we do nothing here.
*/
/* Step 5: Start decompressor */
(void) jpeg_start_decompress(&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* We may need to do some setup of our own at this point before reading
* the data. After jpeg_start_decompress() we have the correct scaled
* output image dimensions available, as well as the output colormap
* if we asked for color quantization.
* In this example, we need to make an output work buffer of the right size.
*/
/* JSAMPLEs per row in output buffer */
row_stride = cinfo.output_width * cinfo.output_components;
/* Make a one-row-high sample array that will go away when done with image */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < cinfo.output_height) {
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
jpeg_read_scanlines函数用来把解压缩的数据传递到内存Buffer中
/* Assume put_scanline_someplace wants a pointer and sample count. */
put_scanline_someplace(buffer[0], row_stride);
}
/* Step 7: Finish decompression */
(void) jpeg_finish_decompress(&cinfo);
/* We can ignore the return value since suspension is not possible
* with the stdio data source.
*/
/* Step 8: Release JPEG decompression object */
/* This is an important step since it will release a good deal of memory. */
jpeg_destroy_decompress(&cinfo);
/* After finish_decompress, we can close the input file.
* Here we postpone it until after no more JPEG errors are possible,
* so as to simplify the setjmp error logic above. (Actually, I don't
* think that jpeg_destroy can do an error exit, but why assume anything...)
*/
fclose(infile);
/* At this point you may want to check to see whether any corrupt-data
* warnings occurred (test whether jerr.pub.num_warnings is nonzero).
*/
/* And we're done! */
return 1;
}
下面开始分析源码,以djpeg.c中的main.c为入口开始分析:(以★开头的是代码,其余的是分析)
★struct jpeg_decompress_struct cinfo;
这个结构体对象包含了JPEG解压缩参数和一些工作函数的指针,咱们的JPEG库的API需要它
★jpeg_create_decompress(&cinfo);
初始化这个结构体对象,这里的jpeg_create_decompress是一个宏替换:
★#define jpeg_create_decompress(cinfo) \
jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \
(size_t) sizeof(struct jpeg_decompress_struct))
这里的JPEG_LIB_VERSION在jpeglib.h头文件中定义,其目的是为了同步JPEG库和调用者的版本一致,如果调用者用的JPEG_LIB_VERSION和库里面的JPEG_LIB_VERSION不一样,程序会退出运行。下面看看函数jpeg_CreateDecompress的实现(在jdapimin.c中):
★GLOBAL(void)
jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, size_t structsize)
{
int i;
/* Guard against version mismatches between library and caller. */
cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */
if (version != JPEG_LIB_VERSION) //这里判断使用者的版本和库版本是否一致
ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version);
// 同样调用者的结构体对象jpeg_decompress_struct的大小也要同库一致
if (structsize != SIZEOF(struct jpeg_decompress_struct))
ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE,
(int) SIZEOF(struct jpeg_decompress_struct), (int) structsize);
/* For debugging purposes, zero the whole master structure.
* But error manager pointer is already there, so save and restore it.
*/
{ // 关于错误处理这里先不看,先当咱们程序不会出错^oo^
struct jpeg_error_mgr * err = cinfo->err;
将cinfo这个结构体对象清0
MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct));
cinfo->err = err;
}
// is_decompressor是boolean 值,TRUE表示要解压缩
cinfo->is_decompressor = TRUE;
/* Initialize a memory manager instance for this object */
// 为这个对象初始化一个内存管理实例,下面会分析这个接口
jinit_memory_mgr((j_common_ptr) cinfo);
/* Zero out pointers to permanent structures. */
cinfo->progress = NULL;
cinfo->src = NULL; // cinfo->src是被压缩的数据源地址
// NUM_QUANT_TBLS指定量化表个数,默认为4,最好不要改动
// NUM_HUFF_TBLS指定量化表个数,默认为4,最好不要改动
for (i = 0; i < NUM_QUANT_TBLS; i++)
cinfo->quant_tbl_ptrs[i] = NULL; // 表指针先置空
for (i = 0; i < NUM_HUFF_TBLS; i++) {
cinfo->dc_huff_tbl_ptrs[i] = NULL; // 表指针先置空
cinfo->ac_huff_tbl_ptrs[i] = NULL; // 表指针先置空
}
/* Initialize marker processor so application can override methods
* for COM, APPn markers before calling jpeg_read_header.
*/
jinit_marker_reader(cinfo);
/* And initialize the overall input controller. */
jinit_input_controller(cinfo); // 初始化输入控制
/* OK, I'm ready */
// global_state验证所有任务队列的有效性,即目前程序到了一个什么状态,这里 DSTATE_START为200,表示完成了create_decompress
cinfo->global_state = DSTATE_START;
}
jinit_memory_mgr实现(在jmemmgr.c中)
★jinit_memory_mgr((j_common_ptr) cinfo);
/*
* Memory manager initialization.
* When this is called, only the error manager pointer is valid in cinfo!
*/
这个函数调用完毕后,cinfo结构里的错误管理指针就有效了
这里函数参数j_common_ptr cinfo把cinfo强制转换成j_common_ptr类型,j_common_ptr类型结构如下:
struct jpeg_common_struct {
jpeg_common_fields; /* Fields common to both master struct types
};
而jpeg_common_fields是个宏替换:
#define jpeg_common_fields \
struct jpeg_error_mgr * err; /* Error handler module */\
struct jpeg_memory_mgr * mem; /* Memory manager module */\
struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\
void * client_data; /* Available for use by application */\
boolean is_decompressor; /* So common code can tell which is which */\
int global_state /* For checking call sequence validity */
GLOBAL(void)
jinit_memory_mgr (j_common_ptr cinfo)
{
my_mem_ptr mem;
long max_to_use;
int pool;
size_t test_mac;
cinfo->mem = NULL; /* for safety if init fails */
/* Check for configuration errors.
* SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably
* doesn't reflect any real hardware alignment requirement.
* The test is a little tricky: for X>0, X and X-1 have no one-bits
* in common if and only if X is a power of 2, ie has only one one-bit.
* Some compilers may give an "unreachable code" warning here; ignore it.
*/
if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0)
ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE);
/* MAX_ALLOC_CHUNK must be representable as type size_t, and must be
* a multiple of SIZEOF(ALIGN_TYPE).
* Again, an "unreachable code" warning may be ignored here.
* But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK.
*/
test_mac = (size_t) MAX_ALLOC_CHUNK;
if ((long) test_mac != MAX_ALLOC_CHUNK ||
(MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0)
ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK);
我们返回的max_to_use的值是1000000
max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */
/* Attempt to allocate memory manager's control block */
分配内存管理控制块的内存
mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr));
如果失败就释放并返回
if (mem == NULL) {
jpeg_mem_term(cinfo); /* system-dependent cleanup */这里面没啥东西,可以自己写一些处理之类的
ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0);
}
/* OK, fill in the method pointers */
// 填充函数指针
mem->pub.alloc_small = alloc_small;
mem->pub.alloc_large = alloc_large;
mem->pub.alloc_sarray = alloc_sarray;
mem->pub.alloc_barray = alloc_barray;
mem->pub.request_virt_sarray = request_virt_sarray;
mem->pub.request_virt_barray = request_virt_barray;
mem->pub.realize_virt_arrays = realize_virt_arrays;
mem->pub.access_virt_sarray = access_virt_sarray;
mem->pub.access_virt_barray = access_virt_barray;
mem->pub.free_pool = free_pool;
mem->pub.self_destruct = self_destruct;
/* Make MAX_ALLOC_CHUNK accessible to other modules */
mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK;
/* Initialize working state */
max_memory_to_use是为JPEG对象分配的内存极限大小
mem->pub.max_memory_to_use = max_to_use;
// JPOOL_NUMPOOLS == 2, JPOOL_PERMANENT == 0
for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) {
mem->small_list[pool] = NULL;
mem->large_list[pool] = NULL;
}
mem->virt_sarray_list = NULL;
mem->virt_barray_list = NULL;
mem->total_space_allocated = SIZEOF(my_memory_mgr);
/* Declare ourselves open for business */
cinfo->mem = & mem->pub;
/* Check for an environment variable JPEGMEM; if found, override the
* default max_memory setting from jpeg_mem_init. Note that the
* surrounding application may again override this value.
* If your system doesn't support getenv(), define NO_GETENV to disable
* this feature.
*/
#ifndef NO_GETENV
{ char * memenv;
if ((memenv = getenv("JPEGMEM")) != NULL) {
char ch = 'x';
if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) {
if (ch == 'm' || ch == 'M')
max_to_use *= 1000L;
mem->pub.max_memory_to_use = max_to_use * 1000L;
}
}
}
#endif
}
★jinit_marker_reader(cinfo);
★jinit_input_controller(cinfo);
★jpeg_mem_init(cinfo) 返回一个值,简单不多解释
GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
return DEFAULT_MAX_MEM; /* default for max_memory_to_use */
}
★mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr));
GLOBAL(void *)
jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
return (void *) malloc(sizeofobject);
}
★jpeg_mem_term(cinfo);