Chinaunix首页 | 论坛 | 博客
  • 博客访问: 151661
  • 博文数量: 85
  • 博客积分: 366
  • 博客等级: 一等列兵
  • 技术积分: 455
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-16 18:32
个人简介

闲下来的时候看看书

文章分类

全部博文(85)

文章存档

2016年(1)

2013年(2)

2012年(81)

2011年(1)

我的朋友

分类:

2012-09-14 17:22:22

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

阅读(974) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~