GDI+是支持各种常见图片格式的,自带各种编解码器。它可以很方便的从各种格式创建image,也可以将image保存为各种格式的磁盘文件或流。
出于以下两点原因,我封了一个jpeg压缩类(修改下应该也可用于其他格式):
1,同上篇一样,有时会需要在内存中直接操作,比如知道某bmp图片数据部分(不包含bmp文件头)在内存中的头指针和size,需要转换为jpg,同样保存在某段内存中。
2,省去了找第三方Jpeg库或自己写压缩解压库的麻烦。虽然效率可能一般,主要图个方便。
以下为代码,include了几个必须的头,实际项目中注意改掉,使用的时候定义对象,start,convert,finish就好了:
#include <Windows.h>
#include <GdiPlus.h>
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();
}