使用WIC有效地编码多个图像

时间:2012-09-23 00:02:31

标签: c++ image encoding wic

我正在使用Windows Imaging Component库对大量图像进行编码。理想情况下,我想用某些属性设置一次编码器,然后为我的所有图像重新使用该编码器。然而,在我看到的所有示例中,似乎为单个图像创建了编码器。

我正在读取和写入字节流,而不是文件,并且可能有多个线程同时运行。

以下是一段代码:

CComPtr<IWICBitmapEncoder> pEncoder;
CComPtr<IWICBitmapFrameEncode> pBitmapFrame;
CComPtr<IPropertyBag2> pPropertyBag;
CComPtr<IWICStream> pStream;
CComPtr<IStream> pOutputStream;

HRESULT hr;
//  Setup memory stream, which is needed to stage raw image bits
if (CreateStreamOnHGlobal(NULL, TRUE, &pOutputStream) != S_OK)
{
    LogAssert(false, "Could not create pOutputStream. Err (%d)", GetLastError());
}
//Setup WIC stream which encapsulates the output stream
hr = m_pFactory->CreateStream(&pStream);

hr = pStream->InitializeFromIStream(pOutputStream);
hr = m_pFactory->CreateEncoder(GUID_ContainerFormatWmp, NULL, &pEncoder);
hr = pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
hr = pEncoder->CreateNewFrame(&pBitmapFrame, &pPropertyBag);
SetEncodingProperties(pPropertyBag);
hr = pBitmapFrame->Initialize(pPropertyBag);

问题1: 我正在写一个使用CreateStreamOnHGlobal创建的IStream。我可以为多个图片重复使用pStreampOutputStream吗?任何线程安全问题?

问题2: 这段代码的哪一部分可以完成一次,哪些部分需要针对不同的图像重复?所有初始化似乎都相互关联。

1 个答案:

答案 0 :(得分:1)

所有内置WIC对象在Windows 7中都已更新为线程安全的,如下所述:http://msdn.microsoft.com/en-us/library/ee720061%28v=vs.85%29.aspx#_multi_threaded_apartment_support

在以前的Windows版本中,对象不是线程安全的,Windows会自动将调用封送到另一个线程,以便可以从多个线程访问它。这只有在你正确初始化COM时才有效 - 如果你要从多个线程访问对象,你需要从可以访问你的对象的所有线程调用CoInitializeEx和多线程选项,你需要确保CoInitializeEx返回成功( S_OK或S_FALSE)。

一次为单个IStream对象提供两个不同的编码器是不安全的。一旦你在编码器对象上调用了Commit,就可以安全地在其他地方使用流,但是将它与其他编码器一起使用并没有用。您不能在单个文件中包含多个图像(除非图像格式支持多个帧,但您只需要一个编码器对象)。我想你可以在将流大小设置为0之前将其设置为0,但是分配HGLOBAL流可能不会比这更快。

在你的情况下创建一个IWICStream是没有意义的,因为你已经有了一个IStream,这就是编码器所需要的。 IWICStream主要作为便利函数存在,用于从文件,固定大小的内存缓冲区或现有流的一部分创建流。 InitializeFromIStream方法存在的原因是IWICStream不支持Clone方法。 InitializeFromIStream是一种解决方法,它可以在不支持Clone的情况下使用独立游标获取新的IStream对象,但它执行此操作的方式不是(也不可能)是线程安全的。 (为了使其工作,基础流必须一次只能由一个线程访问。通常,编码器或解码器对象将确保这一点,只要流一次仅分配给一个编码器/解码器。 )

由于你关注性能,你应该知道,像WIC编码器那样,写入HGLOBAL流是O(n ** 2),因为扩大HGLOBAL涉及将所有现有数据复制到一个新的位置。

如果您要在Windows 7之前从多个线程访问WIC,我建议使用单线程公寓,并确保只在该线程中访问在一个线程中初始化的对象。这将节省将调用编组到另一个线程的成本。

应该可以使用您的设置保存IPropertyBag2对象并使用它来初始化所有位图帧,只要编码器类是相同的并且您不打算使用来自多个单线程公寓的相同的(或单线程和多线程公寓)。

但是,当你应该更关心编写图像数据的过程时,我认为你太过担心会对性能产生微不足道的影响。使用不必一直复制内存的流类型(可能在幕后使用固定大小的内存缓冲区,但在适当时可以报告较小的大小),如果您的图像文件很大(可能会有帮助) ,如果你正在使用非常大的图像,你应该考虑使用imagemagick)。给每个线程完全独​​立的对象(作为单个对象很可能一次只能在一个线程中工作)也可能有所帮助。