Direct2D教程(十二)图层
什么是Layers?
Layer,中文译成图层,在Direct2D中可以用来完成一些特殊效果,使用Layer的时候,先将Layer Push到render target,然后进行绘制,此时是直接绘制在Layer上的,绘制完毕后,将Layer Pop出来,刚刚绘制在Layer上的内容就会组合到render target上。在Direct2D中,Layer使用接口ID2D1Layer来表示。
和画刷一样,Layer由render target创建,属于设备相关的资源,Layer可以用于任何render target上,只要二者在相同的资源作用域内,在同一时间内,Layer只能用于一个render target。
尽管Layer为创建特效提供了强大的技术支持,但是过度使用Layer将导致D2D程序性能下降,因为操作Layer是比较耗时的,比如清除Layer上的内容,将Layer上绘制的内容混合到render target上都需要时间。
使用Layer的步骤
在Direct2D中,使用Layer的步骤非常简单,首先创建一个Layer,在创建的时候,会指定一系列Layer的属性,在绘制之前,先将Layer Push到当前的render target上,然后进行绘制操作,绘制完毕后将Layer Pop出来即可。
创建Layer
创建Layer使用CreateLayer函数,该函数定义如下:
- virtual HRESULT CreateLayer(
- [in, optional] D2D1_SIZE_F *size,
- [out] ID2D1Layer **layer
- ) = 0;
参数说明:
size,这是一个矩形区域,定义了创建的Layer的大小(像素尺寸),通常这个值设置为NULL,当调用PushLayer函数时,会自动为Layer分配最小的所需尺寸(通常是render target的尺寸)。
layer,这是一个ID2D1Layer**类型的变量,用来接收创建好的Layer。
创建Layer代码如下。

- ID2D1Layer* g_pLayer = NULL ;
- // Create layer
- hr = g_pRenderTarget->CreateLayer(NULL, &g_pLayer) ;
- if (FAILED(hr))
- {
- MessageBox(hWnd, "Create layer failed!", "Error", 0) ;
- return ;
- }

Push Layer
在BeginDraw函数调用完之后,并且在具体绘制开始之前,将Layer Push到render target。
PushLayer函数定义如下:
- void PushLayer(
- const D2D1_LAYER_PARAMETERS &layerParameters,
- [in] ID2D1Layer *layer
- );
参数说明:
layerParameters,这是一个D2D1_LAYER_PARAMETERS类型的变量,它可以指定一系列Layer属性,D2D1_LAYER_PARAMETERS结构体定义如下:

- struct D2D1_LAYER_PARAMETERS {
- D2D1_RECT_F contentBounds;
- ID2D1Geometry *geometricMask;
- D2D1_ANTIALIAS_MODE maskAntialiasMode;
- D2D1_MATRIX_3X2_F maskTransform;
- FLOAT opacity;
- ID2D1Brush *opacityBrush;
- D2D1_LAYER_OPTIONS layerOptions;
- };

参数说明
- contentBounds,这是一个矩形区域,指定了Layer的绘制范围,也就是说在该范围之外的内容都不会被绘制。
- geometricMask,这是一个几何图形,它指定了Layer的哪一部分被显示,比如你可以设置一Path Geometry,然后绘制一个位图,这样,只有PathGeometry内部的位图会显示,相当于给几何图形加上了位图纹理。
- maskAntialiasMode,这个暂时用不到,不理它。
- maskTransform,这是一个变换矩阵,指定了施加在第二个参数上的几何变换。
- opacity,这是一个透明值,指定了Layer与render target混合时使用的透明值。
- opacityBrush,这是一个画刷,用来改变Layer的透明值。
- layerOptions,一个D2D1_LAYER_OPTIONS变量,基本用不到,从windows 8开始,使用D2D1_LAYER_OPTIONS1来代替。
Direct2D提供了一个函数LayerParameters来初始化上面这些参数,这个函数为上面每个参数都提供了一个默认值,如下:

- D2D1_LAYER_PARAMETERS LayerParameters(
- _In_ const D2D1_RECT_F &contentBounds = D2D1::InfiniteRect(),
- _In_opt_ ID2D1Geometry *geometricMask = NULL,
- D2D1_ANTIALIAS_MODE maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
- D2D1_MATRIX_3X2_F maskTransform = D2D1::IdentityMatrix(),
- FLOAT opacity = 1.0,
- ID2D1Brush *opacityBrush = NULL,
- D2D1_LAYER_OPTIONS layerOptions = D2D1_LAYER_OPTIONS_NONE
- );

在这里,我们使用这个函数创建一个Layer,在随后的介绍中,会指定某些特定的参数来达到特殊的效果。
- g_pRenderTarget->PushLayer(
- D2D1::LayerParameters(),
- g_pLayer
- ) ;
绘制
这里可以绘制任意内容,几何图形,文本,图片等,为了演示Layer的特殊效果,这里通常绘制位图。
Pop Layer
绘制完毕后,将Layer Pop出来,这样已经在Layer上绘制的内容就会与render target上的内容混合。
Demo
下面几个Demo都是通过位图来演示的,原始的位图如下,三个可爱的企鹅。
限定位图绘制区域
在PushLayer的时候指定一个矩形区域,在这个区域内的图形将被显示,而这个区域外的图形都将被屏蔽掉,为了方便查看结果,这里绘制一个位图,可以清楚的看到位图的那一部分被绘制了。
代码

- g_pRenderTarget->BeginDraw() ;
- g_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
- g_pRenderTarget->PushLayer(
- D2D1::LayerParameters(D2D1::RectF(100, 100, 400, 400)),
- g_pLayer
- ) ;
- D2D1_SIZE_F size = g_pBitmap->GetSize() ;
- D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(0.f, 0.f) ;
- // Draw bitmap
- g_pRenderTarget->DrawBitmap(
- g_pBitmap,
- D2D1::RectF(
- upperLeftCorner.x,
- upperLeftCorner.y,
- upperLeftCorner.x + size.width,
- upperLeftCorner.y + size.height)
- ) ;
- // Pop layer before EndDraw
- g_pRenderTarget->PopLayer() ;
- g_pRenderTarget->EndDraw() ;

效果图
透明效果
在PushLayer调用完之后,先绘制一个位图,然后再绘制三个矩形,这三个矩形使用绿颜色的画刷来绘制,所以最终的结果就是位图和矩形混合后的结果,可以看到矩形呈透明状,创建绿色画刷的代码省略。
代码

- g_pRenderTarget->BeginDraw() ;
- g_pRenderTarget->PushLayer(D2D1::LayerParameters(), g_pLayer) ;
- g_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
- D2D1_SIZE_F size = g_pBitmap->GetSize() ;
- D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(0.f, 0.f) ;
- // Draw bitmap
- g_pRenderTarget->DrawBitmap(
- g_pBitmap,
- D2D1::RectF(
- upperLeftCorner.x,
- upperLeftCorner.y,
- upperLeftCorner.x + size.width,
- upperLeftCorner.y + size.height)
- ) ;
- // Opacity mask 1
- g_pRenderTarget->FillRectangle(D2D1::RectF(100, 100, 200, 200), g_pBlackBrush) ;
- // Opacity mask 2
- g_pRenderTarget->FillRectangle(D2D1::RectF(200, 200, 300, 300), g_pBlackBrush) ;
- // Opacity mask 3
- g_pRenderTarget->FillRectangle(D2D1::RectF(300, 300, 400, 400), g_pBlackBrush) ;
- // Pop layer before EndDraw
- g_pRenderTarget->PopLayer() ;
- g_pRenderTarget->EndDraw() ;

效果图
圆形渐变画刷
先创建一个圆形的渐变色画刷,调用PushLayer的时候将这个画刷作为参数传递给PushLayer的第一个参数。接下来绘制一个位图,当调用PopLayer的时候两次绘制的内容将被混合到一起,最后形成一个夜视镜的效果。
代码

- g_pRenderTarget->BeginDraw() ;
- // Clear background color to white
- g_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
- g_pRenderTarget->PushLayer(
- D2D1::LayerParameters(
- D2D1::InfiniteRect(),
- NULL,
- D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
- D2D1::IdentityMatrix(),
- 1.0f,
- g_pRadialGradientBrush,
- D2D1_LAYER_OPTIONS_NONE),
- g_pLayer
- );
- D2D1_SIZE_F size = g_pBitmap->GetSize() ;
- D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(0.f, 0.f) ;
- // Draw bitmap
- g_pRenderTarget->DrawBitmap(
- g_pBitmap,
- D2D1::RectF(
- upperLeftCorner.x,
- upperLeftCorner.y,
- upperLeftCorner.x + size.width,
- upperLeftCorner.y + size.height)
- ) ;
- // Pop layer before EndDraw
- g_pRenderTarget->PopLayer() ;
- g_pRenderTarget->EndDraw() ;

效果图
最后一个例子代码下载。
== Happy Coding ==
Direct2D教程(十二)图层的更多相关文章
- CRL快速开发框架系列教程十二(MongoDB支持)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- 无废话ExtJs 入门教程十二[下拉列表联动:Combobox_Two]
无废话ExtJs 入门教程十二[下拉列表联动:Combobox_Two] extjs技术交流,欢迎加群(201926085) 不管是几级下拉列表的联动实现本质上都是根据某个下拉列表的变化,去动态加载其 ...
- webpack4 系列教程(十二):处理第三方JavaScript库
教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十二):处理第三方 JavaScript 库>原文地址.或者来我的小站看更多内容:godbm ...
- RabbitMQ入门教程(十二):消息确认Ack
原文:RabbitMQ入门教程(十二):消息确认Ack 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csd ...
- WebGL简易教程(十二):包围球与投影
目录 1. 概述 2. 实现详解 3. 具体代码 4. 参考 1. 概述 在之前的教程中,都是通过物体的包围盒来设置模型视图投影矩阵(MVP矩阵),来确定物体合适的位置的.但是在很多情况下,使用包围盒 ...
- Direct2D教程(二)来看D2D世界中的Hello,World
引子 任何一门语言的第一个教程几乎都是Hello,world.我们也不例外,但是这里不是教大家打印Hello,world,而是编写一个简单的D2D绘制程序,让大家对Direct2D的程序结构及编程方法 ...
- WPF教程十二:了解自定义控件的基础和自定义无外观控件
这一篇本来想先写风格主题,主题切换.自定义配套的样式.但是最近加班.搬家.新租的房子打扫卫生,我家宝宝6月中旬要出生协调各种的事情,导致了最近精神状态不是很好,又没有看到我比较喜欢的主题风格去模仿的, ...
- Redis教程(十二):服务器管理命令总结
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/140.html 一.概述: Redis在设计之初就被定义为长时间不间断运行 ...
- struts2.1.6教程十二、总结
本教程对struts2的基本知识进行了一些说明,关于struts2的更多详细内容应参看struts2的官方文档及提供的app实例. 下面对struts2的基本执行流程作一简要说明,此流程说明可以结合官 ...
- Spring Boot系列教程十二:Spring boot集成Redis
一.创建项目 项目名称为 "springboot_redis",创建过程中勾选 "Web","Redis",第一次创建Maven需要下载依赖 ...
随机推荐
- python基础学习笔记——列表及元组
列表 列表的介绍 列表是python的基础数据类型之一 ,其他编程语言也有类似的数据类型. 列表的索引和切片 列表和字符串一样也拥有索引: lst = ['刘德华','周润发','周杰伦','向华强 ...
- java8新特性:接口的默认方法与静态方法
接口中一共可以定义三种方法: 1.抽象方法,也就是需要实现者必须实现的方法,最常见的那种 2.默认方法,不需要实现者实现 3.静态方法,不需要实现者实现 默认方法: 允许在已有的接口中添加新方法,而同 ...
- 计算n的阶乘(n!)末尾0的个数
题目: 给定一个正整数n,请计算n的阶乘n!末尾所含有“0”的个数. 举例: 5!=120,其末尾所含有的“0”的个数为1: 10!= 3628800,其末尾所含有的“0”的个数为2: 20!= 24 ...
- PostgreSQL order by 排序问题
默认的排序为order by 字段名, 如果该字段不允许为空的情况下可以这样操作, 但是当字段允许为null时,order by 字段名的方式会导致: 升序时(asc): 会从最小值开始升序,最后面接 ...
- [automator篇][9] 列表,找孩子
private boolean ClickByCollInfo(int CLICK, String classname, String id, String text) { UiSelector ui ...
- linux 下java jar包的方法
test.java 和 example.jar 在同一目录:并且test.java引用了example.jar 首先编译test.java: javac -cp example.jar test. ...
- 解决11g r2,12c使用wm_concat报错问题
创建type CREATE OR REPLACE TYPE zh_concat_im AUTHID CURRENT_USER AS OBJECT ( CURR_STR ), STATIC FUNCTI ...
- HDU——2068RPG的错排(错排公式)
RPG的错排 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...
- HDU-2768 Cat vs. Dog
题意一开始是理解错的...结果就各种WA啦~ 对于两个观众,假如有某只宠物,一个人讨厌另一个人却喜欢,这两个人就是有矛盾的,连边. 最后求最小顶点覆盖.因为把这个覆盖点集去掉的话剩下的图中没有两个点是 ...
- openstack是什么?能干什么?
openstack是什么?能干什么?涉及的初衷是什么?由什么来组成?刚接触openstack,说openstack不是一个软件,而是由多个组件进行组合,这是一个更深层次的理解,当我们看到dashboa ...