版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://rtfsc.blogbus.com/logs/23148378.html
不论是进行压缩时还是解压缩时,都需要用到FILE,使得我们如果想在内存中直接压缩或解压缩图像还要自己实现相应的结构,
总之,比较麻烦,尤其对初学者,更是不知从何处入手,幸运的是,libjpeg给我们提供了源代码,今天我就为大家介绍,怎样修改源代码,
使libjpeg可以非常容易的直接处理内存中的图像,而无需借助文件操作。
一、建立自己的libjpeg工程
二、分析并修改源代码
然后我们用内存拷贝的方式替换掉所有的文件操作(I/O),也就实现了内存中进行图像压缩和解压缩的目标。
1
2
3
4
5
11
12
13
14
15
16
17
18
19
20
21
我们只要找到所有引用outfile的函数,就可以知道libjpeg是怎样压缩图像到文件的,因此,我们继续搜outfile,搜索结果如下:
Find all "outfile", Subfolders, Find Results 1, "Entire Solution"
已经见过了,我们只需要把这八处改了就可以实现我们的目标了。如下:
EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, char* outdata)); // 由EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile));改写
char * outdata;
jdatadst.cpp文件第87行empty_output_buffer (j_compress_ptr cinfo)函数
memcpy(dest->outdata,dest->buffer,OUTPUT_BUF_SIZE);// 由JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE)改写
jdatadst.cpp文件第114行term_destination (j_compress_ptr cinfo)
memcpy(dest->outdata,dest->buffer,datacount);
删除fflush(dest->outfile);和if (ferror(dest->outfile))及相关的其它语句。
peg_stdio_dest (j_compress_ptr cinfo, char* outdata)
dest->outdata = outdata;
另外,由于只有到压缩完才能知道图像压缩完后的数据量大小,我们还需要一个指示图像数据大小的变量。
typedef struct {
} my_destination_mgr;
我们将通过jpeg_stdio_dest函数提供pSize指针,并在jpeg_stdio_dest的实现函数里对新添加的变量进行初始化,如下:
GLOBAL(void)
jpeg_stdio_dest (j_compress_ptr cinfo, char * outdata, int *pSize)
{
}
改写声明函数
EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo, char* outdata, int *pSize));
jdatadst.cpp文件第87行empty_output_buffer (j_compress_ptr cinfo)函数
memcpy(dest->outdata+dest->nOutOffset,dest->buffer,OUTPUT_BUF_SIZE);// 由JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE)改写
dest->nOutOffset+=OUTPUT_BUF_SIZE;
*(dest->pSize)=dest->nOutOffset;
jdatadst.cpp文件第114行term_destination (j_compress_ptr cinfo)
memcpy(dest->outdata+dest->nOutOffset,dest->buffer,datacount);
dest->nOutOffset+=datacount;
*(dest->pSize)=dest->nOutOffset;
重新编译工程,这样我们就实现了压缩bmp位图到内存中,当然,调用jpeg_stdio_dest之前,我们需要先分配足够的内存,并把内存指针传递给jpeg_stdio_dest函数,
好了,我们再分析libjpeg在解压缩jpg图像时,是怎样从jpg文件读入图像数据的。
我们先看我们在解压缩图像时调用的与文件操作有关的函数,如下:
jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile)。
在该函数的实现代码中找到了my_src_ptr结构,并且,我们发现与文件操作有关的该结构的成员变量为infile,参考上面内容,我们搜索infile,搜索结果如下:
Find all "infile", Subfolders, Find Results 1, "Entire Solution"
根据上面的经验,我们考虑,除了将FILE *类型变量改为char *类型的变量外,还要添加两个变量,图像大小的变量及图像偏移量,这跟图像压缩时差不多,所不同的是,
图像压缩时,图像大小是由libjpeg库返回,所以在调用是提供给libjpeg库的是个指针,而在解压缩时,图像数据大小是由调用者通过变量(不是指针)提供给libjpeg库。
由于我详细讲解了图像压缩时的我们所做的工作,我想读者朋友们很容易就能理解解压缩时所做的更改,下面我只列出我们所改写的代码,就不再详细讲解了。
jpeglib.h 第911行
EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, char * indata,int nSize));
jdatasrc.cpp 第33行
typedef struct {
} my_source_mgr;
jdatasrc.cpp 第183行
GLOBAL(void)
jpeg_stdio_src (j_decompress_ptr cinfo, char * indata, int nSize)
{
}
jdatasrc.cpp 第91行
METHODDEF(boolean)
fill_input_buffer (j_decompress_ptr cinfo)
{
}
至此,libjpeg库的源代码中所有要改的东西我们都已经完成,剩下的事情就是我们编写一段测试程序测试一下。
三、编写测试代码
进行图像压缩和解压缩的步骤,在编写本例的测试代码时,我们在上次提供的测试代码的基础上进行改进,如下:
调用原来的libjpeg库时,需要为这两个函数提供已经打开的jpg文件句柄,而对于新的libjpeg库,不需要打开jpg文件了,压缩时,
我们需要提供足够大的内存区给libjpeg 库,解压缩时,只需要把存放有jpeg格式图像的内存区提供给libjpeg库就行了,下面详细介绍
对于改写后的jpeg_stdio_dest和jpeg_stdio_src这两个函数的调用方法。
我们在libjpeg库内没有对该内存区进行越界访问检查并且要足够大,否则会出现内存越界访问的危险,当整个图像压缩工作完成后,pSize
返回jpg图像数据的大小。测试代码如下:
生成的数据,nSize 当然是这个jpg数据的大小。测试代码如下:
测试图像时出现的图像倾斜的问题,好了,编译并运行一下测试程序,看看效果吧,如果愿意继续交流,请给我发邮
收藏到: Del.icio.us
转自http://my.unix-center.net/~Simon_fu/?p=565
熟悉libjpeg的朋友都知道libjpeg是一个开源的库。Linux和Android都是用libjpeg来支持jpeg文件的,可见其功能多么强大。但是默认情况下libjpeg只能处理jpeg文件的解码,或者把图像编码到jpeg文件。在嵌入式设备中没有文件系统也是很正常的事情,难道我们就不能利用libjpeg的强大功能了吗?当然不是!本文将会介绍怎样扩展libjpeg让其能够解码内存中的 jpeg数据。
struct jpeg_source_mgr {
constJOCTET * next_input_byte;
size_t bytes_in_buffer;
JMETHOD(void, init_source, (j_decompress_ptr cinfo));
JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo));
JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes));
JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired));
JMETHOD(void, term_source, (j_decompress_ptr cinfo));
};
可以看出source manager对象可以注册多个回调函数来对数据进行读写。在看jdatasrc.c中的代码:
typedefstruct {
structjpeg_source_mgr pub;
FILE * infile;
JOCTET * buffer;
boolean start_of_file;
} my_source_mgr;
该文件为jpeglib的source manger初始化和管理的地方。上面的数据结构是内部使用的源数据。可以看出其源数据只支持文件输入(infile变量),并提供缓存功能(buffer变量)。
EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo, FILE * infile));
通过这个接口我们可以看出它的source manager只能接收文件作为输入。该函数的实现在jdatasrc.c文件中。
typedefstruct {
UINT8* img_buffer;
UINT32 buffer_size;
UINT32 pos;
}BUFF_JPG;
typedefstruct {
structjpeg_source_mgr pub;
union{
BUFF_JPG jpg;
VFS_FILE * infile;
};
JOCTET * buffer;
boolean start_of_file;
} my_source_mgr;
可以看出我们通过union来支持内存数据(jpg变量)或者文件输入。因为需要负责读写必须要标识出当前内存读写的位置,所以必须要在BUFF_JPG数据结构中定义pos变量。
METHODDEF(boolean)
jpg_fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
size_t nbytes;
if(src->jpg.img_buffer == NULL || src->jpg.pos >= src->jpg.buffer_size){
nbytes = -1;
}
else{
nbytes = (src->jpg.pos + INPUT_BUF_SIZE > src->jpg.buffer_size ? /
src->jpg.buffer_size - src->jpg.pos : INPUT_BUF_SIZE);
MEMCPY(src->buffer, src->jpg.img_buffer + src->jpg.pos, nbytes);
src->jpg.pos += nbytes;
}
if(nbytes <= 0) {
if(src->start_of_file)
ERREXIT(cinfo, JERR_INPUT_EMPTY);
WARNMS(cinfo, JWRN_JPEG_EOF);
src->buffer[0] = (JOCTET) 0xFF;
src->buffer[1] = (JOCTET) JPEG_EOI;
nbytes = 2;
}
src->pub.next_input_byte = src->buffer;
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
returnTRUE;
}
可以看出我们读取数据都是从内存缓存中读取,如果到达缓存末尾就返回-1。
METHODDEF(void)
skip_input_data (j_decompress_ptr cinfo,long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
if(num_bytes > 0) {
while(num_bytes > (long ) src->pub.bytes_in_buffer) {
num_bytes -= (long) src->pub.bytes_in_buffer;
(void) fill_input_buffer(cinfo);
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
请注意显示地调用了fill_input_buffer,而不是调用注册给source manager的回调函数。这样做是不严谨的,虽然只支持文件输入的情况下,这样写没有任何问题,但是如果我们增加其他的输入方式的话(比如内存数据输入),这样写将不会调用到我们注册给Source manager的fill_input_buffer回调函数。所以如上的代码修改为:
METHODDEF(void)
skip_input_data (j_decompress_ptr cinfo,long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
if(num_bytes > 0) {
while(num_bytes > (long ) src->pub.bytes_in_buffer) {
num_bytes -= (long) src->pub.bytes_in_buffer;
//(void) fill_input_buffer(cinfo);
(void) src->pub.fill_input_buffer(cinfo);
}
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
调用我们注册的回调函数来读取数据。
GLOBAL(void)
jpeg_stdio_buffer_src (j_decompress_ptr cinfo, UINT8 * buffer, UINT32 size)
{
my_src_ptr src;
if(cinfo->src == NULL) {
cinfo->src = (structjpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
SIZEOF(my_source_mgr));
src = (my_src_ptr) cinfo->src;
src->buffer = (JOCTET *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
INPUT_BUF_SIZE * SIZEOF(JOCTET));
}
src = (my_src_ptr) cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = jpg_fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart;
src->pub.term_source = term_source;
//src->infile = infile;
src->jpg.img_buffer = buffer;
src->jpg.buffer_size = size;
src->jpg.pos = 0;
src->pub.bytes_in_buffer = 0;
src->pub.next_input_byte = NULL;
}
通过该函数会发现:我们用户输入的buffer初始化了my_source_mgr,并用我们实现的回调函数 jpg_fill_input_buffer初始化了jpeg_source_mgr数据结构中的fill_input_buffer。这样每次 libjpeg读取数据就将会调用jpg_fill_input_buffer来读取内存jpeg数据了。
EXTERN(void) jpeg_stdio_buffer_src JPP((j_decompress_ptr cinfo, UINT8 * buffer, UINT32 size));
http://my.chinaunix.net/space.php?uid=16427792&do=blog&id=240220