解决应用 libjpeg 保存时因磁盘写入失败导致法度退出的题目

    添加时间:2013-5-28 点击量:

    0. libjpeg 介绍


    libjpeg 是一个完全用C说话编写的库,包含了被广泛应用的JPEG解码、JPEG编码和其他的JPEG功能的实现。这个库由自力JPEG工作组保护。
    参考:http://zh.wikipedia.org/wiki/Libjpeg


    本文基于 libjpeg9 对应用 libjpeg 保存时因磁盘写入失败导致法度退出的题目进行解析,文中的代码和解决题目的办法均可连络 libjpeg9 编译经由过程。


    1.应用 libjpeg 保存的办法。


      不久不多说,直接上代码:



    /
    
    将 rgb 数据保存到 jpeg 文件
    /
    int rgb_to_jpeg(LPRgbImage img, const char filename) {
    FILE
    f;
    struct jpeg_compress_struct jcs;
    // 声明错误处理惩罚器,并赋值给jcs.err域
    struct jpeg_error_mgr jem;
    unsigned
    char pData;
    int error_flag = 0;

    jcs.err
    = jpeg_std_error(&jem);
    jpeg_create_compress(
    &jcs);

    f
    = fopen(filename, wb);
    if (f == NULL) {
    return -1;
    }
    // android 下应用以下办法,来解决应用 fwrite 写文件时 sd 卡满而不返回错误的题目
    setbuf(f, NULL);

    jpeg_stdio_dest(
    &jcs, f);
    jcs.image_width
    = img->width; // 图像尺寸
    jcs.image_height = img->height; // 图像尺寸
    jcs.input_components = 3; // 在此为1,默示灰度图, 若是是彩色位图,则为3
    jcs.in_color_space = JCS_RGB; // JCS_GRAYSCALE默示灰度图,JCS_RGB默示彩像
    jpeg_set_defaults(&jcs);
    jpeg_set_quality(
    &jcs, 1001); // 图像质量,100 高
    jpeg_start_compress(&jcs, TRUE);

    while (jcs.next_scanline < jcs.image_height) {
    pData
    = img->rgb + jcs.image_width jcs.next_scanline 3;
    jpeg_write_scanlines(
    &jcs, &pData, 1);
    }

    jpeg_finish_compress(
    &jcs);
    jpeg_destroy_compress(
    &jcs);

    fclose (f);
    return error_flag;
    }



      libjpeg 也可已用来解码(读取 jpeg)文件:



    /
    
    从 jpeg 文件读取数据,并保存到 RgbImage 中返回
    /
    LPRgbImage jpeg_to_rgb(
    const char filename) {
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE
    f;
    LPRgbImage pRgbImage;
    JSAMPROW row_pointer[
    1];

    f
    = fopen(filename, rb);
    if (f == NULL) {
    return NULL;
    }

    // 将 jpeg 错误处理惩罚中的异常退出回调批改为我们本身的回调函数,包管法度不异常退出
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(
    &cinfo);
    jpeg_stdio_src(
    &cinfo, f);
    jpeg_read_header(
    &cinfo, TRUE);

    pRgbImage
    = (LPRgbImage) malloc(sizeof (RgbImage));
    if (pRgbImage == NULL) {
    fclose(f);
    return NULL;
    }

    pRgbImage
    ->width = cinfo.image_width;
    pRgbImage
    ->height = cinfo.image_height;
    pRgbImage
    ->linesize = libcfc_align_size(cinfo.image_width 3);
    pRgbImage
    ->rgb = (unsigned char) malloc(pRgbImage->linesize cinfo.image_height);
    if (pRgbImage->rgb == NULL) {
    free(pRgbImage);
    fclose(f);
    return NULL;
    }

    jpeg_start_decompress(
    &cinfo);
    row_pointer[
    0] = pRgbImage->rgb;
    while (cinfo.output_scanline < cinfo.output_height) {
    row_pointer[
    0] = pRgbImage->rgb
    + (cinfo.image_height - cinfo.output_scanline - 1) pRgbImage->linesize;
    jpeg_read_scanlines(
    &cinfo, row_pointer, 1);

    }
    jpeg_finish_decompress(
    &cinfo);
    jpeg_destroy_decompress(
    &cinfo);

    fclose(f);
    return pRgbImage;
    }



      代码中应用了 LPRgbImage ,这是我自定义的一个布局体的指针类型,用来默示一个 rgb 的图像,本文后面会给出完全的代码。


    2. 题目描述


      应用以上办法保存 rgb 数据到 jpeg 文件时,若是磁盘空间满或其他原因导致不克不及写文件失败,全部过程会被停止。但我们的期望往往是磁盘空间满时给出友爱提示,而不是法度直接挂掉。


    3. 题目解析


      1)在开辟景象上重现此题目,法度会在把握台上打印“Output file write error --- out of disk space?”,然撤退猬缩出。


      2)在 libjpeg 的源代码中搜刮 “Output file write error --- out of disk space?”,找到 jerror.h 文件,内容对应


          JMESSAGE(JERR_FILE_WRITE, Output file write error --- out of disk space?)。


      3)可以看出,JERR_FILE_WRITE 是 libjpeg 给这个题目描述信息定义的一个编号。


      4)查找 JERR_FILE_WRITE 这个编号被引用过的处所,发明有六个文件应用过这个符号(我应用的是 libjpeg9,其他版本应当也不会影响本文的解析过程)。


      5)JERR_FILE_WRITE 被引用的情势为:


          ERREXIT(cinfo, JERR_FILE_WRITE);


        ERREXIT 是一个宏,转到这个宏的定义,这个宏同样的被定义在 jerror.h 中,其定义如下:



    #define ERREXIT(cinfo,code)  \
    
    ((cinfo)
    ->err->msg_code = (code), ((cinfo)->err->error_exit) ((j_common_ptr) (cinfo)))


        可以看出来,ERREXIT 宏做了两件事:


          a)将编号(本文中评论辩论的题目编号对应 JERR_FILE_WRITE)赋值给 (cinfo)->err->msg_code


          b)调用 (cinfo)->err->error_exit) 回调。


        cinfo 就是 初始化 libjpeg 时指定的 struct jpeg_decompress_struct。


        (cinfo)->err->msg_code 是 libjpeg 处理惩罚错误的错误代码。


         (cinfo)->err->error_exit 是 libjpeg 在呈现错误时,用来退出的回调函数。我们的法度就是如许被退出的。 


    4. 解决办法


      经由过程解析,知道了法度退出是(cinfo)->err->error_exit 实现的,是以我们可以让这个回调函数指针指向我们本身的函数。


      并且,因为 libjpeg 会在失足的时辰给 (cinfo)->err->msg_code 一个值,之个值就是 libjpeg 定义的错误描述编号,非零的,所以可以在调用 libjpeg 的函数之前将这个值设置为 0,调用完成后在搜检这个时是否为 0,如许来断定 libjpeg 的函数调用是否成功。


    5. 在 android 下的题目


      碰到这个题目是因为要做一个 android 的播放器,此中解码应用了 ffmpeg,截图保存应用 libjpeg。本文上方描述的办法并不克不及完全奏效。


      在 android 下保存截图,若是应用的 sd 卡满,导致保存失败,并不会回调我们应用  (cinfo)->err->error_exit 指定的函数。每次都邑生成一个大小为 0 的文件。


      经由过程解析,认为这事 android 写文件时缓存机制的题目:sd  卡满了,但写文件是是先写到缓存的,是以每次写入文件都邑先写到缓存中,不会返回失败。并且每次调用 fwrite 返回已经写入的数据数是正确的,比及封闭文件或刷新缓存的时辰,才会失足。


      为懂得决这个题目,在打开文件时,将文件的缓存封闭,如许,就能应用本文提到的办法来解决题目了。



    setbuf(f, NULL);



    6. 停止语


      下面供给本文源代码的完全版,代码中应用的位图必须是 24 位位图,且扫描次序是自下而上的。





    /
    
    jpeg_sample.c

    Created on: 2013-5-27
    Author: chenf
    QQ: 99951468
    /

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

    //////////////////////////////////////////////////////////////////////////////////////////////////
    // 用于存取 bmp 文件的布局和函数的定义

    #define TAG_TO_UINT16(l, h) ( (uint16_t) ( (l) | (h << 8) ) )
    /
    默示一个位图的文件头
    /
    #pragma pack(push, 1) // 批改字节对齐体式格式
    typedef
    struct _libcfc_bitmap_file_header_t {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
    } libcfc_bitmap_file_header_t;
    #pragma pack(pop)

    /
    默示一个位图信息头
    /
    #pragma pack(push, 1) // 批改字节对齐体式格式
    typedef
    struct _libcfc_bitmap_info_header_t {
    uint32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t biXPelsPerMeter;
    int32_t biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
    } libcfc_bitmap_info_header_t;
    #pragma pack(pop)

    /
    默示一个位图的头部
    /
    #pragma pack(push, 1) // 批改字节对齐体式格式
    typedef
    struct _libcfc_bitmap_header_t {
    libcfc_bitmap_file_header_t file_header;
    libcfc_bitmap_info_header_t info_header;
    } libcfc_bitmap_header_t;
    #pragma pack(pop)

    /
    初始化位图文件头
    /
    void libcfc_bitmap_init_header(libcfc_bitmap_header_t p_bitmap_header) {
    // 固定值
    p_bitmap_header->file_header.bfType = TAG_TO_UINT16(BM);
    // 固定值
    p_bitmap_header->file_header.bfReserved1 = 0;
    // 固定值
    p_bitmap_header->file_header.bfReserved2 = 0;
    // 固定值
    p_bitmap_header->file_header.bfOffBits = sizeof(libcfc_bitmap_header_t);

    // 需指定
    p_bitmap_header->file_header.bfSize = 0; //bmpheader.bfOffBits + widthheightbpp/8;

    // 固定值
    p_bitmap_header->info_header.biSize = sizeof(libcfc_bitmap_info_header_t);

    // 需指定
    p_bitmap_header->info_header.biWidth = 0;
    // 需指定
    p_bitmap_header->info_header.biHeight = 0;

    // 固定值
    p_bitmap_header->info_header.biPlanes = 1;

    // 需指定
    p_bitmap_header->info_header.biBitCount = 24;

    // 视景象指定 #
    p_bitmap_header->info_header.biCompression = 0;
    // 视景象指定 #
    p_bitmap_header->info_header.biSizeImage = 0;

    // 选填 -
    p_bitmap_header->info_header.biXPelsPerMeter = 100;
    // 选填 -
    p_bitmap_header->info_header.biYPelsPerMeter = 100;
    // 选填 -
    p_bitmap_header->info_header.biClrUsed = 0;
    // 选填 -
    p_bitmap_header->info_header.biClrImportant = 0;
    }

    // 用于存取 bmp 文件的布局和函数的定义
    //////////////////////////////////////////////////////////////////////////////////////////////////



    /
    用于获取对齐大小的宏,即获得不小于输入数字的小 4 的倍数
    /
    #define libcfc_align_size(size) ( ( ( size ) + sizeof( int ) - 1 ) & ~( sizeof( int ) - 1 ) )

    /
    应用 rgb 数据默示的一个图像
    /
    typedef
    struct tagRgbImage {
    unsigned
    char rgb;
    int width;
    int height;
    int linesize;
    } RgbImage,
    LPRgbImage;

    /
    处理惩罚 jpeg 类库中失足退出的逻辑
    /
    static void jpeg_error_exit_handler(j_common_ptr cinfo) {
    // 什么也不做
    }

    /
    将 rgb 数据保存到 jpeg 文件
    /
    int rgb_to_jpeg(LPRgbImage img, const char filename) {
    FILE
    f;
    struct jpeg_compress_struct jcs;
    // 声明错误处理惩罚器,并赋值给jcs.err域
    struct jpeg_error_mgr jem;
    unsigned
    char pData;
    int error_flag = 0;

    jcs.err
    = jpeg_std_error(&jem);
    jpeg_create_compress(
    &jcs);
    // 将 jpeg 错误处理惩罚中的异常退出回调批改为我们本身的回调函数,包管法度不异常退出
    jcs.err->error_exit = jpeg_error_exit_handler;

    f
    = fopen(filename, wb);
    if (f == NULL) {
    return -1;
    }
    // android 下应用以下办法,来解决应用 fwrite 写文件时 sd 卡满而不返回错误的题目
    setbuf(f, NULL);

    jpeg_stdio_dest(
    &jcs, f);
    jcs.image_width
    = img->width; // 图像尺寸
    jcs.image_height = img->height; // 图像尺寸
    jcs.input_components = 3; // 在此为1,默示灰度图, 若是是彩色位图,则为3
    jcs.in_color_space = JCS_RGB; // JCS_GRAYSCALE默示灰度图,JCS_RGB默示彩像
    jpeg_set_defaults(&jcs);
    jpeg_set_quality(
    &jcs, 1001); // 图像质量,100 高
    jpeg_start_compress(&jcs, TRUE);

    while (jcs.next_scanline < jcs.image_height) {
    pData
    = img->rgb + jcs.image_width jcs.next_scanline 3;
    // 调用前,先将 jcs.err->msg_code 设置为 0
    jcs.err->msg_code = 0;
    jpeg_write_scanlines(
    &jcs, &pData, 1);
    // 调用完成后,搜检 jcs.err->msg_code 是否为 0
    if (jcs.err->msg_code != 0) {
    error_flag
    = -1;
    break;
    }
    }

    jpeg_finish_compress(
    &jcs);
    jpeg_destroy_compress(
    &jcs);

    fclose (f);
    return error_flag;
    }

    /
    从 jpeg 文件读取数据,并保存到 RgbImage 中返回
    /
    LPRgbImage jpeg_to_rgb(
    const char filename) {
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE
    f;
    LPRgbImage pRgbImage;
    JSAMPROW row_pointer[
    1];

    f
    = fopen(filename, rb);
    if (f == NULL) {
    return NULL;
    }

    // 将 jpeg 错误处理惩罚中的异常退出回调批改为我们本身的回调函数,包管法度不异常退出
    cinfo.err = jpeg_std_error(&jerr);
    cinfo.err
    ->error_exit = jpeg_error_exit_handler;
    jpeg_create_decompress(
    &cinfo);
    jpeg_stdio_src(
    &cinfo, f);
    jpeg_read_header(
    &cinfo, TRUE);

    pRgbImage
    = (LPRgbImage) malloc(sizeof (RgbImage));
    if (pRgbImage == NULL) {
    fclose(f);
    return NULL;
    }

    pRgbImage
    ->width = cinfo.image_width;
    pRgbImage
    ->height = cinfo.image_height;
    pRgbImage
    ->linesize = libcfc_align_size(cinfo.image_width 3);
    pRgbImage
    ->rgb = (unsigned char) malloc(pRgbImage->linesize cinfo.image_height);
    if (pRgbImage->rgb == NULL) {
    free(pRgbImage);
    fclose(f);
    return NULL;
    }

    jpeg_start_decompress(
    &cinfo);
    row_pointer[
    0] = pRgbImage->rgb;
    while (cinfo.output_scanline < cinfo.output_height) {
    row_pointer[
    0] = pRgbImage->rgb
    + (cinfo.image_height - cinfo.output_scanline - 1) pRgbImage->linesize;
    jpeg_read_scanlines(
    &cinfo, row_pointer, 1);

    }
    jpeg_finish_decompress(
    &cinfo);
    jpeg_destroy_decompress(
    &cinfo);

    fclose(f);
    return pRgbImage;
    }

    /
    将 rgb 数据保存成 bmp 文件
    /
    int rgb_to_bmp(LPRgbImage img, const char filename) {
    libcfc_bitmap_header_t header;
    FILE
    f;
    int size;

    f
    = fopen(filename, wb);
    if (f == NULL) {
    return -1;
    }

    libcfc_bitmap_init_header(
    &header);
    size
    = img->linesize img->height;
    header.file_header.bfSize
    = sizeof(header) + size;
    header.info_header.biWidth
    = img->width;
    header.info_header.biHeight
    = img->height;
    if1 != fwrite(&header, sizeof(header), 1, f)) {
    fclose (f);
    return -1;
    }

    if (size != fwrite(img->rgb, 1, size, f)) {
    fclose (f);
    return -1;
    }

    fclose (f);
    return 0;
    }

    /
    从 bmp 文件读取 rgb 数据,并保存到 RgbImage 中返回
    /
    LPRgbImage bmp_to_rgb(
    const char filename) {
    libcfc_bitmap_header_t header;
    FILE
    f;
    LPRgbImage pRgbImage;
    int size;

    f
    = fopen(filename, rb);
    if (f == NULL) {
    return NULL;
    }

    if1 != fread(&header, sizeof(header), 1, f)) {
    fclose (f);
    return NULL;
    }

    pRgbImage
    = (LPRgbImage) malloc(sizeof (RgbImage));
    if (pRgbImage == NULL) {
    fclose (f);
    return NULL;
    }

    pRgbImage
    ->width = header.info_header.biWidth;
    pRgbImage
    ->height = header.info_header.biHeight;
    if (pRgbImage->height < 0) {
    pRgbImage
    ->height = -pRgbImage->height;
    }
    pRgbImage
    ->linesize = libcfc_align_size(header.info_header.biWidth 3);
    size
    = pRgbImage->linesize pRgbImage->height;
    pRgbImage
    ->rgb = (unsigned char) malloc(size);
    if (pRgbImage->rgb == NULL) {
    free(pRgbImage);
    fclose (f);
    return NULL;
    }

    if (size != fread(pRgbImage->rgb, 1, size, f)) {
    free (pRgbImage
    ->rgb);
    free (pRgbImage);
    fclose (f);
    return NULL;
    }

    fclose(f);
    return pRgbImage;
    }

    int main () {
    LPRgbImage pRgbImage
    = bmp_to_rgb(d:\\gamerev.bmp);
    if (pRgbImage == NULL ) {
    return -1;
    }

    rgb_to_bmp(pRgbImage,
    d:\\gamerev2.bmp);

    free (pRgbImage
    ->rgb);
    free (pRgbImage);
    }


    View Code


       源代码链接:http://files.cnblogs.com/baiynui1983/jpeg_sample.rar


    文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》
    分享到: