Using complex paths for CompositionClip

438 views Asked by At

Visuals in UWP/WinUI can have a Clip applied to them which is an instance of a CompositionClip. One of the types of CompositionClip is a GeometricClip which can have a Geometry of various types derived from the CompositionGeometry class. I'm seeking to have a clip that has the shape of a rounded rectangle but with different elliptical corner radii as follows

enter image description here

This would ideally be suited for the CompositionRoundedRectangleGeometry but that class has a limitation that it supports having the same elliptical radius on all four corners, not allowing for different radii. So that leaves CompositionPathGeometry whose Path is defined by a CompositionPath instance. This is where I've reached a dead end.

This CompositionPath implements the IGeometrySource2D interface. I couldn't figure out how to use this IGeometrySource2D interface to construct a complex path with lines and curves. Searching online led to this article which too skirts using that interface and instead uses the classes built around CanvasPathGeometry which is from the Windows Community Toolkit and, if I understand correctly, only meant for C# projects as per this GitHub page and won't work for me since I need something in C++.

So the questions are:

  • How do we use IGeometrySource2D and CompositionPath to create complex shapes? If it's not feasible, then
  • Is there any equivalent of CanvasPathGeometry for C++/WinRT language projection?

P.S. I'm aware that WinUI 3 offers RectangleClip(as per this answer) that would work for my case but that isn't available in UWP, so that's not a feasible solution for me.

EDIT:

Here's an attempt using Win2D.UWP NuGet package, based on the suggestions/pointers shared in the comments.

auto visual = _compositor->CreateSpriteVisual();
visual->Size = float2(500.0f);
visual->Offset = float3(50, 50, 0);
visual->Brush = _compositor->CreateColorBrush(Colors::Red);

auto canvasPathBuilder = ref new CanvasPathBuilder(ref new CanvasDevice());
canvasPathBuilder->BeginFigure(radius.topLeft.x, 0);
canvasPathBuilder->AddLine(visual->Size.x - radius.topRight.x, 0);
canvasPathBuilder->AddArc(float2(visual->Size.x , radius.topRight.y ), radius.topRight.x, radius.topRight.y,
    0, CanvasSweepDirection::Clockwise, CanvasArcSize::Small);
canvasPathBuilder->AddLine(visual->Size.x, visual->Size.y - radius.bottomRight.y);
canvasPathBuilder->AddArc(float2(visual->Size.x - radius.bottomRight.x, visual->Size.y), radius.bottomRight.x, radius.bottomRight.y,
    0, CanvasSweepDirection::Clockwise, CanvasArcSize::Small);
canvasPathBuilder->AddLine(radius.bottomLeft.x, visual->Size.y);
canvasPathBuilder->AddArc(float2(0, visual->Size.y - radius.bottomLeft.y), radius.bottomLeft.x, radius.bottomLeft.y,
    0, CanvasSweepDirection::Clockwise, CanvasArcSize::Small);
canvasPathBuilder->AddLine(0, radius.topLeft.y);
canvasPathBuilder->AddArc(float2(radius.topLeft.x, 0), radius.topLeft.x, radius.topLeft.y,
     0, CanvasSweepDirection::Clockwise, CanvasArcSize::Small);
canvasPathBuilder->EndFigure(CanvasFigureLoop::Closed);

auto canvasGeometry = CanvasGeometry::CreatePath(canvasPathBuilder);
auto compositionPath = ref new CompositionPath(canvasGeometry);
auto pathGeometry = _compositor->CreatePathGeometry();
pathGeometry->Path = compositionPath;
visual->Clip = _compositor->CreateGeometricClip(pathGeometry);

This appears to work

Win2DRoundedRecangle

Now, the next step would be to work to remove the Win2D dependency for CanvasPath et al and to use ID2D1Geometry and other interfaces to supply the IGeomterySource2D since I would prefer not having NuGet packages as a project dependency.

0

There are 0 answers