libjpeg-turbo使用例項(編解碼jpeg、jpg轉bmp、bmp轉jpg程式碼)
阿新 • • 發佈:2019-02-08
libjpeg-turbo庫用於jpeg影象編解碼,上一節說了編譯過程:編譯libjpeg-turbo 。現在說說jpeg的編碼、解碼使用方法。
Windows上GDI介面支援的都是點陣圖格式(DDB\DIB)影象,這裡只說bmp編碼成jpeg格式圖片並儲存到本地和jpeg解碼成bmp格式並儲存到本地。
bmp轉jpeg
傳入讀取的bmp檔案二進位制資料,輸出編碼後的jpeg流和大小。int Bmp2Jpeg_Compress(void* lpBmpBuffer, int nWidth, int nHeight, OUT void** ppJpegBuffer, OUT unsigned long* pOutSize) { jpeg_compress_struct toWriteInfo; jpeg_error_mgr errorMgr; toWriteInfo.err = jpeg_std_error(&errorMgr); //註冊失敗的回撥函式 toWriteInfo.err->error_exit = error_exit; jpeg_create_compress(&toWriteInfo); //儲存壓縮後的圖片 //FILE* fp = NULL; //_wfopen_s(&fp, L"c:\\output.jpg", L"wb+"); //jpeg_stdio_dest(&toWriteInfo, fp); //確定要用於輸出壓縮的jpeg的資料空間 jpeg_mem_dest(&toWriteInfo, (unsigned char**)ppJpegBuffer, pOutSize); toWriteInfo.image_width = nWidth; toWriteInfo.image_height = nHeight; toWriteInfo.jpeg_width = nWidth / 2; toWriteInfo.jpeg_height = nHeight / 2; toWriteInfo.input_components = 4;// 在此為1,表示灰度圖, 如果是彩色點陣圖,則為4 toWriteInfo.in_color_space = JCS_EXT_BGRA; //JCS_GRAYSCALE表示灰度圖,JCS_RGB表示彩色影象 jpeg_set_defaults(&toWriteInfo); jpeg_set_quality(&toWriteInfo, 100, TRUE); //設定壓縮質量100表示100% jpeg_start_compress(&toWriteInfo, TRUE); int nRowStride = nWidth*4; // 如果不是索引圖,此處需要乘以4 JSAMPROW row_pointer[1]; // 一行點陣圖 while (toWriteInfo.next_scanline < toWriteInfo.image_height) { row_pointer[0] = (JSAMPROW)((unsigned char*)lpBmpBuffer + toWriteInfo.next_scanline*nRowStride); jpeg_write_scanlines(&toWriteInfo, row_pointer, 1); } jpeg_finish_compress(&toWriteInfo); jpeg_destroy_compress(&toWriteInfo); return 0; }
input_components指定為4個位元組,目前的Windows支援的都是32位點陣圖;nRowStride = nWidth*4 表示每一行點陣圖資料的位元組數,每個畫素4位元組;還有一個關鍵的地方in_color_space = JCS_EXT_BGRA,涉及到Windows中資料在記憶體中的排列方式(little-endian, 低位元組存放在記憶體的低位)ARGB在記憶體中為BGRA(PS 我嘗試過使用JCS_EXT_ARGB,結果顏色全部取反了)。
編碼完成後,直接寫入檔案即可儲存為jpeg檔案:
//libjpeg為我們壓縮好了jpeg資料,只需要往檔案裡面寫入即可 FILE* fpOut = NULL; fopen_s(&fpOut, "c:\\out.jpg", "wb+"); if (fp) { fwrite(pOutBuffer, 1, lOutSize, fpOut); fclose(fpOut); }
jpeg轉DIB
unsigned long lOutSize = nWidthBits*cInfo.image_height 通過jpeg圖片的尺寸計算DIB資料區大小,通常是高度*寬度*畫素位元組數。int Jpeg2DIB_DeCompress(void* lpJpegBuffer, unsigned long nInSize, OUT void** ppDibBuffer, OUT unsigned long* pOutSize, OUT int* pWidth, OUT int* pHeight) { jpeg_decompress_struct cInfo; jpeg_create_decompress(&cInfo); jpeg_error_mgr errorMgr; cInfo.err = jpeg_std_error(&errorMgr); cInfo.err->error_exit = error_exit; jpeg_mem_src(&cInfo, (const unsigned char*)lpJpegBuffer, nInSize); jpeg_read_header(&cInfo, TRUE); jpeg_start_decompress(&cInfo); JSAMPROW row_pointer[1]; int nBitCounts = cInfo.num_components * 8; int nWidthBits = cInfo.image_width*cInfo.num_components;// ((cInfo.image_width*nBitCounts + 31) >> 5) << 2; unsigned long lOutSize = nWidthBits*cInfo.image_height; unsigned char* pOutBuffer = (unsigned char*)malloc(lOutSize); row_pointer[0] = pOutBuffer; while (cInfo.output_scanline<cInfo.output_height) { row_pointer[0] = pOutBuffer + (cInfo.image_height - cInfo.output_scanline-1)*nWidthBits; jpeg_read_scanlines(&cInfo, row_pointer, 1); } jpeg_finish_decompress(&cInfo); jpeg_destroy_decompress(&cInfo); *ppDibBuffer = pOutBuffer; *pOutSize = lOutSize; *pWidth = abs((int)cInfo.image_width); *pHeight = abs((int)cInfo.image_height); return 0; } int Jpeg2DIB_DeCompress2(const char* pFile, OUT void** ppDibBuffer, OUT unsigned long* pOutSize, OUT int* pWidth, OUT int* pHeight) { FILE* fp = NULL; fopen_s(&fp, pFile, "rb"); if (NULL == fp) return -1; fseek(fp, 0, SEEK_END); long lSize = ftell(fp); fseek(fp, 0, SEEK_SET); void* lpBuffer = malloc(lSize); if (NULL == lpBuffer) return -1; size_t nRead = fread(lpBuffer, 1, lSize, fp); fclose(fp); int nRet = Jpeg2DIB_DeCompress(lpBuffer, nRead, ppDibBuffer, pOutSize, pWidth, pHeight); free(lpBuffer); return nRet; }
和編碼一樣,解碼時jpeg庫也是一行一行進行的迴圈呼叫 jpeg_read_scanlines。
這樣解碼後,並不是真正的DIB資料,因為cInfo.num_components=3,也就是說解碼的資料是RGB的,每個畫素佔用3個位元組。我們需要再次轉換成ARGB的Windows上支援的DIB格式。
//RGB to ARGB
unsigned long nDestSize = nWidth * 4 * nHeight;
DWORD* pArgbData = (DWORD*)malloc(nDestSize);
DWORD* pArgbDataTemp = pArgbData;
unsigned char* pRgbData = (unsigned char*)lpOutData;
int nOffset = nOutSize-3, i = 0;
while (nOffset>=0)
{
/*注意,在window系統中記憶體以little-endian儲存,即低位元組存放在記憶體的低位 0xARGB -- 0xBGRA
/除去忽略的A 即alpha通道位 讀取記憶體中的資料為 BGR 需要轉換成 RGB
/bmp點陣圖會忽略掉alpha通道位,設定成任意數值都可以以
*/
DWORD dwColor = 0x00000000 + RGB(pRgbData[nOffset+2], pRgbData[nOffset + 1], pRgbData[nOffset]);
*pArgbDataTemp = dwColor;
pArgbDataTemp++;
nOffset -= 3;
}
迴圈轉換後,得到的就是標準的DIB資料了,可以呼叫GDI API建立與之關聯的點陣圖控制代碼,然後貼圖繪製到介面上。
BITMAP bmp = { 0 };
bmp.bmWidth = nWidth;
bmp.bmHeight = nHeight;
bmp.bmWidthBytes = nWidth * 4;
bmp.bmPlanes = 1;
bmp.bmBitsPixel = 32;
bmp.bmBits = pArgbData;
HBITMAP hBitmap = CreateBitmapIndirect(&bmp);
還可以把DIB資料儲存到本地,bmp點陣圖影象。
BID儲存為點陣圖檔案
int SaveDIBToBmpFile(const char* pFile, void* pDibBuffer, unsigned long nBufferSize, int nWidth, int nHeight)
{
BITMAPFILEHEADER fHeader;
int nStructSize1 = sizeof(BITMAPFILEHEADER);
int nStructSize2 = sizeof(BITMAPINFO)-sizeof(RGBQUAD);
memset(&fHeader, 0, nStructSize1);
memcpy(&fHeader, "BM", 2);
fHeader.bfSize = nStructSize1 + nStructSize2 + nBufferSize;
fHeader.bfOffBits = nStructSize1 + nStructSize2;
BITMAPINFO bmpInfo = { nStructSize2 };
bmpInfo.bmiHeader.biWidth = nWidth;
bmpInfo.bmiHeader.biHeight = nHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = 0;
FILE* fp = NULL;
fopen_s(&fp, pFile, "wb+");
if (NULL == fp)
return -1;
/* 寫入 bmp點陣圖檔案,
* 檔案格式:點陣圖檔案頭+點陣圖資訊頭+點陣圖資料
* 寫入資料頭前,需要填充相應的欄位
* 詳細結構說明,見 http://blog.csdn.net/mfcing/article/details/7451670
*/
fwrite(&fHeader, 1, nStructSize1, fp);
fwrite(&bmpInfo, 1, nStructSize2, fp);
fwrite(pDibBuffer, 1, nBufferSize, fp);
fclose(fp);
return 0;
}
程式執行圖
jpeg圖片轉換成bmp圖片後,儲存成功