Win2d 模糊在 C++ 中并不总是有效
Win2d blur doesn't always work in c++
我正在尝试用 C++ 编写 WP 应用程序,但遇到模糊 .png 图像的奇怪问题。它有时有效,有时无效。当它不起作用时,它看起来好像图像是不可见的。只是为了确保我在 C# 中实现了相同的东西并且它工作得很好。这是我的 C# 代码
private async void blurDefaultAvatar() {
try {
var storageFile = await Package.Current.InstalledLocation.GetFileAsync("Assets\menu_user.png");
using(var imgStream = await storageFile.OpenReadAsync()) {
using(var stream = await getBlurredImageStreamWithStream(imgStream, CanvasBitmapFileFormat.Png)) {
var bitmap = new BitmapImage();
bitmap.SetSource(stream);
blurredAvatar.Source = bitmap;
}
}
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine("Avatar load fail: {0}", e.Message);
}
}
private async Task<IRandomAccessStream> getBlurredImageStreamWithStream(IRandomAccessStream stream, CanvasBitmapFileFormat format) {
try {
var device = new CanvasDevice();
var bitmap = await CanvasBitmap.LoadAsync(device, stream);
var renderer = new CanvasRenderTarget(device, bitmap.SizeInPixels.Width, bitmap.SizeInPixels.Height, bitmap.Dpi);
using(var ds = renderer.CreateDrawingSession()) {
var blur = new GaussianBlurEffect();
blur.BlurAmount = 30.0f;
blur.Source = bitmap;
ds.DrawImage(blur);
}
var imgStream = new InMemoryRandomAccessStream();
await renderer.SaveAsync(imgStream, format);
return imgStream;
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine("Avatar blur fail: {0}", e.Message);
return null;
}
}
或多或少(我希望)等同于 c++
void MainPage::blurDefaultAvatar(){
concurrency::create_task(Package::Current->InstalledLocation->GetFileAsync(L"Assets\menu_user.png")).then([](concurrency::task<StorageFile^> t){
try{
auto storageFile = t.get();
return concurrency::create_task(storageFile->OpenReadAsync());
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nAvatar not found: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return concurrency::create_task(concurrency::create_async([]()->IRandomAccessStreamWithContentType^{ return nullptr; }));
}
}, concurrency::task_continuation_context::use_current()).then([this](concurrency::task<IRandomAccessStreamWithContentType^> t){
try{
auto imgStream = t.get();
concurrency::create_task(getBlurredImageStreamWithStream(imgStream, CanvasBitmapFileFormat::Png)).then([this](IRandomAccessStream^ stream){
if(stream!=nullptr && stream->Size>0){
auto bitmap = ref new BitmapImage();
bitmap->SetSource(stream);
blurredAvatar->Source = bitmap;
}
});
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nAvatar failed to read: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
}
});
}
IAsyncOperation<IRandomAccessStream^>^ MainPage::getBlurredImageStreamWithStream(IRandomAccessStream^ stream, CanvasBitmapFileFormat format){
return concurrency::create_async([stream, format]() -> IRandomAccessStream^{
auto imgStream = ref new InMemoryRandomAccessStream();
auto device = ref new CanvasDevice();
return concurrency::create_task(CanvasBitmap::LoadAsync(device, stream)).then([stream, device, format, imgStream](concurrency::task<CanvasBitmap^> t){
try {
auto bitmap = t.get();
auto renderer = ref new CanvasRenderTarget(device, bitmap->SizeInPixels.Width, bitmap->SizeInPixels.Height, bitmap->Dpi);
auto ds = renderer->CreateDrawingSession();
auto blur = ref new GaussianBlurEffect();
blur->BlurAmount = 30.0f;
blur->Source = bitmap;
ds->DrawImage(blur);
return concurrency::create_task(renderer->SaveAsync(imgStream, format));
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nBitmap load fail: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return concurrency::create_task(concurrency::create_async([]()->void{}));
}
}, concurrency::task_continuation_context::use_current()).then([imgStream](concurrency::task<void> t){
try{
t.get();
return imgStream;
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nStream save fail: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return (InMemoryRandomAccessStream^)nullptr;
}
}).get();
});
}
只需按一下按钮即可调用方法。知道哪里出了问题吗?
您需要关闭()/处置() 绘图会话。所以在 C# 版本中你有:
using(var ds = renderer.CreateDrawingSession()) {
...
ds.DrawImage(blur);
}
离开 using 的范围在绘图会话中调用 Close()。在 C++/CX 中,您使用 "delete ds" 调用 Close()。所以:
auto ds = renderer->CreateDrawingSession();
auto blur = ref new GaussianBlurEffect();
blur->BlurAmount = 30.0f;
blur->Source = bitmap;
ds->DrawImage(blur);
delete ds; // <<<<<---- add this
return concurrency::create_task(renderer->SaveAsync(imgStream, format));
此页面包含有关 'delete' 和 IDisposable 的更多信息。
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh699870.aspx
你看到这个有时有效有时无效的原因是当 ds 超出范围时它也会关闭。有时这发生在 SaveAsync 调用获取 D2D 锁之前,有时发生在之后。这里的最终结果是 SaveAsync 要么在绘制模糊之前保存渲染目标的内容,要么在绘制模糊之后保存内容。
我正在尝试用 C++ 编写 WP 应用程序,但遇到模糊 .png 图像的奇怪问题。它有时有效,有时无效。当它不起作用时,它看起来好像图像是不可见的。只是为了确保我在 C# 中实现了相同的东西并且它工作得很好。这是我的 C# 代码
private async void blurDefaultAvatar() {
try {
var storageFile = await Package.Current.InstalledLocation.GetFileAsync("Assets\menu_user.png");
using(var imgStream = await storageFile.OpenReadAsync()) {
using(var stream = await getBlurredImageStreamWithStream(imgStream, CanvasBitmapFileFormat.Png)) {
var bitmap = new BitmapImage();
bitmap.SetSource(stream);
blurredAvatar.Source = bitmap;
}
}
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine("Avatar load fail: {0}", e.Message);
}
}
private async Task<IRandomAccessStream> getBlurredImageStreamWithStream(IRandomAccessStream stream, CanvasBitmapFileFormat format) {
try {
var device = new CanvasDevice();
var bitmap = await CanvasBitmap.LoadAsync(device, stream);
var renderer = new CanvasRenderTarget(device, bitmap.SizeInPixels.Width, bitmap.SizeInPixels.Height, bitmap.Dpi);
using(var ds = renderer.CreateDrawingSession()) {
var blur = new GaussianBlurEffect();
blur.BlurAmount = 30.0f;
blur.Source = bitmap;
ds.DrawImage(blur);
}
var imgStream = new InMemoryRandomAccessStream();
await renderer.SaveAsync(imgStream, format);
return imgStream;
} catch(Exception e) {
System.Diagnostics.Debug.WriteLine("Avatar blur fail: {0}", e.Message);
return null;
}
}
或多或少(我希望)等同于 c++
void MainPage::blurDefaultAvatar(){
concurrency::create_task(Package::Current->InstalledLocation->GetFileAsync(L"Assets\menu_user.png")).then([](concurrency::task<StorageFile^> t){
try{
auto storageFile = t.get();
return concurrency::create_task(storageFile->OpenReadAsync());
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nAvatar not found: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return concurrency::create_task(concurrency::create_async([]()->IRandomAccessStreamWithContentType^{ return nullptr; }));
}
}, concurrency::task_continuation_context::use_current()).then([this](concurrency::task<IRandomAccessStreamWithContentType^> t){
try{
auto imgStream = t.get();
concurrency::create_task(getBlurredImageStreamWithStream(imgStream, CanvasBitmapFileFormat::Png)).then([this](IRandomAccessStream^ stream){
if(stream!=nullptr && stream->Size>0){
auto bitmap = ref new BitmapImage();
bitmap->SetSource(stream);
blurredAvatar->Source = bitmap;
}
});
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nAvatar failed to read: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
}
});
}
IAsyncOperation<IRandomAccessStream^>^ MainPage::getBlurredImageStreamWithStream(IRandomAccessStream^ stream, CanvasBitmapFileFormat format){
return concurrency::create_async([stream, format]() -> IRandomAccessStream^{
auto imgStream = ref new InMemoryRandomAccessStream();
auto device = ref new CanvasDevice();
return concurrency::create_task(CanvasBitmap::LoadAsync(device, stream)).then([stream, device, format, imgStream](concurrency::task<CanvasBitmap^> t){
try {
auto bitmap = t.get();
auto renderer = ref new CanvasRenderTarget(device, bitmap->SizeInPixels.Width, bitmap->SizeInPixels.Height, bitmap->Dpi);
auto ds = renderer->CreateDrawingSession();
auto blur = ref new GaussianBlurEffect();
blur->BlurAmount = 30.0f;
blur->Source = bitmap;
ds->DrawImage(blur);
return concurrency::create_task(renderer->SaveAsync(imgStream, format));
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nBitmap load fail: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return concurrency::create_task(concurrency::create_async([]()->void{}));
}
}, concurrency::task_continuation_context::use_current()).then([imgStream](concurrency::task<void> t){
try{
t.get();
return imgStream;
} catch(Exception^ e){
std::wstringstream wss;
wss<<"\nStream save fail: '"<<e->Message->Data()<<"'\n";
OutputDebugString(wss.str().c_str());
return (InMemoryRandomAccessStream^)nullptr;
}
}).get();
});
}
只需按一下按钮即可调用方法。知道哪里出了问题吗?
您需要关闭()/处置() 绘图会话。所以在 C# 版本中你有:
using(var ds = renderer.CreateDrawingSession()) {
...
ds.DrawImage(blur);
}
离开 using 的范围在绘图会话中调用 Close()。在 C++/CX 中,您使用 "delete ds" 调用 Close()。所以:
auto ds = renderer->CreateDrawingSession();
auto blur = ref new GaussianBlurEffect();
blur->BlurAmount = 30.0f;
blur->Source = bitmap;
ds->DrawImage(blur);
delete ds; // <<<<<---- add this
return concurrency::create_task(renderer->SaveAsync(imgStream, format));
此页面包含有关 'delete' 和 IDisposable 的更多信息。 https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh699870.aspx
你看到这个有时有效有时无效的原因是当 ds 超出范围时它也会关闭。有时这发生在 SaveAsync 调用获取 D2D 锁之前,有时发生在之后。这里的最终结果是 SaveAsync 要么在绘制模糊之前保存渲染目标的内容,要么在绘制模糊之后保存内容。