这几天网上看的libjpeg内存压缩讲的不是很清楚,自己就去看了下源文档,理解了调用原理才能实现符合程序的压缩方案,。
背景:对屏幕上的小矩行进行压缩
自定义协议:数据长度+RECT结构+jpeg数据部分
协议头
typedef struct tagBuffAreaHeader
{
int nBuffLength;
RECT rect;
}BuffAreaHeader;
自定义目标输出管理器
typedef struct {
struct jpeg_destination_mgr pub;//公共属性
unsigned char ** outbuffer; //每次剪辑后剪辑数据的首地址
unsigned long * outsize; //剪辑数据长度
unsigned char *newbuf; //新缓冲区
size_t offset; //偏移量
JOCTET * buffer; //整个缓冲区的首地址
size_t bufsize; //整个缓冲区的大小
} MyMemDesMgr,* MyMemDesMgrPtr;
libjpeg提供了一个接口使我们可以改写自己的输入或输出管理器,这里通过改写jdatadst.c文件实现自己的目标管理器。
头文件JpegMgr.h
//修改自JPEG library jdatadst.c 的init_destination,
//当调用jpeg_start_compress时调用此函数
//在这个函数里面我们为数据留出协议头的空间
METHODDEF(void) My_init_destination (j_compress_ptr cinfo);
//修改自JPEG library jdatadst.c 的term_mem_destination
//当调用jpeg_finish_compress是会调用此函数,而
//jpeg_abort 或者jpeg_destroy函数中不会调用,因此
//要使用“错误处理机制”来解决内存泄漏的问题
METHODDEF(void) My_term_destination (j_compress_ptr cinfo);
//修改自JPEG library jdatadst.c 的empty_output_buffer
//修改当压缩缓冲区中的内存不足是会调用此函数
METHODDEF(boolean) My_empty_output_buffer (j_compress_ptr cinfo);
//修改自JPEG library jdatadst.c 的jpeg_stdio_dest
//指定压缩对象的目标管理器为我们自定义的目标管理器
GLOBAL(MyMemDesMgrPtr)
My_jpeg_mem_dest (j_compress_ptr cinfo,unsigned char ** outbuffer, unsigned long * outsize);
源文件JpegMgr.cpp
#define INCREASE 1024*100
GLOBAL(MyMemDesMgrPtr)
My_jpeg_mem_dest (j_compress_ptr cinfo,unsigned char ** outbuffer, unsigned long * outsize)
{
MyMemDesMgrPtr dest;
if (outbuffer == NULL || outsize == NULL)
{
ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
}
//第一次初始化压缩对象
if (cinfo->dest == NULL) {
cinfo->dest = (struct jpeg_destination_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
SIZEOF(MyMemDesMgr));
}
//指定为自定义的接口函数
dest = (MyMemDesMgrPtr) cinfo->dest;
dest->pub.init_destination = My_init_destination;
dest->pub.empty_output_buffer = My_empty_output_buffer;
dest->pub.term_destination = My_term_destination;
//对传入成员变量的操作
dest->outbuffer = outbuffer;
dest->outsize = outsize;
if (*outbuffer == NULL || outsize == 0)
{
dest->newbuf = (unsigned char*)malloc(INCREASE);
if (dest->newbuf == NULL)
ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
outbuffer = dest->newbuf;
}
dest->pub.next_output_byte = dest->buffer = *outbuffer;
dest->pub.free_in_buffer = dest->bufsize = *outsize;
*outsize = 0;
return dest;
}
METHODDEF(void)
My_init_destination (j_compress_ptr cinfo)
{
MyMemDesMgrPtr dest = (MyMemDesMgrPtr) cinfo->dest;
//判断是否缓冲区足够留出协议头
if (dest->pub.free_in_buffer <= sizeof(BuffAreaHeader)+1)
{
size_t offset = dest->pub.free_in_buffer;
My_empty_output_buffer(cinfo);
dest->pub.next_output_byte -= offset;
dest->pub.free_in_buffer += offset;
}
dest->offset = dest->bufsize - dest->pub.free_in_buffer;
*dest->outbuffer = dest->buffer + dest->offset;
dest->pub.free_in_buffer -= sizeof(BuffAreaHeader);
dest->pub.next_output_byte += sizeof(BuffAreaHeader);
}
METHODDEF(boolean)
My_empty_output_buffer (j_compress_ptr cinfo)
{
MyMemDesMgrPtr dest = (MyMemDesMgrPtr) cinfo->dest;
//当缓冲区不足时以每次100k的速度增长
size_t newsize = dest->bufsize + INCREASE;
JOCTET* newbuf = (JOCTET*)malloc(newsize);
if (newbuf == NULL)
ERREXIT1(cinfo,JERR_OUT_OF_MEMORY,10);
MEMCOPY(newbuf, dest->buffer, dest->bufsize);
if (dest->newbuf != NULL)
free(dest->newbuf);
dest->newbuf = dest->buffer = newbuf;
dest->pub.next_output_byte = dest->buffer + dest->bufsize;
dest->pub.free_in_buffer = INCREASE;
dest->bufsize += INCREASE;
return TRUE;
}
METHODDEF(void)
My_term_destination (j_compress_ptr cinfo)
{
MyMemDesMgrPtr dest = (MyMemDesMgrPtr) cinfo->dest;
//计算剪辑长度
size_t offset = dest->bufsize - dest->pub.free_in_buffer;
*dest->outsize = offset - dest->offset;
*dest->outbuffer = dest->buffer + dest->offset;
}
自定义错误处理机制
//自定义错误处理机制
typedef struct {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
}my_error_mgr, *my_error_ptr;
METHODDEF(void)
my_error_exit (j_common_ptr cinfo);
错误处理函数
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
longjmp(myerr->setjmp_buffer, 1);
}
在初始化压缩对象的时候只需要调用
m_cinfo->err = jpeg_std_error(&m_jerr.pub);
m_jerr.pub.error_exit = my_error_exit;
设定成自定义的错误处理函数,实现压缩的时候只要不调用jpeg_abort 或 jpeg_destroy压缩产生的数据会放在一片连续的内存空间
协议头+jpeg数据+协议头+jpeg数据...............+协议头+jpeg数据,每次调用jpeg_finish_compress后通过在My_jpeg_mem_dest中传入的第二个和第三个参数可以得到指向每个协议头的地址和数据长度(包括协议头的长度)将数据据写进去。
压缩流程
Allocate and initialize a JPEG compression object//初始化压缩对象
Specify thedestination for the compressed data (eg, a file)//指定目标管理器为自定义的目标管理器,这里调用My_jpeg_mem_dest来指定
Setparameters for compression, including image size & colorspace//设定压缩参数宽度、高度、颜色模式等
jpeg_start_compress(...);//这里会在内部调用我们自定义的接口函数
while (scanlines remain to be written)
jpeg_write_scanlines(...);//直接从内存中读取bmp数据
jpeg_finish_compress(...);//这里会在内部调用我们自定义的接口函数
Release theJPEG compression object//如果不想连续压缩的话可以释放压缩对象