内存中图像压缩






4.70/5 (28投票s)
2003年8月16日
3分钟阅读

230962

3513
内存图像压缩/解压缩
引言
本文旨在展示一种加载位图/jpg/tiff/gif/png(任何你可以用 GDI+ 加载的格式)然后将其转换为位图/tiff/jpg/png(任何你可以用 GDI+ 保存的格式)并保存到内存中的一种可能的方法。本文不会教你任何关于图像压缩的有用知识,因为它使用 GDI+ 来完成这项工作。此外,在尝试此示例之前,请查看 CP 上的精彩教程,了解如何在你的计算机上运行 GDI+。这里提供的解决方案非常简单易懂,但不幸的是,它不是最优的。我想问题是,我们为什么需要它呢?这个问题的答案是,如果我们偶尔在程序中执行一项操作,但不是经常执行,我们更倾向于使用简单而非最优的解决方案,而不是复杂的、最优的解决方案(显然,每个人都有自己的偏好)。此外,本文面向初学者,因此你不需要完全了解位图和图像压缩,即可理解代码(我不会阻止你以后有机会学习它们)。
背景
老实说,我没有广泛地使用这段代码。这段代码是在我参考 MSDN 查找我一段时间没用过的某个函数时创建的。然后我遇到了函数 CreateStreamOnHGlobal(...)
,它立刻提醒我,我们可以将 GDI+ 位图对象保存到 IStream
对象。于是我再次把注意力从原来的项目上转移开了,甚至在我意识到之前,我已经在编写代码来验证这一事实了。此外,我记得很多次,当我在 CP 上阅读论坛时,我遇到了关于如何将图像压缩到内存中的问题。
使用代码
嗯,正如你所看到的,它并没有太多内容。有一个你可以复制并粘贴到你的代码中的单一函数。但在你使用它之前,你必须在你的应用程序中链接并启动 GDI+。同样,CP 上也有关于如何执行此操作的精彩教程。
// load image
Image image(L"Nice.bmp");
// Create stream with 0 size
IStream* pIStream = NULL;
if(CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&pIStream) != S_OK)
{
AfxMessageBox(_T("Failed to create stream on global memory!"));
return;
}
// Get encoder class id for jpg compression
// for other compressions use
// image/bmp
// image/jpeg
// image/gif
// image/tiff
// image/png
CLSID pngClsid;
GetEncoderClsid(L"image/jpeg", &pngClsid);
// Setup encoder parameters
EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = EncoderQuality;
encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
// setup compression level
ULONG quality = 50;
encoderParameters.Parameter[0].Value = &quality;
// Save the image to the stream
Status SaveStatus = image.Save(pIStream, &pngClsid, &encoderParameters);
if(SaveStatus != Ok)
{
// this should free global memory used by the stream
// according to MSDN
pIStream->Release();
AfxMessageBox(_T("Failed to save to stream!"));
return;
}
// get the size of the stream
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
if(pIStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize) != S_OK)
{
pIStream->Release();
AfxMessageBox(_T("Failed to get the size of the stream!"));
return;
}
// now move the pointer to the beginning of the file
if(pIStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) != S_OK)
{
pIStream->Release();
AfxMessageBox(_T("Failed to move the file pointer to "
"the beginning of the stream!"));
return;
}
// here you can do what ever you want
/*
1. You can use global memory
HGLOBAL hg;
if(GetHGlobalFromStream(pIStream, &hg) = S_OK)
... use hg for something
2. Copy it into some other buffer
char *pBuff = new char[ulnSize.QuadPart];
// Read the stream directly into the buffer
ULONG ulBytesRead;
if(pIStream->Read(pBuff, ulnSize.QuadPart, &ulBytesRead) != S_OK)
{
pIStream->Release();
return;
}
*/
// I am going to save it to the file just so we can
// load the jpg to a gfx program
CFile fFile;
if(fFile.Open(_T("test.jpg"), CFile::modeCreate | CFile::modeWrite))
{
char *pBuff = new char[ulnSize.QuadPart];
// Read the stream directly into the buffer
ULONG ulBytesRead;
if(pIStream->Read(pBuff, ulnSize.QuadPart, &ulBytesRead) != S_OK)
{
pIStream->Release();
delete pBuff;
return;
}
fFile.Write(pBuff, ulBytesRead);
fFile.Close();
delete pBuff;
}
else AfxMessageBox(_T("Failed to save data to the disk!"));
// Free memory used by the stream
pIStream->Release();
代码非常简单。首先,我加载图像,在这种情况下是 Nice.bmp。然后,我必须构造 IStream
对象,我们将使用它来将图像保存到其中。我们还需要设置图像编码器。请参考 MSDN 了解编码器参数的详细说明以及 GetEncoderClassID
函数。然后我们就可以保存了,一旦我们成功保存了数据,我们就可以使用流,它包含压缩后的图像,就像它在磁盘上一样。为了证明我正在将这些字节保存到磁盘上,以便我们可以将其加载到图形程序中。演示代码包含 nice.bmp,它是由演示软件压缩的。压缩后的图像应该在 MemImage 示例源代码文件夹中。
结论
如有任何问题、建议等,请与我联系,我将尽快尝试回复。此外,英语是我的第三语言,因此请随时给我提供改进写作技巧的建议。请记住,我不是 GDI+ 专家,我也不经常使用它。玩得开心!
许可证
本文档没有明确的许可证,但可能包含文章文本或下载文件本身的使用条款。如有疑问,请通过下方的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。