关于GDI+的那些事(3)——在内存中将bmp数据压缩为jpeg

GDI+是支持各种常见图片格式的,自带各种编解码器。它可以很方便的从各种格式创建image,也可以将image保存为各种格式的磁盘文件或流。


出于以下两点原因,我封了一个jpeg压缩类(修改下应该也可用于其他格式):

1,同上篇一样,有时会需要在内存中直接操作,比如知道某bmp图片数据部分(不包含bmp文件头)在内存中的头指针和size,需要转换为jpg,同样保存在某段内存中。

2,省去了找第三方Jpeg库或自己写压缩解压库的麻烦。虽然效率可能一般,主要图个方便。


以下为代码,include了几个必须的头,实际项目中注意改掉,使用的时候定义对象,start,convert,finish就好了:

#include 
#include 
using namespace Gdiplus;

//在内存中利用GDI+将BMP数据转换为JPEG数据的类
class GDIplusEncoder
{
	IStream *pStmBMP;
	IStream *pStmJPG;
	Image *image;

	CLSID encoderClsid;
	EncoderParameters encoderParameters;
	ULONG quality;	

	BITMAPFILEHEADER fileHeader;
	BITMAPINFOHEADER bitmapHeader;

    int GetEncoderClsid(const WCHAR* format, CLSID* pClsid); 
    DWORD GetStreamSize(IStream *Stm);

public:
	BYTE *outbuffer;    //存放压缩结果的地址
	ULONG jpegsize;     //压缩结果的size

	GDIplusEncoder();

	void Start(DWORD maxsize);
	void Finish();

	void ConvertToJpeg(BYTE *src);
};

//copy来的获取编码器CLSID函数
//CLSID即多用途网际邮件扩充协议类型标识,具体神马玩意我也不清楚,反正压缩要用到
int GDIplusEncoder::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)   
{   
    UINT num = 0;		// number of image encoders   
    UINT size = 0;		// size of the image encoder array in bytes   
    ImageCodecInfo* pImageCodecInfo = NULL;   
    GetImageEncodersSize(&num, &size);   
    if(size == 0)   
        return -1;		//   Failure   
    
    pImageCodecInfo =  (ImageCodecInfo*)(malloc(size));   
    if(pImageCodecInfo == NULL)   
        return -1;		//   Failure   
    
    GetImageEncoders(num, size, pImageCodecInfo);   
    for(UINT j = 0; j < num; ++j)   
    {   
        if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )   
        {   
            *pClsid = pImageCodecInfo[j].Clsid;   
            free(pImageCodecInfo);   
            return j;	//   Success   
        }           
    }   
    free(pImageCodecInfo);   
    return -1;			//   Failure   
}

//计算流size
DWORD GDIplusEncoder::GetStreamSize(IStream *Stm)
{
	ULARGE_INTEGER begin;
	ULARGE_INTEGER end;
	LARGE_INTEGER zero;
	zero.QuadPart = 0;
	DWORD size;

	Stm->Seek(zero, STREAM_SEEK_SET, &begin);
	Stm->Seek(zero, STREAM_SEEK_END, &end);
	Stm->Seek(zero, STREAM_SEEK_SET, NULL);

	size = DWORD(end.QuadPart - begin.QuadPart);

	return size;
}

GDIplusEncoder::GDIplusEncoder()
{
	GetEncoderClsid(L"image/jpeg", &encoderClsid);
	encoderParameters.Count = 1;
	encoderParameters.Parameter[0].Guid = EncoderQuality;
	encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
	encoderParameters.Parameter[0].NumberOfValues = 1;
	quality = 70;   //压缩质量参数,可根据需要设置
	encoderParameters.Parameter[0].Value = &quality;

	// Part.1 Create Bitmap File Header
	fileHeader.bfType		= 0x4D42;
	fileHeader.bfSize		= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 256*256*3;
	fileHeader.bfReserved1	= 0;
	fileHeader.bfReserved2	= 0;
	fileHeader.bfOffBits	= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	// Part.2 Create Bitmap Info Header
	bitmapHeader.biSize		= sizeof(BITMAPINFOHEADER); 
	bitmapHeader.biWidth	= 256;
	bitmapHeader.biHeight	= 256;
	bitmapHeader.biPlanes	= 1;
	bitmapHeader.biBitCount	= 24;
	bitmapHeader.biCompression = 0;		//BI_RGB
	bitmapHeader.biSizeImage= 256*256*3;
}

//maxsize为压缩后得到jpg图片的最大大小,例如:100K
void GDIplusEncoder::Start(DWORD maxsize)
{
	outbuffer = new BYTE[maxsize];
}

void GDIplusEncoder::Finish()
{
	if(outbuffer)
	{
		delete outbuffer;
		outbuffer = NULL;
	}
}

void GDIplusEncoder::ConvertToJpeg( BYTE *src)
{
	CreateStreamOnHGlobal(NULL, TRUE, &pStmBMP);	
	pStmBMP->Write(&fileHeader, sizeof(BITMAPFILEHEADER), NULL);
	pStmBMP->Write(&bitmapHeader, sizeof(BITMAPINFOHEADER), NULL);
	pStmBMP->Write(src, 256*256*3, NULL);

	image = image->FromStream(pStmBMP, 0);
	
	CreateStreamOnHGlobal(NULL, TRUE, &pStmJPG);
	image->Save( pStmJPG, &encoderClsid, &encoderParameters);

	jpegsize = GetStreamSize(pStmJPG);
	pStmJPG->Read(outbuffer, jpegsize, NULL);

	if(pStmBMP) pStmBMP->Release();
	if(pStmJPG) pStmJPG->Release();
	image->~Image();
}



你可能感兴趣的:(Windows)