To avoid an XY question, first the Y: Is there a way to create a text on a SpriteVisual that does not require Win2D?
Now the X: The problem I'm trying to solve is that I need text on a SpriteVisual (instead of a TextBlock) to apply perspective rotate 3D Expression animations on it. Text on a SpriteVisual can be done with Win2D. Here's a piece of the Microsoft sample code:
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;
To color text in this requires Win2D ICanvasBrush. But I want to animate the color in an Expression Animation, so I need the text color to come from a CompositionBrush. Now I could create a CompositionMaskBrush with the text brush as the mask and the CompositionBrush as the source. But with a CompositionMaskBrush I cannot add an additional mask or any other composition effects to it, which is problematic for me.
Is there an alternate approach or perhaps an enhancement to the Composition API in an Insider Build that I'm not familiar with?
Y-answer: No, you can only draw text on CompositionDrawingSurface
using Win2d CanvasDrawingSession
. But you may get Visual
from XAML TextBlock
with ElementCompositionPreview.GetElementVisual(yourTextBlock)
and animate it with Composition API expressions + XAML color storyboards.
X-answer: You can create an alpha mask from your text CompositionDrawingSurface
and apply it to CompositionColorBrush
:
private void CreateColoredText(string text)
{
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, CanvasDevice.GetSharedDevice());
var spriteTextVisual = compositor.CreateSpriteVisual();
spriteTextVisual.Size = new Vector2(512, 512);
var maskDrawingSurface = graphicsDevice.CreateDrawingSurface(new Size(512, 512), DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
using (var ds = CanvasComposition.CreateDrawingSession(maskDrawingSurface))
{
ds.Clear(Colors.Transparent);
ds.DrawText(text, new Rect(0, 0, 512, 512), Colors.Lime, new CanvasTextFormat
{
FontSize = 32,
FontWeight = FontWeights.Bold,
VerticalAlignment = CanvasVerticalAlignment.Center,
HorizontalAlignment = CanvasHorizontalAlignment.Center,
LineSpacing = 32
});
}
var maskSurfaceBrush = compositor.CreateSurfaceBrush(maskDrawingSurface);
var surfaceTextBrush = compositor.CreateColorBrush(Colors.DeepPink);
var maskBrush = compositor.CreateMaskBrush();
maskBrush.Mask = maskSurfaceBrush;
maskBrush.Source = surfaceTextBrush;
var colorAnimation = compositor.CreateColorKeyFrameAnimation();
colorAnimation.InsertKeyFrame(0.5f, Colors.DeepSkyBlue);
colorAnimation.InsertKeyFrame(1, Colors.DeepPink);
colorAnimation.Duration = TimeSpan.FromMilliseconds(1500);
colorAnimation.IterationBehavior = AnimationIterationBehavior.Forever;
surfaceTextBrush.StartAnimation("Color", colorAnimation);
spriteTextVisual.Brush = maskBrush;
ElementCompositionPreview.SetElementChildVisual(Grid, spriteTextVisual);
}
Now you can animate color and perspective for your purposes.