Skip to content

Commit 306f14c

Browse files
clarkezoneajbennet
authored andcommitted
Add HelloVectors c++/winrt sample (#7)
1 parent 1c4af5c commit 306f14c

12 files changed

Lines changed: 6007 additions & 0 deletions
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3+
<application xmlns="urn:schemas-microsoft-com:asm.v3">
4+
<windowsSettings>
5+
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
6+
</windowsSettings>
7+
</application>
8+
</assembly>
Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
// HelloVectors_win32_cpp.cpp : This file contains the 'main' function. Program execution begins and ends there.
2+
//
3+
4+
#include "pch.h"
5+
#include "LottieLogo1.h"
6+
#include "desktopcompositionwindow.h"
7+
8+
using namespace winrt;
9+
using namespace Windows::Foundation::Numerics;
10+
11+
///////////////////////////////////////////////////////////////////////////////////////
12+
// Scenario 1: construct a simple shape using ShapeVisual
13+
///////////////////////////////////////////////////////////////////////////////////////
14+
15+
void Scenario1SimpleShape(const Compositor & compositor, const ContainerVisual & root) {
16+
17+
// Create a new ShapeVisual that will contain our drawings
18+
ShapeVisual shape = compositor.CreateShapeVisual();
19+
shape.Size({ 100.0f,100.0f });
20+
21+
// Create a circle geometry and set its radius
22+
auto circleGeometry = compositor.CreateEllipseGeometry();
23+
circleGeometry.Radius({ 30.0f, 30.0f });
24+
25+
// Create a shape object from the geometry and give it a color and offset
26+
auto circleShape = compositor.CreateSpriteShape(circleGeometry);
27+
circleShape.FillBrush(compositor.CreateColorBrush(Windows::UI::Colors::Orange()));
28+
circleShape.Offset({ 50.0f, 50.0f });
29+
30+
// Add the circle to our shape visual
31+
shape.Shapes().Append(circleShape);
32+
33+
// Add to the visual tree
34+
root.Children().InsertAtTop(shape);
35+
}
36+
37+
// end Scenario 1
38+
39+
///////////////////////////////////////////////////////////////////////////////////////
40+
// Scenario 2 construct a simple path using ShapeVisual, Composition Path and Direct2D
41+
///////////////////////////////////////////////////////////////////////////////////////
42+
43+
// Helper function to create a GradientBrush
44+
Windows::UI::Composition::CompositionLinearGradientBrush CreateGradientBrush(const Compositor & compositor)
45+
{
46+
auto gradBrush = compositor.CreateLinearGradientBrush();
47+
gradBrush.ColorStops().InsertAt(0, compositor.CreateColorGradientStop(0.0f, Windows::UI::Colors::Orange()));
48+
gradBrush.ColorStops().InsertAt(1, compositor.CreateColorGradientStop(0.5f, Windows::UI::Colors::Yellow()));
49+
gradBrush.ColorStops().InsertAt(2, compositor.CreateColorGradientStop(1.0f, Windows::UI::Colors::Red()));
50+
return gradBrush;
51+
}
52+
53+
// Helper class for converting geometry to a composition compatible geometry source
54+
struct GeoSource final : implements<GeoSource,
55+
Windows::Graphics::IGeometrySource2D,
56+
ABI::Windows::Graphics::IGeometrySource2DInterop>
57+
{
58+
public:
59+
GeoSource(com_ptr<ID2D1Geometry> const & pGeometry) :
60+
_cpGeometry(pGeometry)
61+
{ }
62+
63+
IFACEMETHODIMP GetGeometry(ID2D1Geometry** value) override
64+
{
65+
_cpGeometry.copy_to(value);
66+
return S_OK;
67+
}
68+
69+
IFACEMETHODIMP TryGetGeometryUsingFactory(ID2D1Factory*, ID2D1Geometry** result) override
70+
{
71+
*result = nullptr;
72+
return E_NOTIMPL;
73+
}
74+
75+
private:
76+
com_ptr<ID2D1Geometry> _cpGeometry;
77+
};
78+
79+
void Scenario2SimplePath(const Compositor & compositor, const ContainerVisual & root) {
80+
// Same steps as for SimpleShapeImperative_Click to create, size and host a ShapeVisual
81+
ShapeVisual shape = compositor.CreateShapeVisual();
82+
shape.Size({ 500.0f, 500.0f });
83+
shape.Offset({ 300.0f, 0.0f, 1.0f });
84+
85+
// Create a D2D Factory
86+
com_ptr<ID2D1Factory> d2dFactory;
87+
check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
88+
89+
com_ptr<GeoSource> result;
90+
com_ptr<ID2D1PathGeometry> path;
91+
92+
// use D2D factory to create a path geometry
93+
check_hresult(d2dFactory->CreatePathGeometry(path.put()));
94+
95+
// for the path created above, create a Geometry Sink used to add points to the path
96+
com_ptr<ID2D1GeometrySink> sink;
97+
check_hresult(path->Open(sink.put()));
98+
99+
// Add points to the path
100+
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
101+
sink->BeginFigure({ 1, 1 }, D2D1_FIGURE_BEGIN_FILLED);
102+
sink->AddLine({ 300, 300 });
103+
sink->AddLine({ 1, 300 });
104+
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
105+
106+
// Close geometry sink
107+
check_hresult(sink->Close());
108+
109+
// Create a GeoSource helper object wrapping the path
110+
result.attach(new GeoSource(path));
111+
CompositionPath trianglePath = CompositionPath(result.as<Windows::Graphics::IGeometrySource2D>());
112+
113+
// create a CompositionPathGeometry from the composition path
114+
CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(trianglePath);
115+
116+
// create a SpriteShape from the CompositionPathGeometry, give it a gradient fill and add to our ShapeVisual
117+
CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
118+
spriteShape.FillBrush(CreateGradientBrush(compositor));
119+
120+
// Add the SpriteShape to our shape visual
121+
shape.Shapes().Append(spriteShape);
122+
123+
// Add to the visual tree
124+
root.Children().InsertAtTop(shape);
125+
}
126+
127+
// end Scenario 2
128+
129+
///////////////////////////////////////////////////////////////////////////////////////
130+
// Scenario 3: Build a morph animation using ShapeVisual, two CompositionPath's and a animated CompositionPathGeometry
131+
///////////////////////////////////////////////////////////////////////////////////////
132+
133+
// Helper to build a CompositionPath using a provided callback function
134+
CompositionPath BuildPath(com_ptr<ID2D1Factory> const & d2dFactory, std::function<void(com_ptr<ID2D1GeometrySink> const & sink)> builder)
135+
{
136+
// See scenario 2 for a more detailed explanation of the items here
137+
com_ptr<GeoSource> result;
138+
com_ptr<ID2D1PathGeometry> path;
139+
check_hresult(d2dFactory->CreatePathGeometry(path.put()));
140+
com_ptr<ID2D1GeometrySink> sink;
141+
142+
check_hresult(path->Open(sink.put()));
143+
144+
builder(sink);
145+
146+
check_hresult(sink->Close());
147+
148+
result.attach(new GeoSource(path));
149+
CompositionPath trianglePath = CompositionPath(result.as<Windows::Graphics::IGeometrySource2D>());
150+
return trianglePath;
151+
}
152+
153+
// Helper to build a square CompositionPath
154+
CompositionPath BuildSquarePath(com_ptr<ID2D1Factory> const & d2dFactory)
155+
{
156+
auto squareBuilder = [](com_ptr<ID2D1GeometrySink> const & sink) {
157+
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
158+
sink->BeginFigure({ -90, -146 }, D2D1_FIGURE_BEGIN_FILLED);
159+
sink->AddBezier({ { -90.0F, -146.0F }, { 176.0F, -148.555F }, { 176.0F, -148.555F } });
160+
sink->AddBezier({ { 176.0F, -148.555F }, { 174.445F, 121.445F }, { 174.445F, 121.445F } });
161+
sink->AddBezier({ { 174.445F, 121.445F }, { -91.555F, 120.0F }, { -91.555F, 120.0F } });
162+
sink->AddBezier({ { -91.555F, 120.0F }, { -90.0F, -146.0F }, { -90.0F, -146.0F } });
163+
sink->AddLine({ 1, 300 });
164+
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
165+
};
166+
167+
return BuildPath(d2dFactory, squareBuilder);
168+
}
169+
170+
// Helper to build a circular CompositionPath
171+
CompositionPath BuildCirclePath(com_ptr<ID2D1Factory> const & d2dFactory)
172+
{
173+
auto circleBuilder = [](com_ptr<ID2D1GeometrySink> const & sink) {
174+
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
175+
sink->BeginFigure({ 42.223F, -146 }, D2D1_FIGURE_BEGIN_FILLED);
176+
sink->AddBezier({ { 115.248F, -146 }, { 174.445F, -86.13F }, { 174.445F, -12.277F } });
177+
sink->AddBezier({ { 174.445F, 61.576F }, { 115.248F, 121.445F }, { 42.223F, 121.445F } });
178+
sink->AddBezier({ { -30.802F, 121.445F }, { -90, 61.576F }, { -90, -12.277F } });
179+
sink->AddBezier({ { -90, -86.13F }, { -30.802F, -146 }, { 42.223F, -146 } });
180+
sink->AddLine({ 1, 300 });
181+
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
182+
};
183+
184+
return BuildPath(d2dFactory, circleBuilder);
185+
}
186+
187+
// Scenario 3: create morph animation
188+
void Scenario3PathMorphImperative(const Compositor & compositor, const ContainerVisual & root) {
189+
// Same steps as for SimpleShapeImperative_Click to create, size and host a ShapeVisual
190+
ShapeVisual shape = compositor.CreateShapeVisual();
191+
shape.Size({ 500.0f, 500.0f });
192+
shape.Offset({ 600.0f, 0.0f, 1.0f });
193+
194+
com_ptr<ID2D1Factory> d2dFactory;
195+
check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
196+
197+
// Call helper functions that use Win2D to build square and circle path geometries and create CompositionPath's for them
198+
auto squarePath = BuildSquarePath(d2dFactory);
199+
200+
auto circlePath = BuildCirclePath(d2dFactory);
201+
202+
// Create a CompositionPathGeometry, CompositionSpriteShape and set offset and fill
203+
CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(squarePath);
204+
CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
205+
spriteShape.Offset({ 150.0f, 200.0f });
206+
spriteShape.FillBrush(CreateGradientBrush(compositor));
207+
208+
// Create a PathKeyFrameAnimation to set up the path morph passing in the circle and square paths
209+
auto playAnimation = compositor.CreatePathKeyFrameAnimation();
210+
playAnimation.Duration(std::chrono::seconds(4));
211+
playAnimation.InsertKeyFrame(0, squarePath);
212+
playAnimation.InsertKeyFrame(0.3F, circlePath);
213+
playAnimation.InsertKeyFrame(0.6F, circlePath);
214+
playAnimation.InsertKeyFrame(1.0F, squarePath);
215+
216+
// Make animation repeat forever and start it
217+
playAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
218+
playAnimation.Direction(AnimationDirection::Alternate);
219+
compositionPathGeometry.StartAnimation(L"Path", playAnimation);
220+
221+
// Add the SpriteShape to our shape visual
222+
shape.Shapes().Append(spriteShape);
223+
224+
// Add to the visual tree
225+
root.Children().InsertAtTop(shape);
226+
}
227+
228+
// end Scenario 3
229+
230+
///////////////////////////////////////////////////////////////////////////////////////
231+
// Scenario 4: Use output from LottieViewer https://www.microsoft.com/store/productId/9P7X9K692TMW
232+
// to play back anamiation generated in Adobe After Effects and converted using Bodymovin
233+
///////////////////////////////////////////////////////////////////////////////////////
234+
235+
// Helper funciton for playing back lottie generated animations
236+
ScalarKeyFrameAnimation Play(const Compositor & compositor, Visual const & visual) {
237+
auto progressAnimation = compositor.CreateScalarKeyFrameAnimation();
238+
progressAnimation.Duration(std::chrono::seconds(5));
239+
progressAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
240+
progressAnimation.Direction(AnimationDirection::Alternate);
241+
auto linearEasing = compositor.CreateLinearEasingFunction();
242+
progressAnimation.InsertKeyFrame(0, 0, linearEasing);
243+
progressAnimation.InsertKeyFrame(1, 1, linearEasing);
244+
245+
visual.Properties().StartAnimation(L"Progress", progressAnimation);
246+
return progressAnimation;
247+
}
248+
249+
// Scenario 4
250+
void Scenario4PlayLottieOutput(const Compositor & compositor, const ContainerVisual & root) {
251+
//configure a container visual
252+
float width = 400.0f, height = 400.0f;
253+
SpriteVisual container = compositor.CreateSpriteVisual();
254+
container.Size({ width, height });
255+
container.Offset({ 0.0f, 350.0f, 1.0f });
256+
root.Children().InsertAtTop(container);
257+
258+
AnimatedVisuals::LottieLogo1 bmv;
259+
260+
//NOTE to make this scenario compile with prerelease Microsoft.UI.Xaml package 190131001 you need to edit: …\UWPCompositionDemos\HelloVectors\packages\Microsoft.UI.Xaml.2.1.190131001-prerelease\build\native\Microsoft.UI.Xaml.targets
261+
//and change <ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'UAP'"> with <ItemGroup>
262+
263+
winrt::Windows::Foundation::IInspectable diags;
264+
auto avptr = bmv.TryCreateAnimatedVisual(compositor, diags);
265+
266+
auto visual = avptr.RootVisual();
267+
container.Children().InsertAtTop(visual);
268+
269+
//// Calculate a scale to make the animation fit into the specified visual size
270+
container.Scale({ width / avptr.Size().x, height / avptr.Size().y, 1.0f });
271+
272+
auto playanimation = Play(compositor, visual);
273+
}
274+
275+
// end scenario 3
276+
277+
// Bootstap the app
278+
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
279+
{
280+
init_apartment(apartment_type::single_threaded);
281+
282+
// Create a dispatcher controller required when using Windows::UI::Composition in win32
283+
auto controller = CreateDispatcherQueueController();
284+
285+
// Callback that will build a visual tree containing each sample
286+
auto buildvisualtree =
287+
[](const Compositor & compositor, const Visual & root) {
288+
289+
Scenario1SimpleShape(compositor, root.as<ContainerVisual>());
290+
Scenario2SimplePath(compositor, root.as<ContainerVisual>());
291+
Scenario3PathMorphImperative(compositor, root.as<ContainerVisual>());
292+
Scenario4PlayLottieOutput(compositor, root.as<ContainerVisual>());
293+
};
294+
295+
// Composition Window. For more details see TODO: link to basic walkthrough
296+
CompositionWindow window(buildvisualtree);
297+
298+
// Win32 MessageLoop
299+
MSG message;
300+
301+
while (GetMessage(&message, nullptr, 0, 0))
302+
{
303+
DispatchMessage(&message);
304+
}
305+
}
306+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28307.421
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloVectors_win32_cpp", "HelloVectors_win32_cpp.vcxproj", "{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|x64 = Debug|x64
11+
Debug|x86 = Debug|x86
12+
Release|x64 = Release|x64
13+
Release|x86 = Release|x86
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Debug|x64.ActiveCfg = Debug|x64
17+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Debug|x64.Build.0 = Debug|x64
18+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Debug|x86.ActiveCfg = Debug|Win32
19+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Debug|x86.Build.0 = Debug|Win32
20+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Release|x64.ActiveCfg = Release|x64
21+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Release|x64.Build.0 = Release|x64
22+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Release|x86.ActiveCfg = Release|Win32
23+
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}.Release|x86.Build.0 = Release|Win32
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {52261925-DAE7-437F-AEC3-D587A68E420B}
30+
EndGlobalSection
31+
EndGlobal

0 commit comments

Comments
 (0)