diff --git a/AIDevGallery.SourceGenerator/Models/HardwareAccelerator.cs b/AIDevGallery.SourceGenerator/Models/HardwareAccelerator.cs
index 64da1c35..2d3e57f7 100644
--- a/AIDevGallery.SourceGenerator/Models/HardwareAccelerator.cs
+++ b/AIDevGallery.SourceGenerator/Models/HardwareAccelerator.cs
@@ -10,5 +10,6 @@ internal enum HardwareAccelerator
{
CPU,
DML,
- QNN
+ QNN,
+ WCRAPI
}
\ No newline at end of file
diff --git a/AIDevGallery.SourceGenerator/SamplesSourceGenerator.cs b/AIDevGallery.SourceGenerator/SamplesSourceGenerator.cs
index dd319abe..913a0ea2 100644
--- a/AIDevGallery.SourceGenerator/SamplesSourceGenerator.cs
+++ b/AIDevGallery.SourceGenerator/SamplesSourceGenerator.cs
@@ -52,7 +52,7 @@ private void ExecuteSharedCodeEnumGeneration(SourceProductionContext context, Im
{
var filePath = type!.Locations[0].SourceTree?.FilePath;
- if (filePath != null)
+ if (filePath != null && !filePath.Contains(@"\obj\"))
{
if (!filePaths.Contains(filePath))
{
@@ -66,7 +66,15 @@ private void ExecuteSharedCodeEnumGeneration(SourceProductionContext context, Im
foreach (var filePath in filePaths)
{
var fileName = Path.GetFileNameWithoutExtension(filePath);
- if (File.Exists(Path.ChangeExtension(filePath, ".xaml")))
+ var extension = Path.GetExtension(filePath);
+ var filePathWithoutExtension = filePath.Substring(0, filePath.Length - extension.Length);
+ if (fileName.EndsWith(".xaml", StringComparison.InvariantCultureIgnoreCase) && File.Exists(filePathWithoutExtension))
+ {
+ fileName = Path.GetFileNameWithoutExtension(filePathWithoutExtension);
+ sourceBuilder.AppendLine($" {fileName}Cs,");
+ sourceBuilder.AppendLine($" {fileName}Xaml,");
+ }
+ else if (File.Exists(Path.ChangeExtension(filePath, ".xaml")))
{
sourceBuilder.AppendLine($" {fileName}Cs,");
sourceBuilder.AppendLine($" {fileName}Xaml,");
@@ -89,14 +97,22 @@ private void ExecuteSharedCodeEnumGeneration(SourceProductionContext context, Im
{
var fileName = Path.GetFileNameWithoutExtension(filePath);
var filePathXaml = Path.ChangeExtension(filePath, ".xaml");
- if (File.Exists(filePathXaml))
+ var extension = Path.GetExtension(filePath);
+ var filePathWithoutExtension = filePath.Substring(0, filePath.Length - extension.Length);
+ if (fileName.EndsWith(".xaml", StringComparison.InvariantCultureIgnoreCase) && File.Exists(filePathWithoutExtension))
+ {
+ fileName = Path.GetFileNameWithoutExtension(fileName);
+ sourceBuilder.AppendLine($" SharedCodeEnum.{fileName}Cs => \"{fileName}.xaml.cs\",");
+ sourceBuilder.AppendLine($" SharedCodeEnum.{fileName}Xaml => \"{fileName}.xaml\",");
+ }
+ else if (File.Exists(filePathXaml))
{
- sourceBuilder.AppendLine($" SharedCodeEnum.{Path.GetFileNameWithoutExtension(filePath)}Xaml => \"{Path.GetFileName(filePathXaml)}\",");
- sourceBuilder.AppendLine($" SharedCodeEnum.{Path.GetFileNameWithoutExtension(filePath)}Cs => \"{Path.GetFileName(filePath)}\",");
+ sourceBuilder.AppendLine($" SharedCodeEnum.{fileName}Xaml => \"{Path.GetFileName(filePathXaml)}\",");
+ sourceBuilder.AppendLine($" SharedCodeEnum.{fileName}Cs => \"{Path.GetFileName(filePath)}\",");
}
else
{
- sourceBuilder.AppendLine($" SharedCodeEnum.{Path.GetFileNameWithoutExtension(filePath)} => \"{Path.GetFileName(filePath)}\",");
+ sourceBuilder.AppendLine($" SharedCodeEnum.{fileName} => \"{Path.GetFileName(filePath)}\",");
}
}
@@ -111,6 +127,17 @@ private void ExecuteSharedCodeEnumGeneration(SourceProductionContext context, Im
{
var fileName = Path.GetFileNameWithoutExtension(filePath);
var filePathXaml = Path.ChangeExtension(filePath, ".xaml");
+ var extension = Path.GetExtension(filePath);
+ var filePathWithoutExtension = filePath.Substring(0, filePath.Length - extension.Length);
+
+ // handle .xaml.cs files
+ if (File.Exists(filePathWithoutExtension))
+ {
+ filePathXaml = filePathWithoutExtension;
+ fileName = Path.GetFileNameWithoutExtension(fileName);
+ }
+
+
if (File.Exists(filePathXaml))
{
var fileContentXaml = XamlSourceCleanUp(File.ReadAllText(filePathXaml));
diff --git a/AIDevGallery/Controls/ModelSelectionControl.xaml b/AIDevGallery/Controls/ModelSelectionControl.xaml
index 6c9b313c..c12e260c 100644
--- a/AIDevGallery/Controls/ModelSelectionControl.xaml
+++ b/AIDevGallery/Controls/ModelSelectionControl.xaml
@@ -91,7 +91,7 @@
ToolTipService.ToolTip="More info">
-
+
@@ -170,11 +170,19 @@
Click="ModelCard_Click"
Icon="{ui:FontIcon Glyph=}"
Tag="{x:Bind ModelDetails}"
+ Visibility="{x:Bind ModelDetails.Size, Converter={StaticResource NotZeroToVisibilityConverter}}"
Text="View model card" />
+
@@ -265,7 +273,7 @@
ToolTipService.ToolTip="More info">
-
+
@@ -434,7 +442,7 @@
ToolTipService.ToolTip="More info">
-
+
@@ -528,11 +536,19 @@
Click="ModelCard_Click"
Icon="{ui:FontIcon Glyph=}"
Tag="{x:Bind ModelDetails}"
+ Visibility="{x:Bind ModelDetails.Size, Converter={StaticResource NotZeroToVisibilityConverter}}"
Text="View model card" />
+
diff --git a/AIDevGallery/Controls/ModelSelectionControl.xaml.cs b/AIDevGallery/Controls/ModelSelectionControl.xaml.cs
index d7be0b92..10cc964d 100644
--- a/AIDevGallery/Controls/ModelSelectionControl.xaml.cs
+++ b/AIDevGallery/Controls/ModelSelectionControl.xaml.cs
@@ -240,13 +240,22 @@ private void PopulateModelDetailsLists()
if (modelDetails.Compatibility.CompatibilityState == ModelCompatibilityState.Compatible)
{
- AvailableModels.Add(new AvailableModel(modelDetails));
+ if (modelDetails.HardwareAccelerators.Contains(HardwareAccelerator.WCRAPI))
+ {
+ // insert APIs on top
+ AvailableModels.Insert(0, new AvailableModel(modelDetails));
+ }
+ else
+ {
+ AvailableModels.Add(new AvailableModel(modelDetails));
+ }
}
else
{
if (model.Size == 0)
{
- UnavailableModels.Add(new BaseModel(modelDetails));
+ // insert APIs on top
+ UnavailableModels.Insert(0, new BaseModel(modelDetails));
}
else
{
@@ -352,6 +361,18 @@ private void ModelCard_Click(object sender, RoutedEventArgs e)
}
}
+ private void ApiDocumentation_Click(object sender, RoutedEventArgs e)
+ {
+ if (sender is MenuFlyoutItem btn && btn.Tag is ModelDetails details)
+ {
+ // we are in the sample view, open in app modelcard
+ if (ModelCardVisibility == Visibility.Visible)
+ {
+ App.MainWindow.Navigate("apis", details);
+ }
+ }
+ }
+
private void CopyModelPath_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem btn && btn.Tag is ModelDetails details)
diff --git a/AIDevGallery/Helpers/ModelDetailsHelper.cs b/AIDevGallery/Helpers/ModelDetailsHelper.cs
index f5a63fa8..1220b657 100644
--- a/AIDevGallery/Helpers/ModelDetailsHelper.cs
+++ b/AIDevGallery/Helpers/ModelDetailsHelper.cs
@@ -34,7 +34,7 @@ public static ModelDetails GetModelDetailsFromApiDefinition(ModelType modelType,
Id = apiDefinition.Id,
Icon = apiDefinition.Icon,
Name = apiDefinition.Name,
- HardwareAccelerators = [HardwareAccelerator.QNN],
+ HardwareAccelerators = [HardwareAccelerator.WCRAPI],
IsUserAdded = false,
SupportedOnQualcomm = true,
ReadmeUrl = apiDefinition.ReadmeUrl,
diff --git a/AIDevGallery/MainWindow.xaml.cs b/AIDevGallery/MainWindow.xaml.cs
index e673c359..76e6f4b0 100644
--- a/AIDevGallery/MainWindow.xaml.cs
+++ b/AIDevGallery/MainWindow.xaml.cs
@@ -97,7 +97,7 @@ private void Navigate(Type page, object? param = null)
if (page == typeof(APISelectionPage) && NavFrame.Content is APISelectionPage apiPage && param != null)
{
// No need to navigate to the APISelectionPage again, we just want to navigate to the right subpage
- apiPage.SetSelectedAPIInMenu((ModelType)param);
+ apiPage.SetSelectedApiInMenu((ModelType)param);
}
else
{
diff --git a/AIDevGallery/Models/ModelCompatibility.cs b/AIDevGallery/Models/ModelCompatibility.cs
index 3705740d..bbc783c9 100644
--- a/AIDevGallery/Models/ModelCompatibility.cs
+++ b/AIDevGallery/Models/ModelCompatibility.cs
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using AIDevGallery.Samples;
using AIDevGallery.Utils;
using System;
+using System.Linq;
namespace AIDevGallery.Models;
@@ -20,9 +22,23 @@ private ModelCompatibility()
public static ModelCompatibility GetModelCompatibility(ModelDetails modelDetails)
{
string description = string.Empty;
-
ModelCompatibilityState compatibility;
- if (modelDetails.HardwareAccelerators.Contains(HardwareAccelerator.CPU) ||
+
+ // check if WCR API
+ if (ModelTypeHelpers.ApiDefinitionDetails.Any(md => md.Value.Id == modelDetails.Id))
+ {
+ var apiType = ModelTypeHelpers.ApiDefinitionDetails.FirstOrDefault(md => md.Value.Id == modelDetails.Id).Key;
+ if (WcrCompatibilityChecker.GetApiAvailability(apiType) != WcrApiAvailability.NotSupported)
+ {
+ compatibility = ModelCompatibilityState.Compatible;
+ }
+ else
+ {
+ compatibility = ModelCompatibilityState.NotCompatible;
+ description = "This Windows Copilot Runtime API requires a Copilot+ PC and a Windows 11 Insider Preview Build 26120.3073 (Dev and Beta Channels).";
+ }
+ }
+ else if (modelDetails.HardwareAccelerators.Contains(HardwareAccelerator.CPU) ||
(modelDetails.HardwareAccelerators.Contains(HardwareAccelerator.QNN) && DeviceUtils.IsArm64()))
{
compatibility = ModelCompatibilityState.Compatible;
diff --git a/AIDevGallery/Models/Samples.cs b/AIDevGallery/Models/Samples.cs
index 57b16079..e6f5d6cd 100644
--- a/AIDevGallery/Models/Samples.cs
+++ b/AIDevGallery/Models/Samples.cs
@@ -156,7 +156,8 @@ internal enum HardwareAccelerator
{
CPU,
DML,
- QNN
+ QNN,
+ WCRAPI
}
#pragma warning restore SA1402 // File may only contain a single type
diff --git a/AIDevGallery/Pages/APISelectionPage.xaml.cs b/AIDevGallery/Pages/APISelectionPage.xaml.cs
index 07d18f48..c494045b 100644
--- a/AIDevGallery/Pages/APISelectionPage.xaml.cs
+++ b/AIDevGallery/Pages/APISelectionPage.xaml.cs
@@ -6,7 +6,9 @@
using AIDevGallery.Telemetry.Events;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
+using System;
using System.Collections.Generic;
+using System.Linq;
namespace AIDevGallery.Pages;
@@ -25,7 +27,13 @@ protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.Parameter is ModelType type)
{
- SetSelectedAPIInMenu(type);
+ SetSelectedApiInMenu(type);
+ }
+ else if (e.Parameter is ModelDetails details &&
+ ModelTypeHelpers.ApiDefinitionDetails.Any(md => md.Value.Id == details.Id))
+ {
+ var apiType = ModelTypeHelpers.ApiDefinitionDetails.FirstOrDefault(md => md.Value.Id == details.Id).Key;
+ SetSelectedApiInMenu(apiType);
}
else
{
@@ -63,7 +71,7 @@ private void NavView_SelectionChanged(NavigationView sender, NavigationViewSelec
}
}
- public void SetSelectedAPIInMenu(ModelType selectedType)
+ public void SetSelectedApiInMenu(ModelType selectedType)
{
foreach (var item in NavView.MenuItems)
{
diff --git a/AIDevGallery/Pages/ScenarioSelectionPage.xaml.cs b/AIDevGallery/Pages/ScenarioSelectionPage.xaml.cs
index 88639965..de8ef348 100644
--- a/AIDevGallery/Pages/ScenarioSelectionPage.xaml.cs
+++ b/AIDevGallery/Pages/ScenarioSelectionPage.xaml.cs
@@ -21,8 +21,7 @@ internal record FilterRecord(string? Tag, string Text);
new(null, "All" ),
new("npu", "NPU" ),
new("gpu", "GPU" ),
-
- // new("wcr-api", "WCR API" )
+ new("wcr-api", "WCR API" )
];
private static LastInternalNavigation? lastInternalNavigation;
diff --git a/AIDevGallery/ProjectGenerator/Generator.cs b/AIDevGallery/ProjectGenerator/Generator.cs
index 0590f165..26f590af 100644
--- a/AIDevGallery/ProjectGenerator/Generator.cs
+++ b/AIDevGallery/ProjectGenerator/Generator.cs
@@ -674,10 +674,16 @@ private string CleanXamlSource(string xamlCode, string newNamespace, out string
if (match.Success)
{
var oldClassFullName = match.Groups[1].Value;
- _ = oldClassFullName[..oldClassFullName.LastIndexOf('.')];
className = oldClassFullName[(oldClassFullName.LastIndexOf('.') + 1)..];
- xamlCode = xamlCode.Replace(match.Value, @$"x:Class=""{newNamespace}.Sample""");
+ if (oldClassFullName.Contains(".SharedCode."))
+ {
+ xamlCode = xamlCode.Replace(match.Value, @$"x:Class=""{newNamespace}.{className}""");
+ }
+ else
+ {
+ xamlCode = xamlCode.Replace(match.Value, @$"x:Class=""{newNamespace}.Sample""");
+ }
}
else
{
diff --git a/AIDevGallery/ProjectGenerator/Template/Utils/HardwareAccelerator.cs b/AIDevGallery/ProjectGenerator/Template/Utils/HardwareAccelerator.cs
index e6a08d3c..2b64b73e 100644
--- a/AIDevGallery/ProjectGenerator/Template/Utils/HardwareAccelerator.cs
+++ b/AIDevGallery/ProjectGenerator/Template/Utils/HardwareAccelerator.cs
@@ -4,5 +4,6 @@ internal enum HardwareAccelerator
{
CPU,
DML,
- QNN
+ QNN,
+ WCRAPI
}
\ No newline at end of file
diff --git a/AIDevGallery/Samples/SharedCode/PhiSilicaClient.cs b/AIDevGallery/Samples/SharedCode/WcrApis/PhiSilicaClient.cs
similarity index 100%
rename from AIDevGallery/Samples/SharedCode/PhiSilicaClient.cs
rename to AIDevGallery/Samples/SharedCode/WcrApis/PhiSilicaClient.cs
diff --git a/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml b/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml
new file mode 100644
index 00000000..d6ff2c4f
--- /dev/null
+++ b/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+ This Windows Copilot Runtime API requires a model download. Click "Make Available" to request the model download via Windows Update.
+
+
+
+
+
+
+
+
+ Windows Update
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml.cs b/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml.cs
new file mode 100644
index 00000000..f8fce323
--- /dev/null
+++ b/AIDevGallery/Samples/SharedCode/WcrApis/WcrModelDownloader.xaml.cs
@@ -0,0 +1,149 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.Windows.Management.Deployment;
+using System;
+using System.Threading.Tasks;
+using Windows.Foundation;
+using Windows.System;
+
+namespace AIDevGallery.Samples.SharedCode;
+internal sealed partial class WcrModelDownloader : UserControl
+{
+ public event EventHandler? DownloadClicked;
+
+ public int DownloadProgress
+ {
+ get { return (int)GetValue(DownloadProgressProperty); }
+ set { SetValue(DownloadProgressProperty, value); }
+ }
+
+ // Using a DependencyProperty as the backing store for DownloadProgress. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty DownloadProgressProperty =
+ DependencyProperty.Register("DownloadProgress", typeof(int), typeof(WcrModelDownloader), new PropertyMetadata(0));
+
+ public string ErrorMessage
+ {
+ get { return (string)GetValue(ErrorMessageProperty); }
+ set { SetValue(ErrorMessageProperty, value); }
+ }
+
+ // Using a DependencyProperty as the backing store for ErrorMessage. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty ErrorMessageProperty =
+ DependencyProperty.Register("ErrorMessage", typeof(string), typeof(WcrModelDownloader), new PropertyMetadata("Error downloading model"));
+
+ public WcrApiDownloadState State
+ {
+ get { return (WcrApiDownloadState)GetValue(StateProperty); }
+ set { SetValue(StateProperty, value); }
+ }
+
+ // Using a DependencyProperty as the backing store for State. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty StateProperty =
+ DependencyProperty.Register("State", typeof(WcrApiDownloadState), typeof(WcrModelDownloader), new PropertyMetadata(WcrApiDownloadState.NotStarted, OnStateChanged));
+
+ private static void OnStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((WcrModelDownloader)d).UpdateState((WcrApiDownloadState)e.NewValue);
+ }
+
+ private void UpdateState(WcrApiDownloadState state)
+ {
+ switch (state)
+ {
+ case WcrApiDownloadState.NotStarted:
+ NotDownloadedContent.Visibility = Visibility.Visible;
+ loadingRingContainer.Visibility = Visibility.Collapsed;
+ errorContent.Visibility = Visibility.Collapsed;
+ this.Visibility = Visibility.Visible;
+ break;
+ case WcrApiDownloadState.Downloading:
+ NotDownloadedContent.Visibility = Visibility.Collapsed;
+ loadingRingContainer.Visibility = Visibility.Visible;
+ errorContent.Visibility = Visibility.Collapsed;
+ this.Visibility = Visibility.Visible;
+ break;
+ case WcrApiDownloadState.Downloaded:
+ NotDownloadedContent.Visibility = Visibility.Collapsed;
+ loadingRingContainer.Visibility = Visibility.Collapsed;
+ errorContent.Visibility = Visibility.Collapsed;
+ this.Visibility = Visibility.Collapsed;
+ break;
+ case WcrApiDownloadState.Error:
+ NotDownloadedContent.Visibility = Visibility.Collapsed;
+ loadingRingContainer.Visibility = Visibility.Collapsed;
+ errorContent.Visibility = Visibility.Visible;
+ this.Visibility = Visibility.Visible;
+ break;
+ default:
+ break;
+ }
+ }
+
+ public WcrModelDownloader()
+ {
+ this.InitializeComponent();
+ }
+
+ public async Task SetDownloadOperation(IAsyncOperationWithProgress operation)
+ {
+ if (operation == null)
+ {
+ return false;
+ }
+
+ operation.Progress = (result, progress) =>
+ {
+ DispatcherQueue.TryEnqueue(() =>
+ {
+ DownloadProgress = (int)(progress.Progress * 100);
+ });
+ };
+
+ State = WcrApiDownloadState.Downloading;
+
+ try
+ {
+ var result = await operation;
+
+ if (result.Status == PackageDeploymentStatus.CompletedSuccess)
+ {
+ State = WcrApiDownloadState.Downloaded;
+ return true;
+ }
+ else
+ {
+ State = WcrApiDownloadState.Error;
+ ErrorMessage = result.ExtendedError.Message;
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorMessage = ex.Message;
+ State = WcrApiDownloadState.Error;
+ }
+
+ return false;
+ }
+
+ private void DownloadModelClicked(object sender, RoutedEventArgs e)
+ {
+ DownloadClicked?.Invoke(this, EventArgs.Empty);
+ }
+
+ private async void WindowsUpdateHyperlinkClicked(Microsoft.UI.Xaml.Documents.Hyperlink sender, Microsoft.UI.Xaml.Documents.HyperlinkClickEventArgs args)
+ {
+ var uri = new Uri("ms-settings:windowsupdate");
+ await Launcher.LaunchUriAsync(uri);
+ }
+}
+
+internal enum WcrApiDownloadState
+{
+ NotStarted,
+ Downloading,
+ Downloaded,
+ Error
+}
\ No newline at end of file
diff --git a/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml b/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml
index e125d4f9..8427c4d5 100644
--- a/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml
+++ b/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml
@@ -3,11 +3,11 @@
x:Class="AIDevGallery.Samples.WCRAPIs.BackgroundRemover"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:AIDevGallery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:samples="using:AIDevGallery.Samples"
+ xmlns:shared="using:AIDevGallery.Samples.SharedCode"
mc:Ignorable="d">
@@ -99,5 +99,10 @@
VerticalAlignment="Center"
IsActive="True"
Visibility="Collapsed" />
+
diff --git a/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml.cs b/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml.cs
index 7c505283..ab21d9f7 100644
--- a/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml.cs
+++ b/AIDevGallery/Samples/WCRAPIs/BackgroundRemover.xaml.cs
@@ -3,6 +3,7 @@
using AIDevGallery.Models;
using AIDevGallery.Samples.Attributes;
+using AIDevGallery.Samples.SharedCode;
using Microsoft.Graphics.Imaging;
using Microsoft.UI;
using Microsoft.UI.Xaml;
@@ -11,7 +12,6 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.UI.Xaml.Shapes;
-using Microsoft.Windows.Management.Deployment;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -31,6 +31,7 @@ namespace AIDevGallery.Samples.WCRAPIs;
Model1Types = [ModelType.BackgroundRemover],
Scenario = ScenarioType.ImageBackgroundRemover,
Id = "79eca6f0-3092-4b6f-9a81-94a2aff22559",
+ SharedCode = [SharedCodeEnum.WcrModelDownloaderCs, SharedCodeEnum.WcrModelDownloaderXaml],
Icon = "\uEE6F")]
internal sealed partial class BackgroundRemover : BaseSamplePage
{
@@ -44,19 +45,21 @@ public BackgroundRemover()
protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
- if (!ImageObjectExtractor.IsAvailable())
+ if (ImageObjectExtractor.IsAvailable())
{
- sampleParams.ShowWcrModelLoadingMessage = true;
- var loadResult = await ImageObjectExtractor.MakeAvailableAsync();
- if (loadResult.Status != PackageDeploymentStatus.CompletedSuccess)
- {
- throw new InvalidOperationException(loadResult.ExtendedError.Message);
- }
+ WcrModelDownloader.State = WcrApiDownloadState.Downloaded;
}
sampleParams.NotifyCompletion();
}
+ private async void WcrModelDownloader_DownloadClicked(object sender, EventArgs e)
+ {
+ var operation = ImageObjectExtractor.MakeAvailableAsync();
+
+ await WcrModelDownloader.SetDownloadOperation(operation);
+ }
+
private async void LoadImage_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
diff --git a/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml b/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml
index 1eefc2dc..02291c28 100644
--- a/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml
+++ b/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml
@@ -3,10 +3,9 @@
x:Class="AIDevGallery.Samples.WCRAPIs.ImageDescription"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:AIDevGallery.Controls"
+ xmlns:shared="using:AIDevGallery.Samples.SharedCode"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:samples="using:AIDevGallery.Samples"
mc:Ignorable="d">
@@ -57,5 +56,10 @@
+
diff --git a/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml.cs b/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml.cs
index 294fd379..db90c346 100644
--- a/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml.cs
+++ b/AIDevGallery/Samples/WCRAPIs/ImageDescription.xaml.cs
@@ -3,11 +3,11 @@
using AIDevGallery.Models;
using AIDevGallery.Samples.Attributes;
+using AIDevGallery.Samples.SharedCode;
using Microsoft.Graphics.Imaging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.Windows.AI.Generative;
-using Microsoft.Windows.Management.Deployment;
using System;
using System.IO;
using System.Linq;
@@ -25,6 +25,7 @@ namespace AIDevGallery.Samples.WCRAPIs;
Model1Types = [ModelType.ImageDescription],
Scenario = ScenarioType.ImageDescribeImageWcr,
Id = "a1b1f64f-bc57-41a3-8fb3-ac8f1536d757",
+ SharedCode = [SharedCodeEnum.WcrModelDownloaderCs, SharedCodeEnum.WcrModelDownloaderXaml],
Icon = "\uEE6F")]
internal sealed partial class ImageDescription : BaseSamplePage
@@ -39,19 +40,21 @@ public ImageDescription()
protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
sampleParams.ShowWcrModelLoadingMessage = true;
- if (!ImageDescriptionGenerator.IsAvailable())
+ if (ImageDescriptionGenerator.IsAvailable())
{
- var loadResult = await ImageDescriptionGenerator.MakeAvailableAsync();
- if (loadResult.Status != PackageDeploymentStatus.CompletedSuccess)
- {
- throw new InvalidOperationException(loadResult.ExtendedError.Message);
- }
+ WcrModelDownloader.State = WcrApiDownloadState.Downloaded;
}
- _imageDescriptor = await ImageDescriptionGenerator.CreateAsync();
sampleParams.NotifyCompletion();
}
+ private async void WcrModelDownloader_DownloadClicked(object sender, EventArgs e)
+ {
+ var operation = ImageDescriptionGenerator.MakeAvailableAsync();
+
+ await WcrModelDownloader.SetDownloadOperation(operation);
+ }
+
private async void LoadImage_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
@@ -142,7 +145,8 @@ private async void DescribeImage(SoftwareBitmap bitmap)
try
{
using var bitmapBuffer = ImageBuffer.CreateCopyFromBitmap(bitmap);
- var describeTask = _imageDescriptor?.DescribeAsync(bitmapBuffer);
+ _imageDescriptor ??= await ImageDescriptionGenerator.CreateAsync();
+ var describeTask = _imageDescriptor.DescribeAsync(bitmapBuffer);
if (describeTask != null)
{
describeTask.Progress += (asyncInfo, delta) =>
diff --git a/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml b/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml
index e7c68f8f..1b7760cb 100644
--- a/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml
+++ b/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml
@@ -3,16 +3,16 @@
x:Class="AIDevGallery.Samples.WCRAPIs.IncreaseFidelity"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:AIDevGallery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:samples="using:AIDevGallery.Samples"
+ xmlns:shared="using:AIDevGallery.Samples.SharedCode"
mc:Ignorable="d">
-
+
@@ -114,5 +114,10 @@
VerticalAlignment="Center"
IsActive="True"
Visibility="Collapsed" />
+
diff --git a/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml.cs b/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml.cs
index c71727e3..e3c75b83 100644
--- a/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml.cs
+++ b/AIDevGallery/Samples/WCRAPIs/IncreaseFidelity.xaml.cs
@@ -3,6 +3,7 @@
using AIDevGallery.Models;
using AIDevGallery.Samples.Attributes;
+using AIDevGallery.Samples.SharedCode;
using Microsoft.Graphics.Imaging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -25,6 +26,7 @@ namespace AIDevGallery.Samples.WCRAPIs;
Model1Types = [ModelType.ImageScaler],
Scenario = ScenarioType.ImageIncreaseFidelity,
Id = "f1e235d1-f1c9-41c7-b489-7e4f95e54668",
+ SharedCode = [SharedCodeEnum.WcrModelDownloaderCs, SharedCodeEnum.WcrModelDownloaderXaml],
Icon = "\uEE6F")]
internal sealed partial class IncreaseFidelity : BaseSamplePage
{
@@ -37,26 +39,21 @@ public IncreaseFidelity()
protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
- if (!ImageScaler.IsAvailable())
+ if (ImageScaler.IsAvailable())
{
- sampleParams.ShowWcrModelLoadingMessage = true;
- var loadResult = await ImageScaler.MakeAvailableAsync();
- if (loadResult.Status != PackageDeploymentStatus.CompletedSuccess)
- {
- throw new InvalidOperationException(loadResult.ExtendedError.Message);
- }
- }
-
- _imageScaler = await ImageScaler.CreateAsync();
- ScaleSlider.Maximum = _imageScaler.MaxSupportedScaleFactor;
- if (_imageScaler.MaxSupportedScaleFactor >= 2)
- {
- ScaleSlider.Value = 2;
+ WcrModelDownloader.State = WcrApiDownloadState.Downloaded;
}
sampleParams.NotifyCompletion();
}
+ private async void WcrModelDownloader_DownloadClicked(object sender, EventArgs e)
+ {
+ var operation = ImageScaler.MakeAvailableAsync();
+
+ await WcrModelDownloader.SetDownloadOperation(operation);
+ }
+
private async void LoadImage_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
@@ -131,23 +128,35 @@ private async Task SetImage(IRandomAccessStream stream)
private async void ScaleImage()
{
- if (_imageScaler != null && _originalImage != null)
+ if (_originalImage == null)
{
- ScaledPanel.Visibility = Visibility.Collapsed;
- Loader.Visibility = Visibility.Visible;
-
- var newWidth = (int)(_originalImage.PixelWidth * ScaleSlider.Value);
- var newHeight = (int)(_originalImage.PixelHeight * ScaleSlider.Value);
+ return;
+ }
- var bitmap = await Task.Run(() =>
+ if (_imageScaler == null)
+ {
+ _imageScaler = await ImageScaler.CreateAsync();
+ ScaleSlider.Maximum = _imageScaler.MaxSupportedScaleFactor;
+ if (_imageScaler.MaxSupportedScaleFactor >= 2)
{
- return _imageScaler.ScaleSoftwareBitmap(_originalImage, newWidth, newHeight);
- });
-
- Loader.Visibility = Visibility.Collapsed;
- ScaledPanel.Visibility = Visibility.Visible;
- await SetImageSource(ScaledImage, bitmap, ScaledDimensionsTxt);
+ ScaleSlider.Value = 2;
+ }
}
+
+ ScaledPanel.Visibility = Visibility.Collapsed;
+ Loader.Visibility = Visibility.Visible;
+
+ var newWidth = (int)(_originalImage.PixelWidth * ScaleSlider.Value);
+ var newHeight = (int)(_originalImage.PixelHeight * ScaleSlider.Value);
+
+ var bitmap = await Task.Run(() =>
+ {
+ return _imageScaler.ScaleSoftwareBitmap(_originalImage, newWidth, newHeight);
+ });
+
+ Loader.Visibility = Visibility.Collapsed;
+ ScaledPanel.Visibility = Visibility.Visible;
+ await SetImageSource(ScaledImage, bitmap, ScaledDimensionsTxt);
}
private async Task SetImageSource(Image image, SoftwareBitmap softwareBitmap, Run textBlock)
diff --git a/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml b/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml
index a7776ff0..34e6bd4d 100644
--- a/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml
+++ b/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml
@@ -3,11 +3,11 @@
x:Class="AIDevGallery.Samples.WCRAPIs.OCRLineSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:AIDevGallery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:samples="using:AIDevGallery.Samples"
+ xmlns:shared="using:AIDevGallery.Samples.SharedCode"
mc:Ignorable="d">
@@ -79,5 +79,10 @@
VerticalAlignment="Center"
IsActive="True"
Visibility="Collapsed" />
+
diff --git a/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml.cs b/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml.cs
index 720783f9..e658f053 100644
--- a/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml.cs
+++ b/AIDevGallery/Samples/WCRAPIs/OCRLineSample.xaml.cs
@@ -3,6 +3,7 @@
using AIDevGallery.Models;
using AIDevGallery.Samples.Attributes;
+using AIDevGallery.Samples.SharedCode;
using Microsoft.Graphics.Imaging;
using Microsoft.UI;
using Microsoft.UI.Xaml;
@@ -10,7 +11,6 @@
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.UI.Xaml.Shapes;
-using Microsoft.Windows.Management.Deployment;
using Microsoft.Windows.Vision;
using System;
using System.Linq;
@@ -32,6 +32,7 @@ namespace AIDevGallery.Samples.WCRAPIs;
Model1Types = [ModelType.TextRecognitionOCR],
Scenario = ScenarioType.ImageDetectTextLines,
Id = "e26ef7bc-d847-4b2e-862a-74d872bb8635",
+ SharedCode = [SharedCodeEnum.WcrModelDownloaderCs, SharedCodeEnum.WcrModelDownloaderXaml],
Icon = "\uEE6F")]
internal sealed partial class OCRLineSample : BaseSamplePage
{
@@ -44,21 +45,21 @@ public OCRLineSample()
protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
- if (!TextRecognizer.IsAvailable())
+ if (TextRecognizer.IsAvailable())
{
- sampleParams.ShowWcrModelLoadingMessage = true;
- var loadResult = await TextRecognizer.MakeAvailableAsync();
- if (loadResult.Status != PackageDeploymentStatus.CompletedSuccess)
- {
- throw new InvalidOperationException(loadResult.ExtendedError.Message);
- }
+ WcrModelDownloader.State = WcrApiDownloadState.Downloaded;
}
- _textRecognizer = await TextRecognizer.CreateAsync();
-
sampleParams.NotifyCompletion();
}
+ private async void WcrModelDownloader_DownloadClicked(object sender, EventArgs e)
+ {
+ var operation = TextRecognizer.MakeAvailableAsync();
+
+ await WcrModelDownloader.SetDownloadOperation(operation);
+ }
+
private async void LoadImage_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
@@ -147,6 +148,7 @@ private async Task RecognizeAndAddTextAsync(SoftwareBitmap bitmap)
Loader.Visibility = Visibility.Visible;
RectCanvas.Visibility = Visibility.Collapsed;
using var imageBuffer = ImageBuffer.CreateBufferAttachedToBitmap(bitmap);
+ _textRecognizer ??= await TextRecognizer.CreateAsync();
RecognizedText? result = _textRecognizer?.RecognizeTextFromImage(imageBuffer, new TextRecognizerOptions());
if (result == null)
{
diff --git a/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml b/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml
index 43c30488..d6dba7b3 100644
--- a/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml
+++ b/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml
@@ -3,11 +3,11 @@
x:Class="AIDevGallery.Samples.WCRAPIs.OCRSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:controls="using:AIDevGallery.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:samples="using:AIDevGallery.Samples"
+ xmlns:shared="using:AIDevGallery.Samples.SharedCode"
mc:Ignorable="d">
@@ -80,5 +80,10 @@
VerticalAlignment="Center"
IsActive="True"
Visibility="Collapsed" />
+
diff --git a/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml.cs b/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml.cs
index 264fcae3..e9a6db01 100644
--- a/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml.cs
+++ b/AIDevGallery/Samples/WCRAPIs/OCRSample.xaml.cs
@@ -3,6 +3,7 @@
using AIDevGallery.Models;
using AIDevGallery.Samples.Attributes;
+using AIDevGallery.Samples.SharedCode;
using Microsoft.Graphics.Imaging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Documents;
@@ -26,6 +27,7 @@ namespace AIDevGallery.Samples.WCRAPIs;
Model1Types = [ModelType.TextRecognitionOCR],
Scenario = ScenarioType.ImageDetectText,
Id = "8f072b64-74fc-4511-b84f-e09d56394f07",
+ SharedCode = [SharedCodeEnum.WcrModelDownloaderCs, SharedCodeEnum.WcrModelDownloaderXaml],
Icon = "\uEE6F")]
internal sealed partial class OCRSample : BaseSamplePage
{
@@ -38,21 +40,22 @@ public OCRSample()
protected override async Task LoadModelAsync(SampleNavigationParameters sampleParams)
{
- if (!TextRecognizer.IsAvailable())
+ if (TextRecognizer.IsAvailable())
{
- sampleParams.ShowWcrModelLoadingMessage = true;
- var loadResult = await TextRecognizer.MakeAvailableAsync();
- if (loadResult.Status != PackageDeploymentStatus.CompletedSuccess)
- {
- throw new InvalidOperationException(loadResult.ExtendedError.Message);
- }
- }
+ WcrModelDownloader.State = WcrApiDownloadState.Downloaded;
- _textRecognizer = await TextRecognizer.CreateAsync();
+ }
sampleParams.NotifyCompletion();
}
+ private async void WcrModelDownloader_DownloadClicked(object sender, EventArgs e)
+ {
+ var operation = TextRecognizer.MakeAvailableAsync();
+
+ await WcrModelDownloader.SetDownloadOperation(operation);
+ }
+
private async void LoadImage_Click(object sender, RoutedEventArgs e)
{
var window = new Window();
@@ -132,10 +135,7 @@ private async Task SetImage(IRandomAccessStream stream)
public async Task RecognizeAndAddTextAsync(SoftwareBitmap bitmap)
{
- if (_textRecognizer == null)
- {
- return;
- }
+ _textRecognizer ??= await TextRecognizer.CreateAsync();
OutputPanel.Visibility = Visibility.Collapsed;
Loader.Visibility = Visibility.Visible;
diff --git a/AIDevGallery/Utils/AppUtils.cs b/AIDevGallery/Utils/AppUtils.cs
index c7931012..8ba06079 100644
--- a/AIDevGallery/Utils/AppUtils.cs
+++ b/AIDevGallery/Utils/AppUtils.cs
@@ -85,6 +85,8 @@ public static string GetHardwareAcceleratorString(HardwareAccelerator hardwareAc
return "GPU";
case HardwareAccelerator.QNN:
return "NPU";
+ case HardwareAccelerator.WCRAPI:
+ return "WCR";
default:
return hardwareAccelerator.ToString();
}
@@ -101,6 +103,8 @@ public static string GetHardwareAcceleratorDescription(HardwareAccelerator hardw
return "This model will run on supported GPUs with DirectML";
case HardwareAccelerator.QNN:
return "This model will run on Qualcomm NPUs";
+ case HardwareAccelerator.WCRAPI:
+ return "The model used by this Windows Copilot Runtime API will run on NPU";
}
}
diff --git a/AIDevGallery/Utils/WcrCompatibilityChecker.cs b/AIDevGallery/Utils/WcrCompatibilityChecker.cs
new file mode 100644
index 00000000..da30ccf7
--- /dev/null
+++ b/AIDevGallery/Utils/WcrCompatibilityChecker.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using AIDevGallery.Models;
+using Microsoft.Graphics.Imaging;
+using Microsoft.Windows.AI.Generative;
+using Microsoft.Windows.Vision;
+using System;
+using System.Collections.Generic;
+
+namespace AIDevGallery.Utils;
+internal static class WcrCompatibilityChecker
+{
+ private static readonly Dictionary> CompatibilityCheckers = new Dictionary>
+ {
+ {
+ ModelType.PhiSilica, LanguageModel.IsAvailable
+ },
+ {
+ ModelType.TextRecognitionOCR, TextRecognizer.IsAvailable
+ },
+ {
+ ModelType.ImageScaler, ImageScaler.IsAvailable
+ },
+ {
+ ModelType.BackgroundRemover, ImageObjectExtractor.IsAvailable
+ },
+ {
+ ModelType.ImageDescription, ImageDescriptionGenerator.IsAvailable
+ }
+ };
+
+ public static WcrApiAvailability GetApiAvailability(ModelType type)
+ {
+ if (!CompatibilityCheckers.TryGetValue(type, out Func? isAvailable))
+ {
+ return WcrApiAvailability.NotSupported;
+ }
+
+ try
+ {
+ return isAvailable() ? WcrApiAvailability.Available : WcrApiAvailability.NotAvailable;
+ }
+ catch
+ {
+ return WcrApiAvailability.NotSupported;
+ }
+ }
+}
+
+internal enum WcrApiAvailability
+{
+ Available,
+ NotAvailable,
+ NotSupported
+}
\ No newline at end of file