注意下载了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);
|