WinUI3开发_图像羽化

什么是羽化?

图像羽化是指将图像边缘进行渐变式透明化并融合背景当中,实现一种平滑的融合效果,一般应用在画中画,或者一张图插入到另外一种图当中。
在软件开发里也经常将图像羽化用于UI开发中,这么做能够起到美观作用
以WinUI 3的demo应用程序Gallery为例:
在这里插入图片描述
上图是使用了羽化,下图是关闭羽化效果:
在这里插入图片描述
可以看到没有羽化效果图像看起来平平无奇,与整个UI融合的并不好,看起来像是一个广告板,而使用了羽化效果会让图像看起来就是UI背景的一部分。

如何在WinUI3中使用羽化效果?

想要在WinUI3中实现羽化效果不能通过单纯的静态XML方式实现,需要静态与动态结合,使用代码方式实现,需要使用WinUI3较为底层的类:Visual,Visual里有一个Compositor模块,Compositor是合成器模块,它是用来创建管理每个控件的视觉、动画、合成效果例如:各种 Brush(画刷)动画(KeyFrameAnimation)Effect(效果)Surface(绘图表面)功能模块的工厂类,它可以从任何一个控件里获取到,然后生命周期由某个控件来管理,它属于较为底层的工厂模块,它必须使用某个控件的Compositor,它不能单独被创建,因为它的一些渲染之类的使用的是Composition Engine的组件,而该组件会使用DirectX来使用GPU完成绘制,每个控件都有一个DirectXComposition Engine组件来绘制自身Visual,所以Compositor一般是通过获取某个组件上的来实现具体功能,但它所提供的API不针对该组件,它只使用该组件的GPU相关的模块并且生命周期由该组件管理。
那么如何通过Compositor实现羽化效果呢?Compositor里有一个合成器模块:CompositionMaskBrush,该模块用于实现不规则图形,它接受两个参数:背景、Mask,它会根据Mask的透明度信息,调整背景上面的每个像素的透明度,也就是蒙版融合。

实现一个羽化效果

首先原始效果是这个样子的:
在这里插入图片描述
接下来我们要在这个img下方的边缘部分实现羽化效果,具体实现方式是首先创建一个Grid,并在里面放置一张图片控件和一个渐变的Rectangle控件

<Grid x:Name="Root1"  Width="400" Height="300" Margin="0, 100, 0, 40">
    <Image x:Name="BgImg" Source="C:\\Users\\stephen\\Downloads\\test.jpg" Stretch="UniformToFill" />
        <Rectangle x:Name="MaskRect">
        <Rectangle.Fill>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Color="#FF000000" Offset="0.0"/>
                <GradientStop Color="#00FFB4FF" Offset="1.0"/>
            </LinearGradientBrush>
        </Rectangle.Fill>
    </Rectangle>
</Grid>

Grid控件可以让每个控件大小保持一致并且XY都为0,这样后续作为Mark的时候可以更好的对齐。
增加之后效果如下:
在这里插入图片描述
那么这个时候就需要在代码里开始实现这个功能了,首先先获取GridCompositor,因为我们要在Grid里面绘制:

 auto compositor = ElementCompositionPreview::GetElementVisual(Root1()).Compositor();

然后获取GridVisual,这些目的是为了将融合后的图像直接替换原本的GridVsual,因为融合后的图像必须有个载体显示:

 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设置为ImageVisual,这样它就会实时获取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);

随后我们按照上面一样的步骤获取MaskSurface

 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);

最终运行效果:
在这里插入图片描述
可以看到实现与Gallery一样的下方边缘羽化效果,与整个软件背景融为一体了,效果比之前硬广告板那样的效果自然很多,该羽化效果不光可以应用在图像上,也可以应用在其它任何控件上,只要该控件的Visual不是透明的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

17岁boy想当攻城狮

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值