Freigeben über


Optimieren von Animationen, Medien und Bildern

Erstellen Sie WinUI-Apps mit reibungslosen Animationen, hoher Bildfrequenz und leistungsstarker Medienaufnahme und -wiedergabe.

Animationen reibungslos gestalten

Ein wichtiger Aspekt von WinUI-Apps ist eine reibungslose Interaktion. Dazu gehören Touch-Manipulationen, die "an Ihrem Finger haften", flüssige Übergänge und Animationen sowie kleine Bewegungen, die Rückmeldung bei Eingaben liefern. Im XAML-Framework gibt es einen Thread namens Kompositionsthread, der der Komposition und Animation der visuellen Elemente einer App zugeordnet ist. Da der Kompositionsthread vom UI-Thread (dem Thread, der Framework und Entwicklercode ausführt) getrennt ist, können Apps unabhängig von komplizierten Layoutdurchläufen oder erweiterten Berechnungen eine konsistente Framerate und reibungslose Animationen erzielen. In diesem Abschnitt wird gezeigt, wie Sie den Kompositionsthread verwenden, um die Animationen einer App butterweich zu halten. Weitere Informationen zu Animationen finden Sie in der Übersicht über Animationen. Weitere Informationen zum Erhöhen der Reaktionsfähigkeit einer App bei intensiven Berechnungen finden Sie unter "Reaktionsfähigkeit des UI-Threads beibehalten".

Verwenden Sie unabhängige statt abhängige Animationen

Unabhängige Animationen können zum Zeitpunkt der Erstellung von Anfang bis Ende berechnet werden, da sich Änderungen an der animierten Eigenschaft nicht auf die restlichen Objekte in einer Szene auswirken. Unabhängige Animationen können daher im Kompositionsthread anstelle des UI-Threads ausgeführt werden. Dadurch wird sichergestellt, dass sie reibungslos bleiben, da der Kompositionsthread in einem konsistenten Rhythmus aktualisiert wird.

Alle diese Arten von Animationen sind garantiert unabhängig:

Abhängige Animationen wirken sich auf das Layout aus, was daher ohne zusätzliche Eingaben aus dem UI-Thread nicht berechnet werden kann. Abhängige Animationen umfassen Änderungen an Eigenschaften wie Breite und Höhe. Abhängige Animationen werden standardmäßig nicht ausgeführt und erfordern eine Zustimmung des App-Entwicklers. Wenn dies aktiviert ist, werden sie reibungslos ausgeführt, wenn der UI-Thread nicht blockiert bleibt, aber sie beginnen zu stuttern, wenn das Framework oder die App viele andere Aufgaben im UI-Thread ausführt.

Fast alle Animationen im XAML-Framework sind standardmäßig unabhängig, es gibt jedoch einige Aktionen, die Sie ausführen können, um diese Optimierung zu deaktivieren. Achten Sie insbesondere auf diese Szenarien:

  • Festlegen der EnableDependentAnimation-Eigenschaft , damit eine abhängige Animation im UI-Thread ausgeführt werden kann. Konvertieren Sie diese Animationen in eine unabhängige Version. Animieren Sie beispielsweise ScaleTransform.ScaleX und ScaleTransform.ScaleY anstelle der Breite und Höhe eines Objekts. Scheuen Sie sich nicht, Objekte wie Bilder und Text zu skalieren. Das Framework wendet bilineare Skalierung nur an, während die ScaleTransform animiert wird. Das Bild/der Text wird in der endgültigen Größe neugerastert, um sicherzustellen, dass das Bild/der Text stets klar dargestellt wird.
  • Erstellen von Aktualisierungen pro Frame, die im Wesentlichen abhängige Animationen darstellen. Ein Beispiel hierfür ist das Anwenden von Transformationen im Handler des CompositionTarget.Rendering-Ereignisses .
  • Ausführen einer Animation, die in einem Element als unabhängig betrachtet wird, wobei die CacheMode-Eigenschaft auf BitmapCache festgelegt ist. Dies gilt als abhängig, da der Cache für jeden Frame neu gerastert werden muss.

Animieren Sie ein WebView2 oder MediaPlayerElement nicht.

Webinhalte, die in einem WebView2-Steuerelement gehostet werden, werden nicht direkt vom XAML-Framework gerendert, sodass die Integration mit dem Rest der Szene zusätzliche Arbeit erfordert. Dieser Aufwand erhöht sich, wenn Sie das Steuerelement um den Bildschirm animieren und Synchronisierungsprobleme verursachen können, z. B. wenn der Webinhalt mit umgebendem XAML nicht synchronisiert wird. Wenn Sie eine Animation um Webinhalte benötigen, animieren Sie die umgebenden WinUI-Komponenten anstelle der WebView2-Oberfläche selbst. Frühere Anleitungen können WebViewBrush erwähnen; In modernen WinUI-Apps ist WebView2 das unterstützte Webhosting-Steuerelement und es gibt keinen direkten WebViewBrush-Ersatz.

Das Animieren eines MediaPlayerElements ist eine ähnlich schlechte Idee. Über die Leistungseinbuße hinaus kann es zu Zerreißen oder anderen Artefakten in den wiedergegebenen Videoinhalten führen.

Sparsames Verwenden von endlosen Animationen

Die meisten Animationen werden für einen bestimmten Zeitraum ausgeführt, aber durch Festlegen der Timeline.Duration-Eigenschaft auf Forever kann eine Animation unbegrenzt ausgeführt werden. Es wird empfohlen, die Verwendung unendlicher Animationen zu minimieren, da sie kontinuierlich CPU-Ressourcen verbrauchen und verhindern können, dass die CPU in einen Niedrigen-Strom- oder Leerlaufzustand wechselt, was zu einem schnelleren Stromausfall führt.

Das Hinzufügen eines Handlers für CompositionTarget.Rendering ähnelt dem Ausführen einer unendlichen Animation. Normalerweise ist der UI-Thread nur aktiv, wenn es zu tun ist, aber das Hinzufügen eines Handlers für dieses Ereignis erzwingt die Ausführung jedes Frames. Entfernen Sie den Handler, wenn keine Arbeit erforderlich ist, und registrieren Sie ihn erneut, wenn er erneut benötigt wird.

Verwenden der Animationsbibliothek

Der Namespace "Microsoft.UI.Xaml.Media.Animation" enthält eine Bibliothek mit leistungsstarken, reibungslosen Animationen, die ein Erscheinungsbild mit anderen Windows-Animationen aufweisen. Die relevanten Klassen haben "Thema" in ihrem Namen und werden in der Animationsübersicht beschrieben. Diese Bibliothek unterstützt viele gängige Animationsszenarien, z. B. das Animieren der ersten Ansicht der App und das Erstellen von Zustands- und Inhaltsübergängen. Wir empfehlen, diese Animationsbibliothek so oft wie möglich zu verwenden, um die Leistung und Konsistenz für WinUI-Apps zu erhöhen.

Hinweis Die Animationsbibliothek kann nicht alle möglichen Eigenschaften animieren. Xaml-Szenarien, in denen die Animationsbibliothek nicht angewendet wird, finden Sie unter Storyboardanimationen.

Unabhängiges Animieren von CompositeTransform3D-Eigenschaften

Sie können jede Eigenschaft eines CompositeTransform3D unabhängig voneinander animieren, sodass nur die benötigten Animationen angewendet werden. Beispiele und weitere Informationen finden Sie unter "UIElement.Transform3D". Weitere Informationen zum Animieren von Transformationen finden Sie unter Storyboard-Animationen und Keyframe- und Easing-Funktionsanimationen.

Optimieren von Medienressourcen

Audio, Video und Bilder sind überzeugende Formen von Inhalten, die von den meisten Apps verwendet werden. Da die Medienaufnahmeraten steigen und Inhalte von der Standarddefinition in high definition verschoben werden, erhöht sich die Menge an Ressourcen, die zum Speichern, Decodieren und Wiedergeben dieser Inhalte erforderlich sind. Das XAML-Framework basiert auf der modernen Windows-Medieninfrastruktur, sodass WinUI-Apps viele dieser Verbesserungen automatisch erben. Hier finden Sie einige zusätzliche Tipps, mit denen Sie die Medien in Ihrer WinUI-App optimal nutzen können.

Freigabe der Medienstreams

Mediendateien sind einige der am häufigsten verwendeten und kostspieligsten Ressourcen, die in der Regel von Apps verwendet werden. Da Mediendateiressourcen den Speicherbedarf Ihrer App erheblich erhöhen können, müssen Sie daran denken, das Handle für Medien freizugeben, sobald die App sie nicht mehr benötigt.

Wenn Ihre App z. B. mit einem RandomAccessStream - oder einem IInputStream-Objekt arbeitet, rufen Sie die Close-Methode für das Objekt auf, wenn die App die Verwendung abgeschlossen hat, um das zugrunde liegende Objekt freizugeben.

Videowiedergabe im Vollbildmodus nach Möglichkeit anzeigen

Verwenden Sie in WinUI-Apps immer die IsFullWindow-Eigenschaft im MediaPlayerElement , um das Rendering im Vollfenstermodus zu aktivieren und zu deaktivieren. Dadurch wird sichergestellt, dass Optimierungen auf Systemebene während der Medienwiedergabe verwendet werden.

Das XAML-Framework kann die Anzeige von Videoinhalten optimieren, wenn es das einzige Element ist, das gerendert wird, was zu einer Erfahrung führt, die weniger Leistung verwendet und höhere Frameraten liefert. Legen Sie für die effizienteste Medienwiedergabe die Größe eines MediaPlayerElements auf die Breite und Höhe des Bildschirms fest und zeigen keine anderen XAML-Elemente an.

Es gibt legitime Gründe, XAML-Elemente in einem MediaPlayerElement zu überlagern, das die volle Breite und Höhe des Bildschirms einnimmt, z. B. Untertitel oder vorübergehende Transportsteuerungen. Achten Sie darauf, diese Elemente auszublenden (set Visibility="Collapsed"), wenn sie nicht benötigt werden, um die Medienwiedergabe wieder in den effizientesten Zustand zu versetzen.

Deaktivierung anzeigen und Energie sparen

Um zu verhindern, dass die Anzeige deaktiviert wird, wenn die Benutzeraktion nicht mehr erkannt wird, z. B. wenn eine App Video abspielt, können Sie DisplayRequest.RequestActive aufrufen.

Um Energie und Akkulaufzeit zu sparen, sollten Sie DisplayRequest.RequestRelease aufrufen, um die Anzeigeanforderung freizugeben, sobald sie nicht mehr benötigt wird.

Unten sind einige Situationen aufgeführt, in denen Sie die Displayanforderung freigeben sollten:

  • Die Videowiedergabe wird angehalten, z. B. durch Benutzeraktion, Pufferung oder Anpassung aufgrund begrenzter Bandbreite.
  • Die Wiedergabe wird gestoppt. Beispielsweise ist die Wiedergabe des Videos beendet oder die Darstellung vorüber.
  • Ein Wiedergabefehler ist aufgetreten. Es können beispielsweise Probleme mit der Netzwerkverbindung bestehen, oder eine Datei kann beschädigt sein.

Platzieren anderer Elemente an der Seite des eingebetteten Videos

Häufig bieten Apps eine eingebettete Ansicht, in der Videos auf einer Seite wiedergegeben werden. Nun haben Sie offensichtlich die Vollbildoptimierung nicht mehr, da das MediaPlayerElement nicht die Größe der Seite hat und andere XAML-Objekte dargestellt werden. Achten Sie darauf, dass Sie versehentlich in diesen Modus wechseln, indem Sie einen Rahmen um ein MediaPlayerElement zeichnen.

Zeichnen Sie KEINE XAML-Elemente über dem Video, wenn sie sich im eingebetteten Modus befinden. Wenn Sie dies tun, ist das Framework gezwungen, etwas mehr Arbeit zu leisten, um die Szene zusammenzustellen. Das Platzieren von Transportsteuerelementen unter einem eingebetteten Medienelement anstelle des Videos ist ein gutes Beispiel für die Optimierung für diese Situation. In diesem Bild zeigt der rote Balken eine Reihe von Transportsteuerelementen an (Wiedergabe, Anhalten, Beenden usw.).

MediaPlayerElement mit Überlagerungselementen

Platzieren Sie diese Steuerelemente nicht auf Medien, die nicht im Vollbildmodus angezeigt werden. Platzieren Sie stattdessen die Steuerelemente irgendwo außerhalb des Bereichs, in dem die Medien gerendert werden. In der nächsten Abbildung werden die Steuerelemente unter den Medien platziert.

MediaPlayerElement mit benachbarten Elementen

Verzögern des Festlegens der Quelle für ein MediaPlayerElement

Medienmodule sind teure Objekte, und das XAML-Framework verzögert das Laden von DLLs und das Erstellen großer Objekte so lange wie möglich. Das MediaPlayerElement wird gezwungen, diese Arbeit zu erledigen, nachdem die Quelle über die Source-Eigenschaft festgelegt wurde. Wenn Sie dies festlegen, wenn der Benutzer wirklich bereit ist, Medien wiederzugeben, wird der Großteil der dem MediaPlayerElement zugeordneten Kosten so lange wie möglich verzögert.

"MediaPlayerElement.PosterSource festlegen"

Durch das Festlegen von MediaPlayerElement.PosterSource kann XAML einige GPU-Ressourcen freigeben, die ansonsten verwendet worden wären. Diese API ermöglicht es einer App, so wenig Arbeitsspeicher wie möglich zu verwenden.

Verbessern des Medienbereinigungs

Scrubbing ist immer eine schwierige Aufgabe für Medienplattformen, um wirklich responsiv zu sein. Im Allgemeinen erreichen Personen dies, indem Sie den Wert eines Schiebereglers ändern. Hier sind ein paar Tipps, wie Sie dies so effizient wie möglich gestalten:

  • Aktualisieren Sie den Wert von Slider basierend auf einem Timer, der die Position auf dem MediaPlayerElement.MediaPlayer abfragt. Stellen Sie sicher, dass Sie eine angemessene Aktualisierungshäufigkeit für Ihren Timer verwenden. Die Position-Eigenschaft aktualisiert nur alle 250 Millisekunden während der Wiedergabe.
  • Die Größe der Schrittfrequenz auf dem Schieberegler muss mit der Länge des Videos skaliert werden.
  • Abonnieren Sie die PointerPressed-, PointerMoved-, PointerReleased-Ereignisse auf dem Schieberegler, um die PlaybackRate-Eigenschaft auf 0 festzulegen, wenn der Benutzer den Daumen des Schiebereglers zieht.
  • Stellen Sie im PointerReleased-Ereignishandler die Medienposition manuell auf die Position des Schiebereglers ein, um ein optimales Einrasten beim Scrubbing zu erzielen.

Zuordnen der Videoauflösung zur Geräteauflösung

Das Decodieren von Videos benötigt viel Arbeitsspeicher und GPU-Zyklen. Wählen Sie daher ein Videoformat in der Nähe der Auflösung aus, bei der es angezeigt wird. Es gibt keinen Sinn, die Ressourcen zum Decodieren von 1080-Videos zu verwenden, wenn sie auf eine viel kleinere Größe skaliert werden. Viele Apps verfügen nicht über dasselbe Video, das in unterschiedlichen Auflösungen codiert ist. Wenn sie verfügbar ist, verwenden Sie jedoch eine Codierung, die der Auflösung nahe ist, in der sie angezeigt wird.

Die Auswahl von Medienformaten kann ein sensibles Thema sein und wird häufig von Geschäftsentscheidungen gesteuert. Aus Sicht der Leistung des Windows App SDK empfehlen wir H.264-Videos als primäres Videoformat und AAC und MP3 als bevorzugte Audioformate. Für die lokale Dateiwiedergabe ist MP4 der bevorzugte Dateicontainer für Videoinhalte. Die H.264-Decodierung wird durch die neueste Grafikhardware beschleunigt. Auch wenn die Hardwarebeschleunigung für die VC-1-Decodierung allgemein verfügbar ist, ist die Beschleunigung bei einem großen Satz von Grafikhardware auf dem Markt in vielen Fällen auf eine partielle Beschleunigungsstufe (oder IDCT-Ebene) beschränkt, anstatt auf einen Full-Stream-Hardware-Offload (d. h. VLD-Modus).

Wenn Sie die volle Kontrolle über den Prozess der Videoinhaltsgenerierung haben, müssen Sie herausfinden, wie Sie ein gutes Gleichgewicht zwischen Komprimierungseffizienz und GOP-Struktur behalten. Relativ kleinere GOP-Größe mit B-Bildern kann die Leistung im Such- oder Trickmodus erhöhen.

Verwenden Sie WAV-Dateien mit unkomprimierten PCM-Daten, um den Verarbeitungsaufwand zu verringern, der für komprimierte Audioformate typisch ist, wenn Sie kurze Audioeffekte mit geringer Latenz verwenden, z. B. in Spielen.

Optimieren von Bildressourcen

Skalieren von Bildern auf die entsprechende Größe

Bilder werden mit sehr hohen Auflösungen erfasst, was dazu führen kann, dass Apps mehr CPU verwenden, wenn sie die Bilddaten decodieren und mehr Arbeitsspeicher nach dem Laden vom Datenträger. Es ist jedoch nicht sinnvoll, ein hochauflösendes Bild im Arbeitsspeicher zu decodieren und zu speichern, um es nur kleiner als seine systemeigene Größe anzuzeigen. Erstellen Sie stattdessen eine Version des Bilds in der genauen Größe, in der es auf dem Bildschirm dargestellt wird, mit den Eigenschaften DecodePixelWidth und DecodePixelHeight.

Führen Sie dies nicht aus:

<Image Source="ms-appx:///Assets/highresCar.jpg"
       Width="300" Height="200"/>    <!-- BAD CODE DO NOT USE.-->

Gehen Sie stattdessen wie folgt vor:

<Image>
    <Image.Source>
    <BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
                 DecodePixelWidth="300" DecodePixelHeight="200"/>
    </Image.Source>
</Image>

Die Einheiten für DecodePixelWidth und DecodePixelHeight sind standardmäßig physische Pixel. Die DecodePixelType-Eigenschaft kann verwendet werden, um dieses Verhalten zu ändern: Das Festlegen von DecodePixelType auf "Logical " führt automatisch zur Decodierungsgröße, die dem aktuellen Skalierungsfaktor des Systems entspricht, ähnlich wie andere XAML-Inhalte. Daher wäre es in der Regel sinnvoll, DecodePixelType auf "Logical " festzulegen, wenn Beispielsweise "DecodePixelWidth " und "DecodePixelHeight " den Eigenschaften "Height" und "Width" des Bildsteuerelements entsprechen sollen, in dem das Bild angezeigt wird. Mit dem Standardverhalten der Verwendung physischer Pixel müssen Sie den aktuellen Skalierungsfaktor des Systems selbst berücksichtigen. und Sie sollten auf Skalierungsänderungsbenachrichtigungen lauschen, falls der Benutzer seine Anzeigeeinstellungen ändert.

Wenn DecodePixelWidth/Height explizit größer festgelegt ist als das Bild auf dem Bildschirm angezeigt wird, verwendet die App unnötig zusätzlichen Arbeitsspeicher – bis zu 4 Bytes pro Pixel – was für große Bilder schnell teuer wird. Das Bild wird auch mit bilinearer Skalierung nach unten skaliert, was dazu führen könnte, dass es für große Skalierungsfaktoren verschwommen angezeigt wird.

Wenn DecodePixelWidth/DecodePixelHeight explizit kleiner festgelegt ist als das Bild auf dem Bildschirm angezeigt wird, wird es skaliert und könnte pixeliert angezeigt werden.

In einigen Fällen, in denen eine geeignete Dekodiergröße nicht im Voraus bestimmt werden kann, sollten Sie sich auf die automatische Anpassung an die richtige Größe von XAML verlassen. XAML wird dabei mit der bestmöglichen Anstrengung versuchen, das Bild in der passenden Größe zu dekodieren, wenn keine explizite DecodePixelWidth/DecodePixelHeight angegeben ist.

Sie sollten eine explizite Decodierungsgröße festlegen, wenn Sie die Größe des Bildinhalts vorab kennen. Sie sollten außerdem DecodePixelType auf Logical setzen, wenn die bereitgestellte Decodierungsgröße relativ zu anderen XAML-Elementgrößen ist. Wenn Sie beispielsweise die Inhaltsgröße explizit mit Image.Width und Image.Height festlegen, können Sie DecodePixelType auf DecodePixelType.Logical festlegen, um dieselben logischen Pixelabmessungen wie ein Bildsteuerelement zu verwenden und dann explizit BitmapImage.DecodePixelWidth und/oder BitmapImage.DecodePixelHeight zu verwenden, um die Größe des Bilds zu steuern, um potenziell große Speichereinsparungen zu erzielen.

Beachten Sie, dass Image.Stretch beim Bestimmen der Größe des decodierten Inhalts berücksichtigt werden sollte.

Decodierung der richtigen Größe

Wenn Sie keine explizite Decodierungsgröße festlegen, versucht XAML, Arbeitsspeicher zu sparen, indem es ein Bild auf die genaue Größe decodiert, in der es auf dem Bildschirm gemäß dem anfänglichen Layout der Seite erscheint. Sie werden empfohlen, Ihre Anwendung so zu schreiben, dass sie dieses Feature nach Möglichkeit nutzen kann. Dieses Feature wird deaktiviert, wenn eine der folgenden Bedingungen erfüllt ist.

  • Das BitmapImage ist nach dem Festlegen des Inhalts mit SetSourceAsync oder UriSource mit der Live-XAML-Struktur verbunden.
  • Das Bild wird mit synchroner Decodierung wie SetSource decodiert.
  • Das Bild wird durch Festlegen von Opazität auf 0 oder Sichtbarkeit auf Collapsed für das Element des Hostbildes oder Brush oder jedes übergeordnete Element ausgeblendet.
  • Das Bildsteuerelement oder Pinsel verwendet einen Stretch von None.
  • Das Bild wird als NineGrid verwendet.
  • CacheMode="BitmapCache" wird für das Bildelement oder für ein beliebiges übergeordnetes Element festgelegt.
  • Der Bildpinsel ist nicht rechteckig (z. B. wenn er auf eine Form oder auf Text angewendet wird).

In den oben genannten Szenarien ist das Festlegen einer expliziten Decodierungsgröße die einzige Möglichkeit, Speichereinsparungen zu erzielen.

Sie sollten immer ein BitmapImage an den Live-Baum anfügen, bevor Sie die Quelle festlegen. Jedes Mal, wenn ein Bildelement oder Pinsel im Markup angegeben wird, ist dies automatisch der Fall. Beispiele finden Sie unten unter der Überschrift "Live-Tree-Beispiele". Sie sollten setSource immer vermeiden und stattdessen SetSourceAsync verwenden, wenn Sie eine Streamquelle festlegen. Und es ist ratsam, das Ausblenden von Bildinhalten zu vermeiden (entweder mit null Opazität oder reduzierter Sichtbarkeit), während das ImageOpened-Ereignis ausgelöst wird. Dies ist eine Ermessenssache: Sie profitieren nicht von der automatischen Decodierung in der passenden Größe, wenn sie erfolgt. Wenn Die App zunächst Bildinhalte ausblenden muss, sollte sie auch die Decodierungsgröße explizit festlegen, falls möglich.

Beispiele für Live-Baum

Beispiel 1 (gut) – Uniform Resource Identifier (URI), der im Markup angegeben ist.

<Image x:Name="myImage" UriSource="Assets/cool-image.png"/>

Beispiel 2-Markup – URI, der in CodeBehind angegeben ist.

<Image x:Name="myImage"/>

Beispiel 2 CodeBehind (gut) – Verbinden des BitmapImage mit dem Baum, bevor die UriSource festgelegt wird.

var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);

Beispiel 2 code-behind (schlecht) – Festlegen der UriSource von BitmapImage vor dem Verbinden mit der Baumstruktur.

var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;

Zwischenspeicherungsoptimierungen

Zwischenspeicherungsoptimierungen gelten für Bilder, die UriSource verwenden, um Inhalte aus einem App-Paket oder aus dem Web zu laden. Der URI wird verwendet, um den zugrunde liegenden Inhalt eindeutig zu identifizieren, und intern lädt das XAML-Framework den Inhalt nicht mehrmals herunter oder decodiert den Inhalt nicht. Stattdessen werden die zwischengespeicherten Software- oder Hardwareressourcen verwendet, um den Inhalt mehrmals anzuzeigen.

Die Ausnahme bei dieser Optimierung besteht darin, dass das Bild mehrmals mit unterschiedlichen Auflösungen angezeigt wird (die explizit oder über die automatische Decodierung der rechten Größe angegeben werden können). Jeder Cacheeintrag speichert auch die Auflösung des Bilds, und wenn XAML kein Bild mit einem Quell-URI finden kann, der der erforderlichen Auflösung entspricht, wird eine neue Version in dieser Größe decodiert. Die codierten Bilddaten werden jedoch nicht erneut heruntergeladen.

Daher sollten Sie die Verwendung von UriSource beim Laden von Bildern aus einem App-Paket nutzen und die Verwendung eines Dateidatenstroms und von SetSourceAsync vermeiden, wenn sie nicht erforderlich ist.

Bilder in virtualisierten Panels (z. B. ListView)

Wenn ein Bild aus der Baumstruktur entfernt wird – entweder weil die App es explizit entfernt hat oder weil es sich in einem modernen virtualisierten Bereich befindet und beim Scrollen aus der Ansicht implizit entfernt wurde – optimiert XAML die Speicherausnutzung, indem die Hardware-Ressourcen für das Bild freigegeben werden, da sie nicht mehr benötigt werden. Der Arbeitsspeicher wird nicht sofort freigegeben, sondern während der Frameaktualisierung, die stattfindet, nachdem das Bildelement eine Sekunde lang nicht mehr in der Struktur vorhanden ist, freigegeben.

Daher sollten Sie versuchen, moderne virtualisierte Bedienoberflächen zum Speichern von Bildlisten zu verwenden.

Software-gerasterte Bilder

Wenn ein Bild für einen nicht rechteckigen Pinsel oder für ein NineGrid verwendet wird, verwendet das Bild einen Softwarerasterungspfad, der keine Bilder skaliert. Darüber hinaus muss es eine Kopie des Images sowohl im Software- als auch im Hardwarespeicher speichern. Wenn beispielsweise ein Bild als Pinsel für eine Ellipse verwendet wird, wird das potenziell große Vollbild zweimal intern gespeichert. Wenn Sie NineGrid oder einen nicht rechteckigen Pinsel verwenden, sollte Ihre App ihre Bilder vorab auf die Größe skalieren, in der sie gerendert werden.

Hintergrund-Thread-Bildladen

XAML verfügt über eine interne Optimierung, die es ermöglicht, den Inhalt eines Bilds asynchron auf eine Oberfläche im Hardwarespeicher zu decodieren, ohne dass eine Zwischenoberfläche im Softwarespeicher erforderlich ist. Dies reduziert spitzen Speicherauslastung und Renderinglatenz. Dieses Feature wird deaktiviert, wenn eine der folgenden Bedingungen erfüllt ist.

  • Das Bild wird als NineGrid verwendet.
  • CacheMode="BitmapCache" wird für das Bildelement oder für ein beliebiges übergeordnetes Element festgelegt.
  • Der Bildpinsel ist nicht rechteckig (z. B. wenn er auf eine Form oder auf Text angewendet wird).

SoftwareBitmapSource

Die SoftwareBitmapSource-Klasse austauscht interoperable nicht komprimierte Bilder zwischen verschiedenen WinRT-Namespaces wie BitmapDecoder, Kamera-APIs und XAML. Diese Klasse entfernt eine zusätzliche Kopie, die in der Regel mit WriteableBitmap erforderlich wäre, und dies hilft, Spitzenspeicher und Quell-zu-Bildschirm-Latenz zu reduzieren.

Die SoftwareBitmap , die Quellinformationen bereitstellt, kann auch so konfiguriert werden, dass ein benutzerdefinierter IWICBitmap-Speicher verwendet wird, um einen wiederverwendbaren Sicherungsspeicher bereitzustellen, mit dem die App Speicher nach Bedarf neu zuordnen kann. Dies ist ein erweiterter C++-Anwendungsfall.

Ihre App sollte SoftwareBitmap und SoftwareBitmapSource verwenden, um mit anderen WinRT-APIs zu arbeiten, die Bilder erzeugen und nutzen. Und Ihre App sollte SoftwareBitmapSource verwenden, wenn nicht komprimierte Bilddaten geladen werden, anstatt WriteableBitmap zu verwenden.

Verwenden von GetThumbnailAsync für Miniaturansichten

Ein Anwendungsfall zum Skalieren von Bildern ist das Erstellen von Miniaturansichten. Obwohl Sie DecodePixelWidth und DecodePixelHeight verwenden können, um kleine Versionen von Bildern bereitzustellen, bietet Windows noch effizientere APIs zum Abrufen von Miniaturansichten. GetThumbnailAsync stellt die Miniaturansichten für Bilder bereit, die das Dateisystem bereits zwischengespeichert haben. Dies bietet noch bessere Leistung als die XAML-APIs, da das Bild nicht geöffnet oder decodiert werden muss.

Initialisieren Sie in einer Windows App SDK-App den Picker mit Ihrem App Fensterhandle, indem Sie beispielsweise das Hauptfenster als App.MainWindow speichern.

FileOpenPicker picker = new FileOpenPicker();
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
picker.FileTypeFilter.Add(".bmp");
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;

StorageFile file = await picker.PickSingleFileAsync();

StorageItemThumbnail fileThumbnail = await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 64);

BitmapImage bmp = new BitmapImage();
await bmp.SetSourceAsync(fileThumbnail);

Image img = new Image();
img.Source = bmp;

Bilder einmal entschlüsseln

Um zu verhindern, dass Bilder mehrmals decodiert werden, weisen Sie die Image.Source-Eigenschaft einem URI zu, anstatt Speicherströme zu verwenden. Das XAML-Framework kann denselben URI an mehreren Stellen mit einem einzigen decodierten Bild verknüpfen, kann jedoch bei mehreren Speicherströmen, die dieselben Daten enthalten, nicht dieselbe Verbindung herstellen und erstellt für jeden Speicherstrom ein anderes decodiertes Bild.