admin管理员组文章数量:1516870
什么是羽化?
图像羽化
是指将图像
边缘
进行
渐变式透明化
并融合背景当中,实现一种
平滑的融合效果
,一般应用在
画中画
,或者一
张图插入到另外一种图
当中。
在软件开发里也经常将
图像羽化用于UI开发中
,这么做能够起到
美观作用
。
以WinUI 3的demo应用程序
Gallery
为例:
如何在WinUI3中使用羽化效果?
想要在WinUI3中实现羽化效果不能通过单纯的静态XML方式实现,需要静态与动态结合,使用代码方式实现,需要使用WinUI3较为底层的类:
Visual
,Visual里有一个
Compositor
模块,Compositor是合成器模块,它是用来创建管理每个控件的视觉、动画、合成效果例如:
各种 Brush(画刷)
、
动画(KeyFrameAnimation)
、
Effect(效果)
、
Surface(绘图表面)
功能模块的工厂类,它可以从任何一个控件里获取到,然后生命周期由某个控件来管理,它属于较为底层的工厂模块,它必须使用某个控件的Compositor,它不能单独被创建,因为它的一些渲染之类的使用的是
Composition Engine
的组件,而该组件会使用
DirectX
来使用
GPU
完成绘制,每个控件都有一个
DirectX
的
Composition Engine
组件来绘制自身Visual,所以Compositor一般是通过获取某个组件上的来实现具体功能,但它所提供的API不针对该组件,它只使用该组件的GPU相关的模块并且生命周期由该组件管理。
那么如何通过
Compositor
实现羽化效果呢?Compositor里有一个合成器模块:
CompositionMaskBrush
,该模块用于实现不规则图形,它接受两个参数:背景、Mask,它会根据Mask的透明度信息,调整背景上面的每个像素的透明度,也就是蒙版融合。
实现一个羽化效果
首先原始效果是这个样子的:
<Gridx:Name="Root1"Width="400"Height="300"Margin="0, 100, 0, 40"><Imagex:Name="BgImg"Source="C:\\Users\\stephen\\Downloads\\test.jpg"Stretch="UniformToFill"/><Rectanglex:Name="MaskRect"><Rectangle.Fill><LinearGradientBrushStartPoint="0,0"EndPoint="0,1"><GradientStopColor="#FF000000"Offset="0.0"/><GradientStopColor="#00FFB4FF"Offset="1.0"/></LinearGradientBrush></Rectangle.Fill></Rectangle></Grid>
Grid控件可以让每个控件大小保持一致并且XY都为0,这样后续作为
Mark
的时候可以更好的对齐。
增加之后效果如下:
auto compositor =ElementCompositionPreview::GetElementVisual(Root1()).Compositor();
然后获取
Grid
的
Visual
,这些目的是为了将融合后的图像直接替换原本的
Grid
的
Vsual
,因为融合后的图像必须有个载体显示:
Visual rootVisual =ElementCompositionPreview::GetElementVisual(Root1());现在我们已经有了Grid的Compositor和Visual,接下来要再获取Image的Visual:
Visual bgVisual =ElementCompositionPreview::GetElementVisual(BgImg());
Visual是每个控件较为底层的一个类
,负责整个控件生命周期的
渲染
、
动画
、
一切可视化效果
,获取到Image的Visual之后我们使用compositor来创建一个
CompositionVisualSurface
,CompositionVisualSurface是一个表面工具功能,它是用于获取某个控件的表面图像,表面图像就是控件可视区域显示的图像,与截图一样。
CompositionVisualSurface bgSurface = compositor.CreateVisualSurface();
bgSurface.SourceVisual(bgVisual);{auto expr = compositor.CreateExpressionAnimation(L"v.Size");
expr.SetReferenceParameter(L"v", rootVisual);
bgSurface.StartAnimation(L"SourceSize", expr);}
然后将
SourceVisual
设置为
Image
的
Visual
,这样它就会实时获取Image的表面图像。
bgSurface.SourceVisual(bgVisual);注意调用它之后虽然会实时获取但是它获取时的size并不会改变,默认还是0x0,所以需要使用表达式动画,来让它能够实时根据控件大小变化而改变大小:
{auto expr = compositor.CreateExpressionAnimation(L"v.Size");
expr.SetReferenceParameter(L"v", rootVisual);
bgSurface.StartAnimation(L"SourceSize", expr);}
注意这里获取的是Grid的,这里解释一下为什么,在WinUI3里面有两种大小,一种是
Visual大小
,一种是
实际大小
,
Visual大小是你图像显示出来时能够客观看到的大小
,
而实际大小是真正的宽高,控件的宽高大小
,例如Image Visual大小是200x300,但实际大小是600x400,这个原因是因为你在布局控件里使用了它,并且使用
Height/Width
属性来控制控件大小(如果在布局控件里修改宽高属性仅修改
Visual
的,布局控件会强制改变它的实际宽高),但
布局控件会强制改变它的大小
,例如
Grid
,如果在
Image
使用了
UniformToFill
拉伸了它,那么它的
Visual
大小也会改变,就会导致我们实际根据宽高设置时会发现跟我们预想的不一样,所以这里使用
Grid
的宽高来作为实际应用的宽高。
接下来如果想要在别的地方使用它,例如合成、绘制到别处就需要使用
Brush
,所以为它创建一个
Brush
:
CompositionSurfaceBrush bgBrush = compositor.CreateSurfaceBrush(bgSurface);
随后我们按照上面一样的步骤获取
Mask
的
Surface
:
Visual maskVisual =ElementCompositionPreview::GetElementVisual(MaskRect());
CompositionVisualSurface maskSurface = compositor.CreateVisualSurface();
maskSurface.SourceVisual(maskVisual);{auto expr = compositor.CreateExpressionAnimation(L"v.Size");
expr.SetReferenceParameter(L"v", maskVisual);
maskSurface.StartAnimation(L"SourceSize", expr);}
CompositionSurfaceBrush maskBrush = compositor.CreateSurfaceBrush(maskSurface);最后我们就可以使用CompositionMaskBrush来合成了,它接受两个参数一个是Source和Mask,Source就是背景,Mask就是蒙版,会根据Mask上面的透明度信息来调整Source的像素透明度:
CompositionMaskBrush maskEffect = compositor.CreateMaskBrush();
maskEffect.Source(bgBrush);
maskEffect.Mask(maskBrush);Tips
需要值得注意的是MaskBrush在融合的时候Mask的坐标从左上(0, 0)开始,并且它使用的是实际大小作为
最后图像已经融合好了,那么就要将它设置到Grid表面去了,接下来就要使用SpriteVisual了,SpriteVisual是每个控件元素里的画布,也就是实际显示内容的模块,在WinUI3里面每个控件都以树形结构表示,控件里有许多子节点,其中SpriteVisual就是用来控制要显示的内容的。
SpriteVisual sprite = compositor.CreateSpriteVisual();
sprite.RelativeSizeAdjustment(float2{1.f,1.f});
sprite.Brush(maskEffect);
RelativeSizeAdjustment
是用来设置显示时的缩放程度,0~1是Float类型,通过Brush给它设置一个新的画刷。
最后通过SetElementChildVisual来给控件增加一个渲染子节点SpriteVisual,由于Grid的Visual根节点本身就是透明的什么都没有,所以新增的子节点不会被Grid本身的渲染内容所遮挡:
ElementCompositionPreview::SetElementChildVisual(Root1(), sprite);改变之后Grid可视区域就变成了刚刚融合的图像,由于Grid内部有控件会遮挡所以这里需要将这两个控件隐藏掉:
BgImg().Opacity(0);MaskRect().Opacity(0);
最终运行效果:
版权声明:本文标题:掌握图像处理新技巧 - WinUI3中的图像羽化实践 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.betaflare.com/web/1772319670a3273510.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。


发表评论