├── .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 | FluidBanner Demo 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 | --------------------------------------------------------------------------------