Rubrique spéciale sur Windows 10 - 2015
Volume 30, numéro 11
Cet article a fait l'objet d'une traduction automatique.
Graphisme et animation : l’API de composition Windows passe à Windows 10
Par Kenny Kerr | Windows 2015
Le moteur de composition Windows, également appelé en tant que le Gestionnaire de fenêtres bureau (DWM), obtient une nouvelle API pour Windows 10. DirectComposition a été la principale interface de composition, mais comme une API COM classiques, il était inaccessible en grande partie au développeur de l'application moyenne. La nouvelle API de composition de Windows est basée sur le Windows Runtime (WinRT) et fournit la base pour le rendu de hautes performances en combinant le monde des graphiques de mode immédiat offertes par Direct2D et Direct3D avec une arborescence visuelle retenue qui arbore maintenant de l'animation éprouvée et capacités d'effets.
J'ai tout d'abord parlé le DWM en 2006, lorsque Windows Vista était en version bêta (goo.gl/19jCyR). Il vous permettait de contrôler l'étendue de l'effet de flou pour une fenêtre donnée et pour créer un arrière-plan personnalisé qui mélangée bien avec le bureau. Figure 1 illustre la hauteur de cette prime dans Windows 7. Il était possible de produire l'affichage avec accélération matérielle avec Direct3D et Direct2D pour créer des visuels époustouflants pour votre application (goo.gl/IufcN1). Vous pouvez fusionner même le monde ancien de GDI et contrôles utilisateur avec le DWM (goo.gl/9ITISE). Malgré tout, tout Observateur aiguisé pourrait indiquer que le Gestionnaire de fenêtrage a davantage à vous offrir, beaucoup plus. La fonctionnalité 3D Flip Windows dans Windows 7 a été convaincre la preuve.
Figure 1 Windows Aero
Windows 8 a introduit une nouvelle API pour le DWM appelé DirectComposition, son nom donnant hommage à la famille DirectX de APIs COM classiques qui a inspiré de sa conception. DirectComposition a commencé à donner aux développeurs une image plus claire de ce que le Gestionnaire de fenêtrage a été capable de faire. Il également proposé la terminologie améliorée. Le DWM est vraiment le moteur de composition Windows et il était capable de produire des effets fantastiques dans Windows Vista et Windows 7, car elle changé radicalement la façon étaient rendues de bureautiques windows. Par défaut, le moteur de composition créé une surface de redirection pour chaque fenêtre de niveau supérieur. Je cela décrit en détail dans mon article de juin 2014 (goo.gl/oMlVa4). Ces surfaces de redirection faisaient partie d'une arborescence visuelle et DirectComposition autorisée des applications afin de tirer parti de cette même technologie pour fournir une API en mode mémorisé léger pour graphiques hautes performances. DirectComposition proposé une arborescence visuelle et gestion surface autorisée de l'application décharger la production des effets et des animations pour le moteur de composition. J'ai décrit ces fonctionnalités dans mon août (goo.gl/CNwnWR) et les colonnes de septembre 2014 (goo.gl/y7ZMLL). J'ai même créé un cours sur Pluralsight un rendu haute performance avec DirectComposition (goo.gl/fgg0XN).
Windows 8 ont été lancées avec DirectComposition, ainsi que des améliorations impressionnantes pour le reste de la famille DirectX des API, mais elle également Office de pionnier dans une nouvelle ère de l'API Windows pour toujours susceptibles de modifier la façon dont les développeurs examiner le système d'exploitation. L'introduction de Windows Runtime éclipsé tout le reste. Microsoft s'est engagé une nouvelle méthode fraîche pour créer des applications et accéder aux services du système d'exploitation lui indiquer le retrait éventuel de l'API Win32 soi-disant qui a longtemps été le principal moyen pour créer des applications et d'interagir avec le système d'exploitation. Windows 8 a débuté au départ, mais Windows 8.1 fixe beaucoup de problèmes et Windows 10 fournit désormais une API beaucoup plus complète répondant à de nombreux développeurs plus intéressés de développement d'applications de première classe, nay, applications sérieuses, pour Windows.
Windows 10 livrés en juillet 2015 avec un aperçu de la composition de nouvel API qui n'était pas encore prêt pour la production. Il a toujours fait l'objet de modifications et par conséquent n'a pas pu être utilisé dans les applications Windows universelles soumises au Windows Store. C'est tout aussi bien, car l'API est maintenant disponible pour la production de la composition a changé considérablement et pour améliorer. Cette mise à jour Windows 10 est également la première fois que la même composition API est disponible sur tous les facteurs de forme, en donnant davantage de conviction à la partie de la plate-forme Windows universel universal. COMPOSITION fonctionne de la même que si vous ciblez votre puissance de bureau affichage multiple ou petit smartphone dans votre poche.
Naturellement, la chose que tout le monde aime Windows Runtime est qu'il remet finalement la promesse d'un common language runtime pour Windows. Si vous préférez code en c#, vous pouvez utiliser le Windows Runtime directement via la prise en charge intégrée de Microsoft .NET Framework. Si, comme moi, vous préférez utiliser C++, vous pouvez utiliser le Windows Runtime sans abstractions intermédiaires ou coûteux. Windows Runtime est basé sur COM et non .NET et en tant que tel, idéale pour la consommation de C++. Je vais utiliser C++ moderne pour le Windows Runtime (moderncpp.com), la projection de langage C++ standard, mais vous puissiez la suivre dans votre langage préféré que l'API est la même, quel que soit. Je vais proposent même des exemples en c# pour illustrer comment en toute transparence le Windows Runtime peut prendre en charge différentes langues.
L'API de composition Windows lui-même distances de ses racines de DirectX. DirectComposition fourni à un objet de périphérique, modélisé d'après les périphériques Direct3D et Direct2D, la nouvelle API de composition de Windows démarre avec un constructeur. Elle fait, cependant, Office le même objectif, agissant en tant que la fabrique pour les ressources de la composition. En outre, la composition de Windows est très similaire à DirectComposition. Il est une cible de composition qui représente la relation entre une fenêtre et son arborescence visuelle. Les différences deviennent plus évidentes à mesure que vous regardez de plus près visuels. Une DirectComposition visual avait une propriété de contenu qui a fourni une bitmap quelconque. La bitmap a été un des trois éléments : une surface de composition, une chaîne de permutation DXGI ou la surface de la redirection d'une autre fenêtre. Une application DirectComposition classique consistait à des éléments visuels et des surfaces, surfaces tant que le contenu ou les bitmaps des différents éléments visuels. Comme illustré à la Figure 2, une arborescence visuelle de la composition est une bête légèrement différent. Le nouvel objet visual n'a aucune propriété de contenu et est rendu à la place avec un pinceau de composition. Cela s'avère pour être une abstraction plus flexible. Tandis que le pinceau peut rendre juste un bitmap comme avant, des pinceaux de couleur unie peuvent être créées très efficacement et plus élaboré pinceaux peut être définis, au moins sur le plan conceptuel, d'une manière semblable à la Direct2D fournit des effets peuvent être traités comme des images. L'abstraction droite fait toute la différence.
Figure 2 l'arborescence visuelle de Composition Windows
Examinons quelques exemples pratiques pour illustrer comment tout cela fonctionne et vous donner un aperçu de ce qui est possible. Là encore, vous pouvez choisir votre projection de langage favorite WinRT. Vous pouvez créer un compositeur avec C++ moderne, comme suit :
using namespace Windows::UI::Composition;
Compositor compositor;
De même, vous pouvez faire de même avec c# :
using Windows.UI.Composition;
Compositor compositor = new Compositor();
Vous pouvez même utiliser la syntaxe plus attractive offerte par C + c++ / CX :
using namespace Windows::UI::Composition;
Compositor ^ compositor = ref new Compositor();
Ceux-ci sont équivalentes du point de vue de l'API et simplement reflètent les différences de projection de langage. Il existe essentiellement deux façons dans lequel vous pouvez écrire une application Windows universelle dès aujourd'hui. Peut-être l'approche la plus courante consiste à utiliser l'espace de noms Windows.UI.Xaml du système d'exploitation. Si XAML n'est pas particulièrement important de votre application, vous pouvez également utiliser le modèle d'application sous-jacent directement avec aucune dépendance sur XAML. J'ai décrit le modèle d'application WinRT dans mon article d'août 2013 (goo.gl/GI3OKP). Avec cette approche, vous avez besoin d'une implémentation minimale des interfaces IFrameworkView et IFrameworkViewSource et vous êtes fin prêt. Figure 3 fournit une structure de base en c# que vous pouvez utiliser pour commencer. Composition de Windows offre également une intégration étroite avec XAML, mais nous allons commencer par une application XAML libre simple car elle fournit un terrain de jeu plus simple dans lequel pour en savoir plus sur la composition. Je reviendrai au XAML un peu plus loin dans cet article.
Figure 3 modèle d'Application Windows Runtime en c#
using Windows.ApplicationModel.Core;
using Windows.UI.Core;
class View : IFrameworkView, IFrameworkViewSource
{
static void Main()
{
CoreApplication.Run(new View());
}
public IFrameworkView CreateView()
{
return this;
}
public void SetWindow(CoreWindow window)
{
// Prepare composition resources here...
}
public void Run()
{
CoreWindow window = CoreWindow.GetForCurrentThread();
window.Activate();
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessUntilQuit);
}
public void Initialize(CoreApplicationView applicationView) { }
public void Load(string entryPoint) { }
public void Uninitialize() { }
}
Il se trouve dans la méthode SetWindow de l'application (voir Figure 3) que le constructeur doit être construit. En fait, c'est le point le plus ancien dans le cycle de vie de l'application que cela peut se produire parce que le constructeur dépend de répartiteur de fenêtre et c'est le point auquel fenêtre et répartiteur sont enfin existant. La relation entre le constructeur et l'affichage de l'application peut ensuite être établie en créant une cible de composition :
CompositionTarget m_target = nullptr;
// ...
m_target = compositor.CreateTargetForCurrentView();
Il est essentiel que l'application maintenir la cible de composition, veillez à rendre une variable membre de votre implémentation IFrameworkView. Comme indiqué précédemment, la cible de composition représente la relation entre la fenêtre ou de vue et de son arborescence visuelle. Vous pouvez faire avec une cible de composition de définir la racine visual. En général, ce sera un conteneur visuel :
ContainerVisual root = compositor.CreateContainerVisual();
m_target.Root(root);
Ici, j'utilise C++, qui ne possède pas de prise en charge linguistique pour les propriétés, donc la propriété Root est projetée comme méthodes d'accesseur. C# est très similaire à l'ajout de la syntaxe de la propriété :
ContainerVisual root = compositor.CreateContainerVisual();
m_target.Root = root;
DirectComposition ne fourni qu'un seul type de visuels et pris en charge différents types de surfaces pour représenter le contenu bitmap. Composition de Windows offre des hiérarchies de classes de petites qui représentent les différents types d'éléments visuels, des pinceaux et des animations, encore qu'un seul type de surface et il ne peut être créé à l'aide de C++ car elle fait partie de l'API Windows composition interop destiné aux infrastructures XAML et les développeurs d'applications plus complexes.
Hiérarchie des classes visual Figure 4. Un CompositionObject est une ressource soutenue par le constructeur. Tous les objets de la composition peuvent avoir leurs propriétés animées. Un élément visuel fournit une multitude de propriétés pour contrôler de nombreux aspects de la position relative du visuel, apparence, l'extrait et options de rendu. Il inclut une propriété de matrice de transformation, ainsi que les raccourcis de l'échelle et de rotation. Il s'agit d'une classe de base puissante. En revanche, ContainerVisual est une classe relativement simple qui ajoute simplement une propriété Children. Alors que vous pouvez créer directement les éléments visuels de conteneur, un SpriteVisual ajoute la possibilité d'associer un pinceau afin que l'élément visuel peut rendre réellement pixels de son propre.
Figure 4 Composition visuels
Étant donné un visuel du conteneur racine, je peux créer n'importe quel nombre d'enfants visuels :
VisualCollection children = root.Children();
Il peut s'agir également visuels de conteneur, mais plus probablement, ils seront visuels sprite. Je pourrais l'ajouter trois éléments visuels en tant qu'enfants de la racine visual en utilisant une boucle for en C++ :
using namespace Windows::Foundation::Numerics;
for (unsigned i = 0; i != 3; ++i)
{
SpriteVisual visual = compositor.CreateSpriteVisual();
visual.Size(Vector2{ 300.0f, 200.0f });
visual.Offset(Vector3{ 50 + 20.0f * i, 50 + 20.0f * i });
children.InsertAtTop(visual);
}
Vous pouvez facilement imaginer la fenêtre de l'application dans Figure 5, et encore ce code ne provoquera pas quoi que ce soit restitué, car il n'existe aucun pinceau associé à ces éléments visuels. La hiérarchie de classes de pinceau est indiquée dans Figure 6. Un CompositionBrush est simplement une classe de base pour les pinceaux et n'offre aucune fonctionnalité qui lui sont propres. Un CompositionColorBrush est le type le plus simple, qu'une propriété de couleur pour le rendu des visuels de couleur unie. Cela peut vous paraître très intéressant, mais n'oubliez pas que vous pouvez connecter des animations à cette propriété de couleur. Les classes CompositionEffectBrush et CompositionSurfaceBrush sont liées, mais sont des formes plus complexes, car elles bénéficient par d'autres ressources. Un CompositionSurfaceBrush affichera une surface de composition pour les éléments visuels attachés. Il possède un certain nombre de propriétés bitmap contrôle de dessin, telles que l'interpolation, l'alignement et étirement, ne pas de mentionner la surface proprement dite. Un CompositionEffectBrush prend un nombre de formes de surface pour produire des effets différents.
Figure 5 enfants visuels dans une fenêtre
Figure 6 Composition pinceaux
Création et application d'un pinceau de couleur sont simple. Voici un exemple en c# :
using Windows.UI;
CompositionColorBrush brush = compositor.CreateColorBrush();
brush.Color = Color.FromArgb(0xDC, 0x5B, 0x9B, 0xD5);
visual.Brush = brush;
La structure Color est fourni par le Windows.UI sportifs et espace de noms alpha, rouge, verte et bleue comme valeurs de couleurs 8 bits, un écart de la préférence DirectComposition et Direct2D pour les valeurs de couleur à virgule flottante. Une fonctionnalité intéressante de cette approche pour les éléments visuels et les pinceaux est que la propriété de couleur peut être modifiée à tout moment, et les éléments visuels faisant référence au même pinceau seront automatiquement mis à jour. En effet, comme j'ai déjà évoqué, la propriété de couleur peut même être animée. Par conséquent, comment cela fonctionne-t-il ? Ceci nous amène aux classes d'animation.
La hiérarchie de classes d'animation est illustrée Figure 7. La classe de base CompositionAnimation permet de stocker des valeurs nommées pour une utilisation avec des expressions. Je reviendrai sur les expressions dans un instant. Une KeyFrameAnimation fournit des propriétés classiques animation basée sur une image clé comme comportement de durée, itération et arrêt. Les différentes classes d'animation de trame clé offrent des méthodes spécifiques au type d'insertion d'images clés, ainsi que les propriétés de l'animation spécifiques au type. Par exemple, ColorKeyFrameAnimation permet d'insérer des images clés avec des valeurs de couleur et une propriété pour contrôler l'espace de couleurs d'interpolation entre les images clés.
Figure 7 Composition Animations
Création d'un objet d'animation et d'appliquer cette animation sur un objet particulier de composition sont étonnamment facile. Supposons que je veux animer l'opacité d'un élément visuel. Je pourrais définir opacité de l'élément visuel à 50 pour cent directement avec une valeur scalaire en C++, comme suit :
visual.Opacity(0.5f);
Ou bien, je peux créer un objet scalaire animation avec des images clés pour produire une variable d'animation entre 0,0 et 1,0, représentant de 0 pour cent à 100 pour cent opacité :
ScalarKeyFrameAnimation animation =
compositor.CreateScalarKeyFrameAnimation();
animation.InsertKeyFrame(0.0f, 0.0f); // Optional
animation.InsertKeyFrame(1.0f, 1.0f);
Le premier paramètre de InsertKeyFrame est le décalage relatif à partir du début de l'animation (0,0) à la fin de l'animation (1.0). Le deuxième paramètre est la valeur de la variable d'animation à ce stade dans la chronologie d'animation. Par conséquent, cette animation sera transition douce entre la valeur de 0.0 à 1.0 pendant la durée de l'animation. Je peux ensuite définir la durée globale de cette animation, comme suit :
using namespace Windows::Foundation;
animation.Duration(TimeSpan::FromSeconds(1));
Avec l'animation est prête à l'emploi, je dois simplement connecter à l'objet de la composition et la propriété de mon choix :
visual.StartAnimation(L"Opacity", animation);
La méthode StartAnimation est en fait héritée de la classe de base CompositionObject, ce qui signifie que vous pouvez animer les propriétés d'une variété de classes différentes. Il s'agit d'une autre sortie DirectComposition, où chaque propriété animables fourni des surcharges pour des valeurs scalaires, ainsi que les objets d'animation. Composition de Windows offre un beaucoup plus riche système de propriétés qui ouvre la porte à quelques fonctionnalités très intéressantes. En particulier, il prend en charge la possibilité d'écrire des expressions textuelles pour réduire la quantité de code qui doit être écrit pour des animations et des effets plus intéressant. Ces expressions sont analysées au moment de l'exécution, compilé et exécuté efficacement par le moteur de composition de Windows.
Imaginez que vous avez besoin faire pivoter un élément visuel sur l'axe Y et lui donner l'apparence de profondeur. Propriété RotationAngle du visuel, mesurée en radians, ne suffit pas, car qui ne produisent une transformation qui inclut la perspective. Comme l'élément visuel fait pivoter, le bord le plus proche de l'oeil humain doit apparaître plus grand, tandis que l'arête opposée doit apparaître plus petit. Figure 8 présente un certain nombre de rotation visuels qui illustrent ce comportement.
Figure 8 rotation visuels
Comment pouvez-vous faire un tel effet animé ? Bien, nous allons commencer par une animation d'image clé scalaire pour l'angle de rotation :
ScalarKeyFrameAnimation animation = compositor.CreateScalarKeyFrameAnimation();
animation.InsertKeyFrame(1.0f, 2.0f * Math::Pi,
compositor.CreateLinearEasingFunction());
animation.Duration(TimeSpan::FromSeconds(2));
animation.IterationBehavior(AnimationIterationBehavior::Forever);
La fonction d'accélération linéaire substitue à la fonction d'accélération/décélération par défaut afin de produire un mouvement de rotation continu. Je dois ensuite définir un objet avec une propriété que je peux faire référence au sein d'une expression. Le constructeur fournit une propriété définie dans ce but :
CompositionPropertySet rotation = compositor.CreatePropertySet();
rotation.InsertScalar(L"Angle", 0.0f);
Un jeu de propriétés est également un objet de la composition, afin de pouvoir utiliser la méthode StartAnimation animer ma propriété personnalisée aussi facilement que n'importe quelle propriété intégrée :
rotation.StartAnimation(L"Angle", animation);
J'ai maintenant un objet dont la propriété Angle est en mouvement. Je dois maintenant définir une matrice de transformation pour produire l'effet souhaité, lors de la délégation à cette propriété pour l'angle de rotation animée. Entrer des expressions :
ExpressionAnimation expression =
compositor.CreateExpressionAnimation(
L"pre * Matrix4x4.CreateFromAxisAngle(axis, rotation.Angle) * post");
Une animation d'expression n'est pas un objet d'animation de trame clé, il n'existe aucune image clé relative les décalages d'animation variables peuvent modifier (basés sur une fonction d'interpolation). Au lieu de cela, les expressions font référence simplement aux paramètres qui peuvent elles-mêmes être animés dans le sens plus traditionnel. Malgré tout, c'est à moi pour définir le « pre », « axe », "rotation" et « post » est. Commençons par le paramètre d'axe :
expression.SetVector3Parameter(L"axis", Vector3{ 0.0f, 1.0f, 0.0f });
La méthode CreateFromAxisAngle à l'intérieur de l'expression attend un axe de rotation autour et définit l'axe autour de l'axe des Y. Il prévoit également un angle de rotation et nous pouvons vous reporter à la propriété de rotation avec sa propriété « Angle » animée :
expression.SetReferenceParameter(L"rotation", rotation);
Pour garantir que la rotation s'effectue via le centre de l'élément visuel plutôt que le bord gauche, je dois préalablement multiplier la matrice de rotation créée par CreateFromAxisAngle avec une traduction logiquement vers l'axe pour le point de rotation :
expression.SetMatrix4x4Parameter(
L"pre", Matrix4x4::Translation(-width / 2.0f, -height / 2.0f, 0.0f));
N'oubliez pas que multiplication de matrice n'est pas commutative, par conséquent, les matrices de pré- et post exactement ce que sont vraiment. Enfin, après la matrice de rotation, je peux ajouter une perspective et puis restaurer l'élément visuel à son emplacement d'origine :
expression.SetMatrix4x4Parameter(
L"post", Matrix4x4::PerspectiveProjection(width * 2.0f) *
Matrix4x4::Translation(width / 2.0f, height / 2.0f, 0.0f));
Cela répond à tous les paramètres référencés par l'expression et je peux maintenant utiliser simplement l'animation d'expression pour animer l'élément visuel à l'aide de sa propriété TransformMatrix :
visual.StartAnimation(L"TransformMatrix", expression);
Donc j'ai exploré les différentes façons de créer, remplir et animer des éléments visuels, mais que se passe-t-il si je dois afficher directement les éléments visuels ? DirectComposition proposés à la fois préalloué surfaces et faiblement allouée bitmaps appelés surfaces virtuels alloués à la demande et également redimensionnable. Composition de Windows ne fournit apparemment aucune possibilité de créer des surfaces. Il existe une classe CompositionDrawingSurface, mais aucun moyen de créer sans aide extérieure. La réponse provient de l'API d'interopérabilité composition Windows. Classes WinRT peuvent implémenter des interfaces COM supplémentaires non directement visibles si tout ce que vous avez est les métadonnées d'un composant Windows. Étant donné la connaissance de ces interfaces masqués, vous pouvez facilement interroger pour eux en C++. Naturellement, ce sera un peu plus de travail que vous êtes pas à pas en dehors des abstractions intéressantes offerte aux développeurs standard par l'API de composition Windows. La première chose à faire est de créer un périphérique de rendu et je vais utiliser Direct3D 11 que la composition de Windows ne prend en charge 12 Direct3D :
ComPtr<ID3D11Device> direct3dDevice;
Je vais ensuite préparer le périphérique indicateurs de création :
unsigned flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT |
D3D11_CREATE_DEVICE_SINGLETHREADED;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
La prise en charge BGRA me permet d'utiliser l'API de Direct2D plus abordable pour le rendu avec cet appareil, et la fonction D3D11CreateDevice crée ensuite le périphérique matériel lui-même :
check(D3D11CreateDevice(nullptr, // Adapter
D3D_DRIVER_TYPE_HARDWARE,
nullptr, // Module
flags,
nullptr, 0, // Highest available feature level
D3D11_SDK_VERSION,
set(direct3dDevice),
nullptr, // Actual feature level
nullptr)); // Device context
Je dois interroger DXGI interface du périphérique, parce que c'est ce que je dois créer un périphérique Direct2D :
ComPtr<IDXGIDevice3> dxgiDevice = direct3dDevice.As<IDXGIDevice3>();
Il est maintenant temps de créer le périphérique Direct2D lui-même :
ComPtr<ID2D1Device> direct2dDevice;
Ici encore, j'active la couche de débogage pour les diagnostics ajouté :
D2D1_CREATION_PROPERTIES properties = {};
#ifdef _DEBUG
properties.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
#endif
Je peux tout d'abord créer une fabrique Direct2D pour créer le périphérique. Cela serait utile si je devais créer toutes les ressources indépendantes du périphérique. Ici, j'utiliserai simplement le raccourci fourni par la fonction D2D1CreateDevice :
check(D2D1CreateDevice(get(dxgiDevice), properties, set(direct2dDevice)));
Le périphérique de rendu est prêt. J'ai un périphérique Direct2D que je peux utiliser pour rendre ce que je peux parfaitement imaginer. Maintenant, je dois indiquer au moteur de composition de Windows sur ce périphérique de rendu. Voici où ceux masqués les interfaces existent. Étant donné le constructeur que j'ai utilisée dans l'ensemble, je peux interroger l'interface ICompositorInterop :
namespace abi = ABI::Windows::UI::Composition;
ComPtr<abi::ICompositorInterop> compositorInterop;
check(compositor->QueryInterface(set(compositorInterop)));
ICompositorInterop fournit des méthodes pour la création d'une surface de composition d'une surface DXGI, ce qui serait certainement pratique si vous souhaitez inclure une chaîne de permutation existant dans une arborescence visuelle de la composition, mais elle offre quelque chose est beaucoup plus intéressante. Sa méthode CreateGraphicsDevice crée un objet CompositionGraphicsDevice donné un périphérique de rendu. La classe CompositionGraphicsDevice est une classe normale de composition Windows API, plutôt qu'une interface masquée, mais il ne fournit pas un constructeur vous devez donc utiliser C++ et l'interface ICompositorInterop pour la créer :
CompositionGraphicsDevice device = nullptr;
check(compositorInterop->CreateGraphicsDevice(get(direct2dDevice), set(device)));
CompositionGraphicsDevice étant un type WinRT, je peux utiliser à nouveau C++ moderne, plutôt que des pointeurs et la gestion des erreurs manuelles. Et CompositionGraphicsDevice qui enfin me permet de créer une surface de composition :
using namespace Windows::Graphics::DirectX;
CompositionDrawingSurface surface =
compositionDevice.CreateDrawingSurface(Size{ 100, 100 },
DirectXPixelFormat::B8G8R8A8UIntNormalized,
CompositionAlphaMode::Premultiplied);
Ici je crée un surface 100 x 100 pixels de composition de taille. Notez que cela représente des pixels physiques plutôt que logiques et coordonne reconnaissant les résolutions supposé et fourni par le reste de la composition de Windows. La surface fournit également le rendu de contrôle alpha 32 bits pris en charge par Direct2D. Bien sûr, Direct3D et Direct2D ne sont encore proposé via le Windows Runtime, il est à interfaces masqués à dessiner réellement à cette surface :
ComPtr<abi::ICompositionDrawingSurfaceInterop> surfaceInterop;
check(surface->QueryInterface(set(surfaceInterop)));
Beaucoup comme DirectComposition avant, la composition de Windows fournit les méthodes BeginDraw et EndDraw sur l'interface ICompositionDrawingSurfaceInterop une classification et prendre la place des appels classiques pour les appels de méthode Direct2D en les mêmes noms :
ComPtr<ID2D1DeviceContext> dc;
POINT offset = {};
check(surfaceInterop->BeginDraw(nullptr, // Update rect
__uuidof(dc),
reinterpret_cast<void **>(set(dc)),
&offset));
Composition de Windows prend le périphérique de rendu d'origine fournie au moment où le périphérique de la composition a été créé et l'utilise pour créer un contexte de périphérique ou de cible de rendu. Je peux éventuellement fournir un rectangle de découpage en pixels physiques, mais ici je vais juste optant pour un accès illimité à la surface de rendu. BeginDraw retourne également un décalage, à nouveau en pixels physiques, indiquant l'origine de la surface de dessin souhaitée. Il s'agit pas nécessairement l'angle supérieur gauche de la cible de rendu et doit veiller à modifier ou transformer toutes les commandes de dessin pour correctement prendre en compte ce décalage. Là encore, n'appelez pas BeginDraw sur la cible de rendu que la composition de Windows a déjà fait pour vous. Cette cible de rendu est logiquement détenue par l'API de composition et doit veiller ne pas à conserver après l'appel à EndDraw. La cible de rendu est maintenant prête à utiliser, mais n'est pas conscient de la résolution de logique ou efficace pour l'affichage. Je peux utiliser l'espace de noms Windows::Graphics::Display pour obtenir la valeur PPP logique pour la vue actuelle et définir la résolution Direct2D sera utilisée pour le rendu :
using namespace Windows::Graphics::Display;
DisplayInformation display = DisplayInformation::GetForCurrentView();
float const dpi = display.LogicalDpi();
dc->SetDpi(dpi, dpi);
La dernière étape de rendu peut commencer est de gérer l'offset de la composition d'une certaine manière. Une solution simple consiste à utiliser le décalage pour produire une matrice de transformation. N'oubliez pas que Direct2D entretient des relations commerciales en pixels logiques, donc je dois utiliser le décalage, mais aussi la valeur PPP récemment établie :
dc->SetTransform(D2D1::Matrix3x2F::Translation(offset.x * 96.0f / dpi,
offset.y * 96.0f / dpi));
À ce stade, vous pouvez dessiner au contenu de votre choix avant que la méthode EndDraw sur l'interface d'interopérabilité de la surface pour vous assurer que tout Direct2D par lot les commandes de dessin sont traités et les modifications apportées à la surface sont reflétées dans l'arborescence visuelle de composition :
check(surfaceInterop->EndDraw());
Bien entendu, je n'ai pas encore associé à la surface d'un élément visuel et, comme je l'ai mentionné, visuels plus fournissant une propriété de contenu et doivent être restitués à l'aide d'un pinceau. Heureusement, le constructeur crée un pinceau pour représenter une surface préexistante :
CompositionSurfaceBrush brush = compositor.CreateSurfaceBrush(surface);
Je peux ensuite créer un pinceau sprite normal et utilisez cette forme pour afficher l'élément visuel à la lumière :
SpriteVisual visual = compositor.CreateSpriteVisual();
visual.Brush(brush);
visual.Size(Vector2{ ... });
Si ce n'est pas suffisamment l'interopérabilité pour vous, vous pouvez même prendre un élément XAML et récupérer l'élément visuel composition sous-jacent. Voici un exemple en c# :
using Windows.UI.Xaml.Hosting;
Visual visual = ElementCompositionPreview.GetElementVisual(button);
En dépit de son état apparemment temporaire, ElementCompositionPreview est en fait prêt pour la production et peut être utilisé par les applications soumises au Windows Store. Avec n'importe quel élément d'interface utilisateur, la méthode GetElementVisual statique retourne l'élément visuel à partir de l'arborescence visuelle de composition sous-jacent. Notez qu'il retourne un élément visuel plutôt que ContainerVisual ou SpriteVisual, donc vous ne peut pas directement manipuler les enfants visual ou appliquer un pinceau, mais vous pouvez ajuster les propriétés visuelles nombreuses offertes par la composition de Windows. La classe d'assistance ElementCompositionPreview fournit des méthodes statiques supplémentaires pour l'ajout d'éléments visuels enfants d'une façon contrôlée. Vous pouvez modifier le décalage de l'élément visuel et telles que l'interface utilisateur d'accès au test va continue à travailler au niveau du XAML. Vous pouvez même appliquer une animation directement avec la composition de Windows sans endommager l'infrastructure XAML basé sur elle. Nous allons créer une animation simple scalaire pour faire pivoter le bouton. J'ai besoin récupérer le constructeur de l'élément visuel, puis en créant un objet d'animation fonctionne comme avant :
Compositor compositor = visual.Compositor;
ScalarKeyFrameAnimation animation = compositor.CreateScalarKeyFrameAnimation();
Commençons par créer une animation simple pour lentement faire pivoter le bouton indéfiniment avec une fonction d'accélération linéaire :
animation.InsertKeyFrame(1.0f, (float) (2 * Math.PI),
compositor.CreateLinearEasingFunction());
Je peux ensuite indiquent qu'une rotation unique doit prendre 3 secondes et continuer indéfiniment :
animation.Duration = TimeSpan.FromSeconds(3);
animation.IterationBehavior = AnimationIterationBehavior.Forever;
Enfin, je peux simplement l'animation à l'élément visuel fourni par le code XAML, ordonnant au moteur de composition pour animer sa propriété RotationAngle :
visual.StartAnimation("RotationAngle", animation);
Bien que vous puissiez mener à bien ce XAML uniquement, le moteur de composition Windows fournit plus de puissance et de flexibilité, étant donné qu'il se trouve à un niveau bien inférieur d'abstraction et offre de meilleures performances sans aucun doute. Comme autre exemple, la composition de Windows fournit des animations quaternion n'est actuellement pas pris en charge par XAML.
Il est beaucoup plus parler lorsqu'il s'agit du moteur de composition de Windows. À mon humble avis, il s'agit de l'API WinRT plus novateur à ce jour. La quantité d'énergie à votre disposition explose et, encore, contrairement à tellement autres grands UI et API graphiques, il n'introduit pas un compromis entre performances ou même une courbe d'apprentissage prohibitive. De nombreuses manières, la composition de Windows est représentatif de tout ce qui est bon et intéressantes sur la plate-forme Windows.
Vous pouvez trouver l'équipe Windows Composition sur Twitter : @WinComposition.
Kenny Kerrest programmeur informatique basé à Canada, ainsi que l'auteur de Pluralsight et MVP Microsoft. À l'adresse kennykerr.ca et vous pouvez le suivre sur Twitter : @kennykerr.
Remercie les experts techniques Microsoft suivants pour avoir relu cet article : Mark Aldham, James Clarke, John Serna vous, Jeffrey Stall et Nick Waggoner