├── .gitignore
├── FluidBanner.sln
├── FluidBanner
├── App.xaml
├── App.xaml.cs
├── Assets
│ ├── Images
│ │ ├── Image1.jpg
│ │ ├── Image2.jpg
│ │ ├── Image3.jpg
│ │ ├── Image4.jpg
│ │ ├── Image5.jpg
│ │ └── Image6.jpg
│ ├── LockScreenLogo.scale-200.png
│ ├── SplashScreen.scale-200.png
│ ├── Square150x150Logo.scale-200.png
│ ├── Square44x44Logo.scale-200.png
│ ├── Square44x44Logo.targetsize-24_altform-unplated.png
│ ├── StoreLogo.png
│ └── Wide310x150Logo.scale-200.png
├── FluidBanner.cs
├── FluidBanner.csproj
├── MainPage.xaml
├── MainPage.xaml.cs
├── Package.appxmanifest
├── Properties
│ ├── AssemblyInfo.cs
│ └── Default.rd.xml
└── project.json
├── LICENSE
├── Microsoft.UI.Composition.Toolkit
├── CompositionGraphicsDevice.cpp
├── CompositionGraphicsDevice.h
├── CompositionImage.cpp
├── CompositionImage.h
├── CompositionImageFactory.cpp
├── CompositionImageFactory.h
├── CompositionImageOptions.cpp
├── CompositionImageOptions.h
├── Microsoft.UI.Composition.Toolkit.sln
├── Microsoft.UI.Composition.Toolkit.vcxproj
├── Microsoft.UI.Composition.Toolkit.vcxproj.filters
├── WICBitmapSource.cpp
├── WICBitmapSource.h
├── pch.cpp
└── pch.h
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/FluidBanner.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FluidBanner", "FluidBanner\FluidBanner.csproj", "{56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.UI.Composition.Toolkit", "Microsoft.UI.Composition.Toolkit\Microsoft.UI.Composition.Toolkit.vcxproj", "{08A7D060-593A-4978-8DCC-B2029ACA7C95}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|ARM = Debug|ARM
13 | Debug|x64 = Debug|x64
14 | Debug|x86 = Debug|x86
15 | Release|ARM = Release|ARM
16 | Release|x64 = Release|x64
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|ARM.ActiveCfg = Debug|ARM
21 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|ARM.Build.0 = Debug|ARM
22 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|ARM.Deploy.0 = Debug|ARM
23 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x64.ActiveCfg = Debug|x64
24 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x64.Build.0 = Debug|x64
25 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x64.Deploy.0 = Debug|x64
26 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x86.ActiveCfg = Debug|x86
27 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x86.Build.0 = Debug|x86
28 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Debug|x86.Deploy.0 = Debug|x86
29 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|ARM.ActiveCfg = Release|ARM
30 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|ARM.Build.0 = Release|ARM
31 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|ARM.Deploy.0 = Release|ARM
32 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x64.ActiveCfg = Release|x64
33 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x64.Build.0 = Release|x64
34 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x64.Deploy.0 = Release|x64
35 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x86.ActiveCfg = Release|x86
36 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x86.Build.0 = Release|x86
37 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}.Release|x86.Deploy.0 = Release|x86
38 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|ARM.ActiveCfg = Debug|ARM
39 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|ARM.Build.0 = Debug|ARM
40 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x64.ActiveCfg = Debug|x64
41 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x64.Build.0 = Debug|x64
42 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x86.ActiveCfg = Debug|Win32
43 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x86.Build.0 = Debug|Win32
44 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|ARM.ActiveCfg = Release|ARM
45 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|ARM.Build.0 = Release|ARM
46 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x64.ActiveCfg = Release|x64
47 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x64.Build.0 = Release|x64
48 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x86.ActiveCfg = Release|Win32
49 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x86.Build.0 = Release|Win32
50 | EndGlobalSection
51 | GlobalSection(SolutionProperties) = preSolution
52 | HideSolutionNode = FALSE
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/FluidBanner/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/FluidBanner/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.InteropServices.WindowsRuntime;
6 | using Windows.ApplicationModel;
7 | using Windows.ApplicationModel.Activation;
8 | using Windows.Foundation;
9 | using Windows.Foundation.Collections;
10 | using Windows.UI.Xaml;
11 | using Windows.UI.Xaml.Controls;
12 | using Windows.UI.Xaml.Controls.Primitives;
13 | using Windows.UI.Xaml.Data;
14 | using Windows.UI.Xaml.Input;
15 | using Windows.UI.Xaml.Media;
16 | using Windows.UI.Xaml.Navigation;
17 |
18 | namespace FluidBanner
19 | {
20 | ///
21 | /// Provides application-specific behavior to supplement the default Application class.
22 | ///
23 | sealed partial class App : Application
24 | {
25 | ///
26 | /// Initializes the singleton application object. This is the first line of authored code
27 | /// executed, and as such is the logical equivalent of main() or WinMain().
28 | ///
29 | public App()
30 | {
31 | this.InitializeComponent();
32 | this.Suspending += OnSuspending;
33 | }
34 |
35 | ///
36 | /// Invoked when the application is launched normally by the end user. Other entry points
37 | /// will be used such as when the application is launched to open a specific file.
38 | ///
39 | /// Details about the launch request and process.
40 | protected override void OnLaunched(LaunchActivatedEventArgs e)
41 | {
42 | #if DEBUG
43 | if (System.Diagnostics.Debugger.IsAttached)
44 | {
45 | this.DebugSettings.EnableFrameRateCounter = true;
46 | }
47 | #endif
48 | Frame rootFrame = Window.Current.Content as Frame;
49 |
50 | // Do not repeat app initialization when the Window already has content,
51 | // just ensure that the window is active
52 | if (rootFrame == null)
53 | {
54 | // Create a Frame to act as the navigation context and navigate to the first page
55 | rootFrame = new Frame();
56 |
57 | rootFrame.NavigationFailed += OnNavigationFailed;
58 |
59 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
60 | {
61 | //TODO: Load state from previously suspended application
62 | }
63 |
64 | // Place the frame in the current Window
65 | Window.Current.Content = rootFrame;
66 | }
67 |
68 | if (e.PrelaunchActivated == false)
69 | {
70 | if (rootFrame.Content == null)
71 | {
72 | // When the navigation stack isn't restored navigate to the first page,
73 | // configuring the new page by passing required information as a navigation
74 | // parameter
75 | rootFrame.Navigate(typeof(MainPage), e.Arguments);
76 | }
77 | // Ensure the current window is active
78 | Window.Current.Activate();
79 | }
80 | }
81 |
82 | ///
83 | /// Invoked when Navigation to a certain page fails
84 | ///
85 | /// The Frame which failed navigation
86 | /// Details about the navigation failure
87 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
88 | {
89 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
90 | }
91 |
92 | ///
93 | /// Invoked when application execution is being suspended. Application state is saved
94 | /// without knowing whether the application will be terminated or resumed with the contents
95 | /// of memory still intact.
96 | ///
97 | /// The source of the suspend request.
98 | /// Details about the suspend request.
99 | private void OnSuspending(object sender, SuspendingEventArgs e)
100 | {
101 | var deferral = e.SuspendingOperation.GetDeferral();
102 | //TODO: Save application state and stop any background activity
103 | deferral.Complete();
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image1.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image2.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image3.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image4.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image5.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/Images/Image6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Images/Image6.jpg
--------------------------------------------------------------------------------
/FluidBanner/Assets/LockScreenLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/LockScreenLogo.scale-200.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/Square44x44Logo.targetsize-24_altform-unplated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/StoreLogo.png
--------------------------------------------------------------------------------
/FluidBanner/Assets/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ratishphilip/FluidBanner/cae0fa29a5f3ef488c25aba5cd2e8ead1e36e45f/FluidBanner/Assets/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/FluidBanner/FluidBanner.cs:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016 Ratish Philip
2 | //
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy
5 | // of this software and associated documentation files (the "Software"), to deal
6 | // in the Software without restriction, including without limitation the rights
7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | // copies of the Software, and to permit persons to whom the Software is
9 | // furnished to do so, subject to the following conditions:
10 | //
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | // This file is part of the FluidBanner project:
25 | // https://github.com/ratishphilip/FluidBanner
26 | //
27 |
28 | using System;
29 | using System.Collections.Generic;
30 | using System.Linq;
31 | using System.Numerics;
32 | using System.Threading.Tasks;
33 | using Windows.Foundation;
34 | using Windows.UI;
35 | using Windows.UI.Composition;
36 | using Windows.UI.Xaml;
37 | using Windows.UI.Xaml.Controls;
38 | using Windows.UI.Xaml.Hosting;
39 | using Windows.UI.Xaml.Input;
40 | using Windows.UI.Xaml.Media;
41 | using CompositionExpressionToolkit;
42 | using CompositionProToolkit.Common;
43 | using Microsoft.UI.Composition.Toolkit;
44 |
45 | namespace FluidBanner
46 | {
47 | public sealed class FluidBanner : Panel
48 | {
49 | #region Constants
50 |
51 | public const int DefaultDecodeWidth = 0;
52 | public const int DefaultDecodeHeight = 0;
53 | public const float ScaleDownFactor = 0.7f;
54 | public const float HoverScaleFactor = 1.1f;
55 | public const double DefaultItemGap = 30;
56 | public const float TargetOpacity = 0f;
57 | public static TimeSpan DefaultOpacityAnimationDuration = TimeSpan.FromMilliseconds(900);
58 | public static TimeSpan DefaultScaleAnimationDuration = TimeSpan.FromMilliseconds(900);
59 | public static TimeSpan InsetClipAnimationDuration = TimeSpan.FromMilliseconds(600);
60 | public static TimeSpan InsetAnimationDuration = TimeSpan.FromMilliseconds(500);
61 | public static TimeSpan InsetAnimationDelayDuration = TimeSpan.FromMilliseconds(500);
62 | public static readonly Color DefaultItemBackground = Colors.Black;
63 |
64 | #endregion
65 |
66 | #region Fields
67 |
68 | private Compositor _compositor;
69 | private CompositionImageFactory _imageFactory;
70 | private ContainerVisual _rootContainer;
71 | private LayerVisual _bgLayer;
72 | private LayerVisual _topLayer;
73 | private ImplicitAnimationCollection _implicitAnimationCollection;
74 | private ContainerVisual _selectedVisual;
75 | private ContainerVisual _hoverVisual;
76 | private Dictionary _fluidItems;
77 | private List _images;
78 | private Rect _bannerBounds;
79 | private float _itemWidth;
80 | private float _itemHeight;
81 | private float _availableWidth;
82 | private float _availableHeight;
83 | private bool _isExpanding;
84 | private bool _isCollapsing;
85 | // Animations
86 | private KeyFrameAnimation _expandLeftInset;
87 | private KeyFrameAnimation _expandRightInset;
88 | private KeyFrameAnimation _expandInsetClip;
89 | private KeyFrameAnimation _collapseLeftInset;
90 | private KeyFrameAnimation _collapseRightInset;
91 | private KeyFrameAnimation _collapseInsetClip;
92 |
93 | #endregion
94 |
95 | #region Dependency Properties
96 |
97 | #region DecodeHeight
98 |
99 | ///
100 | /// DecodeHeight Dependency Property
101 | ///
102 | public static readonly DependencyProperty DecodeHeightProperty =
103 | DependencyProperty.Register("DecodeHeight", typeof(int), typeof(FluidBanner),
104 | new PropertyMetadata(DefaultDecodeHeight));
105 |
106 | ///
107 | /// Gets or sets the DecodeHeight property. This dependency property
108 | /// indicates the height, in pixels, that the images are decoded to.
109 | ///
110 | public int DecodeHeight
111 | {
112 | get { return (int)GetValue(DecodeHeightProperty); }
113 | set { SetValue(DecodeHeightProperty, value); }
114 | }
115 |
116 | #endregion
117 |
118 | #region DecodeWidth
119 |
120 | ///
121 | /// DecodeWidth Dependency Property
122 | ///
123 | public static readonly DependencyProperty DecodeWidthProperty =
124 | DependencyProperty.Register("DecodeWidth", typeof(int), typeof(FluidBanner),
125 | new PropertyMetadata(DefaultDecodeWidth));
126 |
127 | ///
128 | /// Gets or sets the DecodeWidth property. This dependency property
129 | /// indicates the width, in pixels, that the images are decoded to.
130 | ///
131 | public int DecodeWidth
132 | {
133 | get { return (int)GetValue(DecodeWidthProperty); }
134 | set { SetValue(DecodeWidthProperty, value); }
135 | }
136 |
137 | #endregion
138 |
139 | #region ItemBackground
140 |
141 | ///
142 | /// ItemBackground Dependency Property
143 | ///
144 | public static readonly DependencyProperty ItemBackgroundProperty =
145 | DependencyProperty.Register("ItemBackground", typeof(Color), typeof(FluidBanner),
146 | new PropertyMetadata(DefaultItemBackground, OnItemBackgroundChanged));
147 |
148 | ///
149 | /// Gets or sets the ItemBackground property. This dependency property
150 | /// indicates the color for the background of the item.
151 | ///
152 | public Color ItemBackground
153 | {
154 | get { return (Color)GetValue(ItemBackgroundProperty); }
155 | set { SetValue(ItemBackgroundProperty, value); }
156 | }
157 |
158 | ///
159 | /// Handles changes to the ItemBackground property.
160 | ///
161 | /// FluidBanner
162 | /// DependencyProperty changed event arguments
163 | private static void OnItemBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
164 | {
165 | var panel = (FluidBanner)d;
166 | panel.OnItemBackgroundChanged();
167 | }
168 |
169 | ///
170 | /// Provides the class instance an opportunity to handle changes to the ItemBackground property.
171 | ///
172 | private void OnItemBackgroundChanged()
173 | {
174 | // Refresh Layout
175 | InvalidateArrange();
176 | }
177 |
178 | #endregion
179 |
180 | #region ItemGap
181 |
182 | ///
183 | /// ItemGap Dependency Property
184 | ///
185 | public static readonly DependencyProperty ItemGapProperty =
186 | DependencyProperty.Register("ItemGap", typeof(double), typeof(FluidBanner),
187 | new PropertyMetadata(DefaultItemGap, OnItemGapChanged));
188 |
189 | ///
190 | /// Gets or sets the ItemGap property. This dependency property
191 | /// indicates the gap between adjacent items in the banner.
192 | ///
193 | public double ItemGap
194 | {
195 | get { return (double)GetValue(ItemGapProperty); }
196 | set { SetValue(ItemGapProperty, value); }
197 | }
198 |
199 | ///
200 | /// Handles changes to the ItemGap property.
201 | ///
202 | /// FluidBanner
203 | /// DependencyProperty changed event arguments
204 | private static void OnItemGapChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
205 | {
206 | var panel = (FluidBanner)d;
207 | panel.OnItemGapChanged();
208 | }
209 |
210 | ///
211 | /// Provides the class instance an opportunity to handle changes to the ItemGap property.
212 | ///
213 | private void OnItemGapChanged()
214 | {
215 | // Refresh Layout
216 | InvalidateArrange();
217 | }
218 |
219 | #endregion
220 |
221 | #region ItemsSource
222 |
223 | ///
224 | /// ItemsSource Dependency Property
225 | ///
226 | public static readonly DependencyProperty ItemsSourceProperty =
227 | DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(FluidBanner),
228 | new PropertyMetadata(null, OnItemsSourceChanged));
229 |
230 | ///
231 | /// Gets or sets the ItemsSource property. This dependency property
232 | /// indicates the collection of Uris of images to be shown in the banner.
233 | ///
234 | public IEnumerable ItemsSource
235 | {
236 | get { return (IEnumerable)GetValue(ItemsSourceProperty); }
237 | set { SetValue(ItemsSourceProperty, value); }
238 | }
239 |
240 | ///
241 | /// Handles changes to the ItemsSource property.
242 | ///
243 | /// FluidBanner
244 | /// DependencyProperty changed event arguments
245 | private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
246 | {
247 | var panel = (FluidBanner)d;
248 | panel.OnItemsSourceChanged();
249 | }
250 |
251 | ///
252 | /// Provides the class instance an opportunity to handle changes to the ItemsSource property.
253 | ///
254 | private void OnItemsSourceChanged()
255 | {
256 | // Clear previous banner items
257 | Children.Clear();
258 |
259 | // Create the CompositionImages based on the new URIs
260 | CreateImages();
261 |
262 | // Refresh Layout
263 | InvalidateArrange();
264 | }
265 |
266 | #endregion
267 |
268 | #region Padding
269 |
270 | ///
271 | /// Padding Dependency Property
272 | ///
273 | public static readonly DependencyProperty PaddingProperty =
274 | DependencyProperty.Register("Padding", typeof(Thickness), typeof(FluidBanner),
275 | new PropertyMetadata(new Thickness(), OnPaddingChanged));
276 |
277 | ///
278 | /// Gets or sets the Padding property. This dependency property
279 | /// indicates the Padding thickness for the content.
280 | ///
281 | public Thickness Padding
282 | {
283 | get { return (Thickness)GetValue(PaddingProperty); }
284 | set { SetValue(PaddingProperty, value); }
285 | }
286 |
287 | ///
288 | /// Handles changes to the Padding property.
289 | ///
290 | /// FluidBanner
291 | /// DependencyProperty changed event arguments
292 | private static void OnPaddingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
293 | {
294 | var panel = (FluidBanner)d;
295 | panel.OnPaddingChanged();
296 | }
297 |
298 | ///
299 | /// Provides the class instance an opportunity to handle changes to the Padding property.
300 | ///
301 | private void OnPaddingChanged()
302 | {
303 | InvalidateArrange();
304 | }
305 |
306 | #endregion
307 |
308 | #endregion
309 |
310 | #region Construction / Initialization
311 |
312 | ///
313 | /// Ctor
314 | ///
315 | public FluidBanner()
316 | {
317 | _fluidItems = new Dictionary();
318 | _images = new List();
319 | Loaded += OnBannerLoaded;
320 | }
321 |
322 | #endregion
323 |
324 | #region Overrides
325 |
326 | ///
327 | /// Handles the Measure pass during Layout
328 | ///
329 | /// Available size
330 | /// Total Size required to accommodate all the Children
331 | protected override Size MeasureOverride(Size availableSize)
332 | {
333 | // Compositor will be null the very first time
334 | if (_compositor == null)
335 | {
336 | InitializeComposition();
337 | }
338 |
339 | return availableSize;
340 | }
341 |
342 | ///
343 | /// Handles the Arrange pass during Layout
344 | ///
345 | /// Final Size of the control
346 | /// Total size occupied by all the Children
347 | protected override Size ArrangeOverride(Size finalSize)
348 | {
349 | if ((ItemsSource == null) || (!ItemsSource.Any()))
350 | return finalSize;
351 |
352 | var padding = Padding;
353 | var paddingSize = padding.CollapseThickness();
354 | var width = finalSize.Width;
355 | var height = finalSize.Height;
356 | var itemCount = ItemsSource.Count();
357 |
358 | _availableWidth = (width - paddingSize.Width).Single();
359 | _availableHeight = (height - paddingSize.Height).Single();
360 | var gapCount = itemCount - 1;
361 | var totalGap = (gapCount * ItemGap).Single();
362 | var availableNonGapWidth = _availableWidth - totalGap;
363 | var availableNonGapHeight = _availableHeight;
364 |
365 | _itemWidth = availableNonGapWidth / itemCount;
366 | _itemHeight = availableNonGapHeight;
367 | _bannerBounds = new Rect(padding.Left, padding.Top, _availableWidth, _availableHeight);
368 |
369 | // Check if any visual is currently selected. If yes then obtain its index.
370 | // The new visual at that index should be selected and added to the toplayer
371 | var selectedVisualIndex = -1;
372 | if (_selectedVisual != null)
373 | {
374 | foreach (var item in _fluidItems)
375 | {
376 | selectedVisualIndex++;
377 | if (ReferenceEquals(item.Key, _selectedVisual))
378 | break;
379 | }
380 | }
381 |
382 | // Clear previous visuals
383 | _fluidItems.Clear();
384 | _rootContainer.Children.RemoveAll();
385 | _rootContainer.Size = finalSize.ToVector2();
386 |
387 | // Background Layer
388 | _bgLayer = _compositor.CreateLayerVisual();
389 | _bgLayer.Size = _rootContainer.Size;
390 | _rootContainer.Children.InsertAtBottom(_bgLayer);
391 | _bgLayer.CenterPoint = new Vector3(_bgLayer.Size * 0.5f, 0);
392 |
393 | // Top Layer
394 | _topLayer = _compositor.CreateLayerVisual();
395 | _topLayer.Size = _rootContainer.Size;
396 | _rootContainer.Children.InsertAtTop(_topLayer);
397 |
398 | // Create Visuals
399 | for (var i = 0; i < itemCount; i++)
400 | {
401 | // Add a visual for the background
402 | var bgVisual = _compositor.CreateSpriteVisual();
403 | bgVisual.Size = new Vector2(_availableWidth, _availableHeight);
404 | bgVisual.Brush = _compositor.CreateColorBrush(ItemBackground);
405 |
406 | // Create the visual for the content
407 | var contentVisual = _compositor.CreateSpriteVisual();
408 | contentVisual.Offset = Vector3.Zero;
409 | contentVisual.Size = new Vector2(_availableWidth, _availableHeight);
410 |
411 | // Load image from the Uri
412 | contentVisual.Brush = _compositor.CreateSurfaceBrush(_images.ElementAt(i).Surface);
413 |
414 | // Create the container for the content
415 | var container = _compositor.CreateContainerVisual();
416 | container.Offset = new Vector3((float)padding.Left, (float)padding.Top, 0);
417 | container.Size = new Vector2(_availableWidth, _availableHeight);
418 |
419 | // Calculate the Inset Clip
420 | var left = i * (_itemWidth + ItemGap.Single());
421 | var right = _availableWidth - (left + _itemWidth);
422 | container.Properties.InsertScalar("LeftInset", left);
423 | container.Properties.InsertScalar("RightInset", right);
424 | // If the current value of 'i' same as the index of the previously selected visual
425 | // then the InsetClip value should be appropriately set
426 | container.Clip = (i == selectedVisualIndex) ?
427 | _compositor.CreateInsetClip(0, 0, 0, 0) : _compositor.CreateInsetClip(left, 0, right, 0);
428 |
429 | // Center Point
430 | container.CenterPoint = new Vector3(left + (_itemWidth / 2f), (float)padding.Top + (_itemHeight / 2f), 0);
431 | contentVisual.CenterPoint = container.CenterPoint;
432 | container.ImplicitAnimations = _implicitAnimationCollection;
433 | contentVisual.ImplicitAnimations = _implicitAnimationCollection;
434 |
435 | // Set the container content
436 | container.Children.InsertAtTop(contentVisual);
437 | container.Children.InsertAtBottom(bgVisual);
438 |
439 | // Is the current value of 'i' same as the index of the previously selected visual
440 | if (i == selectedVisualIndex)
441 | {
442 | // Add to the Top Layer
443 | _topLayer.Children.InsertAtTop(container);
444 | _selectedVisual = container;
445 | }
446 | else
447 | {
448 | // Add to Background Layer
449 | _bgLayer.Children.InsertAtTop(container);
450 | }
451 |
452 | // Add Visual to the fluid items
453 | _fluidItems[container] = new Rect(padding.Left + left, padding.Top, _itemWidth, _itemHeight);
454 | }
455 |
456 | // If there was a previously selected visual then Scale back
457 | // and fade the other visuals in the banner
458 | if (selectedVisualIndex > -1)
459 | {
460 | //
461 | foreach (var child in _bgLayer.Children)
462 | {
463 | child.Scale = new Vector3(ScaleDownFactor, ScaleDownFactor, 1);
464 | child.Opacity = TargetOpacity;
465 | }
466 | }
467 |
468 | // Update Animations' keyframes
469 | _expandRightInset.InsertKeyFrame(1f, _availableWidth - _itemWidth);
470 | _collapseInsetClip.InsertKeyFrame(1f, _availableWidth - _itemWidth);
471 |
472 | // Add the rootContainer to the visual tree
473 | ElementCompositionPreview.SetElementChildVisual(this, _rootContainer);
474 |
475 | return base.ArrangeOverride(finalSize);
476 | }
477 |
478 | #endregion
479 |
480 | #region Event Handlers
481 |
482 | ///
483 | /// Handle the event when the control is loaded.
484 | ///
485 | /// Sender
486 | /// RoutedEventArgs
487 | private void OnBannerLoaded(object sender, RoutedEventArgs e)
488 | {
489 | // Track the pointer pressed event
490 | PointerPressed += OnPointerPressed;
491 | PointerMoved += OnPointerMoved;
492 | }
493 |
494 | ///
495 | /// Handles the PointerMoved event
496 | ///
497 | /// FluidBanner
498 | /// PointerRoutedEventArgs
499 | private void OnPointerMoved(object sender, PointerRoutedEventArgs e)
500 | {
501 | if ((ItemsSource == null) || (!ItemsSource.Any()) ||
502 | _isExpanding || _isCollapsing || _selectedVisual != null)
503 | return;
504 |
505 | var point = e.GetCurrentPoint(this).Position;
506 |
507 | HandlePointerPosition(point);
508 | }
509 |
510 | ///
511 | /// Handles the PointerPressed event
512 | ///
513 | /// FluidBanner
514 | /// PointerRoutedEventArgs
515 | private async void OnPointerPressed(object sender, PointerRoutedEventArgs e)
516 | {
517 | if ((ItemsSource == null) || (!ItemsSource.Any()) ||
518 | _isExpanding || _isCollapsing)
519 | return;
520 |
521 | var point = e.GetCurrentPoint(this).Position;
522 |
523 | if (_selectedVisual == null)
524 | {
525 | _selectedVisual = _fluidItems.Where(t => t.Value.Contains(point))
526 | .Select(t => t.Key)
527 | .FirstOrDefault();
528 |
529 | if (_selectedVisual != null)
530 | {
531 | ResetHover(_hoverVisual);
532 | _hoverVisual = null;
533 | await SelectBannerItem();
534 | }
535 | }
536 | else
537 | {
538 | if (_bannerBounds.Contains(point))
539 | {
540 | ResetBannerItems();
541 | }
542 | }
543 | }
544 |
545 | #endregion
546 |
547 | #region Helpers
548 |
549 | ///
550 | /// Initialize all Composition related stuff here (Compositor, Animations etc)
551 | ///
552 | private void InitializeComposition()
553 | {
554 | var rootVisual = ElementCompositionPreview.GetElementVisual(this);
555 | // Compositor
556 | _compositor = rootVisual.Compositor;
557 |
558 | // Final Value Expressions
559 | var vector3Expr = _compositor.CreateFinalValueExpression();
560 | var scalarExpr = _compositor.CreateFinalValueExpression();
561 |
562 | // Opacity Animation
563 | var opacityAnimation = _compositor.CreateKeyFrameAnimation()
564 | .HavingDuration(DefaultOpacityAnimationDuration)
565 | .ForTarget(() => rootVisual.Opacity);
566 | opacityAnimation.InsertExpressionKeyFrame(1f, scalarExpr);
567 |
568 | // Scale Animation
569 | var scaleAnimation = _compositor.CreateKeyFrameAnimation()
570 | .HavingDuration(DefaultScaleAnimationDuration)
571 | .ForTarget(() => rootVisual.Scale);
572 | scaleAnimation.InsertExpressionKeyFrame(1f, vector3Expr);
573 |
574 | // ImplicitAnimation
575 | _implicitAnimationCollection = _compositor.CreateImplicitAnimationCollection();
576 | _implicitAnimationCollection["Opacity"] = opacityAnimation.Animation;
577 | _implicitAnimationCollection["Scale"] = scaleAnimation.Animation;
578 |
579 | // Expand Animations
580 | _expandLeftInset = _compositor.CreateKeyFrameAnimation()
581 | .HavingDuration(InsetAnimationDuration)
582 | .DelayBy(InsetAnimationDelayDuration);
583 |
584 | _expandLeftInset.InsertKeyFrame(1f, 0);
585 |
586 | _expandRightInset = _compositor.CreateKeyFrameAnimation()
587 | .HavingDuration(InsetAnimationDuration)
588 | .DelayBy(InsetAnimationDelayDuration);
589 |
590 | _expandInsetClip = _compositor.CreateKeyFrameAnimation()
591 | .HavingDuration(InsetClipAnimationDuration);
592 |
593 | _expandInsetClip.InsertKeyFrame(1f, 0);
594 |
595 | // Collapse Animations
596 | _collapseLeftInset = _compositor.CreateKeyFrameAnimation()
597 | .HavingDuration(InsetAnimationDuration);
598 |
599 | _collapseRightInset = _compositor.CreateKeyFrameAnimation()
600 | .HavingDuration(InsetAnimationDuration);
601 |
602 | _collapseInsetClip = _compositor.CreateKeyFrameAnimation()
603 | .HavingDuration(InsetClipAnimationDuration);
604 |
605 | // Root Container
606 | _rootContainer = _compositor.CreateContainerVisual();
607 |
608 | // Image Factory
609 | _imageFactory = CompositionImageFactory.CreateCompositionImageFactory(_compositor);
610 |
611 | // If the ItemsSource has any items then create their corresponding images
612 | CreateImages();
613 | }
614 |
615 | ///
616 | /// Creates CompositionImages based on the URIs in ItemsSource
617 | ///
618 | private void CreateImages()
619 | {
620 | _images.Clear();
621 | var options = new CompositionImageOptions
622 | {
623 | DecodeWidth = DecodeWidth,
624 | DecodeHeight = DecodeHeight
625 | };
626 |
627 | if ((ItemsSource == null) || !ItemsSource.Any())
628 | return;
629 |
630 | foreach (var uri in ItemsSource)
631 | {
632 | _images.Add(_imageFactory.CreateImageFromUri(uri, options));
633 | }
634 | }
635 |
636 | ///
637 | /// Checks if the current Pointer location is above any of the FluidBanner items
638 | /// and takes appropriate action
639 | ///
640 | /// Current Pointer location
641 | private void HandlePointerPosition(Point point)
642 | {
643 | ContainerVisual visual = _fluidItems.Where(t => t.Value.Contains(point))
644 | .Select(t => t.Key)
645 | .FirstOrDefault();
646 |
647 |
648 | // Pointer not on any visual
649 | if (visual == null)
650 | {
651 | if (_hoverVisual != null)
652 | {
653 | ResetHover(_hoverVisual);
654 | _hoverVisual = null;
655 | }
656 | }
657 | else
658 | {
659 | if (ReferenceEquals(_hoverVisual, visual))
660 | return;
661 |
662 | if (_hoverVisual != null)
663 | {
664 | ResetHover(_hoverVisual);
665 | }
666 |
667 | _hoverVisual = visual;
668 | Hover(_hoverVisual);
669 | }
670 | }
671 |
672 | ///
673 | /// Handles the Hover animation for the given visual
674 | ///
675 | /// Visual to animate
676 | private void Hover(ContainerVisual visual)
677 | {
678 | if (visual == null)
679 | return;
680 |
681 | var topIndex = visual.Children.Count - 1;
682 | var child = visual.Children.ElementAt(topIndex);
683 | if (child != null)
684 | {
685 | child.Scale = new Vector3(HoverScaleFactor, HoverScaleFactor, 1);
686 | }
687 | }
688 |
689 | ///
690 | /// Handles the Reset Hover animation for the given visual
691 | ///
692 | /// Visual to animate
693 | private void ResetHover(ContainerVisual visual)
694 | {
695 | if (visual == null)
696 | return;
697 |
698 | var topIndex = visual.Children.Count - 1;
699 | var child = visual.Children.ElementAt(topIndex);
700 | if (child != null)
701 | {
702 | child.Scale = Vector3.One;
703 | }
704 | }
705 |
706 | ///
707 | /// Performs the required animations on the selected Visual
708 | ///
709 | /// Task
710 | private async Task SelectBannerItem()
711 | {
712 | if (_selectedVisual == null)
713 | return;
714 | _isExpanding = true;
715 |
716 | // Bring the selected visual from background layer to the top layer
717 | _bgLayer.Children.Remove(_selectedVisual);
718 | _topLayer.Children.InsertAtTop(_selectedVisual);
719 |
720 | // Scale back and fade the remaining visuals in the banner
721 | foreach (var child in _bgLayer.Children)
722 | {
723 | child.Scale = new Vector3(ScaleDownFactor, ScaleDownFactor, 1);
724 | child.Opacity = TargetOpacity;
725 | }
726 |
727 | // If the first visual is selected, then no need to move it to the leftmost
728 | // position as it is already there. Just do the expand animation.
729 | if (ReferenceEquals(_selectedVisual, _fluidItems.Keys.ElementAt(0)))
730 | {
731 | // Delay the expand animation by the DefaultScaleAnimationDuration
732 | // so that the other visuals in the Banner scale down
733 | await Task.Delay((int)DefaultScaleAnimationDuration.TotalMilliseconds);
734 |
735 | _compositor.CreateScopedBatch(CompositionBatchTypes.Animation,
736 | () =>
737 | {
738 | // Expand the visual to occupy the full size of the banner
739 | _selectedVisual.Clip.StartAnimation("RightInset", _expandInsetClip.Animation);
740 | },
741 | () =>
742 | {
743 | _isExpanding = false;
744 | });
745 |
746 | }
747 | else
748 | {
749 | _compositor.CreateScopedBatch(CompositionBatchTypes.Animation,
750 | () =>
751 | {
752 | // Animate the visual to move to the left most position
753 | _selectedVisual.Clip.StartAnimation("LeftInset", _expandLeftInset.Animation);
754 | _selectedVisual.Clip.StartAnimation("RightInset", _expandRightInset.Animation);
755 | },
756 | () =>
757 | {
758 | _compositor.CreateScopedBatch(CompositionBatchTypes.Animation,
759 | () =>
760 | {
761 | // Expand the visual to occupy the full size of the banner
762 | _selectedVisual.Clip.StartAnimation("RightInset", _expandInsetClip.Animation);
763 | },
764 | () =>
765 | {
766 | _isExpanding = false;
767 | });
768 |
769 | });
770 | }
771 | }
772 |
773 | ///
774 | /// Performs to the required animation to reset selection on the Selected Visual
775 | ///
776 | private void ResetBannerItems()
777 | {
778 | if (_selectedVisual == null)
779 | return;
780 |
781 | _isCollapsing = true;
782 |
783 | // Get the value to set for the LeftInset of the InsetClip
784 | float left;
785 | _selectedVisual.Properties.TryGetScalar("LeftInset", out left);
786 | _collapseLeftInset.InsertKeyFrame(1f, left);
787 |
788 | // Get the value to set for the RightInset of the InsetClip
789 | float right;
790 | _selectedVisual.Properties.TryGetScalar("RightInset", out right);
791 | _collapseRightInset.InsertKeyFrame(1f, right);
792 |
793 | // Use a scoped batch helper to queue up animations one after the other
794 | _compositor.CreateScopedBatch(CompositionBatchTypes.Animation,
795 | () =>
796 | {
797 | // Collapse the visual
798 | _selectedVisual.Clip.StartAnimation("RightInset", _collapseInsetClip.Animation);
799 | },
800 | async () =>
801 | {
802 | // If the first visual is selected, then no need to move it to the leftmost
803 | // position as it is already there.
804 | if (!ReferenceEquals(_selectedVisual, _fluidItems.Keys.ElementAt(0)))
805 | {
806 | // Move the visual back to its original location in the banner
807 | _selectedVisual.Clip.StartAnimation("LeftInset", _collapseLeftInset.Animation);
808 | _selectedVisual.Clip.StartAnimation("RightInset", _collapseRightInset.Animation);
809 | }
810 |
811 | // Fade in the other visuals and scale them to normal size
812 | foreach (var child in _bgLayer.Children)
813 | {
814 | child.Scale = Vector3.One;
815 | child.Opacity = 1;
816 | }
817 |
818 | // Delay the exchange, of selected visual, between the top and background layers
819 | // by the DefaultScaleAnimationDuration so that other visuals in
820 | // the Banner scale back to normal
821 | await Task.Delay((int)DefaultScaleAnimationDuration.TotalMilliseconds);
822 |
823 | // Bring the selected visual from top layer to the background layer
824 | _topLayer.Children.Remove(_selectedVisual);
825 | _bgLayer.Children.InsertAtTop(_selectedVisual);
826 |
827 | _selectedVisual = null;
828 | _isCollapsing = false;
829 |
830 | // Check the current location of the Pointer and whether it is hovering
831 | // over any of the FluidBanner items
832 | HandlePointerPosition(GetPointerPosition());
833 | });
834 | }
835 |
836 | ///
837 | /// Gets the current location of the Pointer
838 | ///
839 | /// Point
840 | private Point GetPointerPosition()
841 | {
842 | var currentWindow = Window.Current;
843 |
844 | Point point;
845 |
846 | try
847 | {
848 | point = currentWindow.CoreWindow.PointerPosition;
849 | }
850 | // Accessing PointerPosition might throw UnauthorizedAccessException
851 | // when the computer is locked
852 | catch (UnauthorizedAccessException)
853 | {
854 | return new Point(double.NegativeInfinity, double.NegativeInfinity);
855 | }
856 |
857 | // Get the current window's bounds
858 | var bounds = currentWindow.Bounds;
859 |
860 | // Get the location of the FluidBanner with respect to the Window content
861 | GeneralTransform coordinate = TransformToVisual(currentWindow.Content);
862 | var location = coordinate.TransformPoint(new Point(0, 0));
863 |
864 | // Convert the point from global coordinates to the FluidBanner coordinates
865 | var result = new Point(point.X - bounds.X - location.X, point.Y - bounds.Y - location.Y);
866 |
867 | return result;
868 | }
869 |
870 |
871 | #endregion
872 | }
873 | }
874 |
--------------------------------------------------------------------------------
/FluidBanner/FluidBanner.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x86
7 | {56D2B3D6-9D4E-4C7A-BE63-C1A0AE03380B}
8 | AppContainerExe
9 | Properties
10 | FluidBanner
11 | FluidBanner
12 | en-US
13 | UAP
14 | 10.0.14388.0
15 | 10.0.10586.0
16 | 14
17 | 512
18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
19 | FluidBanner_TemporaryKey.pfx
20 |
21 |
22 | true
23 | bin\x86\Debug\
24 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
25 | ;2008
26 | full
27 | x86
28 | false
29 | prompt
30 | true
31 |
32 |
33 | bin\x86\Release\
34 | TRACE;NETFX_CORE;WINDOWS_UWP
35 | true
36 | ;2008
37 | pdbonly
38 | x86
39 | false
40 | prompt
41 | true
42 | true
43 |
44 |
45 | true
46 | bin\ARM\Debug\
47 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
48 | ;2008
49 | full
50 | ARM
51 | false
52 | prompt
53 | true
54 |
55 |
56 | bin\ARM\Release\
57 | TRACE;NETFX_CORE;WINDOWS_UWP
58 | true
59 | ;2008
60 | pdbonly
61 | ARM
62 | false
63 | prompt
64 | true
65 | true
66 |
67 |
68 | true
69 | bin\x64\Debug\
70 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
71 | ;2008
72 | full
73 | x64
74 | false
75 | prompt
76 | true
77 |
78 |
79 | bin\x64\Release\
80 | TRACE;NETFX_CORE;WINDOWS_UWP
81 | true
82 | ;2008
83 | pdbonly
84 | x64
85 | false
86 | prompt
87 | true
88 | true
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | App.xaml
97 |
98 |
99 |
100 | MainPage.xaml
101 |
102 |
103 |
104 |
105 |
106 | Designer
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | MSBuild:Compile
129 | Designer
130 |
131 |
132 | MSBuild:Compile
133 | Designer
134 |
135 |
136 |
137 |
138 | {08a7d060-593a-4978-8dcc-b2029aca7c95}
139 | Microsoft.UI.Composition.Toolkit
140 |
141 |
142 |
143 | 14.0
144 |
145 |
146 |
153 |
--------------------------------------------------------------------------------
/FluidBanner/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/FluidBanner/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.InteropServices.WindowsRuntime;
6 | using Windows.Foundation;
7 | using Windows.Foundation.Collections;
8 | using Windows.UI.Xaml;
9 | using Windows.UI.Xaml.Controls;
10 | using Windows.UI.Xaml.Controls.Primitives;
11 | using Windows.UI.Xaml.Data;
12 | using Windows.UI.Xaml.Input;
13 | using Windows.UI.Xaml.Media;
14 | using Windows.UI.Xaml.Navigation;
15 |
16 | // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
17 |
18 | namespace FluidBanner
19 | {
20 | ///
21 | /// An empty page that can be used on its own or navigated to within a Frame.
22 | ///
23 | public sealed partial class MainPage : Page
24 | {
25 | public MainPage()
26 | {
27 | this.InitializeComponent();
28 |
29 | this.Loaded += OnLoaded;
30 | }
31 |
32 | private void OnLoaded(object sender, RoutedEventArgs e)
33 | {
34 | var itemCount = 6;
35 |
36 | var items = new List();
37 | for (var i = 0; i < itemCount; i++)
38 | {
39 | items.Add(new Uri($"ms-appx:///Assets/Images/Image{i + 1}.jpg"));
40 | }
41 |
42 | Banner.ItemsSource = items;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/FluidBanner/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
15 |
16 |
17 | FluidBanner
18 | ratis
19 | Assets\StoreLogo.png
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/FluidBanner/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("FluidBanner")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("FluidBanner")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Version information for an assembly consists of the following four values:
18 | //
19 | // Major Version
20 | // Minor Version
21 | // Build Number
22 | // Revision
23 | //
24 | // You can specify all the values or you can default the Build and Revision Numbers
25 | // by using the '*' as shown below:
26 | // [assembly: AssemblyVersion("1.0.*")]
27 | [assembly: AssemblyVersion("1.0.0.0")]
28 | [assembly: AssemblyFileVersion("1.0.0.0")]
29 | [assembly: ComVisible(false)]
--------------------------------------------------------------------------------
/FluidBanner/Properties/Default.rd.xml:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/FluidBanner/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "CompositionExpressionToolkit": "0.2.4",
4 | "CompositionProToolkit": "0.3.0",
5 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.2"
6 | },
7 | "frameworks": {
8 | "uap10.0": {}
9 | },
10 | "runtimes": {
11 | "win10-arm": {},
12 | "win10-arm-aot": {},
13 | "win10-x86": {},
14 | "win10-x86-aot": {},
15 | "win10-x64": {},
16 | "win10-x64-aot": {}
17 | }
18 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Ratish Philip
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionGraphicsDevice.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 | #include "CompositionGraphicsDevice.h"
7 |
8 | namespace Microsoft {
9 | namespace UI {
10 | namespace Composition {
11 | namespace Toolkit {
12 |
13 | CompositionGraphicsDevice^ CompositionGraphicsDevice::CreateCompositionGraphicsDevice(Compositor^ compositor)
14 | {
15 | CompositionGraphicsDevice^ device = ref new CompositionGraphicsDevice(compositor);
16 |
17 | __abi_ThrowIfFailed(device->InitializeGraphicsDevice());
18 |
19 | return device;
20 | }
21 |
22 | // Creates an ICompositionSurface that can be provided used to create a CompositionSurfaceBrush
23 | // to be associated with a CompositionSpriteVisual.
24 | ICompositionSurface^ CompositionGraphicsDevice::CreateDrawingSurface(Windows::Foundation::Size sizePixels, DirectXPixelFormat pixelFormat, DirectXAlphaMode alphaMode)
25 | {
26 | ComPtr drawingSurface;
27 | ABI::Windows::Foundation::Size size;
28 | size.Height = sizePixels.Height;
29 | size.Width = sizePixels.Width;
30 |
31 | // Make sure to call CreateDrawingSurface under this CompositionGraphicsDevice's state lock
32 | // to avoid any races with D3D initialization and (re)creation of the underlying device.
33 | std::lock_guard lock(_stateLock);
34 |
35 | __abi_ThrowIfFailed(_igraphicsDevice->CreateDrawingSurface(
36 | size,
37 | (ABI::Windows::Graphics::DirectX::DirectXPixelFormat)pixelFormat,
38 | (ABI::Windows::Graphics::DirectX::DirectXAlphaMode)alphaMode,
39 | &drawingSurface));
40 |
41 | return reinterpret_cast(drawingSurface.Get());
42 | }
43 |
44 | // Acquires exclusive access to the underlying D3D11Device for the purpose of drawing to surfaces
45 | // created by this CompositionGraphicsDevice. Once finished, the caller should call ReleaseDrawingLock.
46 | void CompositionGraphicsDevice::AcquireDrawingLock()
47 | {
48 | _drawingLock.lock();
49 | }
50 |
51 | // Releases ownership of the drawing lock associated with the drawing surfaces created
52 | // by this CompositionGraphicsDevice. It is not safe to call this if AcquireDrawingLock
53 | // was not called previously.
54 | void CompositionGraphicsDevice::ReleaseDrawingLock()
55 | {
56 | _drawingLock.unlock();
57 | }
58 |
59 | CompositionGraphicsDevice::CompositionGraphicsDevice(Compositor^ compositor) :
60 | _compositor(compositor),
61 | _threadPoolWait(NULL)
62 | {
63 | }
64 |
65 | CompositionGraphicsDevice::~CompositionGraphicsDevice()
66 | {
67 | UninitializeDX();
68 | }
69 |
70 | // Calls InitializeDX and then creates an ICompositionGraphicsDevice which is
71 | // used to create drawing surfaces to be associated with composition visuals.
72 | HRESULT CompositionGraphicsDevice::InitializeGraphicsDevice()
73 | {
74 | HRESULT hr = S_OK;
75 | ComPtr compositorInterop;
76 | ComPtr graphicsDevice;
77 | ComPtr iCompositor;
78 |
79 | std::lock_guard lock(_stateLock);
80 |
81 | IFC(InitializeDX());
82 |
83 | iCompositor = reinterpret_cast(_compositor);
84 | IFC(iCompositor.As(&compositorInterop));
85 | IFC(compositorInterop->CreateGraphicsDevice(_graphicsFactoryBackingDXDevice.Get(), &graphicsDevice));
86 |
87 | _igraphicsDevice = graphicsDevice;
88 |
89 | Cleanup:
90 | return hr;
91 | }
92 |
93 | // Initializes DirectX and registers for device lost notifications. Should be called with _stateLock held.
94 | HRESULT CompositionGraphicsDevice::InitializeDX()
95 | {
96 | HRESULT hr = S_OK;
97 | ComPtr d3dDevice;
98 | ComPtr d3dContext;
99 | ComPtr dxgiDevice;
100 | ComPtr d2dFactory;
101 | ComPtr d2d1Device;
102 | ComPtr d3dDevice1;
103 | ComPtr d3dDevice4;
104 | ComPtr dxgiFactory;
105 |
106 | UninitializeDX();
107 |
108 | // D3D11_CREATE_DEVICE_BGRA_SUPPORT is required for Direct2D interoperability with Direct3D resources.
109 | UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
110 | D3D_FEATURE_LEVEL featureLevels[] =
111 | {
112 | D3D_FEATURE_LEVEL_11_1,
113 | D3D_FEATURE_LEVEL_11_0,
114 | D3D_FEATURE_LEVEL_10_1,
115 | D3D_FEATURE_LEVEL_10_0,
116 | D3D_FEATURE_LEVEL_9_3,
117 | D3D_FEATURE_LEVEL_9_2,
118 | D3D_FEATURE_LEVEL_9_1
119 | };
120 | D3D_FEATURE_LEVEL usedFeatureLevel;
121 |
122 | IFC(D3D11CreateDevice(
123 | nullptr,
124 | D3D_DRIVER_TYPE_HARDWARE,
125 | nullptr,
126 | creationFlags,
127 | featureLevels,
128 | ARRAYSIZE(featureLevels),
129 | D3D11_SDK_VERSION,
130 | &d3dDevice,
131 | &usedFeatureLevel,
132 | &d3dContext));
133 |
134 | if (FAILED(hr))
135 | {
136 | FAILHARD(hr);
137 | }
138 |
139 | IFC(d3dDevice.As(&d3dDevice1));
140 |
141 | // We're using our own synchronization so we can create the D2D factory in single threaded mode.
142 | IFC(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory1), &d2dFactory));
143 | IFC(d3dDevice.As(&dxgiDevice));
144 | IFC(d2dFactory->CreateDevice(dxgiDevice.Get(), &d2d1Device));
145 |
146 | _graphicsFactoryBackingDXDevice = d2d1Device;
147 |
148 | IFC(d3dDevice.As(&_d3dDevice4));
149 | IFC(AttachDeviceLostHandler());
150 |
151 | Cleanup:
152 | return hr;
153 | }
154 |
155 | // Registers a device removed event with D3D so that we can be called back
156 | // anytime our underlying D3D11Device is lost or reset. Should be called with _stateLock held.
157 | HRESULT CompositionGraphicsDevice::AttachDeviceLostHandler()
158 | {
159 | HRESULT hr = S_OK;
160 |
161 | _deviceLostEvent = CreateEvent(
162 | NULL, // Attributes
163 | TRUE, // Manual reset
164 | FALSE, // Initial state
165 | NULL); // Name
166 | if (_deviceLostEvent == NULL)
167 | {
168 | FAILHARD(HRESULT_FROM_WIN32(GetLastError()));
169 | }
170 |
171 | // It's okay for us to use a weak reference to ourself here since we unregister on destruction.
172 | _threadPoolWait = CreateThreadpoolWait(CompositionGraphicsDevice::OnDeviceLostCallback, (PVOID)this, NULL);
173 | if (_threadPoolWait == NULL)
174 | {
175 | FAILHARD(HRESULT_FROM_WIN32(GetLastError()));
176 | }
177 |
178 | SetThreadpoolWait(_threadPoolWait, _deviceLostEvent, NULL);
179 | hr = _d3dDevice4->RegisterDeviceRemovedEvent(_deviceLostEvent, &_deviceLostRegistrationCookie);
180 | if (FAILED(hr))
181 | {
182 | FAILHARD(hr);
183 | }
184 |
185 | return hr;
186 | }
187 |
188 | // Called in response to the underlying D3D11Device being lost, triggers a DeviceLost
189 | // event to any subscribers.
190 | void CALLBACK CompositionGraphicsDevice::OnDeviceLostCallback(PTP_CALLBACK_INSTANCE, PVOID context, PTP_WAIT, TP_WAIT_RESULT)
191 | {
192 | CompositionGraphicsDevice^ pThis = reinterpret_cast(context);
193 | ComPtr graphicsDeviceInterop;
194 |
195 | if (pThis == nullptr)
196 | {
197 | FAILHARD(E_POINTER);
198 | }
199 |
200 | HRESULT hr = pThis->InitializeDX();
201 | if (FAILED(hr))
202 | {
203 | FAILHARD(hr);
204 | }
205 |
206 | // Set the new DX context as the rendering device on the CompositionGraphicsDevice
207 | hr = pThis->_igraphicsDevice.As(&graphicsDeviceInterop);
208 | if (FAILED(hr))
209 | {
210 | FAILHARD(hr);
211 | }
212 |
213 | ComPtr unknownDXDevice;
214 | hr = pThis->_graphicsFactoryBackingDXDevice.As(&unknownDXDevice);
215 | if (FAILED(hr))
216 | {
217 | FAILHARD(hr);
218 | }
219 |
220 | graphicsDeviceInterop->SetRenderingDevice(unknownDXDevice.Get());
221 |
222 | // fire the event that this graphics device was lost and reinitialized
223 | pThis->DeviceLost(pThis);
224 | }
225 |
226 | // Should be called with _stateLock held.
227 | void CompositionGraphicsDevice::UninitializeDX()
228 | {
229 | if ((_deviceLostRegistrationCookie != 0) && (_d3dDevice4 != nullptr))
230 | {
231 | _d3dDevice4->UnregisterDeviceRemoved(_deviceLostRegistrationCookie);
232 | _deviceLostRegistrationCookie = 0;
233 | }
234 |
235 | if (_deviceLostEvent != NULL)
236 | {
237 | CloseHandle(_deviceLostEvent);
238 | _deviceLostEvent = NULL;
239 | }
240 |
241 | if (_threadPoolWait != NULL)
242 | {
243 | CloseThreadpoolWait(_threadPoolWait);
244 | _threadPoolWait = NULL;
245 | }
246 |
247 | _d3dDevice4 = nullptr;
248 | }
249 |
250 | } // namespace Toolkit
251 | } // namespace Composition
252 | } // namespace UI
253 | } // namespace Microsoft
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionGraphicsDevice.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | namespace Microsoft {
8 | namespace UI {
9 | namespace Composition {
10 | namespace Toolkit {
11 |
12 | // Forward declare for CompositionGraphicsDeviceLostEventHandler delegate.
13 | ref class CompositionGraphicsDevice;
14 |
15 | public delegate void CompositionGraphicsDeviceLostEventHandler(CompositionGraphicsDevice^ sender);
16 |
17 | [Windows::Foundation::Metadata::Internal]
18 | [Windows::Foundation::Metadata::WebHostHidden]
19 | public ref class CompositionGraphicsDevice sealed
20 | {
21 | public:
22 | static CompositionGraphicsDevice^ CreateCompositionGraphicsDevice(Compositor^ compositor);
23 |
24 | // Called anytime the underlying D3D11Device is lost allowing the event handler to redraw
25 | // any device specific resources.
26 | event CompositionGraphicsDeviceLostEventHandler^ DeviceLost;
27 |
28 | ICompositionSurface^ CreateDrawingSurface(
29 | Size sizePixels,
30 | DirectXPixelFormat pixelFormat,
31 | DirectXAlphaMode alphaMode);
32 |
33 | void AcquireDrawingLock();
34 |
35 | void ReleaseDrawingLock();
36 |
37 | virtual ~CompositionGraphicsDevice();
38 |
39 | private:
40 | CompositionGraphicsDevice(Compositor^ compositor);
41 |
42 | HRESULT InitializeGraphicsDevice();
43 |
44 | HRESULT InitializeDX();
45 |
46 | void UninitializeDX();
47 |
48 | HRESULT AttachDeviceLostHandler();
49 |
50 | static void CALLBACK OnDeviceLostCallback(PTP_CALLBACK_INSTANCE, PVOID context, PTP_WAIT, TP_WAIT_RESULT);
51 |
52 | private:
53 | Compositor^ _compositor;
54 |
55 | // Any changes to member variables should be made while holding _stateLock.
56 | ComPtr _graphicsFactoryBackingDXDevice;
57 | ComPtr _igraphicsDevice;
58 | ComPtr _d3dDevice4;
59 | HANDLE _deviceLostEvent;
60 | DWORD _deviceLostRegistrationCookie;
61 | PTP_WAIT _threadPoolWait;
62 |
63 | std::mutex _stateLock;
64 | std::mutex _drawingLock;
65 | };
66 |
67 | } // Toolkit
68 | } // Composition
69 | } // UI
70 | } // Microsoft
71 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImage.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 |
7 | #include "WICBitmapSource.h"
8 |
9 | #include "CompositionImageOptions.h"
10 | #include "CompositionGraphicsDevice.h"
11 | #include "CompositionImage.h"
12 |
13 | namespace Microsoft {
14 | namespace UI {
15 | namespace Composition {
16 | namespace Toolkit {
17 |
18 | CompositionImage::CompositionImage(
19 | Compositor^ compositor,
20 | CompositionGraphicsDevice^ graphicsDevice,
21 | Windows::Foundation::Uri^ uri,
22 | StorageFile^ file,
23 | CompositionImageOptions^ options) :
24 | _compositor(compositor),
25 | _imageOptions(options),
26 | _graphicsDevice(graphicsDevice),
27 | _uri(uri),
28 | _file(file)
29 | {
30 | if (options != nullptr)
31 | {
32 | _sizeDecode.cx = options->DecodeWidth;
33 | _sizeDecode.cy = options->DecodeHeight;
34 | }
35 | }
36 |
37 | CompositionImage::CompositionImage(
38 | Compositor^ compositor,
39 | CompositionGraphicsDevice^ graphicsDevice,
40 | const Array^ pixels,
41 | int pixelWidth,
42 | int pixelHeight) :
43 | _compositor(compositor),
44 | _graphicsDevice(graphicsDevice)
45 | {
46 | _sizeDecode.cx = pixelWidth;
47 | _sizeDecode.cy = pixelHeight;
48 | }
49 |
50 | // Creates a CompostionImage given a byte[] buffer and pixel width and height.
51 | CompositionImage^ CompositionImage::CreateCompositionImage(
52 | Compositor^ compositor,
53 | CompositionGraphicsDevice^ graphicsDevice,
54 | const Array^ pixels,
55 | int pixelWidth,
56 | int pixelHeight)
57 | {
58 | if (pixels == nullptr)
59 | {
60 | ERR(E_INVALIDARG, L"CompositionImage requires a buffer to be created.");
61 | __abi_ThrowIfFailed(E_INVALIDARG);
62 | }
63 |
64 | CompositionImage^ image = ref new CompositionImage(
65 | compositor,
66 | graphicsDevice,
67 | pixels,
68 | pixelWidth,
69 | pixelHeight);
70 |
71 | //
72 | // Create the underlying composition drawing surface using the given graphics device
73 | //
74 | Windows::Foundation::Size initialPixelSize;
75 | initialPixelSize.Width = static_cast(pixelWidth);
76 | initialPixelSize.Height = static_cast(pixelHeight);
77 |
78 | ICompositionSurface^ surface = graphicsDevice->CreateDrawingSurface(
79 | initialPixelSize,
80 | DirectXPixelFormat::B8G8R8A8UIntNormalized,
81 | DirectXAlphaMode::Premultiplied);
82 |
83 | image->_sizeCompositionSurface = image->_sizeDecode;
84 | image->_compositionSurface =
85 | reinterpret_cast(surface);
86 |
87 | __abi_ThrowIfFailed(image->_compositionSurface.As(&image->_compositionSurfaceInterop));
88 |
89 | //
90 | // Register for device lost since we must redraw the bitmap into the composition surface
91 | // in the case the graphics device is reset.
92 | //
93 | graphicsDevice->DeviceLost += ref new CompositionGraphicsDeviceLostEventHandler(
94 | image,
95 | &CompositionImage::HandleGraphicsDeviceLost);
96 |
97 | //
98 | // Create a bitmap and draw this bitmap on surfece.
99 | //
100 | image->DrawBitmapOnSurface(pixels, pixelWidth, pixelHeight);
101 |
102 | return image;
103 | }
104 |
105 | // Creates a CompostionImage given a Uri or a StorageFile object, only one should be provided.
106 | CompositionImage^ CompositionImage::CreateCompositionImage(
107 | Compositor^ compositor,
108 | CompositionGraphicsDevice^ graphicsDevice,
109 | Windows::Foundation::Uri^ uri,
110 | StorageFile^ file,
111 | CompositionImageOptions^ options)
112 | {
113 | if (uri != nullptr && file != nullptr)
114 | {
115 | ERR(E_INVALIDARG, L"CompositionImage cannot be created with both a file and uri.");
116 | __abi_ThrowIfFailed(E_INVALIDARG);
117 | }
118 |
119 | if (uri == nullptr && file == nullptr)
120 | {
121 | ERR(E_INVALIDARG, L"CompositionImage requires either a file or uri to be created.");
122 | __abi_ThrowIfFailed(E_INVALIDARG);
123 | }
124 |
125 | CompositionImage^ image = ref new CompositionImage(
126 | compositor,
127 | graphicsDevice,
128 | uri,
129 | file,
130 | options);
131 |
132 | //
133 | // Create the underlying composition drawing surface using the given graphics device
134 | //
135 | Windows::Foundation::Size initialPixelSize;
136 | initialPixelSize.Width = static_cast(image->_sizeDecode.cx);
137 | initialPixelSize.Height = static_cast(image->_sizeDecode.cy);
138 |
139 | ICompositionSurface^ surface = graphicsDevice->CreateDrawingSurface(
140 | initialPixelSize,
141 | DirectXPixelFormat::B8G8R8A8UIntNormalized,
142 | DirectXAlphaMode::Premultiplied);
143 |
144 | image->_sizeCompositionSurface = image->_sizeDecode;
145 | image->_compositionSurface =
146 | reinterpret_cast(surface);
147 |
148 | __abi_ThrowIfFailed(image->_compositionSurface.As(&image->_compositionSurfaceInterop));
149 |
150 | //
151 | // Register for device lost since we must redraw the bitmap into the composition surface
152 | // in the case the graphics device is reset.
153 | //
154 | graphicsDevice->DeviceLost += ref new CompositionGraphicsDeviceLostEventHandler(
155 | image,
156 | &CompositionImage::HandleGraphicsDeviceLost);
157 |
158 | //
159 | // Kick off the image load operation so that the image will download and decode in the background.
160 | //
161 | image->_loadAsyncAction = image->LoadImageAsync();
162 |
163 | return image;
164 | }
165 |
166 | // Abstracts the actual download of the image and drawing into the underlying surface.
167 | // An async action is scheduled as a result of calling LoadImageAsync and
168 | // its result should be obtained vby registering for the ImageLoaded event.
169 | IAsyncAction^ CompositionImage::LoadImageAsync()
170 | {
171 | //
172 | // Downloading, decoding, and then drawing the bitmap could take some time so create an async operation
173 | // to perform this action synchronously off the caller's thread.
174 | //
175 | return create_async([this]() -> void
176 | {
177 | //
178 | // Load the Buffer from the Uri or StorageFile that backs this composition image.
179 | //
180 | IBuffer^ imageRawBuffer = nullptr;
181 | if (_uri != nullptr)
182 | {
183 | imageRawBuffer = LoadImageFromUri(_uri);
184 | }
185 | else if (_file != nullptr)
186 | {
187 | imageRawBuffer = LoadImageFromFile(_file);
188 | }
189 |
190 | if (imageRawBuffer == nullptr)
191 | {
192 | ReportImageLoaded(CompositionImageLoadStatus::FileAccessError);
193 | return;
194 | }
195 |
196 | //
197 | // Decode the buffer into an IWICBitmapSource
198 | //
199 | ComPtr bitmapSource = DecodeBufferIntoBitmap(imageRawBuffer, _sizeDecode);
200 | if (bitmapSource == nullptr)
201 | {
202 | // If for some reason the DecodeBufferIntoBitmap failed report a decode error to ImageLoaded.
203 | ReportImageLoaded(CompositionImageLoadStatus::DecodeError);
204 | return;
205 | }
206 |
207 | //
208 | // Update the size of the loaded bitmap now that it's decoded
209 | //
210 | UINT cxSizeBitmap;
211 | UINT cySizeBitmap;
212 | HRESULT hr = bitmapSource->GetSize(&cxSizeBitmap, &cySizeBitmap);
213 | if (FAILED(hr))
214 | {
215 | ERR(hr, L"Failed to get the size of the decoded bitmap");
216 | ReportImageLoaded(CompositionImageLoadStatus::Other);
217 | return;
218 | }
219 |
220 | _sizeBitmapSource.cx = cxSizeBitmap;
221 | _sizeBitmapSource.cy = cySizeBitmap;
222 |
223 | //
224 | // Draw the bitmap into the composition surface that backs this CompositionImage
225 | //
226 | hr = DrawBitmapOnSurface(bitmapSource);
227 | if (FAILED(hr))
228 | {
229 | ERR(hr, L"Failed to draw bitmap on drawing surface.");
230 |
231 | // Only report an image load error in the case we get a return value other than
232 | // DXGI_ERROR_DEVICE_REMOVED since those error cases are handled by the DeviceLost
233 | // callback which is recover the image load on the new device.
234 | if (hr != DXGI_ERROR_DEVICE_REMOVED)
235 | {
236 | ReportImageLoaded(CompositionImageLoadStatus::NotEnoughResources);
237 | }
238 |
239 | return;
240 | }
241 |
242 | ReportImageLoaded(CompositionImageLoadStatus::Success);
243 | });
244 | };
245 |
246 | // Loads the raw image contents from the Uri synchronously and returns the resulting IBuffer
247 | // which contains the raw image contents.
248 | IBuffer^ CompositionImage::LoadImageFromUri(Uri^ uri)
249 | {
250 | task_completion_event fileLoaded;
251 | task fileLoadTask;
252 |
253 | if (uri != nullptr)
254 | {
255 | if ((uri->SchemeName == L"http") ||
256 | (uri->SchemeName == L"https"))
257 | {
258 | fileLoadTask =
259 | create_task(StorageFile::CreateStreamedFileFromUriAsync(uri->Extension, uri, nullptr));
260 | }
261 | else if (uri->SchemeName == L"file")
262 | {
263 | ERR(E_INVALIDARG, L"Uri cannot have the file scheme, use CreateCompositionImageFromFile instead.");
264 | }
265 | else
266 | {
267 | fileLoadTask = create_task(StorageFile::GetFileFromApplicationUriAsync(uri));
268 | }
269 |
270 | fileLoadTask.then([fileLoaded](StorageFile^ file)
271 | {
272 | fileLoaded.set(LoadImageFromFile(file));
273 | });
274 | }
275 |
276 | return create_task(fileLoaded).get();
277 | }
278 |
279 | // Opens a StorageFile and reads its contents into the returned IBuffer synchronously.
280 | IBuffer^ CompositionImage::LoadImageFromFile(StorageFile^ file)
281 | {
282 | task_completion_event fileLoaded;
283 |
284 | auto fileLoadTask = create_task(file->OpenAsync(FileAccessMode::Read)).then(
285 | [file, fileLoaded](task task)
286 | {
287 | IRandomAccessStream^ readStream = task.get();
288 | unsigned long long size = readStream->Size;
289 |
290 | DataReader^ dataReader = ref new DataReader(readStream);
291 | return create_task(dataReader->LoadAsync(static_cast(size))).then(
292 | [dataReader, fileLoaded](unsigned int numBytesLoaded)
293 | {
294 | IBuffer^ imageRawBuffer = dataReader->ReadBuffer(numBytesLoaded);
295 |
296 | // As a best practice, explicitly close the dataReader resource as soon as it is no longer needed.
297 | delete dataReader;
298 |
299 | fileLoaded.set(imageRawBuffer);
300 | });
301 | });
302 |
303 | return create_task(fileLoaded).get();
304 | }
305 |
306 | // Decodes the image respresented by the raw data contained in imageRawBuffer into a bitmap synchronously.
307 | ComPtr CompositionImage::DecodeBufferIntoBitmap(IBuffer^ imageRawBuffer, SIZE sizeDecode)
308 | {
309 | HRESULT hr = S_OK;
310 | bool succeeded = false;
311 |
312 | ComPtr byteBuffer;
313 | ComPtr wicStream;
314 | ComPtr imagingFactory;
315 | ComPtr bitmapDecoder;
316 | ComPtr bitmapFrame;
317 | ComPtr bitmapSource;
318 | ComPtr sourceTransform;
319 |
320 | GUID pixelFormat = GUID_NULL;
321 |
322 | UINT cxImage = 0;
323 | UINT cyImage = 0;
324 | UINT cxScaled = 0;
325 | UINT cyScaled = 0;
326 |
327 | FAILHARD(CoCreateInstance(
328 | CLSID_WICImagingFactory,
329 | NULL,
330 | CLSCTX_INPROC_SERVER,
331 | IID_IWICImagingFactory,
332 | (LPVOID*)&imagingFactory
333 | ));
334 |
335 | unsigned int bufferLength = imageRawBuffer->Length;
336 | ComPtr inspectableSavedBits(reinterpret_cast(imageRawBuffer));
337 | ComPtr byteAccess;
338 | IFC(inspectableSavedBits.As(&byteAccess));
339 |
340 | BYTE* bytes = nullptr;
341 | IFC(byteAccess->Buffer(&bytes));
342 |
343 | IFC(imagingFactory->CreateStream(wicStream.GetAddressOf()));
344 | IFC(wicStream->InitializeFromMemory(bytes, bufferLength));
345 |
346 | hr = imagingFactory->CreateDecoderFromStream(
347 | wicStream.Get(),
348 | NULL,
349 | WICDecodeMetadataCacheOnDemand,
350 | &bitmapDecoder);
351 | IFC(hr);
352 |
353 | //
354 | // Initialize bitmapFrame with the original image frame. bitmapSource
355 | // is updated throughout this function as additional transforms
356 | // (scaling, conversion, cropping, etc) are added to the image prior
357 | // to the actual decoding.
358 | //
359 | IFC(bitmapDecoder->GetFrame(0, &bitmapFrame));
360 |
361 | bitmapSource = bitmapFrame;
362 |
363 | //
364 | // Determine if source image may have an alpha channel
365 | //
366 | IFC(bitmapSource->GetPixelFormat(&pixelFormat));
367 |
368 | //
369 | // If there's no alpha channel, all pixels can be assumed to be opaque, allowing
370 | // converting to 32bppPBGRA instead of 32bppBGRA. This allows WIC scalers
371 | // to skip premultiplying and unpremultiplying.
372 | //
373 | REFWICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormat32bppPBGRA;
374 |
375 | //
376 | // Compute the size that the image should be resized to, and the
377 | // subrect that should be cropped out.
378 | //
379 | IFC(bitmapSource->GetSize(&cxImage, &cyImage));
380 |
381 | //
382 | // It's safe to use these without a lock, since this is the only thread
383 | // that can access them.
384 | //
385 | if (sizeDecode.cx == 0)
386 | {
387 | sizeDecode.cx = cxImage;
388 | }
389 |
390 | if (sizeDecode.cy == 0)
391 | {
392 | sizeDecode.cy = cyImage;
393 | }
394 |
395 | // Do nothing if decode size is greater than the actual size.
396 | if ((cxImage > (UINT)sizeDecode.cx) ||
397 | (cyImage > (UINT)sizeDecode.cy))
398 | {
399 | //
400 | // Need to reduce both dimensions by the same %; use the one that
401 | // will maximize shrinkage to ensure the image <= max specified.
402 | //
403 | if ((sizeDecode.cx * cyImage) < (sizeDecode.cy * cxImage))
404 | {
405 | // Maximize the width and then scale the height.
406 | cxScaled = sizeDecode.cx;
407 | cyScaled = cxScaled * cyImage / cxImage;
408 | }
409 | else
410 | {
411 | // Maximize the height and then scale the width.
412 | cyScaled = sizeDecode.cy;
413 | cxScaled = cyScaled * cxImage / cyImage;
414 | }
415 | }
416 | else
417 | {
418 | cxScaled = cxImage;
419 | cyScaled = cyImage;
420 | }
421 |
422 | // We cannot decode to a dimension of zero.
423 | cxScaled = max(1u, cxScaled);
424 | cyScaled = max(1u, cyScaled);
425 |
426 | if (cxImage >= INT_MAX || cyImage >= INT_MAX)
427 | {
428 | ERR(E_INVALIDARG, L"Image size is too large to decode.");
429 | IFC(E_INVALIDARG);
430 | }
431 |
432 | //
433 | // Convert pixel format if needed. These converters are chained, so if the bitmap
434 | // is getting a source transform, this wraps the transformer, otherwise the default
435 | // image.
436 | //
437 | if ((pixelFormat != GUID_NULL) && (pixelFormat != desiredPixelFormat))
438 | {
439 | ComPtr formatConverter;
440 |
441 | IFC(imagingFactory->CreateFormatConverter(&formatConverter));
442 | hr = formatConverter->Initialize(
443 | bitmapSource.Get(), // Input bitmap to convert
444 | desiredPixelFormat, // Destination pixel format
445 | WICBitmapDitherTypeNone, // Specified dither pattern
446 | NULL, // Specify a particular palette
447 | 0.0f, // Alpha threshold
448 | WICBitmapPaletteTypeCustom // Palette translation type
449 | );
450 | IFC(hr);
451 |
452 | bitmapSource = formatConverter;
453 | }
454 |
455 | //
456 | // Scale the image if necessary.
457 | //
458 | if ((cxScaled != cxImage) || (cyScaled != cyImage))
459 | {
460 | ComPtr scaler;
461 |
462 | IFC(imagingFactory->CreateBitmapScaler(&scaler));
463 |
464 | WICBitmapInterpolationMode interpolationMode = WICBitmapInterpolationModeFant;
465 |
466 | if ((cxScaled * 2 >= cxImage) && (cyScaled * 2 >= cyImage))
467 | {
468 | //
469 | // If upscaling, or if downscaling less than 50%, use Linear interpolation
470 | // which is faster than Fant and will provide similar quality for these cases.
471 | //
472 |
473 | interpolationMode = WICBitmapInterpolationModeLinear;
474 | }
475 |
476 | hr = scaler->Initialize(
477 | bitmapSource.Get(),
478 | cxScaled,
479 | cyScaled,
480 | interpolationMode);
481 | IFC(hr);
482 |
483 | bitmapSource = scaler;
484 | }
485 |
486 | succeeded = true;
487 |
488 | Cleanup:
489 | if (succeeded)
490 | {
491 | return bitmapSource;
492 | }
493 |
494 | return nullptr;
495 | }
496 |
497 | HRESULT CompositionImage::DrawBitmapOnSurface(const Array^ buffer, int pixelWidth, int pixelHeight)
498 | {
499 | HRESULT hr = S_OK;
500 | ComPtr d2d1DeviceContext;
501 | ComPtr d2d1BitmapSource;
502 | ComPtr compatibleRenderTarget;
503 |
504 | // Keep track of whether or not BeginDraw was called successfully on our backing surface so
505 | // that we know in the Cleanup whether or not to call EndDraw.
506 | bool drawingBegun = false;
507 |
508 | // CompositionGraphicsDevice only allows one surface to be active (i.e. BeginDraw has been called but not EndDraw) so
509 | // we acquire the drawing lock so that all drawing onto composition surfaces created by the CompositionGraphicsDevice
510 | // happen synchronously with no overlap.
511 | _graphicsDevice->AcquireDrawingLock();
512 |
513 | RECT rect;
514 | rect.left = 0;
515 | rect.top = 0;
516 | rect.right = pixelWidth;
517 | rect.bottom = pixelHeight;
518 | POINT offset;
519 |
520 | IFC(_compositionSurfaceInterop->BeginDraw(
521 | &rect,
522 | IID_PPV_ARGS(&d2d1DeviceContext),
523 | &offset));
524 | drawingBegun = true;
525 |
526 | IFC(d2d1DeviceContext->CreateCompatibleRenderTarget(&compatibleRenderTarget));
527 |
528 | //
529 | // Create bitmap and options.
530 | //
531 | D2D1_SIZE_U d2d1Size = D2D1::SizeU(pixelWidth, pixelHeight);
532 |
533 | D2D1_BITMAP_PROPERTIES bitmapProperties =
534 | D2D1::BitmapProperties(
535 | D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED));
536 |
537 | hr = compatibleRenderTarget->CreateBitmap(d2d1Size, buffer->Data, d2d1Size.width * 4, &bitmapProperties, &d2d1BitmapSource);
538 | if (FAILED(hr))
539 | {
540 | ERR(hr, L"Failed to create bitmap form pixels.");
541 | IFC(hr);
542 | }
543 |
544 | D2D1_RECT_F d2d1Rect;
545 | d2d1Rect.left = static_cast(offset.x);
546 | d2d1Rect.top = static_cast(offset.y);
547 | d2d1Rect.right = static_cast(d2d1Rect.left + pixelWidth);
548 | d2d1Rect.bottom = static_cast(d2d1Rect.top + pixelHeight);
549 |
550 | d2d1DeviceContext->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
551 | d2d1DeviceContext->DrawBitmap(
552 | d2d1BitmapSource.Get(),
553 | &d2d1Rect,
554 | 1.0f,
555 | D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
556 | &d2d1Rect);
557 |
558 | Cleanup:
559 | if (drawingBegun)
560 | {
561 | HRESULT endDrawHr = _compositionSurfaceInterop->EndDraw();
562 | if (FAILED(endDrawHr))
563 | {
564 | ERR(endDrawHr, L"Failed to EndDraw");
565 | }
566 | }
567 |
568 | _graphicsDevice->ReleaseDrawingLock();
569 |
570 | return hr;
571 | }
572 |
573 | // Draws the given bitmap on the ICompositionSurface represented by this CompositionImage synchronously.
574 | // This is thread-safe since it only accesses members under the CompositionGraphicsDevice drawing lock.
575 | HRESULT CompositionImage::DrawBitmapOnSurface(ComPtr& bitmapSource)
576 | {
577 | HRESULT hr = S_OK;
578 | ComPtr d2d1DeviceContext;
579 | ComPtr d2d1BitmapSource;
580 | ComPtr compatibleRenderTarget;
581 |
582 | // Keep track of whether or not BeginDraw was called successfully on our backing surface so
583 | // that we know in the Cleanup whether or not to call EndDraw.
584 | bool drawingBegun = false;
585 |
586 | // CompositionGraphicsDevice only allows one surface to be active (i.e. BeginDraw has been called but not EndDraw) so
587 | // we acquire the drawing lock so that all drawing onto composition surfaces created by the CompositionGraphicsDevice
588 | // happen synchronously with no overlap.
589 | _graphicsDevice->AcquireDrawingLock();
590 |
591 | // Resize the composition drawing surface to match the size of the bitmap if needed.
592 | if ((_sizeCompositionSurface.cx != _sizeBitmapSource.cx) ||
593 | (_sizeCompositionSurface.cx != _sizeBitmapSource.cy))
594 | {
595 | hr = _compositionSurfaceInterop->Resize(_sizeBitmapSource);
596 | if (FAILED(hr))
597 | {
598 | ERR(hr, L"Failed to resize composition image surface.");
599 | IFC(hr);
600 | }
601 |
602 | _sizeCompositionSurface = _sizeBitmapSource;
603 | }
604 |
605 | RECT rect;
606 | rect.left = 0;
607 | rect.top = 0;
608 | rect.right = _sizeBitmapSource.cx;
609 | rect.bottom = _sizeBitmapSource.cy;
610 | POINT offset;
611 |
612 | IFC(_compositionSurfaceInterop->BeginDraw(
613 | &rect,
614 | IID_PPV_ARGS(&d2d1DeviceContext),
615 | &offset));
616 | drawingBegun = true;
617 |
618 | IFC(d2d1DeviceContext->CreateCompatibleRenderTarget(&compatibleRenderTarget));
619 |
620 | IFC(compatibleRenderTarget->CreateBitmapFromWicBitmap(
621 | bitmapSource.Get(),
622 | nullptr,
623 | d2d1BitmapSource.GetAddressOf()));
624 |
625 | D2D1_RECT_F destRect;
626 | destRect.left = static_cast(offset.x);
627 | destRect.top = static_cast(offset.y);
628 | destRect.right = static_cast(destRect.left + _sizeBitmapSource.cx);
629 | destRect.bottom = static_cast(destRect.top + _sizeBitmapSource.cy);
630 |
631 | D2D1_RECT_F sourceRect;
632 | sourceRect.left = 0;
633 | sourceRect.top = 0;
634 | sourceRect.right = static_cast(_sizeBitmapSource.cx);
635 | sourceRect.bottom = static_cast(_sizeBitmapSource.cy);
636 |
637 | d2d1DeviceContext->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
638 | d2d1DeviceContext->DrawBitmap(
639 | d2d1BitmapSource.Get(),
640 | &destRect,
641 | 1.0f,
642 | D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
643 | &sourceRect);
644 |
645 | Cleanup:
646 | if (drawingBegun)
647 | {
648 | HRESULT endDrawHr = _compositionSurfaceInterop->EndDraw();
649 | if (FAILED(endDrawHr))
650 | {
651 | ERR(endDrawHr, L"Failed to EndDraw");
652 | }
653 | }
654 |
655 | _graphicsDevice->ReleaseDrawingLock();
656 |
657 | return hr;
658 | }
659 |
660 | // In the case that the CompositionGraphicsDevice that was used by this CompositionImage
661 | // to create our ICompositionSurface is lost, we need to redraw the contents of the bitmap
662 | // to the ICompositionSurface.
663 | void CompositionImage::HandleGraphicsDeviceLost(CompositionGraphicsDevice^ sender)
664 | {
665 | if (_loadAsyncAction != nullptr)
666 | {
667 | _loadAsyncAction->Cancel();
668 | _loadAsyncAction = nullptr;
669 | }
670 |
671 | _loadAsyncAction = LoadImageAsync();
672 | }
673 |
674 | // Fires the ImageLoaded event give the CompositionImageLoadStatus.
675 | void CompositionImage::ReportImageLoaded(CompositionImageLoadStatus status)
676 | {
677 | ImageLoaded(this, status);
678 | }
679 |
680 | } // namespace Toolkit
681 | } // namespace Composition
682 | } // namespace UI
683 | } // namespace Microsoft
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImage.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | namespace Microsoft {
8 | namespace UI {
9 | namespace Composition {
10 | namespace Toolkit {
11 |
12 | [Windows::Foundation::Metadata::WebHostHidden]
13 | public enum class CompositionImageLoadStatus
14 | {
15 | Success,
16 | FileAccessError,
17 | DecodeError,
18 | NotEnoughResources,
19 | Other
20 | };
21 |
22 | // Forward declare for CompositionImageLoadedEventHandler delegate.
23 | ref class CompositionImage;
24 |
25 | public delegate void CompositionImageLoadedEventHandler(
26 | CompositionImage^ sender,
27 | CompositionImageLoadStatus status);
28 |
29 | [Windows::Foundation::Metadata::WebHostHidden]
30 | public ref class CompositionImage sealed
31 | {
32 | // Friend CompositionImageFactory so that CreateCompositionImage does not have to
33 | // be exposed.
34 | friend ref class CompositionImageFactory;
35 |
36 | public:
37 | // Called anytime the bitmap associated with this CompositionImage's Surface is drawn,
38 | // or an error occurs during loading the image. Check the CompositionImageLoadStatus
39 | // for details regarding the success or failure to load and draw the image.
40 | //
41 | // Note that this event makes no guarantees as to what thread the associated CompositionImageLoadedEventHandler
42 | // delegate will be invoked on. If there is a specific thread that the contents of the delegate should be
43 | // executed on, the delegate should implementation should handle that.
44 | event CompositionImageLoadedEventHandler^ ImageLoaded;
45 |
46 | // Gets the actual size of the decoded bitmap.
47 | property Windows::Foundation::Size Size
48 | {
49 | Windows::Foundation::Size get()
50 | {
51 | return Windows::Foundation::Size(
52 | static_cast(_sizeBitmapSource.cx),
53 | static_cast(_sizeBitmapSource.cy));
54 | }
55 | };
56 |
57 | // Gets the ICompositionSurface that backs this CompositionImage.
58 | // When the CompositionImage is first creates, this will be a 0 x 0 Surface
59 | // (or the size specified by any CompositionImageOptions provided at the time of creation)
60 | // and the Surface will be resized and drawn to once the bitmap has been loaded
61 | // and drawn. To be notified when the image has been completely loaded and drawn,
62 | // call CompleteLoadAsync which will return the CompositionImageLoadStatus indicating
63 | // whether the image was successfully loaded or not.
64 | property ICompositionSurface^ Surface
65 | {
66 | ICompositionSurface^ get() {
67 | return reinterpret_cast(_compositionSurface.Get());
68 | }
69 | }
70 |
71 | private:
72 | static CompositionImage^ CreateCompositionImage(
73 | Compositor^ compositor,
74 | CompositionGraphicsDevice^ graphicsDevice,
75 | Uri^ uri,
76 | StorageFile^ file,
77 | CompositionImageOptions^ options);
78 |
79 | static CompositionImage^ CreateCompositionImage(
80 | Compositor^ compositor,
81 | CompositionGraphicsDevice^ graphicsDevice,
82 | const Array^ pixels,
83 | int pixelWidth,
84 | int pixelHeight);
85 |
86 | CompositionImage(
87 | Compositor^ compositor,
88 | CompositionGraphicsDevice^ graphicsDevice,
89 | Uri^ uri,
90 | StorageFile^ file,
91 | CompositionImageOptions^ options);
92 |
93 | CompositionImage(
94 | Compositor^ compositor,
95 | CompositionGraphicsDevice^ graphicsDevice,
96 | const Array^ pixels,
97 | int pixelWidth,
98 | int pixelHeight);
99 |
100 | IAsyncAction^ LoadImageAsync();
101 |
102 | static IBuffer^ LoadImageFromUri(Uri^ uri);
103 |
104 | static IBuffer^ LoadImageFromFile(StorageFile^ file);
105 |
106 | static ComPtr DecodeBufferIntoBitmap(
107 | IBuffer^ imageRawBuffer,
108 | SIZE sizeDecode);
109 |
110 | HRESULT DrawBitmapOnSurface(ComPtr& bitmapSource);
111 |
112 | HRESULT DrawBitmapOnSurface(const Array^ buffer, int pixelWidth, int pixelHeight);
113 |
114 | void HandleGraphicsDeviceLost(CompositionGraphicsDevice^ sender);
115 |
116 | void ReportImageLoaded(CompositionImageLoadStatus status);
117 |
118 | private:
119 | Compositor^ _compositor;
120 | CompositionImageOptions^ _imageOptions;
121 | CompositionGraphicsDevice^ _graphicsDevice;
122 | Uri^ _uri;
123 | StorageFile^ _file;
124 |
125 | ComPtr _compositionSurface;
126 | ComPtr _compositionSurfaceInterop;
127 |
128 | IAsyncAction^ _loadAsyncAction;
129 |
130 | SIZE _sizeBitmapSource{};
131 | SIZE _sizeCompositionSurface{};
132 | SIZE _sizeDecode{};
133 | };
134 |
135 | } // Toolkit
136 | } // Composition
137 | } // UI
138 | } // Microsoft
139 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImageFactory.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 | #include "CompositionGraphicsDevice.h"
7 | #include "CompositionImageOptions.h"
8 | #include "CompositionImage.h"
9 | #include "CompositionImageFactory.h"
10 |
11 | namespace Microsoft {
12 | namespace UI {
13 | namespace Composition {
14 | namespace Toolkit {
15 |
16 | // Returns a CompositionImageFactory that can be used to create CompositionImages
17 | // associated with a particular Compositor instance. A reference should be kept to
18 | // the returned factory for the lifetime of the composition images created by the
19 | // factory.
20 | CompositionImageFactory^ CompositionImageFactory::CreateCompositionImageFactory(Compositor^ compositor)
21 | {
22 | if (compositor == nullptr)
23 | {
24 | __abi_ThrowIfFailed(E_INVALIDARG);
25 | }
26 |
27 | CompositionGraphicsDevice^ device = CompositionGraphicsDevice::CreateCompositionGraphicsDevice(compositor);
28 | return ref new CompositionImageFactory(compositor, device);
29 | }
30 |
31 | CompositionImageFactory::CompositionImageFactory(Compositor^ compositor, CompositionGraphicsDevice^ graphicsDevice) :
32 | _compositor(compositor),
33 | _graphicsDevice(graphicsDevice)
34 | {
35 | }
36 |
37 | // Creates a CompositionImage given a Uri that represents a packaged resource (ms-appx:///)
38 | // a application data resource (ms-appdata:///) or an http(s) resource (http:// or https://)
39 | // Does not support creating a CompositionImage from a Uri with the file scheme, instead
40 | // a StorageFile object should be passed as an argument to CreateImageFromFile.
41 | CompositionImage^ CompositionImageFactory::CreateImageFromUri(Windows::Foundation::Uri^ uri)
42 | {
43 | CompositionImageOptions^ options = ref new CompositionImageOptions();
44 | options->DecodeHeight = 0;
45 | options->DecodeWidth = 0;
46 |
47 | return CreateImageFromUri(uri, options);
48 | }
49 |
50 | // Functions similarly to CreateImageFromUri with the exception that any options provided
51 | // by the CompositionImageOptions are used to initialize the CompositionImage.
52 | CompositionImage^ CompositionImageFactory::CreateImageFromUri(Windows::Foundation::Uri^ uri, CompositionImageOptions^ options)
53 | {
54 | SIZE szDecode = { 0, 0 };
55 | if (options)
56 | {
57 | szDecode.cx = (ULONG)options->DecodeWidth;
58 | szDecode.cy = (ULONG)options->DecodeHeight;
59 | }
60 |
61 | return CompositionImage::CreateCompositionImage(
62 | _compositor,
63 | _graphicsDevice,
64 | uri,
65 | nullptr,
66 | options);
67 | }
68 |
69 | // Creates a CompositionImage given a StorageFile.
70 | CompositionImage^ CompositionImageFactory::CreateImageFromFile(StorageFile^ file)
71 | {
72 | CompositionImageOptions^ options = ref new CompositionImageOptions();
73 | options->DecodeHeight = 0;
74 | options->DecodeWidth = 0;
75 |
76 | return CreateImageFromFile(file, options);
77 | }
78 |
79 | // Functions similarly to CreateImageFromFile with the exception that any options provided
80 | // by the CompositionImageOptions are used to initialize the CompositionImage.
81 | CompositionImage^ CompositionImageFactory::CreateImageFromFile(StorageFile^ file, CompositionImageOptions^ options)
82 | {
83 | SIZE szDecode = { 0, 0 };
84 | if (options)
85 | {
86 | szDecode.cx = (ULONG)options->DecodeWidth;
87 | szDecode.cy = (ULONG)options->DecodeHeight;
88 | }
89 |
90 | return CompositionImage::CreateCompositionImage(
91 | _compositor,
92 | _graphicsDevice,
93 | nullptr,
94 | file,
95 | options);
96 | }
97 |
98 | // Creates a CompositionImage given a pixels buffer.
99 | CompositionImage^ CompositionImageFactory::CreateImageFromPixels(const Array^ pixels, int pixelWidth, int pixelHeight)
100 | {
101 | return CompositionImage::CreateCompositionImage(
102 | _compositor,
103 | _graphicsDevice,
104 | pixels,
105 | pixelWidth,
106 | pixelHeight);
107 | }
108 |
109 | } // namespace Toolkit
110 | } // namespace Composition
111 | } // namespace UI
112 | } // namespace Microsoft
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImageFactory.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | namespace Microsoft {
8 | namespace UI {
9 | namespace Composition {
10 | namespace Toolkit {
11 |
12 | [Windows::Foundation::Metadata::WebHostHidden]
13 | public ref class CompositionImageFactory sealed
14 | {
15 | public:
16 | static CompositionImageFactory^ CreateCompositionImageFactory(
17 | Compositor^ compositor);
18 |
19 | CompositionImage^ CreateImageFromUri(Uri^ uri);
20 |
21 | CompositionImage^ CreateImageFromUri(
22 | Uri^ uri,
23 | CompositionImageOptions^ options);
24 |
25 | CompositionImage^ CreateImageFromFile(StorageFile^ file);
26 |
27 | CompositionImage^ CreateImageFromFile(
28 | StorageFile^ file,
29 | CompositionImageOptions^ options);
30 |
31 | CompositionImage^ CreateImageFromPixels(
32 | const Array^ pixels,
33 | int pixelWidth,
34 | int pixelHeight);
35 |
36 | private:
37 | CompositionImageFactory(
38 | Compositor^ compositor,
39 | CompositionGraphicsDevice^ graphicsDevice);
40 |
41 | private:
42 | CompositionGraphicsDevice^ _graphicsDevice;
43 | Compositor^ _compositor;
44 | };
45 |
46 | } // Toolkit
47 | } // Composition
48 | } // UI
49 | } // Microsoft
50 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImageOptions.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 | #include "CompositionImageOptions.h"
7 |
8 | namespace Microsoft {
9 | namespace UI {
10 | namespace Composition {
11 | namespace Toolkit {
12 |
13 | CompositionImageOptions::CompositionImageOptions()
14 | {
15 | }
16 |
17 | int CompositionImageOptions::DecodeWidth::get()
18 | {
19 | return _decodeWidth;
20 | }
21 |
22 | void CompositionImageOptions::DecodeWidth::set(int value)
23 | {
24 | if (value < 0)
25 | {
26 | ERR(E_INVALIDARG, L"The DecodeWidth property of CompositionImageOptions cannot be less than 0.");
27 | __abi_ThrowIfFailed(E_INVALIDARG);
28 | }
29 |
30 | _decodeWidth = value;
31 | }
32 |
33 | int CompositionImageOptions::DecodeHeight::get()
34 | {
35 | return _decodeHeight;
36 | }
37 |
38 | void CompositionImageOptions::DecodeHeight::set(int value)
39 | {
40 | if (value < 0)
41 | {
42 | ERR(E_INVALIDARG, L"The DecodeHeight property of CompositionImageOptions cannot be less than 0.");
43 | __abi_ThrowIfFailed(E_INVALIDARG);
44 | }
45 |
46 | _decodeHeight = value;
47 | }
48 |
49 | } // namespace Toolkit
50 | } // namespace Composition
51 | } // namespace UI
52 | } // namespace Microsoft
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/CompositionImageOptions.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | namespace Microsoft {
8 | namespace UI {
9 | namespace Composition {
10 | namespace Toolkit {
11 |
12 | // Wrapper class that can be provided to the CompositionImage creation functions
13 | // to specify the desired size of the decoded bitmap.
14 | [Windows::Foundation::Metadata::WebHostHidden]
15 | public ref class CompositionImageOptions sealed
16 | {
17 | public:
18 | CompositionImageOptions();
19 |
20 | property int DecodeWidth
21 | {
22 | int get();
23 | void set(int);
24 | };
25 |
26 | property int DecodeHeight
27 | {
28 | int get();
29 | void set(int);
30 | };
31 |
32 | private:
33 | int _decodeWidth;
34 | int _decodeHeight;
35 | };
36 |
37 | } // Toolkit
38 | } // Composition
39 | } // UI
40 | } // Microsoft
41 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/Microsoft.UI.Composition.Toolkit.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.UI.Composition.Toolkit", "Microsoft.UI.Composition.Toolkit.vcxproj", "{08A7D060-593A-4978-8DCC-B2029ACA7C95}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompositionImageSample", "..\CompositionImageSample\CompositionImageSample.csproj", "{B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|ARM = Debug|ARM
13 | Debug|x64 = Debug|x64
14 | Debug|x86 = Debug|x86
15 | Release|ARM = Release|ARM
16 | Release|x64 = Release|x64
17 | Release|x86 = Release|x86
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|ARM.ActiveCfg = Debug|ARM
21 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|ARM.Build.0 = Debug|ARM
22 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x64.ActiveCfg = Debug|x64
23 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x64.Build.0 = Debug|x64
24 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x86.ActiveCfg = Debug|Win32
25 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Debug|x86.Build.0 = Debug|Win32
26 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|ARM.ActiveCfg = Release|ARM
27 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|ARM.Build.0 = Release|ARM
28 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x64.ActiveCfg = Release|x64
29 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x64.Build.0 = Release|x64
30 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x86.ActiveCfg = Release|Win32
31 | {08A7D060-593A-4978-8DCC-B2029ACA7C95}.Release|x86.Build.0 = Release|Win32
32 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|ARM.ActiveCfg = Debug|ARM
33 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|ARM.Build.0 = Debug|ARM
34 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|ARM.Deploy.0 = Debug|ARM
35 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x64.ActiveCfg = Debug|x64
36 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x64.Build.0 = Debug|x64
37 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x64.Deploy.0 = Debug|x64
38 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x86.ActiveCfg = Debug|x86
39 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x86.Build.0 = Debug|x86
40 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Debug|x86.Deploy.0 = Debug|x86
41 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|ARM.ActiveCfg = Release|ARM
42 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|ARM.Build.0 = Release|ARM
43 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|ARM.Deploy.0 = Release|ARM
44 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x64.ActiveCfg = Release|x64
45 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x64.Build.0 = Release|x64
46 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x64.Deploy.0 = Release|x64
47 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x86.ActiveCfg = Release|x86
48 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x86.Build.0 = Release|x86
49 | {B4BCF99C-78C3-4BB6-8DB1-1B49101892F7}.Release|x86.Deploy.0 = Release|x86
50 | EndGlobalSection
51 | GlobalSection(SolutionProperties) = preSolution
52 | HideSolutionNode = FALSE
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/Microsoft.UI.Composition.Toolkit.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | ARM
7 |
8 |
9 | Debug
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | ARM
19 |
20 |
21 | Release
22 | Win32
23 |
24 |
25 | Release
26 | x64
27 |
28 |
29 |
30 | {08a7d060-593a-4978-8dcc-b2029aca7c95}
31 | WindowsRuntimeComponent
32 | Microsoft.UI.Composition.Toolkit
33 | Microsoft.UI.Composition.Toolkit
34 | en-US
35 | 14.0
36 | true
37 | Windows Store
38 | 10.0.10586.0
39 | 10.0.10240.0
40 | 10.0
41 |
42 |
43 |
44 | DynamicLibrary
45 | true
46 | v140
47 |
48 |
49 | DynamicLibrary
50 | true
51 | v140
52 |
53 |
54 | DynamicLibrary
55 | true
56 | v140
57 |
58 |
59 | DynamicLibrary
60 | false
61 | true
62 | v140
63 |
64 |
65 | DynamicLibrary
66 | false
67 | true
68 | v140
69 |
70 |
71 | DynamicLibrary
72 | false
73 | true
74 | v140
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | false
103 |
104 |
105 | false
106 |
107 |
108 | false
109 |
110 |
111 | false
112 |
113 |
114 | false
115 |
116 |
117 | false
118 |
119 |
120 |
121 | Use
122 | _WINRT_DLL;%(PreprocessorDefinitions)
123 | pch.h
124 | $(IntDir)pch.pch
125 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
126 | /bigobj %(AdditionalOptions)
127 | 28204;4453
128 | false
129 | EditAndContinue
130 | Level4
131 | true
132 |
133 |
134 | Console
135 | false
136 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
137 |
138 |
139 |
140 |
141 | Use
142 | _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)
143 | pch.h
144 | $(IntDir)pch.pch
145 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
146 | /bigobj %(AdditionalOptions)
147 | 28204;4453
148 | Level4
149 | true
150 |
151 |
152 | Console
153 | false
154 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
155 |
156 |
157 |
158 |
159 | Use
160 | _WINRT_DLL;%(PreprocessorDefinitions)
161 | pch.h
162 | $(IntDir)pch.pch
163 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
164 | /bigobj %(AdditionalOptions)
165 | 28204;4453
166 | Level4
167 | true
168 |
169 |
170 | Console
171 | false
172 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
173 |
174 |
175 |
176 |
177 | Use
178 | _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)
179 | pch.h
180 | $(IntDir)pch.pch
181 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
182 | /bigobj %(AdditionalOptions)
183 | 28204;4453
184 | Level4
185 | true
186 |
187 |
188 | Console
189 | false
190 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
191 |
192 |
193 |
194 |
195 | Use
196 | _WINRT_DLL;%(PreprocessorDefinitions)
197 | pch.h
198 | $(IntDir)pch.pch
199 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
200 | /bigobj %(AdditionalOptions)
201 | 28204;4453
202 | Level4
203 | true
204 |
205 |
206 | Console
207 | false
208 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
209 |
210 |
211 |
212 |
213 | Use
214 | _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)
215 | pch.h
216 | $(IntDir)pch.pch
217 | $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)
218 | /bigobj %(AdditionalOptions)
219 | 28204;4453
220 | Level4
221 | true
222 |
223 |
224 | Console
225 | false
226 | WindowsApp.lib;Windowscodecs.lib;%(AdditionalDependencies)
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 | Create
244 | Create
245 | Create
246 | Create
247 | Create
248 | Create
249 |
250 |
251 |
252 |
253 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/Microsoft.UI.Composition.Toolkit.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 09d27466-d553-4479-8728-1054288356d9
6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/WICBitmapSource.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 |
7 | #include
8 | #include "WICBitmapSource.h"
9 |
10 | namespace Microsoft {
11 | namespace UI {
12 | namespace Composition {
13 | namespace Toolkit {
14 |
15 | //------------------------------------------------------------------------------
16 | //
17 | // WICBitmapSourceWrapper::WICBitmapSourceWrapper
18 | //
19 | //------------------------------------------------------------------------------
20 |
21 | _Use_decl_annotations_
22 | WICBitmapSourceWrapper::WICBitmapSourceWrapper(
23 | __in IWICBitmapSource* bitmapSource,
24 | __in IWICBitmapSourceTransform* bitmapSourceTransform,
25 | UINT width,
26 | UINT height,
27 | REFWICPixelFormatGUID pixelFormat)
28 | {
29 | _bitmapSourceTransform = bitmapSourceTransform;
30 | _bitmapSource = bitmapSource;
31 | _width = width;
32 | _height = height;
33 | _pixelFormat = pixelFormat;
34 | }
35 |
36 |
37 | //------------------------------------------------------------------------------
38 | //
39 | // WICBitmapSourceWrapper::CopyPalette
40 | //
41 | //------------------------------------------------------------------------------
42 |
43 | _Use_decl_annotations_
44 | HRESULT
45 | WICBitmapSourceWrapper::CopyPalette(__in IWICPalette* palette)
46 | {
47 | WICPixelFormatGUID sourceFormat;
48 |
49 | if (SUCCEEDED(_bitmapSource->GetPixelFormat(&sourceFormat)) &&
50 | (sourceFormat == _pixelFormat))
51 | {
52 | return _bitmapSource->CopyPalette(palette);
53 | }
54 |
55 | return WINCODEC_ERR_PALETTEUNAVAILABLE;
56 | }
57 |
58 |
59 | //------------------------------------------------------------------------------
60 | //
61 | // WICBitmapSourceWrapper::CopyPixels
62 | //
63 | //------------------------------------------------------------------------------
64 |
65 | _Use_decl_annotations_
66 | HRESULT
67 | WICBitmapSourceWrapper::CopyPixels(
68 | __in_opt const WICRect* prc,
69 | UINT stride,
70 | UINT bufferSize,
71 | __out_ecount(bufferSize) BYTE* buffer)
72 | {
73 | return _bitmapSourceTransform->CopyPixels(
74 | prc,
75 | _width,
76 | _height,
77 | &_pixelFormat,
78 | WICBitmapTransformRotate0,
79 | stride,
80 | bufferSize,
81 | buffer
82 | );
83 | }
84 |
85 |
86 | //------------------------------------------------------------------------------
87 | //
88 | // WICBitmapSourceWrapper::GetPixelFormat
89 | //
90 | //------------------------------------------------------------------------------
91 |
92 | _Use_decl_annotations_
93 | HRESULT
94 | WICBitmapSourceWrapper::GetPixelFormat(__out WICPixelFormatGUID* pixelFormat)
95 | {
96 | *pixelFormat = _pixelFormat;
97 | return S_OK;
98 | }
99 |
100 |
101 | //------------------------------------------------------------------------------
102 | //
103 | // WICBitmapSourceWrapper::GetResolution
104 | //
105 | //------------------------------------------------------------------------------
106 |
107 | _Use_decl_annotations_
108 | HRESULT
109 | WICBitmapSourceWrapper::GetResolution(__out double* dpiX, __out double* dpiY)
110 | {
111 | return _bitmapSource->GetResolution(dpiX, dpiY);
112 | }
113 |
114 |
115 | //------------------------------------------------------------------------------
116 | //
117 | // WICBitmapSourceWrapper::GetSize
118 | //
119 | //------------------------------------------------------------------------------
120 |
121 | _Use_decl_annotations_
122 | HRESULT
123 | WICBitmapSourceWrapper::GetSize(__out UINT* width, __out UINT* height)
124 | {
125 | *width = _width;
126 | *height = _height;
127 | return S_OK;
128 | }
129 |
130 |
131 | //------------------------------------------------------------------------------
132 | //
133 | // WICBitmapSourceWrapper::CreateSourceTransform
134 | //
135 | //------------------------------------------------------------------------------
136 |
137 | _Use_decl_annotations_
138 | bool
139 | WICBitmapSourceWrapper::CreateSourceTransform(
140 | __in IWICBitmapSource* bitmapSource,
141 | UINT desiredWidth,
142 | UINT desiredHeight,
143 | REFWICPixelFormatGUID desiredFormat,
144 | __out IWICBitmapSource** newBitmapSource)
145 | {
146 | HRESULT hr;
147 |
148 | WRL::ComPtr sourceTransform;
149 | bool transformingScale = false;
150 | bool transformingFormat = false;
151 | UINT cxClosest = desiredWidth;
152 | UINT cyClosest = desiredHeight;
153 | WICPixelFormatGUID closestPixelFormat = desiredFormat;
154 | WICPixelFormatGUID currentPixelFormat;
155 | UINT cxImage = 0;
156 | UINT cyImage = 0;
157 |
158 |
159 | *newBitmapSource = NULL;
160 |
161 | hr = bitmapSource->QueryInterface(__uuidof(sourceTransform), (void**)&sourceTransform);
162 |
163 | if (SUCCEEDED(hr))
164 | {
165 | hr = bitmapSource->GetPixelFormat(¤tPixelFormat);
166 | }
167 |
168 | if (SUCCEEDED(hr))
169 | {
170 | hr = bitmapSource->GetSize(&cxImage, &cyImage);
171 | }
172 |
173 | if (SUCCEEDED(hr))
174 | {
175 | hr = sourceTransform->GetClosestPixelFormat(&closestPixelFormat);
176 | }
177 |
178 | if (SUCCEEDED(hr))
179 | {
180 | hr = sourceTransform->GetClosestSize(&cxClosest, &cyClosest);
181 | }
182 |
183 | if (SUCCEEDED(hr))
184 | {
185 | if ((closestPixelFormat == desiredFormat) && (closestPixelFormat != currentPixelFormat))
186 | {
187 | //
188 | // The codec will convert the pixel format for us.
189 | //
190 |
191 | transformingFormat = true;
192 | }
193 |
194 | if (((cxClosest < cxImage) || (cyClosest < cyImage)) &&
195 | ((cxImage >= desiredWidth) && (cyClosest >= desiredHeight)))
196 | {
197 | //
198 | // The codec is partially or fully scaling the image for us.
199 | //
200 |
201 | transformingScale = true;
202 | }
203 | }
204 |
205 | if (transformingFormat || transformingScale)
206 | {
207 | //
208 | // The codec is transforming pixel format and/or source size for us
209 | // Create a wrapper around its IWICBitmapTransform to provide a new
210 | // IWICBitmapSource for the next stage.
211 | //
212 |
213 | *newBitmapSource = new WICBitmapSourceWrapper(
214 | bitmapSource,
215 | sourceTransform.Get(),
216 | transformingScale ? cxClosest : cxImage,
217 | transformingScale ? cyClosest : cyImage,
218 | transformingFormat ? closestPixelFormat : currentPixelFormat);
219 |
220 | if (!*newBitmapSource)
221 | {
222 | ::RaiseFailFastException(nullptr, nullptr, 0);
223 | }
224 | }
225 |
226 | return *newBitmapSource != NULL;
227 | }
228 |
229 | STDMETHODIMP_(ULONG) WICBitmapSourceWrapper::AddRef()
230 | {
231 | return InterlockedIncrement(&_cRef);
232 | }
233 |
234 | STDMETHODIMP_(ULONG) WICBitmapSourceWrapper::Release()
235 | {
236 | ULONG cRef = InterlockedDecrement(&_cRef);
237 |
238 | if (0 == cRef)
239 | {
240 | delete this;
241 | }
242 |
243 | return cRef;
244 | }
245 |
246 | STDMETHODIMP WICBitmapSourceWrapper::QueryInterface(REFIID riid, void** object)
247 | {
248 | if (object == nullptr)
249 | {
250 | return E_POINTER;
251 | }
252 |
253 | if (riid == __uuidof(IUnknown))
254 | {
255 | *object = static_cast(this);
256 | }
257 | else if (riid == IID_IWICBitmapSource)
258 | {
259 | *object = static_cast(this);
260 | }
261 | else
262 | {
263 | return E_NOINTERFACE;
264 | }
265 |
266 | AddRef();
267 | return S_OK;
268 | }
269 | }
270 | }
271 | }
272 | }
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/WICBitmapSource.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | namespace Microsoft {
8 | namespace UI {
9 | namespace Composition {
10 | namespace Toolkit {
11 |
12 | class WICBitmapSourceWrapper sealed :
13 | public IWICBitmapSource
14 | {
15 | public:
16 | STDMETHOD_(ULONG, AddRef)(void);
17 | STDMETHOD_(ULONG, Release)(void);
18 | STDMETHOD(QueryInterface)(REFIID riid, void** object);
19 |
20 | private:
21 | WICBitmapSourceWrapper(
22 | __in IWICBitmapSource* bitmapSource,
23 | __in IWICBitmapSourceTransform* bitmapSourceTransform,
24 | UINT width,
25 | UINT height,
26 | REFWICPixelFormatGUID pixelFormat);
27 |
28 | public:
29 |
30 | // IWICBitmapSource
31 | HRESULT STDMETHODCALLTYPE CopyPalette(__in IWICPalette* pPalette);
32 | HRESULT STDMETHODCALLTYPE CopyPixels(__in_opt const WICRect* prc, UINT stride, UINT bufferSize, __out_ecount(bufferSize) BYTE* buffer);
33 | HRESULT STDMETHODCALLTYPE GetPixelFormat(__out WICPixelFormatGUID* pixelFormat);
34 | HRESULT STDMETHODCALLTYPE GetResolution(__out double* dpiX, __out double* dpiY);
35 | HRESULT STDMETHODCALLTYPE GetSize(__out UINT* width, __out UINT* height);
36 |
37 | static bool CreateSourceTransform(
38 | __in IWICBitmapSource* bitmapSource,
39 | UINT desiredWidth,
40 | UINT desiredHeight,
41 | REFWICPixelFormatGUID desiredFormat,
42 | __out IWICBitmapSource** newBitmapSource);
43 |
44 | private:
45 | UINT _width;
46 | UINT _height;
47 | WICPixelFormatGUID _pixelFormat;
48 |
49 | WRL::ComPtr _bitmapSource;
50 | WRL::ComPtr _bitmapSourceTransform;
51 |
52 | volatile LONG _cRef;
53 | };
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/pch.cpp:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #include "pch.h"
6 |
--------------------------------------------------------------------------------
/Microsoft.UI.Composition.Toolkit/pch.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | //
3 | // Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4 |
5 | #pragma once
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | using namespace concurrency;
21 | using namespace Microsoft::WRL;
22 | using namespace Platform;
23 | using namespace Windows::Storage;
24 | using namespace Windows::Storage::Streams;
25 | using namespace Windows::Graphics::DirectX;
26 | using namespace Windows::UI::Composition;
27 | using namespace Windows::Foundation;
28 |
29 | // Helper to goto a Cleanup label if the provided HRESULT value fails.
30 | #define IFC(expr) {hr = expr; if (FAILED(hr)) goto Cleanup;}
31 |
32 | // Logs an error message and HRESULT using RoOriginateError.
33 | inline void ERR(HRESULT hr, LPCWSTR msg)
34 | {
35 | RoOriginateError(hr, StringReference(msg).GetHSTRING());
36 | }
37 |
38 | // Raises a fail fast exception if the provided HRESULT value fails.
39 | inline void FAILHARD(HRESULT hr)
40 | {
41 | if (FAILED(hr))
42 | {
43 | __debugbreak();
44 | ::RaiseFailFastException(nullptr, nullptr, 0);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FluidBanner
2 | **FluidBanner** control is banner control created using **Windows Composition**. It allows for displaying multiple images in a unique interactive format.
3 |
4 |
5 |
6 | For more details on how this control was implemented, check out my [blog](https://wpfspark.wordpress.com/2016/07/29/creating-a-fluidbanner-control-using-windows-composition/).
7 |
8 |
--------------------------------------------------------------------------------