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;structjpeg_compress_struct jcs;//声明错误处理器,并赋值给jcs.err域

structjpeg_error_mgr jem;

unsignedchar*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, 100, 1); //图像质量,100 最高

jpeg_start_compress(&jcs, TRUE);while (jcs.next_scanline

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);returnerror_flag;

}

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

/**

* 从 jpeg 文件读取数据,并保存到 RgbImage 中返回*/LPRgbImage jpeg_to_rgb(const char*filename) {structjpeg_decompress_struct cinfo;structjpeg_error_mgr jerr;

FILE*f;

LPRgbImage pRgbImage;

JSAMPROW row_pointer[1];

f= fopen(filename, "rb");if (f ==NULL) {returnNULL;

}//将 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);returnNULL;

}

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);returnNULL;

}

jpeg_start_decompress(&cinfo);

row_pointer[0] = pRgbImage->rgb;while (cinfo.output_scanline

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);returnpRgbImage;

}

代码中使用了 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#include#include#include

//

//用于存取 bmp 文件的结构和函数的定义

#define TAG_TO_UINT16(l, h) ( (uint16_t) ( (l) | (h << 8) ) )

/**

* 表示一个位图的文件头*/

#pragma pack(push, 1) //修改字节对齐方式typedefstruct_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) //修改字节对齐方式typedefstruct_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) //修改字节对齐方式typedefstruct_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('B', 'M');//固定值

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 + width*height*bpp/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 数据表示的一个图像*/typedefstructtagRgbImage {

unsignedchar*rgb;intwidth;intheight;intlinesize;

} RgbImage,*LPRgbImage;/**

* 处理 jpeg 类库中出错退出的逻辑*/

static voidjpeg_error_exit_handler(j_common_ptr cinfo) {//什么也不做

}/**

* 将 rgb 数据保存到 jpeg 文件*/

int rgb_to_jpeg(LPRgbImage img, const char*filename) {

FILE*f;structjpeg_compress_struct jcs;//声明错误处理器,并赋值给jcs.err域

structjpeg_error_mgr jem;

unsignedchar*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, 100, 1); //图像质量,100 最高

jpeg_start_compress(&jcs, TRUE);while (jcs.next_scanline

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);returnerror_flag;

}/**

* 从 jpeg 文件读取数据,并保存到 RgbImage 中返回*/LPRgbImage jpeg_to_rgb(const char*filename) {structjpeg_decompress_struct cinfo;structjpeg_error_mgr jerr;

FILE*f;

LPRgbImage pRgbImage;

JSAMPROW row_pointer[1];

f= fopen(filename, "rb");if (f ==NULL) {returnNULL;

}//将 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);returnNULL;

}

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);returnNULL;

}

jpeg_start_decompress(&cinfo);

row_pointer[0] = pRgbImage->rgb;while (cinfo.output_scanline

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);returnpRgbImage;

}/**

* 将 rgb 数据保存成 bmp 文件*/

int rgb_to_bmp(LPRgbImage img, const char*filename) {

libcfc_bitmap_header_t header;

FILE*f;intsize;

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;if (1 != 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;intsize;

f= fopen(filename, "rb");if (f ==NULL) {returnNULL;

}if (1 != fread(&header, sizeof(header), 1, f)) {

fclose (f);returnNULL;

}

pRgbImage= (LPRgbImage) malloc(sizeof(RgbImage));if (pRgbImage ==NULL) {

fclose (f);returnNULL;

}

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);returnNULL;

}if (size != fread(pRgbImage->rgb, 1, size, f)) {

free (pRgbImage->rgb);

free (pRgbImage);

fclose (f);returnNULL;

}

fclose(f);returnpRgbImage;

}intmain () {

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

c语言libjpeg处理图像,解决使用 libjpeg 保存图片时因磁盘写入失败导致程序退出的有关问题...相关推荐

  1. [转]解决STM32开启定时器时立即进入一次中断程序问题

    [转]解决STM32开启定时器时立即进入一次中断程序问题 参考文章: (1)[转]解决STM32开启定时器时立即进入一次中断程序问题 (2)https://www.cnblogs.com/tubuji ...

  2. MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题

    MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题 参考文章: (1)MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题 (2)https://www.cnblogs.com/phil ...

  3. Activiti保存.png 流程图片文件且解决idea中保存图片时显示中文乱码的解决方法

    Activiti保存.png 流程图片文件且解决idea中保存图片时显示中文乱码的解决方法 Eclipse 工具中的操作 流程图片生成的两种方式: 使用 activiti-designer 设计流程图 ...

  4. 解决Pycharm中import时无法识别自己写的程序(转载)

    我们用pycharm打开自己写的代码,当多个文件之间有相互依赖的关系的时候,import无法识别自己写的文件,但是我们写的文件又确实在同一个文件夹中,这种问题可以用下面的方法解决: 1)打开File– ...

  5. 解决helix.toolkit obj模型贴图查找失败导致的异常

    前言 helix.toolkit封装了模型加载的操作,由于按照了标准模型的读取,出现材质或者贴图找不到的情况,容易模型加载异常.因此解决办法是,如果相关资源没找到的话,默认赋上默认材质来解决. 附上本 ...

  6. 登录时提示“配置文件写入失败”解决办法

    操作步骤:删除config.xml文件,重启智能POS,配置登录信息. 文件位置:文件存储-winboxcash/config.xml 转载于:https://www.cnblogs.com/yhbc ...

  7. 使用libjpeg处理图像(libjpeg的使用压缩与解压缩jpg格式)

    转载篇: 标题:利用libjpeg处理图像 作者:赵新国 Email:zhao3728@sina.com 关键字: jpeg, 图像压缩 摘要:文章介绍了采用libjpeg处理图像的方法 前一段时间做 ...

  8. debian9宝塔面板安装php失败,宝塔面板安装php失败:提示No package 'libjpeg' found的解决办法...

    这篇文章主要为大家详细介绍了宝塔面板安装php失败:提示No package 'libjpeg' found的解决办法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借 ...

  9. libjpeg php,使用GD和libjpeg支持编译PHP

    我编译自己的PHP,部分是为了更多地了解PHP如何组合在一起,部分原因是我总是发现我需要默认情况下不可用的模块,这样我可以控制它. 我的问题是,我无法在PHP中获得JPEG支持.使用CentOS 5. ...

最新文章

  1. 手把手教你写移动端瀑布流控件布局篇
  2. Nginx 动静态访问分离
  3. API 类和面向对象简介
  4. Java 8 Friday Goodies:Lambda和XML
  5. 简单阻容降压电路图_升压降压芯片电路
  6. Python总结:RuntimeError: matplotlib does not support generators as input
  7. aop阻止方法运行_新型AOP高级氧化高浓度COD废水处理系统介绍
  8. 交什么样的朋友有什么样的天
  9. oracle删除数据库表空间
  10. evolving checkers players [Fogel and Chellapilla, 2002]
  11. VMare Workstation 12 安装 AsteriskNow freePBX
  12. ABAP 出库单打印 产品 A搭A A搭B显示方式
  13. 百度地图街景图像批量获取
  14. shiro 原理简介
  15. Android 适配器 自定义
  16. [luogu3505][bzoj2088][POI2010]TEL-Teleportation【分层图】
  17. 做跨境电商,用信用卡通道还是Paypal收款比较便宜?
  18. 【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)
  19. 2020年高教社建模国赛真题B题--穿越沙漠
  20. Argo-DCS数据传输笔记

热门文章

  1. Python基本数据类型之set
  2. 简单分享apache封IP的方法
  3. 如何下载64位版本的eclipse 以及配置
  4. 使用Lua 局部变量来优化性能,同一时候比較局部变量和全局变量
  5. Docker-创建支持ssh服务的镜像
  6. CGAffineTransformMakeRotation 实现旋转
  7. 外部SRAM实验,让STM32的外部SRAM操作跟内部SRAM一样(转)
  8. Java单例模式实现(线程安全)
  9. PCTFREEITLCONSISTANT READ
  10. Android-JNI开发系列《九》实战-Bitmap处理实现底片灰度化黑白化暖冷色调等效果