通过


合成画笔

WinUI 应用中屏幕上可见的所有内容都可见,因为它由画笔绘制。 画笔使你能够绘制用户界面(UI)对象,其内容从简单的纯色到图像、绘图和复杂的效果链。 本主题介绍使用 CompositionBrush 进行绘画的概念。

使用 WinUI XAML 应用时,可以选择使用 XAML 画笔CompositionBrush 绘制 UIElement。 通常,若已有 XAML 画笔支持您的方案,那么选择 XAML 画笔会更简单。 例如,对按钮的颜色进行动画处理,或更改带有图像的文本或形状的填充。 如果需要 XAML 画笔不支持的内容,例如动画掩码、动画九网格拉伸或效果链,则可以使用 CompositionBrush 通过 XamlCompositionBrushBase 绘制 UIElement。

使用视觉层时,必须使用 CompositionBrush 绘制 SpriteVisual 的区域。

先决条件

此概述假定你熟悉基本合成应用程序的结构,如 视觉层概述中所述。

使用 CompositionBrush 绘制

CompositionBrush“绘制”其输出的区域。 不同的画笔具有不同类型的输出。 某些画笔使用纯色绘制区域,另一些画笔使用渐变、图像、自定义绘图或效果绘制区域。 还有一些专用画笔可以修改其他画笔的行为。 例如,不透明度掩码可以用来控制 CompositionBrush 所绘制区域的可见性,而九宫格可以用来决定当 CompositionBrush 绘制某一区域时的拉伸效果。 CompositionBrush 可以是以下类型之一:

Class 详细信息
CompositionColorBrush 用纯色绘制区域
CompositionSurfaceBrush 使用 ICompositionSurface 的内容绘制一个区域
CompositionEffectBrush 用合成效果的内容为区域涂色
CompositionMaskBrush 使用具有不透明度掩码的 CompositionBrush 绘制视觉对象
CompositionNineGridBrush 使用 CompositionBrush 和 NineGrid 拉伸在一个区域进行绘制
CompositionLinearGradientBrush 绘制具有线性渐变的区域
CompositionRadialGradientBrush 绘制具有径向渐变的区域
CompositionBackdropBrush 通过对桌面上应用程序窗口后面的背景像素进行采样或应用程序中的像素进行采样来绘制区域。 用作另一个 CompositionBrush 的输入,例如 CompositionEffectBrush

使用纯色绘制

CompositionColorBrush 使用纯色绘制区域。 可通过多种方式指定 SolidColorBrush 的颜色。 例如,可以指定其 alpha、红色、蓝色和绿色(ARGB)通道,或使用 Colors 类提供的预定义颜色之一。

下图和代码显示了一个小的可视树,用于创建一个矩形,这个矩形用黑色画笔描边,并使用具有颜色值0x9ACD32的纯色画笔填充。

CompositionColorBrush

Compositor _compositor;
ContainerVisual _container;
SpriteVisual _colorVisual1, _colorVisual2;
CompositionColorBrush _blackBrush, _greenBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
_container = _compositor.CreateContainerVisual();

_blackBrush = _compositor.CreateColorBrush(Colors.Black);
_colorVisual1 = _compositor.CreateSpriteVisual();
_colorVisual1.Brush = _blackBrush;
_colorVisual1.Size = new Vector2(156, 156);
_colorVisual1.Offset = new Vector3(0, 0, 0);
_container.Children.InsertAtBottom(_colorVisual1);

_greenBrush = _compositor.CreateColorBrush(Color.FromArgb(0xff, 0x9A, 0xCD, 0x32));
_colorVisual2 = _compositor.CreateSpriteVisual();
_colorVisual2.Brush = _greenBrush;
_colorVisual2.Size = new Vector2(150, 150);
_colorVisual2.Offset = new Vector3(3, 3, 0);
_container.Children.InsertAtBottom(_colorVisual2);

使用线性渐变绘制

CompositionLinearGradientBrush 绘制具有线性渐变的区域。 线性渐变在一条线上(即渐变轴上)混合两种或更多颜色。 使用 GradientStop 对象指定渐变中的颜色及其位置。

下图和代码显示了一个 SpriteVisual,使用红色和黄色以 2 个停止点绘制的 LinearGradientBrush。

CompositionLinearGradientBrush

Compositor _compositor;
SpriteVisual _gradientVisual;
CompositionLinearGradientBrush _redyellowBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

_redyellowBrush = _compositor.CreateLinearGradientBrush();
_redyellowBrush.ColorStops.Add(_compositor.CreateColorGradientStop(0, Colors.Red));
_redyellowBrush.ColorStops.Add(_compositor.CreateColorGradientStop(1, Colors.Yellow));
_gradientVisual = _compositor.CreateSpriteVisual();
_gradientVisual.Brush = _redyellowBrush;
_gradientVisual.Size = new Vector2(156, 156);

使用径向渐变进行绘制

CompositionRadialGradientBrush 绘制具有径向渐变的区域。 径向渐变将两种或更多颜色融合在一起,渐变开始于椭圆的中心,扩展到椭圆的半径处结束。 GradientStop 对象用于定义渐变中的颜色及其位置。

下图和代码显示了使用 RadialGradientBrush 和 2 GradientStops 绘制的 SpriteVisual。

CompositionRadialGradientBrush

Compositor _compositor;
SpriteVisual _gradientVisual;
CompositionRadialGradientBrush RGBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

RGBrush = _compositor.CreateRadialGradientBrush();
RGBrush.ColorStops.Add(_compositor.CreateColorGradientStop(0, Colors.Aquamarine));
RGBrush.ColorStops.Add(_compositor.CreateColorGradientStop(1, Colors.DeepPink));
_gradientVisual = _compositor.CreateSpriteVisual();
_gradientVisual.Brush = RGBrush;
_gradientVisual.Size = new Vector2(200, 200);

使用图像绘制

CompositionSurfaceBrush 绘制呈现到 ICompositionSurface 上的像素的区域。 例如,CompositionSurfaceBrush 可以使用 LoadedImageSurface API 绘制一个包含呈现到 ICompositionSurface 上图像的区域。

下图和代码显示了使用 LoadedImageSurface 呈现到 ICompositionSurface 上的 licorice 位图绘制的 SpriteVisual。 CompositionSurfaceBrush 的属性可用于拉伸和对齐视觉对象边界内的位图。

CompositionSurfaceBrush

Compositor _compositor;
SpriteVisual _imageVisual;
CompositionSurfaceBrush _imageBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

_imageBrush = _compositor.CreateSurfaceBrush();

// The loadedSurface has a size of 0x0 till the image has been downloaded, decoded and loaded to the surface. We can assign the surface to the CompositionSurfaceBrush and it will show up once the image is loaded to the surface.
LoadedImageSurface _loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/licorice.jpg"));
_imageBrush.Surface = _loadedSurface;

_imageVisual = _compositor.CreateSpriteVisual();
_imageVisual.Brush = _imageBrush;
_imageVisual.Size = new Vector2(156, 156);

使用自定义绘图进行绘制

CompositionSurfaceBrush 还可以用于绘制由 Win2D(或 D2D)呈现的 ICompositionSurface 的像素区域。

以下代码展示了如何使用 Win2D 将文字内容呈现到 ICompositionSurface 上并绘制成 SpriteVisual。 若要将 Win2D 与 WinUI 配合使用,请在项目中安装 Microsoft.Graphics.Win2D NuGet 包

Compositor _compositor;
CanvasDevice _device;
CompositionGraphicsDevice _compositionGraphicsDevice;
SpriteVisual _drawingVisual;
CompositionSurfaceBrush _drawingBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
_device = CanvasDevice.GetSharedDevice();
_compositionGraphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(_compositor, _device);

_drawingBrush = _compositor.CreateSurfaceBrush();
CompositionDrawingSurface _drawingSurface = _compositionGraphicsDevice.CreateDrawingSurface(
    new Size(256, 256),
    DirectXPixelFormat.B8G8R8A8UIntNormalized,
    DirectXAlphaMode.Premultiplied);

using (var ds = CanvasComposition.CreateDrawingSession(_drawingSurface))
{
    ds.Clear(Colors.Transparent);
    var rect = new Rect(new Point(2, 2), (_drawingSurface.Size.ToVector2() - new Vector2(4, 4)).ToSize());
    ds.FillRoundedRectangle(rect, 15, 15, Colors.LightBlue);
    ds.DrawRoundedRectangle(rect, 15, 15, Colors.Gray, 2);
    ds.DrawText("This is a composition drawing surface", rect, Colors.Black, new CanvasTextFormat()
    {
        FontFamily = "Comic Sans MS",
        FontSize = 32,
        WordWrapping = CanvasWordWrapping.WholeWord,
        VerticalAlignment = CanvasVerticalAlignment.Center,
        HorizontalAlignment = CanvasHorizontalAlignment.Center
    });
}

_drawingBrush.Surface = _drawingSurface;

_drawingVisual = _compositor.CreateSpriteVisual();
_drawingVisual.Brush = _drawingBrush;
_drawingVisual.Size = new Vector2(156, 156);

同样,CompositionSurfaceBrush 还可用于使用 Win2D 互操作通过交换链绘制 SpriteVisual。 此示例 提供了如何使用 Win2D 通过交换链绘制 SpriteVisual 的示例。

使用视频绘制

CompositionSurfaceBrush 还可用于涂绘由使用 MediaPlayer 类加载的视频呈现的 ICompositionSurface 中的像素的区域。

以下代码展示了一个使用加载到 ICompositionSurface 上的视频绘制的 SpriteVisual。

Compositor _compositor;
SpriteVisual _videoVisual;
CompositionSurfaceBrush _videoBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

// MediaPlayer setup with a source URI.
_mediaPlayer = new MediaPlayer();

// Get a source from a URI. This could also come from a file or a stream.
var source = MediaSource.CreateFromUri(new Uri("https://go.microsoft.com/fwlink/?LinkID=809007&clcid=0x409"));
var item = new MediaPlaybackItem(source);
_mediaPlayer.Source = item;
_mediaPlayer.IsLoopingEnabled = true;

// Get the surface from MediaPlayer and put it on a brush.
_videoSurface = _mediaPlayer.GetSurface(_compositor);
_videoBrush = _compositor.CreateSurfaceBrush(_videoSurface.CompositionSurface);

_videoVisual = _compositor.CreateSpriteVisual();
_videoVisual.Brush = _videoBrush;
_videoVisual.Size = new Vector2(156, 156);

使用筛选器效果绘制

CompositionEffectBrush 使用 CompositionEffect 的输出来绘制区域。 视觉层中的效果可以视为应用于源内容的集合(如颜色、渐变、图像、视频、交换链、UI 区域或视觉对象树)的可动画筛选效果。 源内容通常使用另一个 CompositionBrush 进行指定。

下图和代码展示了一个用猫的图像绘制的SpriteVisual,并应用了去饱和滤镜效果。

CompositionEffectBrush

Compositor _compositor;
SpriteVisual _effectVisual;
CompositionEffectBrush _effectBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

var graphicsEffect = new SaturationEffect
{
    Saturation = 0.0f,
    Source = new CompositionEffectSourceParameter("mySource")
};

var effectFactory = _compositor.CreateEffectFactory(graphicsEffect);
_effectBrush = effectFactory.CreateBrush();

CompositionSurfaceBrush surfaceBrush = _compositor.CreateSurfaceBrush();
LoadedImageSurface loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/cat.jpg"));
surfaceBrush.Surface = loadedSurface;

_effectBrush.SetSourceParameter("mySource", surfaceBrush);

_effectVisual = _compositor.CreateSpriteVisual();
_effectVisual.Brush = _effectBrush;
_effectVisual.Size = new Vector2(156, 156);

有关使用 CompositionBrushes 创建效果的详细信息,请参阅 视觉层中的效果

使用应用不透明度掩码的 CompositionBrush 进行绘制

CompositionMaskBrush 绘制一个包含应用于它的不透明度掩码的 CompositionBrush 区域。 不透明度掩码的来源可以是 CompositionColorBrush、CompositionLinearGradientBrush、CompositionSurfaceBrush、CompositionEffectBrush 或 CompositionNineGridBrush 类型的 CompositionBrush。 不透明度掩码必须指定为 CompositionSurfaceBrush。

下图和代码显示了用 CompositionMaskBrush 绘制的 SpriteVisual。 掩码的来源是 CompositionLinearGradientBrush,它使用一个圆形图像作为遮罩,使其呈现为圆形。

CompositionMaskBrush

Compositor _compositor;
SpriteVisual _maskVisual;
CompositionMaskBrush _maskBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

_maskBrush = _compositor.CreateMaskBrush();

CompositionLinearGradientBrush _sourceGradient = _compositor.CreateLinearGradientBrush();
_sourceGradient.ColorStops.Add(_compositor.CreateColorGradientStop(0,Colors.Red));
_sourceGradient.ColorStops.Add(_compositor.CreateColorGradientStop(1,Colors.Yellow));
_maskBrush.Source = _sourceGradient;

LoadedImageSurface loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/circle.png"), new Size(156.0, 156.0));
_maskBrush.Mask = _compositor.CreateSurfaceBrush(loadedSurface);

_maskVisual = _compositor.CreateSpriteVisual();
_maskVisual.Brush = _maskBrush;
_maskVisual.Size = new Vector2(156, 156);

使用 NineGrid 拉伸绘制 CompositionBrush

CompositionNineGridBrush 使用九网格隐喻对 CompositionBrush 进行拉伸以填充某个区域。 通过九宫格比喻,可以以不同于中心的方式拉伸 CompositionBrush 的边缘和角落。 9 网格拉伸的源可以由 CompositionColorBrush、CompositionSurfaceBrush 或 CompositionEffectBrush 类型的任何 CompositionBrush 提供。

以下代码显示了用 CompositionNineGridBrush 绘制的 SpriteVisual。 掩码的来源是 CompositionSurfaceBrush,它使用九网格拉伸。

Compositor _compositor;
SpriteVisual _nineGridVisual;
CompositionNineGridBrush _nineGridBrush;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

_nineGridBrush = _compositor.CreateNineGridBrush();

// nineGridImage.png is 50x50 pixels; nine-grid insets, as measured relative to the actual size of the image, are: left = 1, top = 5, right = 10, bottom = 20 (in pixels)
LoadedImageSurface _imageSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/nineGridImage.png"));
CompositionSurfaceBrush sourceBrush = _compositor.CreateSurfaceBrush(_imageSurface);
_nineGridBrush.Source = sourceBrush;

// Set nine-grid insets.
_nineGridBrush.SetInsets(1, 5, 10, 20);

// Set the appropriate stretch on the SurfaceBrush for the center of the nine-grid.
sourceBrush.Stretch = CompositionStretch.Fill;

_nineGridVisual = _compositor.CreateSpriteVisual();
_nineGridVisual.Brush = _nineGridBrush;
_nineGridVisual.Size = new Vector2(100, 75);

使用背景像素绘制

CompositionBackdropBrush 绘制一个区域,其内容位于该区域后面。 CompositionBackdropBrush 本身从未单独使用,而是作为其他 CompositionBrush(如 EffectBrush)的输入部分。 例如,通过使用 CompositionBackdropBrush 作为模糊效果的输入,可以实现霜化玻璃效果。

下面的代码显示了一个小可视化树,用于使用 CompositionSurfaceBrush 和图像上方的霜化玻璃覆盖来创建图像。 霜化玻璃覆盖是通过在图像上方放置一个用 EffectBrush 填充的 SpriteVisual 来创建的。 EffectBrush 使用 CompositionBackdropBrush 作为模糊效果的输入。

Compositor _compositor;
ContainerVisual _containerVisual;
SpriteVisual _imageVisual;
SpriteVisual _backdropVisual;

_compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;

// Create a container visual to host the visual tree.
_containerVisual = _compositor.CreateContainerVisual();

// Create _imageVisual and add it to the bottom of the container visual.
CompositionSurfaceBrush _licoriceBrush = _compositor.CreateSurfaceBrush();
LoadedImageSurface loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/licorice.jpg"));
_licoriceBrush.Surface = loadedSurface;

_imageVisual = _compositor.CreateSpriteVisual();
_imageVisual.Brush = _licoriceBrush;
_imageVisual.Size = new Vector2(156, 156);
_imageVisual.Offset = new Vector3(0, 0, 0);
_containerVisual.Children.InsertAtBottom(_imageVisual);

// Create a SpriteVisual and add it to the top of the container visual.
// Paint the visual with an EffectBrush that applies blur to the content underneath it.
GaussianBlurEffect blurEffect = new GaussianBlurEffect()
{
    Name = "Blur",
    BlurAmount = 1.0f,
    BorderMode = EffectBorderMode.Hard,
    Source = new CompositionEffectSourceParameter("source")
};

CompositionEffectFactory blurEffectFactory = _compositor.CreateEffectFactory(blurEffect);
CompositionEffectBrush _backdropBrush = blurEffectFactory.CreateBrush();

// Create a BackdropBrush and bind it to the EffectSourceParameter source.
_backdropBrush.SetSourceParameter("source", _compositor.CreateBackdropBrush());

_backdropVisual = _compositor.CreateSpriteVisual();
_backdropVisual.Brush = _backdropBrush;
_backdropVisual.Size = new Vector2(78, 78);
_backdropVisual.Offset = new Vector3(39, 39, 0);
_containerVisual.Children.InsertAtTop(_backdropVisual);

组合 CompositionBrushes

许多 CompositionBrushes 使用其他 CompositionBrushes 作为输入。 例如,可以使用 SetSourceParameter 方法将另一个 CompositionBrush 设置为 CompositionEffectBrush 的输入。 下表概述了 CompositionBrushes 支持的组合。 请注意,使用不受支持的组合将引发异常。

笔刷 EffectBrush.SetSourceParameter() 掩码笔.掩码 MaskBrush.Source NineGridBrush.Source
CompositionColorBrush
CompositionLinear
渐变画笔
CompositionSurfaceBrush
CompositionEffectBrush
CompositionMaskBrush
CompositionNineGridBrush
CompositionBackdropBrush

使用 XAML 画笔与 CompositionBrush 的比较

下表提供了方案列表,以及在应用程序中绘制 UIElement 还是 SpriteVisual 时是否规定 XAML 或合成画笔使用。

注释

如果建议为 XAML UIElement 使用 CompositionBrush,则假定该 CompositionBrush 是通过 XamlCompositionBrushBase 打包的。

情景 XAML UIElement 合成 SpriteVisual
用纯色绘制区域 SolidColorBrush CompositionColorBrush
绘制具有动画颜色的区域 SolidColorBrush CompositionColorBrush
使用静态渐变绘制区域 LinearGradientBrush CompositionLinearGradientBrush
绘制具有动画渐变停止点的区域 CompositionLinearGradientBrush CompositionLinearGradientBrush
使用图像绘制区域 ImageBrush CompositionSurfaceBrush
使用网页绘制区域 WebView2 不适用
使用 NineGrid 拉伸绘制具有图像的区域 图像控件 CompositionNineGridBrush
使用动画的 NineGrid 拉伸绘制区域 CompositionNineGridBrush CompositionNineGridBrush
使用交换链绘制区域 SwapChainPanel CompositionSurfaceBrush w/ swapchain interop
使用视频绘制区域 MediaPlayerElement CompositionSurfaceBrush 与媒体互操作
使用自定义 2D 绘图绘制区域 Win2D 的 CanvasControl CompositionSurfaceBrush 与 Win2D 的互操作
使用非动画掩码绘制区域 使用 XAML 形状 定义掩码 CompositionMaskBrush
使用动画掩码绘制区域 CompositionMaskBrush CompositionMaskBrush
使用动画过滤效果绘制区域 CompositionEffectBrush CompositionEffectBrush
绘制应用于背景像素的效果的区域 CompositionBackdropBrush CompositionBackdropBrush

通过 BeginDraw 和 EndDraw 实现合成本机 DirectX 和 Direct2D 互操作

XAML 画笔与 XamlCompositionBrushBase 互操作