c语言libjpeg处理图像,解决使用 libjpeg 保存图片时因磁盘写入失败导致程序退出的有关问题...
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 保存图片时因磁盘写入失败导致程序退出的有关问题...相关推荐
- [转]解决STM32开启定时器时立即进入一次中断程序问题
[转]解决STM32开启定时器时立即进入一次中断程序问题 参考文章: (1)[转]解决STM32开启定时器时立即进入一次中断程序问题 (2)https://www.cnblogs.com/tubuji ...
- MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题
MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题 参考文章: (1)MUI - 解决弹出输入法时页面高度变小导致底部上浮的问题 (2)https://www.cnblogs.com/phil ...
- Activiti保存.png 流程图片文件且解决idea中保存图片时显示中文乱码的解决方法
Activiti保存.png 流程图片文件且解决idea中保存图片时显示中文乱码的解决方法 Eclipse 工具中的操作 流程图片生成的两种方式: 使用 activiti-designer 设计流程图 ...
- 解决Pycharm中import时无法识别自己写的程序(转载)
我们用pycharm打开自己写的代码,当多个文件之间有相互依赖的关系的时候,import无法识别自己写的文件,但是我们写的文件又确实在同一个文件夹中,这种问题可以用下面的方法解决: 1)打开File– ...
- 解决helix.toolkit obj模型贴图查找失败导致的异常
前言 helix.toolkit封装了模型加载的操作,由于按照了标准模型的读取,出现材质或者贴图找不到的情况,容易模型加载异常.因此解决办法是,如果相关资源没找到的话,默认赋上默认材质来解决. 附上本 ...
- 登录时提示“配置文件写入失败”解决办法
操作步骤:删除config.xml文件,重启智能POS,配置登录信息. 文件位置:文件存储-winboxcash/config.xml 转载于:https://www.cnblogs.com/yhbc ...
- 使用libjpeg处理图像(libjpeg的使用压缩与解压缩jpg格式)
转载篇: 标题:利用libjpeg处理图像 作者:赵新国 Email:zhao3728@sina.com 关键字: jpeg, 图像压缩 摘要:文章介绍了采用libjpeg处理图像的方法 前一段时间做 ...
- debian9宝塔面板安装php失败,宝塔面板安装php失败:提示No package 'libjpeg' found的解决办法...
这篇文章主要为大家详细介绍了宝塔面板安装php失败:提示No package 'libjpeg' found的解决办法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,有需要的朋友可以收藏方便以后借 ...
- libjpeg php,使用GD和libjpeg支持编译PHP
我编译自己的PHP,部分是为了更多地了解PHP如何组合在一起,部分原因是我总是发现我需要默认情况下不可用的模块,这样我可以控制它. 我的问题是,我无法在PHP中获得JPEG支持.使用CentOS 5. ...
最新文章
- 手把手教你写移动端瀑布流控件布局篇
- Nginx 动静态访问分离
- API 类和面向对象简介
- Java 8 Friday Goodies:Lambda和XML
- 简单阻容降压电路图_升压降压芯片电路
- Python总结:RuntimeError: matplotlib does not support generators as input
- aop阻止方法运行_新型AOP高级氧化高浓度COD废水处理系统介绍
- 交什么样的朋友有什么样的天
- oracle删除数据库表空间
- evolving checkers players [Fogel and Chellapilla, 2002]
- VMare Workstation 12 安装 AsteriskNow freePBX
- ABAP 出库单打印 产品 A搭A A搭B显示方式
- 百度地图街景图像批量获取
- shiro 原理简介
- Android 适配器 自定义
- [luogu3505][bzoj2088][POI2010]TEL-Teleportation【分层图】
- 做跨境电商,用信用卡通道还是Paypal收款比较便宜?
- 【经典算法实现 44】理解二维FFT快速傅里叶变换 及 IFFT快速傅里叶逆变换(迭代法 和 递归法)
- 2020年高教社建模国赛真题B题--穿越沙漠
- Argo-DCS数据传输笔记