1. 程式人生 > >libjpeg-turbo使用例項(編解碼jpeg、jpg轉bmp、bmp轉jpg程式碼)

libjpeg-turbo使用例項(編解碼jpeg、jpg轉bmp、bmp轉jpg程式碼)

libjpeg-turbo庫用於jpeg影象編解碼,上一節說了編譯過程:編譯libjpeg-turbo 。現在說說jpeg的編碼、解碼使用方法。

Windows上GDI介面支援的都是點陣圖格式(DDB\DIB)影象,這裡只說bmp編碼成jpeg格式圖片並儲存到本地和jpeg解碼成bmp格式並儲存到本地。

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;
}
傳入讀取的bmp檔案二進位制資料,輸出編碼後的jpeg流和大小。

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

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;
}
unsigned long lOutSize = nWidthBits*cInfo.image_height 通過jpeg圖片的尺寸計算DIB資料區大小,通常是高度*寬度*畫素位元組數。

和編碼一樣,解碼時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圖片後,儲存成功