canvas教程

UWP中的Direct2D

字号+ 作者:H5之家 来源:H5之家 2017-09-20 17:57 我要评论( )

UWP中的Direct2D, 原文:UWP中的Direct2D介绍 DirectX一直是Windows平台中高性能图形的代名词,自Win7开始,微软又推出了Direct2D技术,包装于Direct3D,但专注于

原文:UWP中的Direct2D

介绍

DirectX一直是Windows平台中高性能图形的代名词,自Win7开始,微软又推出了Direct2D技术,包装于Direct3D,但专注于2D图形,并且准备取代GDI这样的传统2D图形技术。对于Direct2D是怎么怎么好的具体描述,可以参考附录1.

不过Direct2D是基于COM技术的,看上去有些老旧的气息,而且是非托管的,似乎也和常见的.net语言有些隔阂。

不过微软也为我们提供了一个工具,一个跨越这一边界的工具,那就是SurfaceImageSource一族。该族中SurfaceImageSource继承自Windows.UI.Xaml.Media.ImageSourceVirtualSurfaceImageSource则继承自SurfaceImageSource,它们和BitmapSource在托管领域有着同样的地位。但同时这两个类又将触角伸到了COM的领域,分别可以query interface至ISurfaceImageSourceNativeIVirtualSurfaceImageSourceNative,再与Direct2D技术接轨。至此,XAML快速的界面技术,Direct2D高效的图形功能,得以合二为一。

本文将简单的介绍一下SurfaceImageSource的使用,为大家呈现一个高效图形应用的小例子(演示代码使用XAML和C++/CX)。

 

准备

代码主要是C++的(略有C++/CX扩展),因为要操作Direct2D和COM。大家可以根据需要自行包装自己的组件来调用。

用到了WIC(Windows Imaging Component)等技术,不过不是本文重点。

 

问题

熟悉WPF的读者可能想到,在classical desktop中使用的WPF,里面的一部分组件有一个神奇的属性,OpacityMask,利用它可以给控件的渲染显示加上一个蒙版,实现各种透明渐变和不规则轮廓等等。

不过到了UWP(更早的从Windows Store App出现开始),虽然大家写的还是一样的XAML,但是OpacityMask属性没了。估计是为了性能考虑吧,不给用这么繁琐的东西了。

但难免有时要用到这样的功能,我们可以依靠高效的Direct2D图形技术来实现它。

看看效果先:

技术分享

技术分享

 

托管的SurfaceImageSource

上文已经提到了,SurfaceImageSourceWindows.UI.Xaml.Media.ImageSource的子类,可以像使用BitmapSource一样使用它,赋给BitmatBrushImageSource什么的。这有点像WriteableBitmap,不过它却是操作Direct2D的入口。

 

我们先用XAML做一个这样的界面:

StarEllipse

Button的Tag记载的是用来做蒙版的图片,我们的例子里使用的图片,为了方便都是400*400的。并且这两个蒙版图片都是用黑白表示的。黑色表示没有,白色表示全有,灰色就是半透明了,操作的是Alpha通道。

 

技术分享

这分别是要显示的图片,和两种蒙版。

 

先看看MainPage声明了哪些成员:

Microsoft::WRL::ComPtr<IDXGIDevice> m_dxgiDevice; Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_d2dDeviceContext; Microsoft::WRL::ComPtr<IWICBitmapSource> m_img; // 这个就是要被蒙版处理的原始图片了

我们善用ComPtr,让C++的RAII机制(资源获取就是初始化)来帮我们实现简单的“垃圾回收”。

 

MainPage::CreateDevice函数中,我们初始化Direct2D的相关设备:

void MainPage::CreateDevice() { static D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; HRESULT hr; ComPtr<ID3D11Device> d3dDevice; HR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, D3D11_CREATE_DEVICE_BGRA_SUPPORT, // 注意,Direct2D画图是BGRA通道顺序,不是常见的RGB featureLevels, extent<decltype(featureLevels)>::value, // C++ type traits,获取array长度 D3D11_SDK_VERSION, &d3dDevice, nullptr, nullptr)); HR(d3dDevice.As(&m_dxgiDevice)); HR(D2D1CreateDevice(m_dxgiDevice.Get(), nullptr, &m_d2dDevice)); HR(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dDeviceContext)); }

 

然后我们看看主要的画图流程:

这是那两个有蒙版设置的button,它们的Tag属性记录了要应用的蒙版的路径。

void MainPage::OnButtonClick(Object^ sender, RoutedEventArgs^ e) { HRESULT hr; auto btn = safe_cast<Button^>(sender); auto maskPath = btn->Tag->ToString(); SurfaceImageSource^ sis = ComPtr<ISurfaceImageSourceNative> sisn; // 跨越托管和非托管的边界 // 转换成IUnknown*也可以 HR(reinterpret_cast<IInspectable*>(sis)->QueryInterface(IID_PPV_ARGS(&sisn))); HR(sisn->SetDevice(m_dxgiDevice.Get())); // 这里需要注意,尽管SurfaceImageSource本身是个ImageSource,但是我们也应该在ImageBrush的层面上完成操作。如果我们将ImageBrush留在XAML上,而只新建和替换(在之后的流程里)SurfaceImageSource,会发生SurfaceImageSource赋值给ImageSource后引用计数增加量,和将ImageSource设为nullptr后引用计数减少量不相等的情况,发生“内存泄漏”。 auto brush = ref new ImageBrush(); brush->ImageSource = sis; canvas->Fill = brush; Draw(sisn.Get(), maskPath->Data()); }

 

Draw函数。画图的操作我们需要在UI线程上完成:

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • HTML5+CSS+jQuery综合案例提高教程

    HTML5+CSS+jQuery综合案例提高教程

    2017-09-19 16:11

  • Developing realistic shaders in Arnold for Cinema 4d , Vol.

    Developing realistic shaders in Arnold for Cinema 4d , Vol.

    2017-09-19 11:03

  • Android实现界面实时更新

    Android实现界面实时更新

    2017-09-17 18:19

  • NView模板绘制原生列表教程

    NView模板绘制原生列表教程

    2017-09-15 09:00

网友点评