diff --git a/README.md b/README.md
index 733bce3..a4cd20a 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ For related documentation, see [Modernize your desktop app using the Visual laye
Minimum requirements for using the Visual layer in desktop apps are listed here. Individual samples might have different requirements, which are listed in the readme for the sample.
-- Visual Studio 2017 - [Get a free copy of Visual Studio 2017 with support for building Universal Windows apps](http://go.microsoft.com/fwlink/?LinkID=280676)
+- Visual Studio 2017 or later - [Get a free copy of Visual Studio](http://go.microsoft.com/fwlink/?LinkID=280676)
- .NET Framework 4.7.2 or later
- Windows 10 version 1803 or later
- Windows 10 SDK 17134 or later
@@ -32,6 +32,7 @@ Minimum requirements for using the Visual layer in desktop apps are listed here.
| [**Hello Composition sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloComposition)Demonstrates how to set up a project to use Composition APIs in a C++ Win32 app.See the [Using the Visual layer with Win32](https://docs.microsoft.com/windows/uwp/composition/using-the-visual-layer-with-win32) tutorial for more info. | ![Hello Composition sample](images/hello-comp-win32.png) |
| [**Hello Vectors sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloVectors)Demonstrates how to use vectors in the Visual layer. | ![Vector graphics UI](images/hello-vectors-win32.png) |
| [**Virtual Surfaces sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/VirtualSurfaces)Demonstrates how to use virtual surfaces in the Visual layer. | ![Virtual surfaces UI](images/virtual-surfaces-win32.png) |
+| [**Advanced Color sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/AdvancedColorImages)Demonstrates how to load advanced color (HDR, High Color Gamut, High precision) images into a Virtual Surface and use an Interaction Tracker that helps create a smooth scrollable surface that responds well to touch, mouse, and precision touchpad. | ![Advanced color image UI](images/advanced-color-win32.png) |
| [**Screen Capture sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/ScreenCaptureforHWND)Demonstrates how to use screen capture APIs. | ![Screen capture UI](images/screen-capture-win32.png) |
### Windows Forms
@@ -40,6 +41,7 @@ Minimum requirements for using the Visual layer in desktop apps are listed here.
| - | - |
| [**Hello Composition sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WinForms/HelloComposition)Demonstrates how to set up a project to use Composition APIs in a Windows Forms app.See the [Using the Visual layer with Windows Forms](https://docs.microsoft.com/windows/uwp/composition/using-the-visual-layer-with-windows-forms) tutorial for more info. | ![Hello Composition sample](images/hello-comp-wf.png) |
| [**Visual Layer Integration sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WinForms/VisualLayerIntegration)Demonstrates how to use a bar graph created with Composition APIs in a Windows Forms app. | ![Bar graph UI](images/bar-graph-winforms.png) |
+| [**Win2D Effects (Acrylic) sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WinForms/AcrylicEffect)Demonstrates how to use Win2D effects to show an acrylic overlay on top of a picture. | ![Acrylic effect](images/acrylic-effect-winforms.png) |
### WPF
@@ -48,12 +50,13 @@ Minimum requirements for using the Visual layer in desktop apps are listed here.
| [**Hello Composition sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/HelloComposition)Demonstrates how to set up a project to use Composition APIs in a WPF app.See the [Using the Visual layer with WPF](https://docs.microsoft.com/windows/uwp/composition/using-the-visual-layer-with-wpf) tutorial for more info. | ![Hello Composition sample](images/hello-comp-wpf.png) |
| [**Visual Layer Integration sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/VisualLayerIntegration)Demonstrates how to use a bar graph created with Composition APIs in a WPF app. | ![Bar graph UI](images/bar-graph-wpf.png) |
| [**Screen Capture sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/ScreenCapture)Demonstrates how to use screen capture APIs. | ![Screen capture UI](images/capture-wpf.png) |
+| [**Win2D Effects (Acrylic) sample**](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/AcrylicEffect)Demonstrates how to use Win2D effects to show an acrylic overlay on top of a picture. | ![Acrylic effect](images/acrlyic-effect-wpf.png) |
## Limitations
While many Visual Layer features work the same when hosted in a desktop application as they do in a UWP app, some features do have limitations. Here are some of the limitations to be aware of:
-- Effect chains rely on [Win2D](http://microsoft.github.io/Win2D/html/Introduction.htm) for the effect descriptions. The [Win2D NuGet package](https://www.nuget.org/packages/Win2D.uwp) is not supported in desktop applications, so you would need to recompile it from the [source code](https://github.com/Microsoft/Win2D).
+- Effect chains rely on [Win2D](http://microsoft.github.io/Win2D/html/Introduction.htm) for the effect descriptions. The [Win2D NuGet package](https://www.nuget.org/packages/Win2D.uwp) is not supported in desktop applications, so you need to manually add the DLL to your projects output folder. See the **Win2D Effects (Acrylic) sample** for [WPF](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WPF/AcrylicEffect) or [Windows Forms](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/dotnet/WinForms/AcrylicEffect) for more information.
- To do hit testing, you need to do bounds calculations by walking the visual tree yourself. This is the same as the Visual Layer in UWP, except in this case there's no XAML element you can easily bind to for hit testing.
- The Visual Layer does not have a primitive for rendering text.
- When two different UI technologies are used together, such as WPF and the Visual Layer, they are each responsible for drawing their own pixels on the screen, and they can't share pixels. As a result, Visual Layer content is always rendered on top of other UI content. (This is known as the _airspace_ issue.) You might need to do extra coding and testing to ensure your Visual layer content resizes with the host UI and doesn't occlude other content.
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages.sln b/cpp/AdvancedColorImages/AdvancedColorImages.sln
new file mode 100644
index 0000000..9e1496a
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.168
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AdvancedColorImages", "AdvancedColorImages\AdvancedColorImages.vcxproj", "{740EF6CC-CDDA-4413-8D66-FF32C113F077}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Debug|x64.ActiveCfg = Debug|x64
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Debug|x64.Build.0 = Debug|x64
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Debug|x86.ActiveCfg = Debug|Win32
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Debug|x86.Build.0 = Debug|Win32
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Release|x64.ActiveCfg = Release|x64
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Release|x64.Build.0 = Release|x64
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Release|x86.ActiveCfg = Release|Win32
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {7B0452A9-175E-4DE1-ACAE-560B0327AF7A}
+ EndGlobalSection
+EndGlobal
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.cpp b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.cpp
new file mode 100644
index 0000000..8f38aa4
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.cpp
@@ -0,0 +1,289 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+
+// main.cpp : Defines the entry point for the application.
+
+#include "stdafx.h"
+#include "AdvancedColorImages.h"
+#include "WinComp.h"
+
+using namespace winrt;
+using namespace Windows::UI;
+using namespace Windows::UI::Composition;
+using namespace Windows::UI::Composition::Desktop;
+using namespace Windows::UI::Input;
+
+
+#define MAX_LOADSTRING 100
+
+// Global Variables:
+HINSTANCE hInst; // current instance
+WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
+WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
+HWND m_childHWnd;
+WinComp* m_winComp;
+
+// Forward declarations of functions included in this code module:
+ATOM MyRegisterClass(HINSTANCE hInstance);
+BOOL InitInstance(HINSTANCE, int);
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+
+int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPWSTR lpCmdLine,
+ _In_ int nCmdShow)
+{
+
+
+ UNREFERENCED_PARAMETER(hPrevInstance);
+ UNREFERENCED_PARAMETER(lpCmdLine);
+
+ winrt::init_apartment(winrt::apartment_type::single_threaded);
+
+ // Initialize global strings
+ LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadStringW(hInstance, IDC_ADVANCEDCOLORIMAGES, szWindowClass, MAX_LOADSTRING);
+ MyRegisterClass(hInstance);
+ // Perform application initialization:
+ if (!InitInstance(hInstance, nCmdShow))
+ {
+ return FALSE;
+ }
+
+ HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_ADVANCEDCOLORIMAGES));
+
+ MSG msg;
+
+ // Main message loop:
+ while (GetMessage(&msg, nullptr, 0, 0))
+ {
+ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+ return (int)msg.wParam;
+}
+
+
+//
+// FUNCTION: MyRegisterClass()
+//
+// PURPOSE: Registers the window class.
+//
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+ WNDCLASSEXW wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ADVANCEDCOLORIMAGES));
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_ADVANCEDCOLORIMAGES);
+ wcex.lpszClassName = szWindowClass;
+ wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+ return RegisterClassExW(&wcex);
+}
+
+//
+// FUNCTION: InitInstance(HINSTANCE, int)
+//
+// PURPOSE: Saves instance handle and creates main window
+//
+// COMMENTS:
+//
+// In this function, we save the instance handle in a global variable and
+// create and display the main program window.
+//
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ hInst = hInstance; // Store instance handle in our global variable
+
+ HWND hWndParent = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
+
+ //SetWindowPos(hWndParent, HWND_TOPMOST, 0, 0, 300, 300, SWP_SHOWWINDOW);
+ if (!hWndParent)
+ {
+ return FALSE;
+ }
+
+ //Create the child HWND with the same size as the parent HWND, so it fills up the entire space.
+
+ RECT rect;
+ ::GetWindowRect(hWndParent, &rect);
+
+ m_childHWnd = CreateWindowW(szWindowClass, szTitle, WS_CHILD,
+ 0, 0, rect.right - rect.left, rect.bottom - rect.top,
+ hWndParent, nullptr, hInstance, nullptr);
+
+ if (!m_childHWnd)
+ {
+ return FALSE;
+ }
+ m_winComp = new WinComp();
+ // Ensure that the DispatcherQueue is initialized. This is required by the Compositor.
+ auto controller = m_winComp->EnsureDispatcherQueue();
+
+ ShowWindow(hWndParent, nCmdShow);
+ UpdateWindow(hWndParent);
+ ShowWindow(m_childHWnd, nCmdShow);
+ UpdateWindow(m_childHWnd);
+ m_winComp->Initialize(m_childHWnd);
+ m_winComp->PrepareVisuals();
+ m_winComp->ConfigureInteraction();
+ m_winComp->LoadImageFromFileName(L"hdr-image.jxr");
+ m_winComp->UpdateViewPort(true);
+ return TRUE;
+}
+
+bool LocateImageFile(HWND hWnd, LPWSTR pszFileName, DWORD cchFileName)
+{
+ pszFileName[0] = L'\0';
+
+ OPENFILENAME ofn;
+ ZeroMemory(&ofn, sizeof(ofn));
+
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hWnd;
+ ofn.lpstrFilter = L"All Image Files\0" L"*.bmp;*.dib;*.wdp;*.mdp;*.hdp;*.gif;*.png;*.jpg;*.jpeg;*.tif;*.ico;*.jxr\0"
+ L"JPEG File Interchange Format\0" L"*.jpg;*.jpeg\0"
+ L"JPEG XR Extented Range Format\0" L"*.jxr\0"
+ L"Windows Bitmap\0" L"*.bmp;*.dib\0"
+ L"High Definition Photo\0" L"*.wdp;*.mdp;*.hdp\0"
+ L"Graphics Interchange Format\0" L"*.gif\0"
+ L"Portable Network Graphics\0" L"*.png\0"
+ L"Tiff File\0" L"*.tif\0"
+ L"Icon\0" L"*.ico\0"
+ L"All Files\0" L"*.*\0"
+ L"\0";
+ ofn.lpstrFile = pszFileName;
+ ofn.nMaxFile = cchFileName;
+ ofn.lpstrTitle = L"Open Image";
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+
+ // Display the Open dialog box.
+ return (GetOpenFileName(&ofn) == TRUE);
+}
+
+//
+// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
+//
+// PURPOSE: Processes messages for the main window.
+//
+// WM_COMMAND - process the application menu
+// WM_PAINT - Paint the main window
+// WM_DESTROY - post a quit message and return
+//
+//
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_COMMAND:
+ {
+ int wmId = LOWORD(wParam);
+ // Parse the menu selections:
+ switch (wmId)
+ {
+
+ case IDM_FILE:
+ WCHAR szFileName[MAX_PATH];
+
+ if (LocateImageFile(hWnd, szFileName, ARRAYSIZE(szFileName)))
+ {
+ m_winComp->LoadImageFromFileName(szFileName);
+ }
+ else
+ {
+ MessageBox(hWnd, L"Failed to load image, select a new one.", L"Application Error", MB_ICONEXCLAMATION | MB_OK);
+ }
+
+ break;
+
+ case IDM_ABOUT:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
+ break;
+ case IDM_EXIT:
+ DestroyWindow(hWnd);
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ }
+ break;
+ case WM_POINTERDOWN:
+ {
+ //Redirect input events to the InteractionTracker for input events.
+ PointerPoint pp = PointerPoint::GetCurrentPoint(GET_POINTERID_WPARAM(wParam));
+ m_winComp->TryRedirectForManipulation(pp);
+ break;
+ }
+
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hWnd, &ps);
+ EndPaint(hWnd, &ps);
+ }
+ break;
+
+ case WM_SIZE:
+ {
+ //Update the child HWND to the new size of the parent HWnd.
+ RECT windowRect;
+ ::GetWindowRect(hWnd, &windowRect);
+ ::SetWindowPos(m_childHWnd, HWND_TOP, 0, 0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, SWP_NOZORDER);
+ if (m_winComp != nullptr)
+ m_winComp->UpdateViewPort(true);
+ }
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.h b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.h
new file mode 100644
index 0000000..1685d3e
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#include "resource.h"
+
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.ico b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.ico
new file mode 100644
index 0000000..b3ec03b
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.ico differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.rc b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.rc
new file mode 100644
index 0000000..1c9ef55
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.rc differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj
new file mode 100644
index 0000000..582191a
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj
@@ -0,0 +1,187 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {740EF6CC-CDDA-4413-8D66-FF32C113F077}
+ Win32Proj
+ AdvancedColorImages
+ 10.0.18362.0
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ Application
+ true
+ v141
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ _DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+ stdcpplatest
+ /await %(AdditionalOptions)
+
+
+ Windows
+ true
+ d2d1.lib; d3d11.lib; dxgi.lib; windowscodecs.lib; dwrite.lib; dxguid.lib; windowsapp.lib;shcore.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ true
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj.filters b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj.filters
new file mode 100644
index 0000000..67013e2
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/AdvancedColorImages.vcxproj.filters
@@ -0,0 +1,78 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.cpp b/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.cpp
new file mode 100644
index 0000000..c5008d7
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.cpp
@@ -0,0 +1,862 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+#include "stdafx.h"
+#include "DirectXTileRenderer.h"
+
+static const float sc_MaxZoom = 1.0f; // Restrict max zoom to 1:1 scale.
+static const unsigned int sc_MaxBytesPerPixel = 16; // Covers all supported image formats.
+static const float sc_nominalRefWhite = 80.0f; // Nominal white nits for sRGB and scRGB.
+
+
+// 400 bins with gamma of 10 lets us measure luminance to within 10% error for any
+// luminance above ~1.5 nits, up to 1 million nits.
+static const unsigned int sc_histNumBins = 400;
+static const float sc_histGamma = 0.1f;
+static const unsigned int sc_histMaxNits = 1000000;
+
+//
+// FUNCTION: Initialize
+//
+// PURPOSE: Initializes all the necessary devices and structures needed for a DirectX Surface rendering operation.
+//
+void DirectXTileRenderer::Initialize(Compositor const& compositor, int tileSize, int surfaceSize) {
+ namespace abi = ABI::Windows::UI::Composition;
+
+ m_compositor = compositor;
+ m_tileSize = tileSize;
+ m_surfaceSize = surfaceSize;
+
+ InitializeTextFormat();
+ CreateDeviceIndependentResources();
+ m_surfaceBrush = CreateVirtualDrawingSurfaceBrush();
+}
+
+CompositionSurfaceBrush DirectXTileRenderer::getSurfaceBrush()
+{
+ return m_surfaceBrush;
+}
+
+
+//
+// FUNCTION: DrawTile
+//
+// PURPOSE: This function iterates through a list of Tiles and draws them wihtin a single BeginDraw/EndDraw session for performance reasons.
+// OPTIMIZATION: This can fail when the surface to be drawn is really large in one go, expecially when the surface is zoomed in by a larger factor.
+//
+bool DirectXTileRenderer::DrawTile(Rect rect)
+{
+ //making sure the update rect doesnt go past the maximum size of the surface.
+ RECT updateRect = { static_cast(rect.X), static_cast(rect.Y), static_cast(min((rect.X + rect.Width),m_surfaceSize)), static_cast(min((rect.Y + rect.Height),m_surfaceSize)) };
+ SIZE updateSize = { updateRect.right - updateRect.left, updateRect.bottom - updateRect.top };
+
+ //Cannot update a surface larger than the max texture size of the hardware. 2048X2048 is the lowest max texture size for relevant hardware.
+ int MAXTEXTURESIZE = D3D_FL9_1_REQ_TEXTURE2D_U_OR_V_DIMENSION;
+ //3 is the buffer here.
+ SIZE constrainedUpdateSize = { min(updateSize.cx, MAXTEXTURESIZE - 3), min(updateSize.cy, MAXTEXTURESIZE - 3) };
+
+ float savedColorCounter = m_colorCounter;
+ //Breaking the BeginDraw/EndDraw calls to update rects that dont exceed the max texture size.
+ for (LONG y = updateRect.top; y < updateRect.bottom; y += constrainedUpdateSize.cy)
+ {
+ for (LONG x = updateRect.left; x < updateRect.right; x += constrainedUpdateSize.cx)
+ {
+ m_colorCounter = savedColorCounter;
+
+ POINT offset{};
+ RECT constrainedUpdateRect = RECT{ x, y, min(x + constrainedUpdateSize.cx, updateRect.right), min(y + constrainedUpdateSize.cy, updateRect.bottom) };
+ com_ptr d2dDeviceContext;
+ com_ptr tileBrush;
+
+ // Begin our update of the surface pixels. Passing nullptr to this call will update the entire surface. We only update the rect area that needs to be rendered.
+ if (!CheckForDeviceRemoved(m_surfaceInterop->BeginDraw(&constrainedUpdateRect, __uuidof(ID2D1DeviceContext), (void**)d2dDeviceContext.put(), &offset)))
+ {
+ return false;
+ }
+
+ d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Red, 0.f));
+
+ //Create a solid color brush for the tiles and which will be set to a different color before rendering.
+ check_hresult(d2dDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Green, 1.0f), tileBrush.put()));
+
+ // Set a transform to draw into this section of the virtual surface using the input coordate space
+ d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Translation((FLOAT)(offset.x - x), (FLOAT)(offset.y - y)));
+
+ D2D1_RECT_F d2dRect = { constrainedUpdateRect.left, constrainedUpdateRect.top, constrainedUpdateRect.right, constrainedUpdateRect.bottom };
+
+ d2dDeviceContext->PushAxisAlignedClip(d2dRect, D2D1_ANTIALIAS_MODE_ALIASED);
+ d2dDeviceContext->DrawImage(m_finalOutput.get());
+ d2dDeviceContext->PopAxisAlignedClip();
+
+ d2dDeviceContext->DrawRectangle(d2dRect, tileBrush.get(), 3.0f);
+
+ m_surfaceInterop->EndDraw();
+ }
+ }
+
+ return true;
+}
+
+
+//
+// FUNCTION: CheckForDeviceRemoved
+//
+// PURPOSE: We may detect device loss on BeginDraw calls. This helper handles this condition or other
+// errors.
+//
+bool DirectXTileRenderer::CheckForDeviceRemoved(HRESULT hr)
+{
+ if (SUCCEEDED(hr))
+ {
+ // Everything is fine -- go ahead and draw
+ return true;
+ }
+ else if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
+ {
+ // We can't draw at this time, but this failure is recoverable. Just skip drawing for
+ // now. We will be asked to draw again once the Direct3D device is recreated
+ return false;
+ }
+ // Any other error is unexpected and, therefore, fatal.
+ check_hresult(hr);
+ return true;
+}
+
+//
+// FUNCTION:Trim
+//
+// PURPOSE: Helper function that calls the trim on the virtualSurface
+//
+void DirectXTileRenderer::Trim(Rect trimRect)
+{
+ RectInt32 trimRects[1];
+ trimRects[0] = RectInt32{ (int)trimRect.X, (int)trimRect.Y, (int)trimRect.Width, (int)trimRect.Height };
+ m_virtualSurface.Trim(trimRects);
+}
+
+//
+// FUNCTION:InitializeTextFormat
+//
+// PURPOSE: Creates the text format
+//
+void DirectXTileRenderer::InitializeTextFormat()
+{
+ check_hresult(::DWriteCreateFactory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(m_dWriteFactory),
+ reinterpret_cast<::IUnknown * *>(m_dWriteFactory.put())));
+
+ check_hresult(m_dWriteFactory->CreateTextFormat(
+ L"Segoe UI",
+ nullptr,
+ DWRITE_FONT_WEIGHT_BOLD,
+ DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL,
+ 60.f,
+ L"en-US",
+ m_textFormat.put()));
+ m_textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
+ m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
+}
+
+//
+// FUNCTION:CreateFactory
+//
+// PURPOSE: Utility function to create the D2DFactory
+//
+void DirectXTileRenderer::CreateFactory()
+{
+ D2D1_FACTORY_OPTIONS options{};
+
+ check_hresult(D2D1CreateFactory(
+ D2D1_FACTORY_TYPE_SINGLE_THREADED,
+ options,
+ m_d2dFactory.put()));
+
+
+}
+
+//
+// FUNCTION:CreateDevice
+//
+// PURPOSE: Utility function to create the D3D11 device
+//
+HRESULT DirectXTileRenderer::CreateDevice(D3D_DRIVER_TYPE const type)
+{
+ WINRT_ASSERT(!m_d3dDevice);
+
+ return D3D11CreateDevice(
+ nullptr,
+ type,
+ nullptr,
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+ nullptr, 0,
+ D3D11_SDK_VERSION,
+ m_d3dDevice.put(),
+ nullptr,
+ nullptr);
+}
+
+void DirectXTileRenderer::CreateDevice()
+{
+ HRESULT hr = CreateDevice(D3D_DRIVER_TYPE_HARDWARE);
+
+ if (DXGI_ERROR_UNSUPPORTED == hr)
+ {
+ hr = CreateDevice(D3D_DRIVER_TYPE_WARP);
+ }
+
+ check_hresult(hr);
+}
+
+//
+// FUNCTION: CreateVirtualDrawingSurface
+//
+// PURPOSE: Creates a VirtualDrawingSurface into which the D2D contents will be drawn.
+//
+CompositionDrawingSurface DirectXTileRenderer::CreateVirtualDrawingSurface(SizeInt32 size)
+{
+ auto graphicsDevice2 = m_graphicsDevice.as();
+
+ m_virtualSurface = graphicsDevice2.CreateVirtualDrawingSurface(
+ size,
+ DirectXPixelFormat::R16G16B16A16Float,
+ DirectXAlphaMode::Premultiplied);
+
+ return m_virtualSurface;
+}
+
+//
+// FUNCTION: CreateVirtualDrawingSurfaceBrush
+//
+// PURPOSE: Creates a VirtualDrawingSurface into which the D2D contents will be drawn. Returns a CompositionSurfaceBrush that can be applied to a Composition Visual.
+//
+CompositionSurfaceBrush DirectXTileRenderer::CreateVirtualDrawingSurfaceBrush()
+{
+ //Virtual Surface's maximum size is 2^24, per dimension. In this sample the size will never exceed the m_surfaceSize (In this case it is 10000*TILESIZE).
+ SizeInt32 size;
+ size.Width = m_surfaceSize;
+ size.Height = m_surfaceSize;
+
+ m_surfaceInterop = CreateVirtualDrawingSurface(size).as();
+
+ ICompositionSurface surface = m_surfaceInterop.as();
+
+ CompositionSurfaceBrush surfaceBrush = m_compositor.CreateSurfaceBrush(surface);
+
+ surfaceBrush.Stretch(CompositionStretch::None);
+ surfaceBrush.HorizontalAlignmentRatio(0);
+ surfaceBrush.VerticalAlignmentRatio(0);
+ surfaceBrush.TransformMatrix(make_float3x2_translation(20.0f, 20.0f));
+
+ return surfaceBrush;
+}
+
+// White level scale is used to multiply the color values in the image; allows the user to
+// adjust the brightness of the image on an HDR display.
+void DirectXTileRenderer::SetRenderOptions(
+ RenderEffectKind effect,
+ float brightnessAdjustment,
+ AdvancedColorInfo const& acInfo,
+ Size windowSize
+)
+{
+ m_dispInfo = acInfo;
+ m_renderEffectKind = effect;
+ m_brightnessAdjust = brightnessAdjustment;
+
+ auto sdrWhite = m_dispInfo ? m_dispInfo.SdrWhiteLevelInNits() : sc_nominalRefWhite;
+
+ UpdateWhiteLevelScale(m_brightnessAdjust, sdrWhite);
+
+ // Adjust the Direct2D effect graph based on RenderEffectKind.
+ // Some RenderEffectKind values require us to apply brightness adjustment
+ // after the effect as their numerical output is affected by any luminance boost.
+ switch (m_renderEffectKind)
+ {
+
+ // Effect graph: ImageSource > ColorManagement > WhiteScale
+ case RenderEffectKind::None:
+ m_whiteScaleEffect.as(m_finalOutput);
+ m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.get());
+ break;
+ }
+}
+
+// When connected to an HDR display, the OS renders SDR content (e.g. 8888 UNORM) at
+// a user configurable white level; this typically is around 200-300 nits. It is the responsibility
+// of an advanced color app (e.g. FP16 scRGB) to emulate the OS-implemented SDR white level adjustment,
+// BUT only for non-HDR content (SDR or WCG).
+void DirectXTileRenderer::UpdateWhiteLevelScale(float brightnessAdjustment, float sdrWhiteLevel)
+{
+ float scale = 1.0f;
+
+ switch (m_imageInfo.imageKind)
+ {
+ case AdvancedColorKind::HighDynamicRange:
+ // HDR content should not be compensated by the SdrWhiteLevel parameter.
+ scale = 1.0f;
+ break;
+
+ case AdvancedColorKind::StandardDynamicRange:
+ case AdvancedColorKind::WideColorGamut:
+ default:
+ scale = sdrWhiteLevel / sc_nominalRefWhite;
+ break;
+ }
+
+ // The user may want to manually adjust brightness specifically for this image, on top of any
+ // white level adjustment for SDR/WCG content. Brightness adjustment using a linear gamma scale
+ // is mainly useful for HDR displays, but can be useful for HDR content tonemapped to an SDR/WCG display.
+ scale *= brightnessAdjustment;
+
+ // SDR white level scaling is performing by multiplying RGB color values in linear gamma.
+ // We implement this with a Direct2D matrix effect.
+ D2D1_MATRIX_5X4_F matrix = D2D1::Matrix5x4F(
+ scale, 0, 0, 0, // [R] Multiply each color channel
+ 0, scale, 0, 0, // [G] by the scale factor in
+ 0, 0, scale, 0, // [B] linear gamma space.
+ 0, 0, 0, 1, // [A] Preserve alpha values.
+ 0, 0, 0, 0); // No offset.
+
+ check_hresult(m_whiteScaleEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, matrix));
+}
+
+// Reads the provided data stream and decodes an image from it using WIC. These resources are device-
+// independent.
+ImageInfo DirectXTileRenderer::LoadImageFromWic(_In_ IStream* imageStream)
+{
+
+ // Decode the image using WIC.
+ com_ptr decoder;
+ check_hresult(
+ m_wicFactory->CreateDecoderFromStream(
+ imageStream,
+ nullptr,
+ WICDecodeMetadataCacheOnDemand,
+ decoder.put()
+ ));
+
+ com_ptr frame;
+ check_hresult(
+ decoder->GetFrame(0, frame.put())
+ );
+
+ return LoadImageCommon(frame.get());
+}
+
+
+// Reads the provided File and decodes an image from it using WIC. These resources are device-
+// independent.
+ImageInfo DirectXTileRenderer::LoadImageFromWic(LPCWSTR szFileName)
+{
+
+ // Create a decoder
+ IWICBitmapDecoder* pDecoder = nullptr;
+
+ // Decode the image using WIC.
+ com_ptr decoder;
+ check_hresult(
+ m_wicFactory->CreateDecoderFromFilename(
+ szFileName, // Image to be decoded
+ nullptr, // Do not prefer a particular vendor
+ GENERIC_READ, // Desired read access to the file
+ WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
+ decoder.put() // Pointer to the decoder
+ ));
+
+
+ // Retrieve the first frame of the image from the decoder
+ com_ptr frame;
+ check_hresult(
+ decoder->GetFrame(0, frame.put())
+ );
+
+ return LoadImageCommon(frame.get());
+
+}
+
+
+
+// After initial decode, obtain image information and do common setup.
+// Populates all members of ImageInfo.
+ImageInfo DirectXTileRenderer::LoadImageCommon(_In_ IWICBitmapSource* source)
+{
+ m_imageInfo = {};
+
+ // Attempt to read the embedded color profile from the image; only valid for WIC images.
+ com_ptr frame;
+ HRESULT hr = (reinterpret_cast<::IUnknown*>(source)->QueryInterface(winrt::guid_of(),
+ reinterpret_cast(winrt::put_abi(frame))));
+ if (hr >= 0)
+ {
+ check_hresult(
+ m_wicFactory->CreateColorContext(m_wicColorContext.put())
+ );
+
+ IWICColorContext* temp = m_wicColorContext.get();
+
+ check_hresult(
+ frame->GetColorContexts(
+ 1,
+ &temp,
+ &m_imageInfo.numProfiles
+ )
+ );
+ }
+
+ // Check whether the image data is natively stored in a floating-point format, and
+ // decode to the appropriate WIC pixel format.
+
+ WICPixelFormatGUID pixelFormat;
+ check_hresult(
+ source->GetPixelFormat(&pixelFormat)
+ );
+
+ com_ptr componentInfo;
+ check_hresult(
+ m_wicFactory->CreateComponentInfo(
+ pixelFormat,
+ componentInfo.put()
+ )
+ );
+
+ com_ptr pixelFormatInfo = componentInfo.as();
+
+
+ WICPixelFormatNumericRepresentation formatNumber;
+ check_hresult(
+ pixelFormatInfo->GetNumericRepresentation(&formatNumber)
+ );
+
+ check_hresult(pixelFormatInfo->GetBitsPerPixel(&m_imageInfo.bitsPerPixel));
+
+ // Calculate the bits per channel (bit depth) using GetChannelMask.
+ // This accounts for nonstandard color channel packing and padding, e.g. 32bppRGB.
+ unsigned char channelMaskBytes[sc_MaxBytesPerPixel];
+ ZeroMemory(channelMaskBytes, ARRAYSIZE(channelMaskBytes));
+ unsigned int maskSize;
+
+ check_hresult(
+ pixelFormatInfo->GetChannelMask(
+ 0, // Read the first color channel.
+ ARRAYSIZE(channelMaskBytes),
+ channelMaskBytes,
+ &maskSize)
+ );
+
+ // Count up the number of bits set in the mask for the first color channel.
+ for (unsigned int i = 0; i < maskSize * 8; i++)
+ {
+ unsigned int byte = i / 8;
+ unsigned int bit = i % 8;
+ if ((channelMaskBytes[byte] & (1 << bit)) != 0)
+ {
+ m_imageInfo.bitsPerChannel += 1;
+ }
+ }
+
+ m_imageInfo.isFloat = (WICPixelFormatNumericRepresentationFloat == formatNumber) ? true : false;
+
+ // When decoding, preserve the numeric representation (float vs. non-float)
+ // of the native image data. This avoids WIC performing an implicit gamma conversion
+ // which occurs when converting between a fixed-point/integer pixel format (sRGB gamma)
+ // and a float-point pixel format (linear gamma). Gamma adjustment, if specified by
+ // the ICC profile, will be performed by the Direct2D color management effect.
+
+ WICPixelFormatGUID fmt = {};
+ if (m_imageInfo.isFloat)
+ {
+ fmt = GUID_WICPixelFormat64bppPRGBAHalf; // Equivalent to DXGI_FORMAT_R16G16B16A16_FLOAT.
+ }
+ else
+ {
+ fmt = GUID_WICPixelFormat64bppPRGBA; // Equivalent to DXGI_FORMAT_R16G16B16A16_UNORM.
+ // Many SDR images (e.g. JPEG) use <=32bpp, so it
+ // is possible to further optimize this for memory usage.
+ }
+
+ check_hresult(
+ m_wicFactory->CreateFormatConverter(m_formatConvert.put())
+ );
+
+ check_hresult(
+ m_formatConvert->Initialize(
+ source,
+ fmt,
+ WICBitmapDitherTypeNone,
+ nullptr,
+ 0.0f,
+ WICBitmapPaletteTypeCustom
+ )
+ );
+
+ UINT width;
+ UINT height;
+ check_hresult(
+ m_formatConvert->GetSize(&width, &height)
+ );
+
+ m_imageInfo.size = Size(static_cast(width), static_cast(height));
+
+ PopulateImageInfoACKind(&m_imageInfo);
+
+ return m_imageInfo;
+}
+
+
+// Simplified heuristic to determine what advanced color kind the image is.
+// Requires that all fields other than imageKind are populated.
+void DirectXTileRenderer::PopulateImageInfoACKind(_Inout_ ImageInfo* info)
+{
+ if (info->bitsPerPixel == 0 ||
+ info->bitsPerChannel == 0 ||
+ info->size.Width == 0 ||
+ info->size.Height == 0)
+ {
+ check_hresult(E_INVALIDARG);
+ }
+
+ info->imageKind = AdvancedColorKind::StandardDynamicRange;
+
+ // Bit depth > 8bpc or color gamut > sRGB signifies a WCG image.
+ // The presence of a color profile is used as an approximation for wide gamut.
+ if (info->bitsPerChannel > 8 || info->numProfiles >= 1)
+ {
+ info->imageKind = AdvancedColorKind::WideColorGamut;
+ }
+
+ // This application currently only natively supports HDR images with floating point.
+ // An image encoded using the HDR10 colorspace is also HDR, but this
+ // is not automatically detected by the application.
+ if (info->isFloat == true)
+ {
+ info->imageKind = AdvancedColorKind::HighDynamicRange;
+ }
+}
+
+
+// Call this after updating any spatial transform state to regenerate the effect graph.
+void DirectXTileRenderer::UpdateImageTransformState()
+{
+ if (m_imageSource)
+ {
+ // When using ID2D1ImageSource, the recommend method of scaling is to use
+ // ID2D1TransformedImageSource. It is inexpensive to recreate this object.
+ D2D1_TRANSFORMED_IMAGE_SOURCE_PROPERTIES props =
+ {
+ D2D1_ORIENTATION_DEFAULT,
+ m_zoom,
+ m_zoom,
+ D2D1_INTERPOLATION_MODE_LINEAR, // This is ignored when using DrawImage.
+ D2D1_TRANSFORMED_IMAGE_SOURCE_OPTIONS_NONE
+ };
+
+ check_hresult(
+ m_d2dContext->CreateTransformedImageSource(
+ m_imageSource.get(),
+ &props,
+ m_scaledImage.put()
+ )
+ );
+
+ // Set the new image as the new source to the effect pipeline.
+ m_colorManagementEffect->SetInput(0, m_scaledImage.get());
+ }
+}
+
+
+void DirectXTileRenderer::CreateDeviceIndependentResources()
+{
+ namespace abi = ABI::Windows::UI::Composition;
+
+ CreateFactory();
+ CreateDevice();
+ com_ptr const dxdevice = m_d3dDevice.as();
+ com_ptr interopCompositor = m_compositor.as();
+
+ check_hresult(m_d2dFactory->CreateDevice(dxdevice.get(), m_d2dDevice.put()));
+ check_hresult(interopCompositor->CreateGraphicsDevice(m_d2dDevice.get(), reinterpret_cast(put_abi(m_graphicsDevice))));
+ check_hresult(
+ CoCreateInstance(
+ CLSID_WICImagingFactory2,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ __uuidof(m_wicFactory),
+ m_wicFactory.put_void())
+ );
+}
+
+// Set HDR10 metadata to allow HDR displays to optimize behavior based on our content.
+void DirectXTileRenderer::EmitHdrMetadata()
+{
+ //auto acKind = m_dispInfo ? m_dispInfo.CurrentAdvancedColorKind : AdvancedColorKind::StandardDynamicRange;
+ //TODO: hardcoded to HDR for now. Need to fix this later.
+ //if (acKind == AdvancedColorKind::HighDynamicRange)
+ {
+ DXGI_HDR_METADATA_HDR10 metadata = {};
+
+ // This sample doesn't do any chrominance (e.g. xy) gamut mapping, so just use default
+ // color primaries values; a more sophisticated app will explicitly set these.
+ // DXGI_HDR_METADATA_HDR10 defines primaries as 1/50000 of a unit in xy space.
+ metadata.RedPrimary[0] = static_cast(m_dispInfo.RedPrimary().X * 50000.0f);
+ metadata.RedPrimary[1] = static_cast(m_dispInfo.RedPrimary().Y * 50000.0f);
+ metadata.GreenPrimary[0] = static_cast(m_dispInfo.GreenPrimary().X * 50000.0f);
+ metadata.GreenPrimary[1] = static_cast(m_dispInfo.GreenPrimary().Y * 50000.0f);
+ metadata.BluePrimary[0] = static_cast(m_dispInfo.BluePrimary().X * 50000.0f);
+ metadata.BluePrimary[1] = static_cast(m_dispInfo.BluePrimary().Y * 50000.0f);
+ metadata.WhitePoint[0] = static_cast(m_dispInfo.WhitePoint().X * 50000.0f);
+ metadata.WhitePoint[1] = static_cast(m_dispInfo.WhitePoint().Y * 50000.0f);
+
+ float effectiveMaxCLL = 0;
+
+ switch (m_renderEffectKind)
+ {
+ // Currently only the "None" render effect results in pixel values that exceed
+ // the OS-specified SDR white level, as it just passes through HDR color values.
+ case RenderEffectKind::None:
+ effectiveMaxCLL = max(m_maxCLL, 0.0f) * m_brightnessAdjust;
+ break;
+
+ default:
+ effectiveMaxCLL = m_dispInfo.SdrWhiteLevelInNits() * m_brightnessAdjust;
+ break;
+ }
+
+ // DXGI_HDR_METADATA_HDR10 defines MaxCLL in integer nits.
+ metadata.MaxContentLightLevel = static_cast(effectiveMaxCLL);
+
+ // The luminance analysis doesn't calculate MaxFrameAverageLightLevel. We also don't have mastering
+ // information (i.e. reference display in a studio), so Min/MaxMasteringLuminance is not relevant.
+ // Leave these values as 0.
+
+ //TODO set
+ /*auto sc = m_deviceResources->GetSwapChain();
+
+ ComPtr sc4;
+ DX::ThrowIfFailed(sc->QueryInterface(IID_PPV_ARGS(&sc4)));
+ DX::ThrowIfFailed(sc4->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(metadata), &metadata));*/
+ }
+}
+
+
+
+void DirectXTileRenderer::CreateImageDependentResources()
+{
+ // Create the Direct2D device object and a corresponding context.
+ com_ptr dxgiDevice;
+ dxgiDevice = m_d3dDevice.as();
+
+
+ com_ptr d2dDevice;
+ d2dDevice = m_d2dDevice.as();
+
+ check_hresult(
+ d2dDevice->CreateDeviceContext(
+ D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
+ m_d2dContext.put()
+ )
+ );
+
+ // Load the image from WIC using ID2D1ImageSource.
+ check_hresult(
+ m_d2dContext->CreateImageSourceFromWic(
+ m_formatConvert.get(),
+ m_imageSource.put()
+ )
+ );
+
+ check_hresult(
+ m_d2dContext->CreateEffect(CLSID_D2D1ColorManagement, m_colorManagementEffect.put())
+ );
+
+ check_hresult(
+ m_colorManagementEffect->SetValue(
+ D2D1_COLORMANAGEMENT_PROP_QUALITY,
+ D2D1_COLORMANAGEMENT_QUALITY_BEST // Required for floating point and DXGI color space support.
+ )
+ );
+
+
+ UpdateImageColorContext();
+
+ // The destination color space is the render target's (swap chain's) color space. This app uses an
+ // FP16 swap chain, which requires the colorspace to be scRGB.
+ com_ptr destColorContext;
+ check_hresult(
+ m_d2dContext->CreateColorContextFromDxgiColorSpace(
+ DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, // scRGB
+ destColorContext.put()
+ )
+ );
+
+ check_hresult(
+ m_colorManagementEffect->SetValue(
+ D2D1_COLORMANAGEMENT_PROP_DESTINATION_COLOR_CONTEXT,
+ destColorContext.get()
+ )
+ );
+
+
+ // White level scale is used to multiply the color values in the image; this allows the user
+ // to adjust the brightness of the image on an HDR display.
+ check_hresult(m_d2dContext->CreateEffect(CLSID_D2D1ColorMatrix, m_whiteScaleEffect.put()));
+
+ // Input to white level scale may be modified in SetRenderOptions.
+ m_whiteScaleEffect->SetInputEffect(0, m_colorManagementEffect.get());
+
+
+}
+
+// Derive the source color context from the image (embedded ICC profile or metadata).
+void DirectXTileRenderer::UpdateImageColorContext()
+{
+ com_ptr sourceColorContext;
+
+ // For most image types, automatically derive the color context from the image.
+ if (m_imageInfo.numProfiles >= 1)
+ {
+ check_hresult(
+ m_d2dContext->CreateColorContextFromWicColorContext(
+ m_wicColorContext.get(),
+ sourceColorContext.put()
+ )
+ );
+ }
+ else
+ {
+ // Since no embedded color profile/metadata exists, select a default
+ // based on the pixel format: floating point == scRGB, others == sRGB.
+ check_hresult(
+ m_d2dContext->CreateColorContext(
+ m_imageInfo.isFloat ? D2D1_COLOR_SPACE_SCRGB : D2D1_COLOR_SPACE_SRGB,
+ nullptr,
+ 0,
+ sourceColorContext.put()
+ )
+ );
+ }
+
+ check_hresult(
+ m_colorManagementEffect->SetValue(
+ D2D1_COLORMANAGEMENT_PROP_SOURCE_COLOR_CONTEXT,
+ sourceColorContext.get()
+ )
+ );
+}
+
+// Uses a histogram to compute a modified version of MaxCLL (ST.2086 max content light level).
+// Performs Begin/EndDraw on the D2D context.
+void DirectXTileRenderer::ComputeHdrMetadata()
+{
+ // Initialize with a sentinel value.
+ m_maxCLL = -1.0f;
+
+ // MaxCLL is not meaningful for SDR or WCG images.
+ if ((!m_isComputeSupported) ||
+ (m_imageInfo.imageKind != AdvancedColorKind::HighDynamicRange))
+ {
+ return;
+ }
+
+ // MaxCLL is nominally calculated for the single brightest pixel in a frame.
+ // But we take a slightly more conservative definition that takes the 99.99th percentile
+ // to account for extreme outliers in the image.
+ float maxCLLPercent = 0.9999f;
+
+
+ m_d2dContext->BeginDraw();
+
+ m_d2dContext->DrawImage(m_histogramEffect.get());
+
+ // We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
+ // is lost. It will be handled during the next call to Present.
+ HRESULT hr = m_d2dContext->EndDraw();
+ if (hr != D2DERR_RECREATE_TARGET)
+ {
+ check_hresult(hr);
+ }
+
+ float* histogramData = new float[sc_histNumBins];
+ check_hresult(
+ m_histogramEffect->GetValue(D2D1_HISTOGRAM_PROP_HISTOGRAM_OUTPUT,
+ reinterpret_cast(histogramData),
+ sc_histNumBins * sizeof(float)
+ )
+ );
+
+ unsigned int maxCLLbin = 0;
+ float runningSum = 0.0f; // Cumulative sum of values in histogram is 1.0.
+ for (int i = sc_histNumBins - 1; i >= 0; i--)
+ {
+ runningSum += histogramData[i];
+ maxCLLbin = i;
+
+ if (runningSum >= 1.0f - maxCLLPercent)
+ {
+ break;
+ }
+ }
+
+ float binNorm = static_cast(maxCLLbin) / static_cast(sc_histNumBins);
+ m_maxCLL = powf(binNorm, 1 / sc_histGamma) * sc_histMaxNits;
+
+ // Some drivers have a bug where histogram will always return 0. Treat this as unknown.
+ m_maxCLL = (m_maxCLL == 0.0f) ? -1.0f : m_maxCLL;
+}
+
+
+// Overrides any pan/zoom state set by the user to fit image to the window size.
+// Returns the computed MaxCLL of the image in nits.
+float DirectXTileRenderer::FitImageToWindow(Size panelSize)
+{
+ if (m_imageSource)
+ {
+ // Set image to be letterboxed in the window, up to the max allowed scale factor.
+ float letterboxZoom = min(
+ panelSize.Width / m_imageInfo.size.Width,
+ panelSize.Height / m_imageInfo.size.Height);
+
+ //m_zoom = min(sc_MaxZoom, letterboxZoom);
+ //Hardcoding to 1 zoom. TODO: Fix this.
+ m_zoom = 1.0f;
+
+ // Center the image.
+ m_imageOffset = D2D1::Point2F(
+ (panelSize.Width - (m_imageInfo.size.Width * m_zoom)) / 2.0f,
+ (panelSize.Height - (m_imageInfo.size.Height * m_zoom)) / 2.0f
+ );
+
+ UpdateImageTransformState();
+
+ // HDR metadata is supposed to be independent of any rendering options, but
+ // we can't compute it until the full effect graph is hooked up, which is here.
+ ComputeHdrMetadata();
+ }
+
+ return m_maxCLL;
+}
+
+//
+// FUNCTION: Constructor for Tile Struct
+//
+// PURPOSE: Creates a Tile object based on rows and columns
+//
+Tile::Tile(int lrow, int lcolumn, int tileSize)
+{
+ int x = lcolumn * tileSize;
+ int y = lrow * tileSize;
+ row = lrow;
+ column = lcolumn;
+ rect = Rect((float)x, (float)y, tileSize, tileSize);
+}
\ No newline at end of file
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.h b/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.h
new file mode 100644
index 0000000..7ba56f7
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/DirectXTileRenderer.h
@@ -0,0 +1,135 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+#pragma once
+
+using namespace winrt;
+using namespace Windows::System;
+using namespace Windows::UI;
+using namespace Windows::UI::Composition;
+using namespace Windows::UI::Composition::Desktop;
+using namespace Windows::Graphics;
+using namespace Windows::Graphics::Display;
+using namespace Windows::Graphics::DirectX;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Numerics;
+
+namespace abi = ABI::Windows::UI::Composition;
+
+struct ImageInfo
+{
+ unsigned int bitsPerPixel;
+ unsigned int bitsPerChannel;
+ bool isFloat;
+ Windows::Foundation::Size size;
+ unsigned int numProfiles;
+ Windows::Graphics::Display::AdvancedColorKind imageKind;
+};
+
+///
+/// Supported render effects which are inserted into the render pipeline.
+/// Includes HDR tonemappers and useful visual tools.
+/// Each render effect is implemented as a custom Direct2D effect.
+///
+enum class RenderEffectKind
+{
+ //ReinhardTonemap,
+ //FilmicTonemap,
+ None,
+ //SdrOverlay,
+ //LuminanceHeatmap
+};
+
+struct Tile
+{
+ Tile(int row, int column, int tileSize);
+ Rect rect;
+ int row;
+ int column;
+};
+
+class DirectXTileRenderer
+{
+public:
+ void Initialize(Compositor const& compositor, int tileSize, int surfaceSize);
+ void Trim(Rect trimRect);
+ CompositionSurfaceBrush getSurfaceBrush();
+ bool DrawTile(Rect rect);
+ void SetRenderOptions(RenderEffectKind effect, float brightnessAdjustment, AdvancedColorInfo const& acInfo, Size windowSize);
+ float FitImageToWindow(Size panelSize);
+ ImageInfo LoadImageFromWic(_In_ IStream* imageStream);
+ ImageInfo LoadImageFromWic(LPCWSTR szFileName);
+ void CreateImageDependentResources();
+
+private:
+ void InitializeTextFormat();
+ void CreateFactory();
+ HRESULT CreateDevice(D3D_DRIVER_TYPE const type);
+ void CreateDevice();
+ CompositionSurfaceBrush CreateVirtualDrawingSurfaceBrush();
+ CompositionDrawingSurface CreateVirtualDrawingSurface(SizeInt32 size);
+ bool CheckForDeviceRemoved(HRESULT hr);
+ void UpdateImageTransformState();
+ void CreateDeviceIndependentResources();
+ void UpdateWhiteLevelScale(float brightnessAdjustment, float sdrWhiteLevel);
+ ImageInfo LoadImageCommon(_In_ IWICBitmapSource* source);
+ void PopulateImageInfoACKind(_Inout_ ImageInfo* info);
+ void EmitHdrMetadata();
+ void UpdateImageColorContext();
+ void ComputeHdrMetadata();
+
+ //member variables
+ com_ptr m_dWriteFactory;
+ com_ptr m_d2dContext;
+ com_ptr m_textFormat;
+ com_ptr m_graphicsDevice;
+ com_ptr m_graphicsDevice2;
+ CompositionVirtualDrawingSurface m_virtualSurface = nullptr;
+ CompositionSurfaceBrush m_surfaceBrush = nullptr;
+ Compositor m_compositor = nullptr;
+ float m_colorCounter = 0.0;
+ int m_tileSize = 0;
+ int m_surfaceSize = 0;
+ com_ptr m_surfaceInterop;
+
+ // WIC and Direct2D resources.
+ com_ptr m_d3dDevice;
+ com_ptr m_d2dDevice;
+ com_ptr m_d2dFactory;
+ com_ptr m_formatConvert;
+ com_ptr m_wicColorContext;
+ com_ptr m_imageSource;
+ com_ptr m_scaledImage;
+ com_ptr m_colorManagementEffect;
+ com_ptr m_whiteScaleEffect;
+ com_ptr m_reinhardEffect;
+ com_ptr m_filmicEffect;
+ com_ptr m_sdrOverlayEffect;
+ com_ptr m_heatmapEffect;
+ com_ptr m_histogramPrescale;
+ com_ptr m_histogramEffect;
+ com_ptr m_finalOutput;
+ com_ptr m_wicFactory;
+
+ // Other renderer members.
+ RenderEffectKind m_renderEffectKind;
+ float m_zoom;
+ float m_minZoom;
+ D2D1_POINT_2F m_imageOffset;
+ D2D1_POINT_2F m_pointerPos;
+ float m_maxCLL; // In nits.
+ float m_brightnessAdjust;
+ AdvancedColorInfo m_dispInfo{nullptr};
+ ImageInfo m_imageInfo;
+ bool m_isComputeSupported;
+};
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/Resource.h b/cpp/AdvancedColorImages/AdvancedColorImages/Resource.h
new file mode 100644
index 0000000..a91b347
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/Resource.h
@@ -0,0 +1,31 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by AdvancedColorImages.rc
+
+#define IDS_APP_TITLE 103
+
+#define IDR_MAINFRAME 128
+#define IDD_ADVANCEDCOLORIMAGES_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDM_FILE 106
+#define IDI_ADVANCEDCOLORIMAGES 107
+#define IDI_SMALL 108
+#define IDC_ADVANCEDCOLORIMAGES 109
+#define IDC_MYICON 2
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+
+#define _APS_NO_MFC 130
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.cpp b/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.cpp
new file mode 100644
index 0000000..7b601ee
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.cpp
@@ -0,0 +1,207 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+#include "stdafx.h"
+#include "TileDrawingManager.h"
+
+
+TileDrawingManager::TileDrawingManager()
+{
+
+}
+
+TileDrawingManager::~TileDrawingManager()
+{
+ delete m_currentRenderer;
+}
+
+void TileDrawingManager::SetRenderer(DirectXTileRenderer* renderer) {
+ m_currentRenderer = renderer;
+};
+
+DirectXTileRenderer* TileDrawingManager::GetRenderer()
+{
+ return m_currentRenderer;
+}
+
+//
+// FUNCTION: UpdateVisibleRegion
+//
+// PURPOSE: More unloaded surface is now visible on screen because of some event like manipulations(zoom, pan, etc.). This method, figures
+// out the new areas that need to be rendered and fires the draw calls. This is the core of the tile drawing logic
+//
+void TileDrawingManager::UpdateVisibleRegion(float3 currentPosition)
+{
+ m_currentPosition = currentPosition;
+ bool stateUpdate = false;
+
+ int requiredTopTileRow = max((int)m_currentPosition.y / TILESIZE - DRAWAHEADTILECOUNT, 0);
+ int requiredBottomTileRow = (int)(m_currentPosition.y + m_viewPortSize.Height) / TILESIZE + DRAWAHEADTILECOUNT;
+ int requiredLeftTileColumn = max((int)m_currentPosition.x / TILESIZE - DRAWAHEADTILECOUNT, 0);
+ int requiredRightTileColumn = (int)(m_currentPosition.x + m_viewPortSize.Width) / TILESIZE + DRAWAHEADTILECOUNT;
+
+ m_currentTopLeftTileRow = (int)m_currentPosition.y / TILESIZE;
+ m_currentTopLeftTileColumn = (int)m_currentPosition.x / TILESIZE;
+
+ //Draws the tiles that are required above the drawn top row.
+ int numberOfRows = (m_drawnTopTileRow - requiredTopTileRow);
+ int numberOfColumns = (m_drawnRightTileColumn - m_drawnLeftTileColumn) + 1;
+ if (numberOfRows > 0 && numberOfColumns > 0)
+ {
+ DrawTileRange(m_drawnLeftTileColumn, requiredTopTileRow, numberOfColumns, numberOfRows);
+ stateUpdate = true;
+ }
+
+ //Draws the tiles that are required below the drawn bottom row.
+ numberOfRows = (requiredBottomTileRow - m_drawnBottomTileRow);
+ numberOfColumns = (m_drawnRightTileColumn - m_drawnLeftTileColumn) + 1;
+ if (numberOfRows > 0 && numberOfColumns > 0)
+ {
+ DrawTileRange(m_drawnLeftTileColumn, m_drawnBottomTileRow + 1, numberOfColumns, numberOfRows);
+ stateUpdate = true;
+ }
+
+ //Update the current drawn top tile row and current drawn bottom tile row.
+ m_drawnTopTileRow = min(requiredTopTileRow, m_drawnTopTileRow);
+ m_drawnBottomTileRow = max(requiredBottomTileRow, m_drawnBottomTileRow);
+
+ //Draws the tiles that are required to the left of the drawn columns.
+ numberOfRows = (m_drawnBottomTileRow - m_drawnTopTileRow) + 1;
+ numberOfColumns = (m_drawnLeftTileColumn - requiredLeftTileColumn);
+ if (numberOfRows > 0 && numberOfColumns > 0)
+ {
+ DrawTileRange(requiredLeftTileColumn, m_drawnTopTileRow, numberOfColumns, numberOfRows);
+ stateUpdate = true;
+ }
+
+ //Draws the tiles that are required to the right of the drawn columns.
+ numberOfRows = (m_drawnBottomTileRow - m_drawnTopTileRow) + 1;
+ numberOfColumns = (requiredRightTileColumn - m_drawnRightTileColumn);
+ if (numberOfRows > 0 && numberOfColumns > 0)
+ {
+ DrawTileRange(m_drawnRightTileColumn + 1, m_drawnTopTileRow, numberOfColumns, numberOfRows);
+ stateUpdate = true;
+ }
+
+ //Update the current drawn left tile columns and current drawn right tile columns.
+ m_drawnLeftTileColumn = min(requiredLeftTileColumn, m_drawnLeftTileColumn);
+ m_drawnRightTileColumn = max(requiredRightTileColumn, m_drawnRightTileColumn);
+
+ // Trimming the tiles that are not visible on screen
+ if (stateUpdate)
+ {
+ Trim(requiredLeftTileColumn, requiredTopTileRow, requiredRightTileColumn, requiredBottomTileRow);
+ }
+}
+
+//
+// FUNCTION: UpdateViewportSize
+//
+// PURPOSE: Updates the Viewport Size of the application.
+//
+void TileDrawingManager::UpdateViewportSize(Size newSize)
+{
+ m_viewPortSize = newSize;
+ //Using the ceil operator to make sure the Virtual Surfaces is loaded with tiles that occupy the entirity of the viewport
+ //not leaving any empty areas on it.
+ m_horizontalVisibleTileCount = (int)ceil(newSize.Width / TILESIZE);
+ m_verticalVisibleTileCount = (int)ceil(newSize.Height / TILESIZE);
+ DrawVisibleTilesByRange();
+}
+
+//
+// FUNCTION: GetRectForTileRange
+//
+// PURPOSE: Gets the rect that needs to be updated for this range of tiles.
+//
+Rect TileDrawingManager::GetRectForTileRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows)
+{
+ int x = tileStartColumn * TILESIZE;
+ int y = tileStartRow * TILESIZE;
+ return Rect((float)x, (float)y, (float)(numColumns * TILESIZE), (float)(numRows * TILESIZE));
+}
+
+//
+// FUNCTION: GetTilesForRange
+//
+// PURPOSE: Converts the tile coordinates into a list of Tile Objects that can be sent to the renderer.
+//
+list TileDrawingManager::GetTilesForRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows)
+{
+ list returnTiles;
+ //get Tile objects for each tile that needs to be rendered.
+ for (int i = tileStartColumn; i < tileStartColumn + numColumns; i++) {
+ for (int j = tileStartRow; j < tileStartRow + numRows; j++) {
+ returnTiles.push_back(Tile(j, i, TILESIZE));
+ }
+ }
+ return returnTiles;
+}
+
+
+//
+// FUNCTION: GetClipRectForRange
+//
+// PURPOSE: Converts the tile coordinates into a list of Tile Objects that can be sent to the renderer.
+//
+Rect TileDrawingManager::GetClipRectForRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows)
+{
+ return Rect((float)tileStartColumn*TILESIZE, (float)tileStartRow*TILESIZE, (float)(numColumns * TILESIZE), (float)(numRows * TILESIZE));
+}
+
+void TileDrawingManager::DrawTileRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows)
+{
+ //m_currentRenderer->DrawTileRange(GetRectForTileRange(tileStartColumn, tileStartRow, numColumns, numRows), GetTilesForRange(tileStartColumn, tileStartRow, numColumns, numRows));
+ m_currentRenderer->DrawTile(GetRectForTileRange(tileStartColumn, tileStartRow, numColumns, numRows));
+
+ //m_currentRenderer->Draw(GetRectForTileRange(tileStartColumn, tileStartRow, numColumns, numRows), GetClipRectForRange(tileStartColumn, tileStartRow, numColumns, numRows));
+}
+
+
+//
+// FUNCTION: DrawVisibleTilesByRange
+//
+// PURPOSE: This function combines all the tiles into a single call, so the rendering is faster as opposed to calling BeginDraw on each tile.
+//
+void TileDrawingManager::DrawVisibleTilesByRange()
+{
+ //The DRAWAHEADTILECOUNT draws tiles that the configured number of tiles outside the viewport to make sure the user doesnt see a lot
+ //of empty areas when scrolling.
+ DrawTileRange(0, 0, m_horizontalVisibleTileCount + DRAWAHEADTILECOUNT, m_verticalVisibleTileCount + DRAWAHEADTILECOUNT);
+
+ //update the tiles that are already drawn, so only the new tiles will have to be rendered when panning.
+ m_drawnRightTileColumn = m_horizontalVisibleTileCount - 1 + DRAWAHEADTILECOUNT;
+ m_drawnBottomTileRow = m_verticalVisibleTileCount - 1 + DRAWAHEADTILECOUNT;
+
+}
+
+//
+// FUNCTION: Trim()
+//
+// PURPOSE: Trims the tiles that are outside these co-ordinates. So only the contents that are visible are rendered, to save on memory.
+//
+void TileDrawingManager::Trim(int leftColumn, int topRow, int rightColumn, int bottomRow)
+{
+ auto trimRect = GetRectForTileRange(
+ leftColumn,
+ topRow,
+ rightColumn - leftColumn + 1,
+ bottomRow - topRow + 1);
+
+ m_currentRenderer->Trim(trimRect);
+
+ m_drawnLeftTileColumn = leftColumn;
+ m_drawnRightTileColumn = rightColumn;
+ m_drawnTopTileRow = topRow;
+ m_drawnBottomTileRow = bottomRow;
+}
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.h b/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.h
new file mode 100644
index 0000000..b51a3be
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/TileDrawingManager.h
@@ -0,0 +1,66 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+
+#pragma once
+
+#include "DirectXTileRenderer.h"
+
+using namespace std;
+using namespace winrt;
+using namespace Windows::Foundation::Numerics;
+using namespace Windows::Foundation;
+
+class TileDrawingManager
+{
+public:
+ TileDrawingManager();
+ ~TileDrawingManager();
+ void UpdateVisibleRegion(float3 currentPosition);
+ void UpdateViewportSize(Size newSize);
+ void SetRenderer(DirectXTileRenderer* renderer);
+ DirectXTileRenderer* GetRenderer();
+
+ const static int TILESIZE = 100;
+ const static int MAXSURFACESIZE = TILESIZE * 10000;
+ const static int DRAWAHEADTILECOUNT = 0; //Number of tiles to draw ahead
+
+private:
+
+ list GetTilesForRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows);
+ void DrawVisibleTilesByRange();
+ Rect GetRectForTileRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows);
+ Rect GetClipRectForRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows);
+ void Trim(int leftColumn, int topRow, int rightColumn, int bottomRow);
+ void DrawTileRange(int tileStartColumn, int tileStartRow, int numColumns, int numRows);
+
+ //member variables
+
+ //These variables reflect the current state of the surface and which tiles are screen.
+ //Helps us figure out the new set of tiles that need to be rendered when there's change because of manipulation
+ //or viewport size changes.
+ int m_drawnTopTileRow = 0;//Keeps track of the top tile row that is currently drawn
+ int m_drawnBottomTileRow = 0;//Keeps track of the bottom tile row that is currently drawn
+ int m_drawnLeftTileColumn = 0;//Keeps track of the left tile colum that is currently drawn
+ int m_drawnRightTileColumn = 0;//Keeps track of the right rile column that is currently drawn
+
+ int m_horizontalVisibleTileCount = 0;//Number of horizonal tiles visible.
+ int m_verticalVisibleTileCount = 0;//Number of vertical tiles visible.
+
+ Size m_viewPortSize;//Size of the viewport.
+ float3 m_currentPosition;//Current position
+ int m_currentTopLeftTileRow = 0;//Row number of the top left tile that is currently visible
+ int m_currentTopLeftTileColumn = 0;//Column number of the top left tile that is currently visible.
+
+ DirectXTileRenderer* m_currentRenderer;
+};
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.cpp b/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.cpp
new file mode 100644
index 0000000..7e3989b
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.cpp
@@ -0,0 +1,335 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+
+#include "stdafx.h"
+#include "WinComp.h"
+
+
+
+//
+// FUNCTION: EnsureDispatcherQueue
+//
+// PURPOSE: It is necessary for a DisptacherQueue to be available on the same thread in which
+// the Compositor runs on. Events for the Compositor are fired using this DispatcherQueue
+//
+DispatcherQueueController WinComp::EnsureDispatcherQueue()
+{
+ namespace abi = ABI::Windows::System;
+
+ DispatcherQueueOptions options
+ {
+ sizeof(DispatcherQueueOptions),
+ DQTYPE_THREAD_CURRENT,
+ DQTAT_COM_ASTA
+ };
+
+ DispatcherQueueController controller{ nullptr };
+ check_hresult(CreateDispatcherQueueController(options, reinterpret_cast(put_abi(controller))));
+
+ return controller;
+}
+
+//
+// FUNCTION:Initialize
+//
+// PURPOSE: Initializes all the key member variables, including the Compositor. This sample hosts directX content inside a visual
+//
+void WinComp::Initialize(HWND hwnd)
+{
+ namespace abi = ABI::Windows::UI::Composition;
+
+ m_window = hwnd;
+ Compositor compositor;
+ m_compositor = compositor;
+ m_dxRenderer = new DirectXTileRenderer();
+ m_dxRenderer->Initialize(m_compositor, TileDrawingManager::TILESIZE, TileDrawingManager::MAXSURFACESIZE);
+ m_TileDrawingManager.SetRenderer(m_dxRenderer);
+
+}
+
+void WinComp::TryRedirectForManipulation(PointerPoint pp)
+{
+ //Redirecting the Pointer input for manipulation by the InteractionTracker
+ m_interactionSource.TryRedirectForManipulation(pp);
+}
+
+void WinComp::TryUpdatePositionBy(float3 const& amount)
+{
+ m_tracker.TryUpdatePositionBy(amount);
+}
+//
+// FUNCTION: PrepareVisuals
+//
+// PURPOSE: Creates the Visual tree and hooks it up to the desktopWindowTarget
+//
+void WinComp::PrepareVisuals()
+{
+ namespace abi = ABI::Windows::UI::Composition::Desktop;
+
+ //Creates a DesktoWindowTarget that can host Windows.UI.Composition Visual tree inside an HWnd
+ auto interop = m_compositor.as();
+ check_hresult(interop->CreateDesktopWindowTarget(m_window, true, reinterpret_cast(put_abi(m_target))));
+
+ auto root = m_compositor.CreateSpriteVisual();
+ //Create a background with Gray color brush.
+ root.Brush(m_compositor.CreateColorBrush({ 0xFF, 0xFE, 0xFE , 0xFE }));
+
+ root.Size(GetWindowSize());
+ m_target.Root(root);
+
+ auto visuals = root.Children();
+ AddD2DVisual(visuals, 0.0f, 0.0f);
+}
+
+//
+// FUNCTION: AddD2DVisual
+//
+// PURPOSE: Creates a SurfaceBrush to host Direct2D content in this visual.
+//
+void WinComp::AddD2DVisual(VisualCollection const& visuals, float x, float y)
+{
+ auto compositor = visuals.Compositor();
+ m_contentVisual = compositor.CreateSpriteVisual();
+ m_contentVisual.Brush(m_TileDrawingManager.GetRenderer()->getSurfaceBrush());
+
+ m_contentVisual.Size(GetWindowSize());
+ m_contentVisual.Offset({ x, y, 0.0f, });
+
+ visuals.InsertAtTop(m_contentVisual);
+}
+
+//
+// FUNCTION: UpdateViewPort
+//
+// PURPOSE: This is called when the Viewport size has changed, because of events like maximize, resize window etc.
+//
+void WinComp::UpdateViewPort(boolean changeContentVisual)
+{
+ //return if the m_window hasn't been set.
+ if (m_window != NULL) {
+ RECT windowRect;
+ ::GetWindowRect(m_window, &windowRect);
+ Size windowSize;
+ windowSize.Height = (windowRect.bottom - windowRect.top) / m_lastTrackerScale;
+ windowSize.Width = (windowRect.right - windowRect.left) / m_lastTrackerScale;
+
+ if (changeContentVisual) {
+ m_contentVisual.Size(windowSize);
+ }
+ m_TileDrawingManager.UpdateViewportSize(windowSize);
+ m_TileDrawingManager.UpdateVisibleRegion(m_lastTrackerPosition / m_lastTrackerScale);
+ }
+}
+
+//
+// FUNCTION: GetWindowSize
+//
+// PURPOSE: Helper function for get the size of the HWnd.
+//
+Size WinComp::GetWindowSize()
+{
+ RECT windowRect;
+ ::GetWindowRect(m_window, &windowRect);
+ return Size({ (float)(windowRect.right - windowRect.left), (float)(windowRect.bottom - windowRect.top) });
+}
+
+//
+// FUNCTION: StartAnimation
+//
+// PURPOSE: Use CompositionPropertySet and Expression Animations to manipulate the Virtual Surface.
+//
+void WinComp::StartAnimation(CompositionSurfaceBrush brush)
+{
+ m_animatingPropset = m_compositor.CreatePropertySet();
+ m_animatingPropset.InsertScalar(L"xcoord", 1.0f);
+ m_animatingPropset.StartAnimation(L"xcoord", m_moveSurfaceExpressionAnimation);
+
+ m_animatingPropset.InsertScalar(L"ycoord", 1.0f);
+ m_animatingPropset.StartAnimation(L"ycoord", m_moveSurfaceUpDownExpressionAnimation);
+
+ m_animatingPropset.InsertScalar(L"scale", 1.0f);
+ m_animatingPropset.StartAnimation(L"scale", m_scaleSurfaceUpDownExpressionAnimation);
+
+ m_animateMatrix = m_compositor.CreateExpressionAnimation(L"Matrix3x2(props.scale, 0.0, 0.0, props.scale, props.xcoord, props.ycoord)");
+ m_animateMatrix.SetReferenceParameter(L"props", m_animatingPropset);
+
+ brush.StartAnimation(L"TransformMatrix", m_animateMatrix);
+}
+
+//
+// FUNCTION: ConfigureInteraction
+//
+// PURPOSE: Configure InteractionTracker on this visual, to enable touch, PTP and mousewheel based interactions.
+//
+void WinComp::ConfigureInteraction()
+{
+ m_interactionSource = VisualInteractionSource::Create(m_contentVisual);
+ m_interactionSource.PositionXSourceMode(InteractionSourceMode::EnabledWithInertia);
+ m_interactionSource.PositionYSourceMode(InteractionSourceMode::EnabledWithInertia);
+ m_interactionSource.ScaleSourceMode(InteractionSourceMode::EnabledWithInertia);
+ m_interactionSource.ManipulationRedirectionMode(VisualInteractionSourceRedirectionMode::CapableTouchpadAndPointerWheel);
+
+ m_tracker = InteractionTracker::CreateWithOwner(m_compositor, *this);
+ m_tracker.InteractionSources().Add(m_interactionSource);
+
+ m_moveSurfaceExpressionAnimation = m_compositor.CreateExpressionAnimation(L"-tracker.Position.X");
+ m_moveSurfaceExpressionAnimation.SetReferenceParameter(L"tracker", m_tracker);
+
+ m_moveSurfaceUpDownExpressionAnimation = m_compositor.CreateExpressionAnimation(L"-tracker.Position.Y");
+ m_moveSurfaceUpDownExpressionAnimation.SetReferenceParameter(L"tracker", m_tracker);
+
+ m_scaleSurfaceUpDownExpressionAnimation = m_compositor.CreateExpressionAnimation(L"tracker.Scale");
+ m_scaleSurfaceUpDownExpressionAnimation.SetReferenceParameter(L"tracker", m_tracker);
+
+ m_tracker.MinPosition(float3(0, 0, 0));
+ m_tracker.MaxPosition(float3(TileDrawingManager::MAXSURFACESIZE, TileDrawingManager::MAXSURFACESIZE, 0));
+
+ m_tracker.MinScale(0.2f);
+ m_tracker.MaxScale(3.0f);
+
+ StartAnimation(m_TileDrawingManager.GetRenderer()->getSurfaceBrush());
+}
+
+// interactionTrackerowner methods.
+
+void WinComp::CustomAnimationStateEntered(InteractionTracker sender, InteractionTrackerCustomAnimationStateEnteredArgs args)
+{
+}
+
+void WinComp::IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args)
+{
+ if (m_zooming)
+ {
+ //dont update the content visual, because the window size hasnt changed.
+ UpdateViewPort(false);
+ }
+ m_zooming = false;
+}
+
+void WinComp::InertiaStateEntered(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args)
+{
+}
+
+void WinComp::InteractingStateEntered(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args)
+{
+
+}
+
+void WinComp::RequestIgnored(InteractionTracker sender, InteractionTrackerRequestIgnoredArgs args)
+{
+}
+
+void WinComp::ValuesChanged(InteractionTracker sender, InteractionTrackerValuesChangedArgs args)
+{
+ if (m_lastTrackerScale == args.Scale())
+ {
+ m_TileDrawingManager.UpdateVisibleRegion(sender.Position() / m_lastTrackerScale);
+ }
+ else
+ {
+ // Don't run tilemanager during a zoom
+ m_zooming = true;
+ }
+
+ m_lastTrackerScale = args.Scale();
+ m_lastTrackerPosition = sender.Position();
+}
+// Based on image and display parameters, choose the best rendering options.
+void WinComp::UpdateDefaultRenderOptions()
+{
+ if (!m_isImageValid)
+ {
+ // Render options are only meaningful if an image is already loaded.
+ return;
+ }
+ //Todo provide knobs for adjusting tonemapping, brightness adjustment etc.
+
+ UpdateRenderOptions();
+}
+
+// Common method for updating options on the renderer.
+void WinComp::UpdateRenderOptions()
+{
+ m_dxRenderer->SetRenderOptions(
+ RenderEffectKind::None,
+ static_cast(3),
+ m_dispInfo,
+ GetWindowSize()
+ );
+
+}
+IAsyncAction WinComp::LoadDefaultImage()
+{
+ Uri uri(L"https://mediaplatstorage1.blob.core.windows.net/windows-universal-samples-media/image-scrgb-icc.jxr");
+
+ /*create_task(StorageFile::CreateStreamedFileFromUriAsync(L"image-scRGB-ICC.jxr", uri, nullptr)).then([=](StorageFile const& imageFile)
+ {
+ LoadImage(imageFile);
+ });
+ */
+
+ StorageFile imageFile{ co_await StorageFile::CreateStreamedFileFromUriAsync(L"image-scRGB-ICC.jxr", uri, nullptr) };
+ //Windows::Storage::StorageFolder storageFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
+ //StorageFile imageFile{ co_await storageFolder.GetFileAsync(L"hdr-image.jpg") };
+
+ co_await LoadImageFromFile(imageFile);
+ //processOp.get();
+
+}
+
+
+IAsyncAction WinComp::OpenFilePicker(HWND hwnd)
+{
+ FileOpenPicker picker;
+ picker.as()->Initialize(hwnd);
+ picker.SuggestedStartLocation(PickerLocationId::Desktop);
+ picker.FileTypeFilter().Append(L".jxr");
+ picker.FileTypeFilter().Append(L".jpg");
+ picker.FileTypeFilter().Append(L".png");
+ picker.FileTypeFilter().Append(L".tif");
+
+ StorageFile imageFile{ co_await picker.PickSingleFileAsync() };
+ co_await LoadImageFromFile(imageFile);
+ //processOp.get();
+
+
+}
+
+void WinComp::LoadImageFromFileName(LPCWSTR szFileName)
+{
+
+ ImageInfo info{ m_dxRenderer->LoadImageFromWic(szFileName) };
+ m_dxRenderer->CreateImageDependentResources();
+ m_dxRenderer->FitImageToWindow(GetWindowSize());
+ // Image loading is done at this point.
+ m_isImageValid = true;
+ UpdateDefaultRenderOptions();
+}
+
+IAsyncOperation WinComp::LoadImageFromFile(StorageFile imageFile)
+{
+
+ IRandomAccessStream ras{ co_await imageFile.OpenAsync(Windows::Storage::FileAccessMode::Read) };
+
+ com_ptr iStream{ nullptr };
+ check_hresult(CreateStreamOverRandomAccessStream(winrt::get_unknown(ras), __uuidof(iStream), iStream.put_void()));
+ ImageInfo info{ m_dxRenderer->LoadImageFromWic(iStream.get()) };
+
+ // Image loading is done at this point.
+ m_isImageValid = true;
+ UpdateDefaultRenderOptions();
+
+ co_return 1;
+
+}
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.h b/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.h
new file mode 100644
index 0000000..822d2af
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/WinComp.h
@@ -0,0 +1,100 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//*********************************************************
+
+
+#pragma once
+
+#include "stdafx.h"
+#include "TileDrawingManager.h"
+#include
+
+using namespace concurrency;
+using namespace ::winrt;
+using namespace ::winrt::impl;
+using namespace Windows::System;
+using namespace Windows::UI;
+using namespace Windows::UI::Composition;
+using namespace Windows::UI::Composition::Interactions;
+using namespace Windows::UI::Composition::Desktop;
+using namespace Windows::UI::Input;
+using namespace Windows::Graphics;
+using namespace Windows::Graphics::Display;
+using namespace Windows::Graphics::DirectX;
+using namespace Windows::Foundation;
+using namespace Windows::Foundation::Numerics;
+using namespace Windows::Storage;
+using namespace Windows::Storage::Pickers;
+using namespace Windows::Web::Syndication;
+using namespace Windows::Storage::Streams;
+
+class WinComp : public implements
+{
+
+public:
+ void Initialize(HWND hwnd);
+ void PrepareVisuals();
+ DispatcherQueueController EnsureDispatcherQueue();
+ void ConfigureInteraction();
+ void UpdateViewPort(boolean changeContentVisual);
+ IAsyncAction LoadDefaultImage();
+ IAsyncAction OpenFilePicker(HWND hwnd);
+ void LoadImageFromFileName(LPCWSTR szFileName);
+ void TryRedirectForManipulation(PointerPoint pp);
+ void TryUpdatePositionBy(float3 const& amount);
+
+ //interaction tracker owner implementation
+ void InertiaStateEntered(InteractionTracker sender, InteractionTrackerInertiaStateEnteredArgs args);
+ void InteractingStateEntered(InteractionTracker sender, InteractionTrackerInteractingStateEnteredArgs args);
+ void RequestIgnored(InteractionTracker sender, InteractionTrackerRequestIgnoredArgs args);
+ void ValuesChanged(InteractionTracker sender, InteractionTrackerValuesChangedArgs args);
+ void CustomAnimationStateEntered(InteractionTracker sender, InteractionTrackerCustomAnimationStateEnteredArgs args);
+ void IdleStateEntered(InteractionTracker sender, InteractionTrackerIdleStateEnteredArgs args);
+ IAsyncOperation LoadImageFromFile(StorageFile imageFile);
+ void UpdateDefaultRenderOptions();
+ void UpdateRenderOptions();
+
+private:
+
+ void AddD2DVisual(VisualCollection const& visuals, float x, float y);
+ void StartAnimation(CompositionSurfaceBrush brush);
+ Size GetWindowSize();
+
+ //member variables
+ Compositor m_compositor{ nullptr };
+ VisualInteractionSource m_interactionSource{ nullptr };
+ SpriteVisual m_viewportVisual{ nullptr };
+ SpriteVisual m_contentVisual{ nullptr };
+ InteractionTracker m_tracker{ nullptr };
+ DesktopWindowTarget m_target{ nullptr };
+ HWND m_window = nullptr;
+ DirectXTileRenderer* m_dxRenderer;
+
+ //animation member variables
+ ExpressionAnimation m_moveSurfaceExpressionAnimation{ nullptr };
+ ExpressionAnimation m_moveSurfaceUpDownExpressionAnimation{ nullptr };
+ ExpressionAnimation m_scaleSurfaceUpDownExpressionAnimation{ nullptr };
+ ExpressionAnimation m_animateMatrix{ nullptr };
+ CompositionPropertySet m_animatingPropset{ nullptr };
+
+ TileDrawingManager m_TileDrawingManager;
+ float m_lastTrackerScale = 1.0f;
+ float3 m_lastTrackerPosition{ 0.0f,0.0f,0.0f };
+ bool m_zooming;
+
+ ImageInfo m_imageInfo;
+ AdvancedColorInfo const& m_dispInfo{ nullptr };
+ bool m_isImageValid;
+ float m_imageMaxCLL;
+};
+
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jpg b/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jpg
new file mode 100644
index 0000000..a2f581b
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jpg differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jxr b/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jxr
new file mode 100644
index 0000000..305814e
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/hdr-image.jxr differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/image-scrgb-icc.jxr b/cpp/AdvancedColorImages/AdvancedColorImages/image-scrgb-icc.jxr
new file mode 100644
index 0000000..31beab1
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/image-scrgb-icc.jxr differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/small.ico b/cpp/AdvancedColorImages/AdvancedColorImages/small.ico
new file mode 100644
index 0000000..b3ec03b
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/small.ico differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.cpp b/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.cpp
new file mode 100644
index 0000000..09f963b
Binary files /dev/null and b/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.cpp differ
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.h b/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.h
new file mode 100644
index 0000000..d322e82
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/stdafx.h
@@ -0,0 +1,48 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+#include
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+// C RunTime Header Files
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+WINRT_WARNING_PUSH
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+// TODO: reference additional headers your program requires here
diff --git a/cpp/AdvancedColorImages/AdvancedColorImages/targetver.h b/cpp/AdvancedColorImages/AdvancedColorImages/targetver.h
new file mode 100644
index 0000000..87c0086
--- /dev/null
+++ b/cpp/AdvancedColorImages/AdvancedColorImages/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include
diff --git a/cpp/AdvancedColorImages/README.md b/cpp/AdvancedColorImages/README.md
new file mode 100644
index 0000000..19d47ad
--- /dev/null
+++ b/cpp/AdvancedColorImages/README.md
@@ -0,0 +1,45 @@
+# Advanced Color Images in Native C++
+
+An example app user interface (UI) that demonstrates the use of the Universal Windows Platform (UWP) [Visual Layer](https://docs.microsoft.com/windows/uwp/composition/visual-layer) APIs ([Windows.UI.Composition](https://docs.microsoft.com/uwp/api/windows.ui.composition)) in a native Win32 C++ App.
+
+![Advanced Color Image in Visual Layer](../../images/advanced-color-win32.png)
+
+The Visual Layer APIs provide a high performance, retained-mode API for graphics, effects, and animations. It's the recommended replacement for DirectComposition in apps that run on Windows 10.
+
+This sample demonstrates how to load Advanced color images (HDR, High Color Gamut, High precision) images into a Virtual Surface and Interaction Tracker that helps create a smooth scrollable surface that responds well to touch, mouse and precision touchpad. We use a tile based approach to show how the tiles are only loaded on a need based, depending on user interaction. Content outside this area is trimmed, to keep the memory consumption down especially when working with large surfaces.
+Developers creating Photo editing experiences will benefit with this sample and the concepts explained in it.
+
+For an introduction to hosting Visual Layer APIs in a native win32 app, see the **Using the Visual Layer with Win32** [tutorial](https://docs.microsoft.com/windows/uwp/composition/using-the-visual-layer-with-win32) and [sample](https://github.com/Microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloComposition). This sample builds on the code introduced there.
+
+## Features
+
+This sample includes the following features:
+
+- Creating a DesktopWindow render target inside a child HWND.
+- A full-on Composition Visual Tree inside this render target.
+- A Composition Virtual Surface that can host native content(Direct2D & DirectWrite in this case).
+- Showcases a canvas of size 250000*250000, that is rendered smoothly as the user navigates in it.
+- Use of InteractionTracker and Expression animations to manipulate the content.
+- Content rendering using Direct2D and DirectWrite and how it interops with Windows.UI.Composition.
+
+## Run the sample
+
+This sample requires:
+
+- Visual Studio 2017 or later - [Get a free copy of Visual Studio](http://go.microsoft.com/fwlink/?LinkID=280676)
+- Windows 10 version 1903 or later
+- Windows 10 SDK 18362 or later - [Get the SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk)
+
+## Limitations
+
+While many Visual Layer features work the same when hosted in a win32 app as they do in a UWP app, some features do have limitations. Here are some of the limitations to be aware of:
+
+- InteractionTracker does not work well with the top-level HWND, hence a child hwnd need to be created if we plan to use InteractionTracker for content manipulation.
+- To do hit testing, you need to do bounds calculations by walking the visual tree yourself. This is the same as the Visual Layer in UWP, except in this case there's no XAML element you can easily bind to for hit testing.
+- The Visual Layer does not have a primitive for rendering text. This sample uses the DirectWrite to render text to a surface.
+
+## See also
+
+We've covered a small subset of Windows Composition features that can be easily integrated into your existing or new win32 app. There are still many others, such as shadows, more animation types, perspective transforms, and so forth. For an overview of other Composition features and the benefits they can bring to your applications, see the [Visual Layer documentation](https://docs.microsoft.com/windows/uwp/composition/visual-layer).
+
+API reference: [Windows.UI.Composition](https://docs.microsoft.com/uwp/api/windows.ui.composition)
\ No newline at end of file
diff --git a/cpp/HelloComposition/HelloComposition/CompositionHost.cpp b/cpp/HelloComposition/HelloComposition/CompositionHost.cpp
index 15169bd..0a36744 100644
--- a/cpp/HelloComposition/HelloComposition/CompositionHost.cpp
+++ b/cpp/HelloComposition/HelloComposition/CompositionHost.cpp
@@ -63,25 +63,45 @@ void CompositionHost::CreateDesktopWindowTarget(HWND window)
m_target = target;
}
-void CompositionHost::CreateCompositionRoot()
-{
- auto root = m_compositor.CreateContainerVisual();
- root.RelativeSizeAdjustment({ 1.0f, 1.0f });
- root.Offset({ 24, 24, 0 });
- m_target.Root(root);
-}
-
-void CompositionHost::AddElement(float size, float x, float y)
-{
- if (m_target.Root())
- {
- auto visuals = m_target.Root().as().Children();
- auto visual = m_compositor.CreateSpriteVisual();
-
- visual.Brush(m_compositor.CreateColorBrush({ 0xDC, 0x5B, 0x9B, 0xD5 }));
- visual.Size({ size, size });
- visual.Offset({ x, y, 0.0f, });
-
- visuals.InsertAtTop(visual);
- }
+
+void CompositionHost::CreateCompositionRoot()
+{
+ auto root = m_compositor.CreateContainerVisual();
+ root.RelativeSizeAdjustment({ 1.0f, 1.0f });
+ root.Offset({ 124, 12, 0 });
+ m_target.Root(root);
+}
+
+void CompositionHost::AddElement(float size, float x, float y)
+{
+ if (m_target.Root())
+ {
+ auto visuals = m_target.Root().as().Children();
+ auto visual = m_compositor.CreateSpriteVisual();
+
+ auto element = m_compositor.CreateSpriteVisual();
+ uint8_t r = (double)(double)(rand() % 255);;
+ uint8_t g = (double)(double)(rand() % 255);;
+ uint8_t b = (double)(double)(rand() % 255);;
+
+ element.Brush(m_compositor.CreateColorBrush({ 255, r, g, b }));
+ element.Size({ size, size });
+ element.Offset({ x, y, 0.0f, });
+
+ auto animation = m_compositor.CreateVector3KeyFrameAnimation();
+ auto bottom = (float)600 - element.Size().y;
+ animation.InsertKeyFrame(1, { element.Offset().x, bottom, 0 });
+
+ using timeSpan = std::chrono::duration>;
+
+ std::chrono::seconds duration(2);
+ std::chrono::seconds delay(3);
+
+ animation.Duration(timeSpan(duration));
+ animation.DelayTime(timeSpan(delay));
+ element.StartAnimation(L"Offset", animation);
+ visuals.InsertAtTop(element);
+
+ visuals.InsertAtTop(visual);
+ }
}
\ No newline at end of file
diff --git a/cpp/HelloComposition/HelloComposition/HelloComposition.cpp b/cpp/HelloComposition/HelloComposition/HelloComposition.cpp
index 0c2db5c..3ad2a1f 100644
--- a/cpp/HelloComposition/HelloComposition/HelloComposition.cpp
+++ b/cpp/HelloComposition/HelloComposition/HelloComposition.cpp
@@ -1,185 +1,203 @@
-// HelloComposition.cpp : Defines the entry point for the application.
-//
-
-#include "pch.h"
-#include "HelloComposition.h"
-#include "CompositionHost.h"
-
-#define MAX_LOADSTRING 100
-
-// Global Variables:
-HINSTANCE hInst; // current instance
-WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
-WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
-
-// Forward declarations of functions included in this code module:
-ATOM MyRegisterClass(HINSTANCE hInstance);
-BOOL InitInstance(HINSTANCE, int);
-LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
-
-int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
- _In_opt_ HINSTANCE hPrevInstance,
- _In_ LPWSTR lpCmdLine,
- _In_ int nCmdShow)
-{
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
-
- // TODO: Place code here.
-
- // Initialize global strings
- LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
- LoadStringW(hInstance, IDC_HELLOCOMPOSITION, szWindowClass, MAX_LOADSTRING);
- MyRegisterClass(hInstance);
-
- // Perform application initialization:
- if (!InitInstance (hInstance, nCmdShow))
- {
- return FALSE;
- }
-
- HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOCOMPOSITION));
-
- MSG msg;
-
- // Main message loop:
- while (GetMessage(&msg, nullptr, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
-
- return (int) msg.wParam;
-}
-
-
-
-//
-// FUNCTION: MyRegisterClass()
-//
-// PURPOSE: Registers the window class.
-//
-ATOM MyRegisterClass(HINSTANCE hInstance)
-{
- WNDCLASSEXW wcex;
-
- wcex.cbSize = sizeof(WNDCLASSEX);
-
- wcex.style = CS_HREDRAW | CS_VREDRAW;
- wcex.lpfnWndProc = WndProc;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOCOMPOSITION));
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
- wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_HELLOCOMPOSITION);
- wcex.lpszClassName = szWindowClass;
- wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
-
- return RegisterClassExW(&wcex);
-}
-
-//
-// FUNCTION: InitInstance(HINSTANCE, int)
-//
-// PURPOSE: Saves instance handle and creates main window
-//
-// COMMENTS:
-//
-// In this function, we save the instance handle in a global variable and
-// create and display the main program window.
-//
-BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
-{
- hInst = hInstance; // Store instance handle in our global variable
-
- HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
-
- if (!hWnd)
- {
- return FALSE;
- }
-
- CompositionHost* compHost = CompositionHost::GetInstance();
- compHost->Initialize(hWnd);
- compHost->AddElement(150, 10, 10);
-
- ShowWindow(hWnd, nCmdShow);
- UpdateWindow(hWnd);
-
- return TRUE;
-}
-
-//
-// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
-//
-// PURPOSE: Processes messages for the main window.
-//
-// WM_COMMAND - process the application menu
-// WM_PAINT - Paint the main window
-// WM_DESTROY - post a quit message and return
-//
-//
-LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- switch (message)
- {
- case WM_COMMAND:
- {
- int wmId = LOWORD(wParam);
- // Parse the menu selections:
- switch (wmId)
- {
- case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
- break;
- case IDM_EXIT:
- DestroyWindow(hWnd);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- }
- break;
- case WM_PAINT:
- {
- PAINTSTRUCT ps;
- HDC hdc = BeginPaint(hWnd, &ps);
- // TODO: Add any drawing code that uses hdc here...
- EndPaint(hWnd, &ps);
- }
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- return 0;
-}
-
-// Message handler for about box.
-INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
-{
- UNREFERENCED_PARAMETER(lParam);
- switch (message)
- {
- case WM_INITDIALOG:
- return (INT_PTR)TRUE;
-
- case WM_COMMAND:
- if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
- {
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
- }
- break;
- }
- return (INT_PTR)FALSE;
-}
+// HelloComposition.cpp : Defines the entry point for the application.
+//
+
+#include "pch.h"
+#include "HelloComposition.h"
+#include "CompositionHost.h"
+
+#define MAX_LOADSTRING 100
+#define BTN_ADD 1000
+
+CompositionHost* compHost = CompositionHost::GetInstance();
+
+// Global Variables:
+HINSTANCE hInst; // current instance
+WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
+WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
+
+// Forward declarations of functions included in this code module:
+ATOM MyRegisterClass(HINSTANCE hInstance);
+BOOL InitInstance(HINSTANCE, int);
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
+
+int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPWSTR lpCmdLine,
+ _In_ int nCmdShow)
+{
+ UNREFERENCED_PARAMETER(hPrevInstance);
+ UNREFERENCED_PARAMETER(lpCmdLine);
+
+ // TODO: Place code here.
+
+ // Initialize global strings
+ LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadStringW(hInstance, IDC_HELLOCOMPOSITION, szWindowClass, MAX_LOADSTRING);
+ MyRegisterClass(hInstance);
+
+ // Perform application initialization:
+ if (!InitInstance(hInstance, nCmdShow))
+ {
+ return FALSE;
+ }
+
+ HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_HELLOCOMPOSITION));
+
+ MSG msg;
+
+ // Main message loop:
+ while (GetMessage(&msg, nullptr, 0, 0))
+ {
+ if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ return (int)msg.wParam;
+}
+
+
+
+//
+// FUNCTION: MyRegisterClass()
+//
+// PURPOSE: Registers the window class.
+//
+ATOM MyRegisterClass(HINSTANCE hInstance)
+{
+ WNDCLASSEXW wcex;
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInstance;
+ wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLOCOMPOSITION));
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_HELLOCOMPOSITION);
+ wcex.lpszClassName = szWindowClass;
+ wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
+
+ return RegisterClassExW(&wcex);
+}
+
+//
+// FUNCTION: InitInstance(HINSTANCE, int)
+//
+// PURPOSE: Saves instance handle and creates main window
+//
+// COMMENTS:
+//
+// In this function, we save the instance handle in a global variable and
+// create and display the main program window.
+//
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ hInst = hInstance; // Store instance handle in our global variable
+
+ HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, 900, 672, nullptr, nullptr, hInstance, nullptr);
+
+ if (!hWnd)
+ {
+ return FALSE;
+ }
+
+ ShowWindow(hWnd, nCmdShow);
+ UpdateWindow(hWnd);
+
+ return TRUE;
+}
+
+//
+// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
+//
+// PURPOSE: Processes messages for the main window.
+//
+// WM_COMMAND - process the application menu
+// WM_PAINT - Paint the main window
+// WM_DESTROY - post a quit message and return
+//
+//
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CREATE:
+ {
+ compHost->Initialize(hWnd);
+ srand(time(nullptr));
+
+ CreateWindow(TEXT("button"), TEXT("Add element"),
+ WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
+ 12, 12, 100, 50,
+ hWnd, (HMENU)BTN_ADD, nullptr, nullptr);
+ }
+ break;
+ case WM_COMMAND:
+ {
+ int wmId = LOWORD(wParam);
+ // Parse the menu selections:
+ switch (wmId)
+ {
+ case IDM_ABOUT:
+ DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
+ break;
+ case IDM_EXIT:
+ DestroyWindow(hWnd);
+ break;
+ case BTN_ADD: // addButton click
+ {
+ double size = (double)(rand() % 150 + 50);
+ double x = (double)(rand() % 600);
+ double y = (double)(rand() % 200);
+ compHost->AddElement(size, x, y);
+ break;
+ }
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ }
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hWnd, &ps);
+ // TODO: Add any drawing code that uses hdc here...
+ EndPaint(hWnd, &ps);
+ }
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+ return 0;
+}
+
+// Message handler for about box.
+INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ UNREFERENCED_PARAMETER(lParam);
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ return (INT_PTR)TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return (INT_PTR)TRUE;
+ }
+ break;
+ }
+ return (INT_PTR)FALSE;
+}
diff --git a/cpp/HelloComposition/README.md b/cpp/HelloComposition/README.md
index 6d71120..7bb0c47 100644
--- a/cpp/HelloComposition/README.md
+++ b/cpp/HelloComposition/README.md
@@ -19,7 +19,7 @@ This sample includes the following features:
This sample requires:
-- Visual Studio 2017 - [Get a free copy of Visual Studio 2017 with support for building Universal Windows apps](http://go.microsoft.com/fwlink/?LinkID=280676)
+- Visual Studio 2017 or later - [Get a free copy of Visual Studio](http://go.microsoft.com/fwlink/?LinkID=280676)
- .NET Framework 4.7.2 or later
- Windows 10 version 1803 or later
- Windows 10 SDK 17134 or later
diff --git a/cpp/HelloComposition/app-ui.png b/cpp/HelloComposition/app-ui.png
index c547e4b..a356248 100644
Binary files a/cpp/HelloComposition/app-ui.png and b/cpp/HelloComposition/app-ui.png differ
diff --git a/cpp/HelloVectors/HelloVectors_win32.cpp b/cpp/HelloVectors/HelloVectors_win32.cpp
index 6b8d6c4..22d7d78 100644
--- a/cpp/HelloVectors/HelloVectors_win32.cpp
+++ b/cpp/HelloVectors/HelloVectors_win32.cpp
@@ -1,306 +1,331 @@
-// HelloVectors_win32_cpp.cpp : This file contains the 'main' function. Program execution begins and ends there.
-//
-
-#include "pch.h"
-#include "LottieLogo1.h"
-#include "desktopcompositionwindow.h"
-
-using namespace winrt;
-using namespace Windows::Foundation::Numerics;
-
-///////////////////////////////////////////////////////////////////////////////////////
-// Scenario 1: construct a simple shape using ShapeVisual
-///////////////////////////////////////////////////////////////////////////////////////
-
-void Scenario1SimpleShape(const Compositor & compositor, const ContainerVisual & root) {
-
- // Create a new ShapeVisual that will contain our drawings
- ShapeVisual shape = compositor.CreateShapeVisual();
- shape.Size({ 100.0f,100.0f });
-
- // Create a circle geometry and set its radius
- auto circleGeometry = compositor.CreateEllipseGeometry();
- circleGeometry.Radius({ 30.0f, 30.0f });
-
- // Create a shape object from the geometry and give it a color and offset
- auto circleShape = compositor.CreateSpriteShape(circleGeometry);
- circleShape.FillBrush(compositor.CreateColorBrush(Windows::UI::Colors::Orange()));
- circleShape.Offset({ 50.0f, 50.0f });
-
- // Add the circle to our shape visual
- shape.Shapes().Append(circleShape);
-
- // Add to the visual tree
- root.Children().InsertAtTop(shape);
-}
-
-// end Scenario 1
-
-///////////////////////////////////////////////////////////////////////////////////////
-// Scenario 2 construct a simple path using ShapeVisual, Composition Path and Direct2D
-///////////////////////////////////////////////////////////////////////////////////////
-
-// Helper function to create a GradientBrush
-Windows::UI::Composition::CompositionLinearGradientBrush CreateGradientBrush(const Compositor & compositor)
-{
- auto gradBrush = compositor.CreateLinearGradientBrush();
- gradBrush.ColorStops().InsertAt(0, compositor.CreateColorGradientStop(0.0f, Windows::UI::Colors::Orange()));
- gradBrush.ColorStops().InsertAt(1, compositor.CreateColorGradientStop(0.5f, Windows::UI::Colors::Yellow()));
- gradBrush.ColorStops().InsertAt(2, compositor.CreateColorGradientStop(1.0f, Windows::UI::Colors::Red()));
- return gradBrush;
-}
-
-// Helper class for converting geometry to a composition compatible geometry source
-struct GeoSource final : implements
-{
-public:
- GeoSource(com_ptr const & pGeometry) :
- _cpGeometry(pGeometry)
- { }
-
- IFACEMETHODIMP GetGeometry(ID2D1Geometry** value) override
- {
- _cpGeometry.copy_to(value);
- return S_OK;
- }
-
- IFACEMETHODIMP TryGetGeometryUsingFactory(ID2D1Factory*, ID2D1Geometry** result) override
- {
- *result = nullptr;
- return E_NOTIMPL;
- }
-
-private:
- com_ptr _cpGeometry;
-};
-
-void Scenario2SimplePath(const Compositor & compositor, const ContainerVisual & root) {
- // Same steps as for SimpleShapeImperative_Click to create, size and host a ShapeVisual
- ShapeVisual shape = compositor.CreateShapeVisual();
- shape.Size({ 500.0f, 500.0f });
- shape.Offset({ 300.0f, 0.0f, 1.0f });
-
- // Create a D2D Factory
- com_ptr d2dFactory;
- check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
-
- com_ptr result;
- com_ptr path;
-
- // use D2D factory to create a path geometry
- check_hresult(d2dFactory->CreatePathGeometry(path.put()));
-
- // for the path created above, create a Geometry Sink used to add points to the path
- com_ptr sink;
- check_hresult(path->Open(sink.put()));
-
- // Add points to the path
- sink->SetFillMode(D2D1_FILL_MODE_WINDING);
- sink->BeginFigure({ 1, 1 }, D2D1_FIGURE_BEGIN_FILLED);
- sink->AddLine({ 300, 300 });
- sink->AddLine({ 1, 300 });
- sink->EndFigure(D2D1_FIGURE_END_CLOSED);
-
- // Close geometry sink
- check_hresult(sink->Close());
-
- // Create a GeoSource helper object wrapping the path
- result.attach(new GeoSource(path));
- CompositionPath trianglePath = CompositionPath(result.as());
-
- // create a CompositionPathGeometry from the composition path
- CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(trianglePath);
-
- // create a SpriteShape from the CompositionPathGeometry, give it a gradient fill and add to our ShapeVisual
- CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
- spriteShape.FillBrush(CreateGradientBrush(compositor));
-
- // Add the SpriteShape to our shape visual
- shape.Shapes().Append(spriteShape);
-
- // Add to the visual tree
- root.Children().InsertAtTop(shape);
-}
-
-// end Scenario 2
-
-///////////////////////////////////////////////////////////////////////////////////////
-// Scenario 3: Build a morph animation using ShapeVisual, two CompositionPath's and a animated CompositionPathGeometry
-///////////////////////////////////////////////////////////////////////////////////////
-
-// Helper to build a CompositionPath using a provided callback function
-CompositionPath BuildPath(com_ptr const & d2dFactory, std::function const & sink)> builder)
-{
- // See scenario 2 for a more detailed explanation of the items here
- com_ptr result;
- com_ptr path;
- check_hresult(d2dFactory->CreatePathGeometry(path.put()));
- com_ptr sink;
-
- check_hresult(path->Open(sink.put()));
-
- builder(sink);
-
- check_hresult(sink->Close());
-
- result.attach(new GeoSource(path));
- CompositionPath trianglePath = CompositionPath(result.as());
- return trianglePath;
-}
-
-// Helper to build a square CompositionPath
-CompositionPath BuildSquarePath(com_ptr const & d2dFactory)
-{
- auto squareBuilder = [](com_ptr const & sink) {
- sink->SetFillMode(D2D1_FILL_MODE_WINDING);
- sink->BeginFigure({ -90, -146 }, D2D1_FIGURE_BEGIN_FILLED);
- sink->AddBezier({ { -90.0F, -146.0F }, { 176.0F, -148.555F }, { 176.0F, -148.555F } });
- sink->AddBezier({ { 176.0F, -148.555F }, { 174.445F, 121.445F }, { 174.445F, 121.445F } });
- sink->AddBezier({ { 174.445F, 121.445F }, { -91.555F, 120.0F }, { -91.555F, 120.0F } });
- sink->AddBezier({ { -91.555F, 120.0F }, { -90.0F, -146.0F }, { -90.0F, -146.0F } });
- sink->AddLine({ 1, 300 });
- sink->EndFigure(D2D1_FIGURE_END_CLOSED);
- };
-
- return BuildPath(d2dFactory, squareBuilder);
-}
-
-// Helper to build a circular CompositionPath
-CompositionPath BuildCirclePath(com_ptr const & d2dFactory)
-{
- auto circleBuilder = [](com_ptr const & sink) {
- sink->SetFillMode(D2D1_FILL_MODE_WINDING);
- sink->BeginFigure({ 42.223F, -146 }, D2D1_FIGURE_BEGIN_FILLED);
- sink->AddBezier({ { 115.248F, -146 }, { 174.445F, -86.13F }, { 174.445F, -12.277F } });
- sink->AddBezier({ { 174.445F, 61.576F }, { 115.248F, 121.445F }, { 42.223F, 121.445F } });
- sink->AddBezier({ { -30.802F, 121.445F }, { -90, 61.576F }, { -90, -12.277F } });
- sink->AddBezier({ { -90, -86.13F }, { -30.802F, -146 }, { 42.223F, -146 } });
- sink->AddLine({ 1, 300 });
- sink->EndFigure(D2D1_FIGURE_END_CLOSED);
- };
-
- return BuildPath(d2dFactory, circleBuilder);
-}
-
-// Scenario 3: create morph animation
-void Scenario3PathMorphImperative(const Compositor & compositor, const ContainerVisual & root) {
- // Same steps as for SimpleShapeImperative_Click to create, size and host a ShapeVisual
- ShapeVisual shape = compositor.CreateShapeVisual();
- shape.Size({ 500.0f, 500.0f });
- shape.Offset({ 600.0f, 0.0f, 1.0f });
-
- com_ptr d2dFactory;
- check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
-
- // Call helper functions that use Win2D to build square and circle path geometries and create CompositionPath's for them
- auto squarePath = BuildSquarePath(d2dFactory);
-
- auto circlePath = BuildCirclePath(d2dFactory);
-
- // Create a CompositionPathGeometry, CompositionSpriteShape and set offset and fill
- CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(squarePath);
- CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
- spriteShape.Offset({ 150.0f, 200.0f });
- spriteShape.FillBrush(CreateGradientBrush(compositor));
-
- // Create a PathKeyFrameAnimation to set up the path morph passing in the circle and square paths
- auto playAnimation = compositor.CreatePathKeyFrameAnimation();
- playAnimation.Duration(std::chrono::seconds(4));
- playAnimation.InsertKeyFrame(0, squarePath);
- playAnimation.InsertKeyFrame(0.3F, circlePath);
- playAnimation.InsertKeyFrame(0.6F, circlePath);
- playAnimation.InsertKeyFrame(1.0F, squarePath);
-
- // Make animation repeat forever and start it
- playAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
- playAnimation.Direction(AnimationDirection::Alternate);
- compositionPathGeometry.StartAnimation(L"Path", playAnimation);
-
- // Add the SpriteShape to our shape visual
- shape.Shapes().Append(spriteShape);
-
- // Add to the visual tree
- root.Children().InsertAtTop(shape);
-}
-
-// end Scenario 3
-
-///////////////////////////////////////////////////////////////////////////////////////
-// Scenario 4: Use output from LottieViewer https://www.microsoft.com/store/productId/9P7X9K692TMW
-// to play back anamiation generated in Adobe After Effects and converted using Bodymovin
-///////////////////////////////////////////////////////////////////////////////////////
-
-// Helper funciton for playing back lottie generated animations
-ScalarKeyFrameAnimation Play(const Compositor & compositor, Visual const & visual) {
- auto progressAnimation = compositor.CreateScalarKeyFrameAnimation();
- progressAnimation.Duration(std::chrono::seconds(5));
- progressAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
- progressAnimation.Direction(AnimationDirection::Alternate);
- auto linearEasing = compositor.CreateLinearEasingFunction();
- progressAnimation.InsertKeyFrame(0, 0, linearEasing);
- progressAnimation.InsertKeyFrame(1, 1, linearEasing);
-
- visual.Properties().StartAnimation(L"Progress", progressAnimation);
- return progressAnimation;
-}
-
-// Scenario 4
-void Scenario4PlayLottieOutput(const Compositor & compositor, const ContainerVisual & root) {
- //configure a container visual
- float width = 400.0f, height = 400.0f;
- SpriteVisual container = compositor.CreateSpriteVisual();
- container.Size({ width, height });
- container.Offset({ 0.0f, 350.0f, 1.0f });
- root.Children().InsertAtTop(container);
-
- AnimatedVisuals::LottieLogo1 bmv;
-
- //NOTE to make this scenario compile with prerelease Microsoft.UI.Xaml package 190131001 you need to edit: …\UWPCompositionDemos\HelloVectors\packages\Microsoft.UI.Xaml.2.1.190131001-prerelease\build\native\Microsoft.UI.Xaml.targets
- //and change with
-
- winrt::Windows::Foundation::IInspectable diags;
- auto avptr = bmv.TryCreateAnimatedVisual(compositor, diags);
-
- auto visual = avptr.RootVisual();
- container.Children().InsertAtTop(visual);
-
- //// Calculate a scale to make the animation fit into the specified visual size
- container.Scale({ width / avptr.Size().x, height / avptr.Size().y, 1.0f });
-
- auto playanimation = Play(compositor, visual);
-}
-
-// end scenario 3
-
-// Bootstap the app
-int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
-{
- init_apartment(apartment_type::single_threaded);
-
- // Create a dispatcher controller required when using Windows::UI::Composition in win32
- auto controller = CreateDispatcherQueueController();
-
- // Callback that will build a visual tree containing each sample
- auto buildvisualtree =
- [](const Compositor & compositor, const Visual & root) {
-
- Scenario1SimpleShape(compositor, root.as());
- Scenario2SimplePath(compositor, root.as());
- Scenario3PathMorphImperative(compositor, root.as());
- Scenario4PlayLottieOutput(compositor, root.as());
- };
-
- // Composition Window. For more details see TODO: link to basic walkthrough
- CompositionWindow window(buildvisualtree);
-
- // Win32 MessageLoop
- MSG message;
-
- while (GetMessage(&message, nullptr, 0, 0))
- {
- DispatchMessage(&message);
- }
-}
-
+// ---------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ---------------------------------------------------------------------------------
+
+// HelloVectors_win32_cpp.cpp :
+// This file contains the 'main' function. Program execution begins and ends there.
+//
+
+#include "pch.h"
+#include "LottieLogo1.h"
+#include "desktopcompositionwindow.h"
+
+using namespace winrt;
+using namespace Windows::Foundation::Numerics;
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Scenario 1: Construct a simple shape using ShapeVisual.
+///////////////////////////////////////////////////////////////////////////////////////
+
+void Scenario1SimpleShape(const Compositor & compositor, const ContainerVisual & root) {
+
+ // Create a new ShapeVisual that will contain our drawings.
+ ShapeVisual shape = compositor.CreateShapeVisual();
+ shape.Size({ 400.0f,400.0f });
+
+ // Create a circle geometry and set its radius.
+ auto circleGeometry = compositor.CreateEllipseGeometry();
+ circleGeometry.Radius({ 30.0f, 30.0f });
+
+ // Create a shape object from the geometry and give it a color and offset.
+ auto circleShape = compositor.CreateSpriteShape(circleGeometry);
+ circleShape.FillBrush(compositor.CreateColorBrush(ColorHelper::FromArgb(255, 0, 209, 193)));
+ circleShape.Offset({ 200.0f, 200.0f });
+
+ // Add the circle to our shape visual.
+ shape.Shapes().Append(circleShape);
+
+ // Add to the visual tree.
+ root.Children().InsertAtTop(shape);
+}
+
+// end Scenario 1
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Scenario 2: Construct a simple path using ShapeVisual, CompositionPath, and Direct2D.
+///////////////////////////////////////////////////////////////////////////////////////
+
+// Helper function to create a GradientBrush.
+Windows::UI::Composition::CompositionLinearGradientBrush CreateGradientBrush(const Compositor & compositor)
+{
+ auto gradBrush = compositor.CreateLinearGradientBrush();
+ gradBrush.ColorStops().InsertAt(0, compositor.CreateColorGradientStop(0.0f, ColorHelper::FromArgb(255, 0, 120, 134)));
+ gradBrush.ColorStops().InsertAt(1, compositor.CreateColorGradientStop(0.5f, ColorHelper::FromArgb(255, 245, 245, 245)));
+ gradBrush.ColorStops().InsertAt(2, compositor.CreateColorGradientStop(1.0f, ColorHelper::FromArgb(255, 0, 209, 193)));
+ return gradBrush;
+}
+
+// Helper class for converting geometry to a composition compatible geometry source.
+struct GeoSource : implements
+{
+public:
+ GeoSource(com_ptr const & pGeometry) :
+ _cpGeometry(pGeometry)
+ { }
+
+ IFACEMETHODIMP GetGeometry(ID2D1Geometry** value) override
+ {
+ _cpGeometry.copy_to(value);
+ return S_OK;
+ }
+
+ IFACEMETHODIMP TryGetGeometryUsingFactory(ID2D1Factory*, ID2D1Geometry** result) override
+ {
+ *result = nullptr;
+ return E_NOTIMPL;
+ }
+
+private:
+ com_ptr _cpGeometry;
+};
+
+void Scenario2SimplePath(const Compositor & compositor, const ContainerVisual & root)
+{
+ // Create, size, and host a ShapeVisual.
+ ShapeVisual shape = compositor.CreateShapeVisual();
+ shape.Size({ 500.0f, 500.0f });
+ shape.Offset({ 400.0f, 25.0f, 1.0f });
+
+ // Create a D2D Factory
+ com_ptr d2dFactory;
+ check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
+
+ com_ptr path;
+
+ // Use D2D factory to create a path geometry.
+ check_hresult(d2dFactory->CreatePathGeometry(path.put()));
+
+ // For the path created above, create a geometry sink used to add points to the path.
+ com_ptr sink;
+ check_hresult(path->Open(sink.put()));
+
+ // Add points to the path.
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ sink->BeginFigure({ 1, 1 }, D2D1_FIGURE_BEGIN_FILLED);
+ sink->AddLine({ 300, 300 });
+ sink->AddLine({ 1, 300 });
+ sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+
+ // Close geometry sink.
+ check_hresult(sink->Close());
+
+ // Create a GeoSource helper object to wrap the path.
+ CompositionPath trianglePath = CompositionPath(make(path));
+
+ // Create a CompositionPathGeometry from the composition path.
+ CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(trianglePath);
+
+ // Create a SpriteShape from the CompositionPathGeometry, give it a gradient fill, and add to our ShapeVisual.
+ CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
+ spriteShape.FillBrush(CreateGradientBrush(compositor));
+
+ // Add the SpriteShape to our shape visual.
+ shape.Shapes().Append(spriteShape);
+
+ // Add to the visual tree.
+ root.Children().InsertAtTop(shape);
+}
+
+// end Scenario 2
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Scenario 3: Build a morph animation using ShapeVisual, two CompositionPaths,
+// and an animated CompositionPathGeometry
+///////////////////////////////////////////////////////////////////////////////////////
+
+// Helper to build a CompositionPath using a provided callback function.
+CompositionPath BuildPath(com_ptr const & d2dFactory, std::function const & sink)> builder)
+{
+ // See scenario 2 for a more detailed explanation of the items here.
+ com_ptr path;
+ check_hresult(d2dFactory->CreatePathGeometry(path.put()));
+ com_ptr sink;
+
+ check_hresult(path->Open(sink.put()));
+
+ builder(sink);
+
+ check_hresult(sink->Close());
+
+ CompositionPath trianglePath = CompositionPath(make(path));
+ return trianglePath;
+}
+
+// Helper to build a square CompositionPath.
+CompositionPath BuildSquarePath(com_ptr const & d2dFactory)
+{
+ auto squareBuilder = [](com_ptr const & sink) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ sink->BeginFigure({ -90, -146 }, D2D1_FIGURE_BEGIN_FILLED);
+ sink->AddBezier({ { -90.0F, -146.0F }, { 176.0F, -148.555F }, { 176.0F, -148.555F } });
+ sink->AddBezier({ { 176.0F, -148.555F }, { 174.445F, 121.445F }, { 174.445F, 121.445F } });
+ sink->AddBezier({ { 174.445F, 121.445F }, { -91.555F, 120.0F }, { -91.555F, 120.0F } });
+ sink->AddBezier({ { -91.555F, 120.0F }, { -90.0F, -146.0F }, { -90.0F, -146.0F } });
+ sink->AddLine({ 1, 300 });
+ sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ };
+
+ return BuildPath(d2dFactory, squareBuilder);
+}
+
+// Helper to build a circular CompositionPath.
+CompositionPath BuildCirclePath(com_ptr const & d2dFactory)
+{
+ auto circleBuilder = [](com_ptr const & sink) {
+ sink->SetFillMode(D2D1_FILL_MODE_WINDING);
+ sink->BeginFigure({ 42.223F, -146 }, D2D1_FIGURE_BEGIN_FILLED);
+ sink->AddBezier({ { 115.248F, -146 }, { 174.445F, -86.13F }, { 174.445F, -12.277F } });
+ sink->AddBezier({ { 174.445F, 61.576F }, { 115.248F, 121.445F }, { 42.223F, 121.445F } });
+ sink->AddBezier({ { -30.802F, 121.445F }, { -90, 61.576F }, { -90, -12.277F } });
+ sink->AddBezier({ { -90, -86.13F }, { -30.802F, -146 }, { 42.223F, -146 } });
+ sink->AddLine({ 1, 300 });
+ sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+ };
+
+ return BuildPath(d2dFactory, circleBuilder);
+}
+
+// Scenario 3: create morph animation
+void Scenario3PathMorphImperative(const Compositor & compositor, const ContainerVisual & root) {
+ // Create, size, and host a ShapeVisual.
+ ShapeVisual shape = compositor.CreateShapeVisual();
+ shape.Size({ 500.0f, 500.0f });
+ shape.Offset({ 0.0f, 350.0f, 1.0f });
+
+ com_ptr d2dFactory;
+ check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.put()));
+
+ // Call helper functions that use Win2D to build square and circle
+ // path geometries and create CompositionPaths for them.
+ auto squarePath = BuildSquarePath(d2dFactory);
+
+ auto circlePath = BuildCirclePath(d2dFactory);
+
+ // Create a CompositionPathGeometry & CompositionSpriteShape, and set offset and fill.
+ CompositionPathGeometry compositionPathGeometry = compositor.CreatePathGeometry(squarePath);
+ CompositionSpriteShape spriteShape = compositor.CreateSpriteShape(compositionPathGeometry);
+ spriteShape.Offset({ 150.0f, 200.0f });
+ spriteShape.FillBrush(CreateGradientBrush(compositor));
+
+ // Create a PathKeyFrameAnimation to set up the path morph, passing in the circle and square paths.
+ auto playAnimation = compositor.CreatePathKeyFrameAnimation();
+ playAnimation.Duration(std::chrono::seconds(4));
+ playAnimation.InsertKeyFrame(0, squarePath);
+ playAnimation.InsertKeyFrame(0.3F, circlePath);
+ playAnimation.InsertKeyFrame(0.6F, circlePath);
+ playAnimation.InsertKeyFrame(1.0F, squarePath);
+
+ // Make animation repeat forever and start it.
+ playAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
+ playAnimation.Direction(AnimationDirection::Alternate);
+ compositionPathGeometry.StartAnimation(L"Path", playAnimation);
+
+ // Add the SpriteShape to our shape visual.
+ shape.Shapes().Append(spriteShape);
+
+ // Add to the visual tree.
+ root.Children().InsertAtTop(shape);
+}
+
+// end Scenario 3
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Scenario 4: Use output from LottieViewer (https://www.microsoft.com/store/productId/9P7X9K692TMW)
+// to play back animation generated in Adobe After Effects and converted using Bodymovin.
+///////////////////////////////////////////////////////////////////////////////////////
+
+// Helper function for playing back Lottie generated animations.
+ScalarKeyFrameAnimation Play(const Compositor & compositor, Visual const & visual)
+{
+ auto progressAnimation = compositor.CreateScalarKeyFrameAnimation();
+ progressAnimation.Duration(std::chrono::seconds(5));
+ progressAnimation.IterationBehavior(AnimationIterationBehavior::Forever);
+ progressAnimation.Direction(AnimationDirection::Alternate);
+ auto linearEasing = compositor.CreateLinearEasingFunction();
+ progressAnimation.InsertKeyFrame(0, 0, linearEasing);
+ progressAnimation.InsertKeyFrame(1, 1, linearEasing);
+
+ visual.Properties().StartAnimation(L"Progress", progressAnimation);
+ return progressAnimation;
+}
+
+// Scenario 4.
+void Scenario4PlayLottieOutput(const Compositor & compositor, const ContainerVisual & root)
+{
+ // Configure a container visual.
+ float width = 400.0f, height = 400.0f;
+ SpriteVisual container = compositor.CreateSpriteVisual();
+ container.Size({ width, height });
+ container.Offset({ 400.0f, 400.0f, 1.0f });
+ root.Children().InsertAtTop(container);
+
+ // NOTE: To make this scenario compile with prerelease Microsoft.UI.Xaml package 190131001 you need to edit:
+ // HelloVectors\packages\Microsoft.UI.Xaml.2.1.190131001-prerelease\build\native\Microsoft.UI.Xaml.targets
+ // and change to .
+
+ winrt::Windows::Foundation::IInspectable diags;
+ auto bmv = make();
+ auto avptr = bmv.TryCreateAnimatedVisual(compositor, diags);
+
+ auto visual = avptr.RootVisual();
+ container.Children().InsertAtTop(visual);
+
+ // Calculate a scale to make the animation fit into the specified visual size.
+ container.Scale({ width / avptr.Size().x, height / avptr.Size().y, 1.0f });
+
+ auto playanimation = Play(compositor, visual);
+}
+
+// end scenario 4
+
+// Bootstap the app.
+int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
+{
+ init_apartment(apartment_type::single_threaded);
+
+ // Create a dispatcher controller required when using Windows::UI::Composition in Win32.
+ auto controller = CreateDispatcherQueueController();
+
+ // Callback that will build a visual tree containing each sample.
+ auto buildvisualtree =
+ [](const Compositor & compositor, const Visual & root) {
+
+ Scenario1SimpleShape(compositor, root.as());
+ Scenario2SimplePath(compositor, root.as());
+ Scenario3PathMorphImperative(compositor, root.as());
+ Scenario4PlayLottieOutput(compositor, root.as());
+ };
+
+ // Composition Window.
+ CompositionWindow window(buildvisualtree);
+
+ // Win32 MessageLoop
+ MSG message;
+
+ while (GetMessage(&message, nullptr, 0, 0))
+ {
+ DispatchMessage(&message);
+ }
+}
diff --git a/cpp/HelloVectors/HelloVectors_win32_cpp.vcxproj b/cpp/HelloVectors/HelloVectors_win32_cpp.vcxproj
index 54f10d6..0ca018d 100644
--- a/cpp/HelloVectors/HelloVectors_win32_cpp.vcxproj
+++ b/cpp/HelloVectors/HelloVectors_win32_cpp.vcxproj
@@ -1,6 +1,6 @@
-
+
Debug
@@ -24,7 +24,7 @@
{836E29C1-61B8-46F2-9BA9-DDC3DE7C44BB}
Win32Proj
HelloVectorswin32cpp
- 10.0.17763.0
+ 10.0.18362.0
10.0.17763.0
@@ -58,8 +58,8 @@
-
-
+
+
@@ -123,7 +123,7 @@
pch.h
- Console
+ Windows
true
@@ -140,7 +140,7 @@
pch.h
- Console
+ Windows
true
true
true
@@ -159,7 +159,7 @@
pch.h
- Console
+ Windows
true
true
true
@@ -193,8 +193,8 @@
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
+
+
+
\ No newline at end of file
diff --git a/cpp/HelloVectors/LottieLogo1.cpp b/cpp/HelloVectors/LottieLogo1.cpp
index 91825ba..901f66e 100644
--- a/cpp/HelloVectors/LottieLogo1.cpp
+++ b/cpp/HelloVectors/LottieLogo1.cpp
@@ -1,4 +1,28 @@
-//------------------------------------------------------------------------------
+// ---------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ---------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
//
// This code was generated by a tool.
//
@@ -21,7 +45,7 @@ using namespace Microsoft::UI::Xaml::Controls;
namespace
{
-struct GeoSource final : implements
{
@@ -128,60 +152,6 @@ struct AnimatedVisual : implements
return _colorBrush_White = _c.CreateColorBrush({ 0xFF, 0xFF, 0xFF, 0xFF });
}
- CompositionPath CompositionPath_00()
- {
- auto result = _compositionPath_00 = CompositionPath(Geometry_00().as());
- return result;
- }
-
- CompositionPath CompositionPath_01()
- {
- auto result = _compositionPath_01 = CompositionPath(Geometry_01().as());
- return result;
- }
-
- CompositionPath CompositionPath_02()
- {
- auto result = _compositionPath_02 = CompositionPath(Geometry_02().as());
- return result;
- }
-
- CompositionPath CompositionPath_03()
- {
- auto result = _compositionPath_03 = CompositionPath(Geometry_03().as());
- return result;
- }
-
- CompositionPath CompositionPath_04()
- {
- auto result = _compositionPath_04 = CompositionPath(Geometry_04().as());
- return result;
- }
-
- CompositionPath CompositionPath_05()
- {
- auto result = _compositionPath_05 = CompositionPath(Geometry_05().as());
- return result;
- }
-
- CompositionPath CompositionPath_06()
- {
- auto result = _compositionPath_06 = CompositionPath(Geometry_06().as());
- return result;
- }
-
- CompositionPath CompositionPath_07()
- {
- auto result = _compositionPath_07 = CompositionPath(Geometry_07().as());
- return result;
- }
-
- CompositionPath CompositionPath_08()
- {
- auto result = _compositionPath_08 = CompositionPath(Geometry_08().as());
- return result;
- }
-
// Layer (Shape): Dot-Y
CompositionContainerShape ContainerShape_00()
{
@@ -1711,9 +1681,8 @@ struct AnimatedVisual : implements
return result;
}
- CanvasGeometry Geometry_00()
+ auto Geometry_00()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1722,13 +1691,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 75.663002F, 0.289999992F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_01()
+ auto Geometry_01()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1737,13 +1704,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -4.35900021F, 70.3919983F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_02()
+ auto Geometry_02()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1752,13 +1717,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 99.1709976F, 0.0659999996F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_03()
+ auto Geometry_03()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1767,13 +1730,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 62.1629982F, 0.289999992F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_04()
+ auto Geometry_04()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1786,13 +1747,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { -13.1960001F, -27.0459995F }, { 8.96000004F, 11.559F }, { 49.5060005F, 11.559F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_05()
+ auto Geometry_05()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1801,13 +1760,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 340.955994F, 213.628006F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_06()
+ auto Geometry_06()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1816,13 +1773,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -1.68099999F, 29.9920006F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_07()
+ auto Geometry_07()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1831,13 +1786,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -1.76800001F, 25.9659996F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_08()
+ auto Geometry_08()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1851,13 +1804,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { 53.6889992F, 43.6080017F }, { 68.9710007F, 41.3569984F }, { 140.393997F, 43.6720009F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_09()
+ auto Geometry_09()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1867,13 +1818,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { -78.625F, -72 }, { -79.375F, -58.25F }, { -80.375F, -39.25F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_10()
+ auto Geometry_10()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1883,13 +1832,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { -75.5F, -66.25F }, { -75.5F, -56.75F }, { -76.5F, -37.75F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_11()
+ auto Geometry_11()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1899,13 +1846,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { -61.4720001F, -34.3689995F }, { -62.25F, -5.75F }, { -62.25F, -5.75F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_12()
+ auto Geometry_12()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1916,13 +1861,11 @@ struct AnimatedVisual : implements
sink->AddBezier({ { -53.5F, -20.5790005F }, { -42.25F, 4.25F }, { -42.25F, 4.25F } });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_13()
+ auto Geometry_13()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1931,13 +1874,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -32.769001F, 57.3650017F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_14()
+ auto Geometry_14()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1946,13 +1887,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -32.75F, 55.75F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_15()
+ auto Geometry_15()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1961,13 +1900,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -48.769001F, 55.3650017F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_16()
+ auto Geometry_16()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1976,13 +1913,11 @@ struct AnimatedVisual : implements
sink->AddLine({ -48.75F, 54.75F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_17()
+ auto Geometry_17()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -1991,13 +1926,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 136.731003F, 7.11499977F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_18()
+ auto Geometry_18()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -2006,13 +1939,11 @@ struct AnimatedVisual : implements
sink->AddLine({ 132, 2.75F });
sink->EndFigure(D2D1_FIGURE_END_OPEN);
check_hresult(sink->Close());
- result.attach(new GeoSource(path));
- return result;
+ return make(path);
}
- CanvasGeometry Geometry_19()
+ auto Geometry_19()
{
- CanvasGeometry result;
com_ptr path;
check_hresult(_d2dFactory->CreatePathGeometry(path.put()));
com_ptr sink;
@@ -2021,13 +1952,11 @@ struct AnimatedVisual : implements