2

我正在尝试用 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();
    });
}

通过按下按钮调用方法。知道可能出了什么问题吗?

4

1 回答 1

2

您需要 Close()/Dispose() 绘图会话。所以在 C# 版本中你有:

using(var ds = renderer.CreateDrawingSession()) {
    ...
    ds.DrawImage(blur);
}

走出使用的范围,在绘图会话上调用 Close()。在 C++/CX 中,您使用“删除 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));

此页面包含有关“删除”和 IDisposable 的更多信息。 https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh699870.aspx

The reason that you're seeing this sometimes work and sometimes not work is because when the ds goes out of scope it is also closed. Sometimes this happens before the SaveAsync call has grabbed the D2D lock, and sometimes it happens after. The net result here is that SaveAsync either saves the contents of the rendertarget before the blur has been drawn to it or it saves the contents after the blur has been drawn.

于 2015-12-15T01:03:14.893 回答