关于libjpeg或libjpeg-turbo使用中的一个注意事项

概述:在使用libjpeg在我的项目中,在测试过程中压缩图像出错,然后程序直接退出了,这对于我们程序来说那肯定是不行的啊。所以就开始找问题啦,在网上找到原因,原来是我们在压缩过程中使用了libjpeg默认的出错处理函数error_exit(),所以导致出错以后程序就整个退出了。今天我主要就是把这个分析的过程记录下来。

我们先看一个例子,我们使用默认的错误处理函数进行图片解压缩。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>

/*读取jpg文件并解压,使用官方自带的error处理函数*/
unsigned char *read_jpeg_file(char *filename)
{
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    FILE *infile;
    JSAMPARRAY buffer;
    int row_stride;

    if (NULL == (infile = fopen(filename, "rb"))) {
        printf("can't open %s\n",filename);
        return NULL;
    }

    cinfo.err = jpeg_std_error(&jerr);

    /*初始化*/
    jpeg_create_decompress(&cinfo);
    /*指定输入文件*/
    jpeg_stdio_src(&cinfo, infile);
    /*读取文件信息*/
    jpeg_read_header(&cinfo, TRUE);

  /*解压*/
    jpeg_start_decompress(&cinfo);
    row_stride = cinfo.output_height * cinfo.output_width * cinfo.output_components;
    /*输出图像信息*/
    printf("output_width = %d\n", cinfo.output_width);
    printf("output_height = %d\n", cinfo.output_height);
    printf("output_components = %d\n", cinfo.output_components);
  /*申请内存用于存储解压数据*/
    unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * row_stride);
  /*逐行扫描获取解压信息*/
    JSAMPROW row_pointer[1];
    while (cinfo.output_scanline < cinfo.output_height) {
        row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.output_width*cinfo.output_components];
        jpeg_read_scanlines(&cinfo,row_pointer ,1);
    }
    /*完成解压*/
    jpeg_finish_decompress(&cinfo);
    /*销毁解压cinfo信息*/
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return data;
}

int main(int argc, char *argv[])
{
  if (argc != 2) {
    printf("Please input decompress img path, ep:./jpeg_error_test.c ./test.jpg\n");
    return -1;
  }
  printf("decompress img: %s\n",argv[1]);
  unsigned char *decompress_img = read_jpeg_file(argv[1]);
  if (NULL == decompress_img) {
    printf("decompress failed\n");
  } else {
    printf("decompress success\n");
    free(decompress_img);
    decompress_img = NULL;
  }
  /*死循环,让程序一直运行*/
  while (1) {
    sleep(1);
  }

  return 0;
}

我们看一下正确的jpg图片,解压缩执行结果:
这里写图片描述
程序解压正确,程序一直运行,未退出,我们复制这张图片,用vim打开我们的图片,随便删除一些内容,损坏这张图片,我们再运行看一下结果:
这里写图片描述

程序直接退出了,那我们看一下libjpeg源码,分析一下为何会直接退出呢。

在我们例子中用了以下的代码:

/*错误处理函数*/
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);

首先看一下,jpeg_std_error()这个函数(在jerror.c文件),这个函数其实就是初始化的作用。最主要的就是error_exit.

这里写图片描述

接下来我们看一下,libjpeg中默认的error_exit函数。

这里写图片描述

从上面的error_exit函数我们知道了,为何程序会退出,就在最后一句啦。exit(EXIT_FAILURE);
那这个函数何时调用的呢,我们在jerror.h中看到如下宏定义:

这里写图片描述

我们搜索ERREXIT关键字,发现在我们解压,压缩这样调用的函数,源代码中出错时都会调用上图中的函数,所以一出现错误,程序就会退出。

那如何解决这样的问题呢,其实在libjpeg源码中,examaple.c中就已经解决了这个问题,怪自己没好好看注释啊,主要还是英文的,我这要好好学习英语啦。那我们看一下这个例子吧,同样是解压缩的例子,和上面例子唯一的区别就是,我们用自己的错误处理函数,代替了原有的错误处理函数error_exit.

例子代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <jpeglib.h>
#include <setjmp.h>

struct skyjpeg_error_mgr {
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct skyjpeg_error_mgr *skyjpeg_error_ptr;

void skyjpeg_error_exit (j_common_ptr cinfo)
{
  char skyjpeg_error_msg[JMSG_LENGTH_MAX];
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    skyjpeg_error_ptr myerr = (skyjpeg_error_ptr) cinfo->err;
    /* Always display the message. */
  /*这是官方例子里面的,打印出错误信息,我们就不用这个,我们让它把错误信息放到一个数组里面*/
    /*(*cinfo->err->output_message) (cinfo);*/
  /*错误信息放到数组*/
  (*cinfo->err->format_message) (cinfo, skyjpeg_error_msg);
  /*打印错误信息*/
  printf("\n error msg:\n      %s\n",skyjpeg_error_msg);
    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

unsigned char *read_jpeg_file(char *filename)
{
    struct jpeg_decompress_struct cinfo;
    struct skyjpeg_error_mgr jerr;

    FILE *infile;
    JSAMPARRAY buffer;
    int row_stride;

    if (NULL == (infile = fopen(filename, "rb"))) {
        printf("can't open %s\n",filename);
        return NULL;
    }

    cinfo.err = jpeg_std_error(&jerr.pub);
  /*用我们定义的skyjpeg_error_exit函数覆盖libjpeg默认的error_exit函数*/
  jerr.pub.error_exit = skyjpeg_error_exit;

    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return NULL;
    }

    /*初始化*/
    jpeg_create_decompress(&cinfo);
    /*指定输入文件*/
    jpeg_stdio_src(&cinfo, infile);
    /*读取文件信息*/
    jpeg_read_header(&cinfo, TRUE);

  /*解压*/
    jpeg_start_decompress(&cinfo);
    row_stride = cinfo.output_height * cinfo.output_width * cinfo.output_components;
    /*输出图像信息*/
    printf("output_width = %d\n", cinfo.output_width);
    printf("output_height = %d\n", cinfo.output_height);
    printf("output_components = %d\n", cinfo.output_components);
  /*申请内存用于存储解压数据*/
    unsigned char *data = (unsigned char *)malloc(sizeof(unsigned char) * row_stride);
  /*逐行扫描获取解压信息*/
    JSAMPROW row_pointer[1];
    while (cinfo.output_scanline < cinfo.output_height) {
        row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.output_width*cinfo.output_components];
        jpeg_read_scanlines(&cinfo,row_pointer ,1);
    }
    /*完成解压*/
    jpeg_finish_decompress(&cinfo);
    /*销毁解压cinfo信息*/
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return data;
}

int main(int argc, char *argv[])
{
  if (argc != 2) {
    printf("Please input decompress img path, ep:./jpeg_error_test.c ./test.jpg\n");
    return -1;
  }
  printf("decompress img: %s\n",argv[1]);
  unsigned char *decompress_img = read_jpeg_file(argv[1]);
  if (NULL == decompress_img) {
    printf("decompress failed\n");
  } else {
    printf("decompress success\n");
    free(decompress_img);
    decompress_img = NULL;
  }
  /*死循环,让程序一直运行*/
  while (1) {
    sleep(1);
  }

  return 0;
}

我们编译完成后,用这个代码执行对已经损坏的jpg文件,解压。我们看看程序是不是还会退出呢。当然,程序没有退出啦,结果如下:

这里写图片描述

其实这里主要用到的就是下面代码,用自己的错误处理函数,覆盖默认的函数。

/*自定义一个错误处理的结构体*/
struct skyjpeg_error_mgr {
    struct jpeg_error_mgr pub;
    jmp_buf setjmp_buffer;
};
typedef struct skyjpeg_error_mgr *skyjpeg_error_ptr;
/*自己的错误处理函数*/
void skyjpeg_error_exit (j_common_ptr cinfo)
{
  char skyjpeg_error_msg[JMSG_LENGTH_MAX];
    /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
    skyjpeg_error_ptr myerr = (skyjpeg_error_ptr) cinfo->err;
    /* Always display the message. */
  /*这是官方例子里面的,打印出错误信息,我们就不用这个,我们让它把错误信息放到一个数组里面*/
    /*(*cinfo->err->output_message) (cinfo);*/
  /*错误信息放到数组*/
  (*cinfo->err->format_message) (cinfo, skyjpeg_error_msg);
  /*打印错误信息*/
  printf("\n error msg:\n      %s\n",skyjpeg_error_msg);
    /* Return control to the setjmp point */
    longjmp(myerr->setjmp_buffer, 1);
}

  cinfo.err = jpeg_std_error(&jerr.pub);
  /*用我们定义的skyjpeg_error_exit函数覆盖libjpeg默认的error_exit函数*/
  jerr.pub.error_exit = skyjpeg_error_exit;

    if (setjmp(jerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return NULL;
    }

总结:
OK,问题解决。但是这些问题也是因为没有好好看例子所以导致了一些问题的出现,所以在使用第三方库时最好还是好好看看例子及注释,避免一些不必要的错误。Peace&Love.

猜你喜欢

转载自blog.csdn.net/dancer__sky/article/details/78879909