如何从 SoftwareBitmap (UWP) 正确释放 BitmapBuffer?

Posted

技术标签:

【中文标题】如何从 SoftwareBitmap (UWP) 正确释放 BitmapBuffer?【英文标题】:How to properly Release BitmapBuffer from SoftwareBitmap (UWP)? 【发布时间】:2018-08-21 09:15:32 【问题描述】:

我很难在 UWP 上的 C++/CX 代码中正确释放使用 SoftwareBitmap.LockBuffer() 方法锁定的 BitmapBuffer

基本代码如下所示(它是来自 Microsoft 的 OpenCV 桥接示例,可用 here。

bool OpenCVHelper::GetPointerToPixelData(SoftwareBitmap^ bitmap, unsigned char** pPixelData, unsigned int* capacity)

   BitmapBuffer^ bmpBuffer = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite);
   IMemoryBufferReference^ reference = bmpBuffer->CreateReference();

   ComPtr<IMemoryBufferByteAccess> pBufferByteAccess;
   if ((reinterpret_cast<IInspectable*>(reference)->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess))) != S_OK)
   
    return false;
   

   if (pBufferByteAccess->GetBuffer(pPixelData, capacity) != S_OK)
   
    return false;
   
   return true;

然后使用此缓冲区 (pPixelData) 来初始化 cv:Mat 对象(浅拷贝)。并且永远不会被释放。

在同一个SoftwareBitmap 对象上连续调用LockBuffer() 引发异常:

Platform::AccessDeniedException ^ 在内存位置 0x00000002CEEFDCC0。 HRESULT:0x80070005 访问被拒绝。 WinRT 信息:位图共享锁被占用

如何正确释放这个缓冲区?尤其是在 C++/CX 中?

我试图保留引用以在不再需要时释放它。在 C++/CX 中,Dispose() 或 Close() 方法不可访问,编译器建议改为调用析构函数:

BitmapBuffer^ bmpBuffer = nullptr;
// ... get the buffer, use it
//((IDisposable^)bmpBuffer)->Dispose();
bmpBuffer->~BitmapBuffer();

但它不起作用(什么都不做)。正在调用析构函数,但对 LockBuffer() 的另一个调用引发了与以前相同的错误。

【问题讨论】:

我不确定你为什么需要明确释放pPixelData 缓冲区。但是如果你想这样做,你可以在它创建的地方释放它,并在这个函数OpenCVHelper::TryConvert在这一行创建它:unsigned char* pPixels = nullptr;。你可以像这样在pPixels = nullptr; 之前释放它return true;。其实它是一个局部变量,在函数结束后会自动释放。 不是pPixelData,而是bmpBuffer(BitmapBuffer)。原因是要释放通过 LockBuffer() 方法获取的锁。否则它只能工作一次(我在文档中没有找到任何信息,一旦你使用 LockBuffer() 就无法释放它)。 【参考方案1】:

如何从 SoftwareBitmap (UWP) 中正确释放 BitmapBuffer?

BitmapBufferIMemoryBufferReference 完成工作后,可以通过调用delete 表达式来关闭这些对象。更多详情请查看Destructors。例如:

SoftwareBitmap^ bitmap = ref new SoftwareBitmap(
    BitmapPixelFormat::Bgra8,
    100,
    200,
    BitmapAlphaMode::Premultiplied);
BitmapBuffer^ bmpBuffer = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite); 
IMemoryBufferReference^ reference = bmpBuffer->CreateReference();
delete reference;
delete bmpBuffer; 
BitmapBuffer^ bmpBuffer2 = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite);

如上代码sn-p所示,删除BitmapBuffer对象后,可以再次成功锁定缓冲区。

【讨论】:

在您的示例中,两个 LockBuffers 恰好位于同一范围内。通常,在 Pawels 的情况下,不需要手动删除对象。 BitmapBuffer^ 引用一旦超出范围就会自动释放,当没有更多引用时,对象本身就会被删除。 Pawel 的代码必须在某处保留引用。考虑一下,一旦底层对象被强行销毁,这些会发生什么。

以上是关于如何从 SoftwareBitmap (UWP) 正确释放 BitmapBuffer?的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 UWP C# StorageFile 以将 SoftwareBitmap 存储到特定路径

如何裁剪矩形的软件位图 - UWP

WinRT C++ (Win10) 从 SoftwareBitmap / BitmapBuffer 访问字节

将 VideoFrame 转换为字节数组

如何从 UWP 项目调用 WPF 项目中存在的函数

如何从本地文件夹uwp显示图像