這幾天網上看的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//如果不想連續壓縮的話可以釋放壓縮對象