-
解决应用 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, 100, 1); // 图像质量,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(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 + 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, 100, 1); // 图像质量,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;
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;
int size;
f = fopen(filename, rb);
if (f == NULL) {
return NULL;
}
if (1 != 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
文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》