Udostępnij przez


Optymalizowanie animacji, multimediów i obrazów

Tworzenie aplikacji platformy uniwersalnej systemu Windows (UWP) z płynną animacją, wysoką szybkością klatek i przechwytywaniem i odtwarzaniem multimediów o wysokiej wydajności.

Sprawianie, że animacje są płynne

Kluczowym aspektem aplikacji platformy UWP jest płynna interakcja. Obejmuje to manipulacje dotykowe, które „przyklejają się do palca”, płynne przejścia i animacje oraz małe ruchy, które zapewniają informację zwrotną. W strukturze XAML istnieje wątek nazywany wątkiem kompozycji poświęconym kompozycji i animacji elementów wizualnych aplikacji. Ponieważ wątek kompozycji jest oddzielony od wątku interfejsu użytkownika (wątku, który uruchamia platformę i kod dewelopera), aplikacje mogą uzyskać spójną szybkość klatek i płynne animacje niezależnie od skomplikowanych przebiegów układu lub rozszerzonych obliczeń. W tej sekcji pokazano, jak używać wątku kompozycji, aby animacje aplikacji były nadzwyczajnie płynne. Aby uzyskać więcej informacji na temat animacji, zobacz Animacje — omówienie. Aby dowiedzieć się więcej na temat zwiększania reaktywności aplikacji podczas wykonywania intensywnych obliczeń, zobacz Zachowaj reaktywność wątku interfejsu użytkownika.

Używaj niezależnych zamiast zależnych animacji

Niezależne animacje można obliczyć od początku do końca w momencie tworzenia, ponieważ zmiany właściwości animowanej nie wpływają na resztę obiektów w scenie. W związku z tym niezależne animacje mogą być uruchamiane w wątku kompozycji zamiast wątku interfejsu użytkownika. Gwarantuje to, że pozostają gładkie, ponieważ wątek kompozycji jest aktualizowany w równomiernym tempie.

Wszystkie te typy animacji mają gwarancję, że są niezależne:

Animacje zależne wpływają na układ, który w związku z tym nie może być obliczany bez dodatkowych danych wejściowych z wątku interfejsu użytkownika. Animacje zależne obejmują modyfikacje właściwości, takich jak Szerokość i Wysokość. Domyślnie animacje zależne nie są uruchamiane i wymagają zgody od dewelopera aplikacji. Po włączeniu są one bezproblemowo uruchamiane, jeśli wątek interfejsu użytkownika pozostaje odblokowany, ale zaczynają się zacinać, jeśli platforma lub aplikacja wykonuje wiele innych zadań w wątku interfejsu użytkownika.

Prawie wszystkie animacje w strukturze XAML są domyślnie niezależne, ale istnieje kilka akcji, które można wykonać, aby wyłączyć tę optymalizację. Należy pamiętać o tych scenariuszach szczególnie:

  • Ustawienie właściwości EnableDependentAnimation, aby umożliwić uruchamianie animacji zależnej w wątku interfejsu użytkownika. Przekonwertuj te animacje na niezależną wersję. Na przykład animowanie ScaleTransform.ScaleX oraz ScaleTransform.ScaleY zamiast Width oraz Height w obiekcie. Nie bój się skalować obiektów, takich jak obrazy i tekst. Struktura stosuje skalowanie dwuliniowe tylko wtedy, gdy ScaleTransform jest animowana. Obraz/tekst zostaną ponownie sformatowane w ostatnim rozmiarze, aby upewnić się, że jest zawsze jasne.
  • Tworzenie aktualizacji wykonywanych dla każdej klatki, które w rzeczywistości są animacjami zależnymi. Przykładem tego jest zastosowanie przekształceń w procedurze obsługi zdarzenia CompositonTarget.Rendering.
  • Uruchamianie dowolnej animacji uważanej za niezależną w elemencie z właściwością CacheMode ustawioną na BitmapCache. Jest to uznawane za zależne, ponieważ pamięć podręczna musi być ponownie rasteryzowana dla każdej ramki.

Nie animuj elementu WebView ani MediaPlayerElement

Zawartość internetowa w kontrolce WebView nie jest bezpośrednio renderowana przez strukturę XAML i wymaga dodatkowej pracy, aby mogła być złożona z resztą sceny. Ta dodatkowa praca sumuje się podczas animowania kontrolki wokół ekranu i może potencjalnie powodować problemy z synchronizacją (na przykład zawartość HTML może nie być zsynchronizowana z resztą zawartości XAML na stronie). Gdy musisz animować kontrolkę WebView, zamień ją na WebViewBrush na czas trwania animacji.

Animowanie MediaPlayerElement jest równie złym pomysłem. Poza szkodą dla wydajności może to spowodować rozerwanie lub inne artefakty w odtwarzanej zawartości wideo.

Uwaga Zalecenia w tym artykule dotyczące MediaPlayerElement mają zastosowanie również do MediaElement. MediaPlayerElement jest dostępna tylko w systemie Windows 10 w wersji 1607, więc jeśli tworzysz aplikację dla poprzedniej wersji systemu Windows, musisz użyć MediaElement.

Używaj nieskończonych animacji oszczędnie

Większość animacji jest wykonywana przez określony czas, ale ustawienie właściwości Timeline.Duration na wartość Forever umożliwia uruchamianie animacji na czas nieokreślony. Zalecamy zminimalizowanie użycia nieskończonych animacji, ponieważ stale zużywają zasoby procesora CPU i mogą uniemożliwić procesorowi przejście do stanu niskiego zasilania lub bezczynności, co powoduje, że szybciej zabraknie zasilania.

Dodanie programu obsługi dla CompositionTarget.Rendering jest podobne do uruchamiania nieskończonej animacji. Zwykle wątek interfejsu użytkownika jest aktywny tylko wtedy, gdy istnieje praca do wykonania, ale dodanie obsługi dla tego zdarzenia wymusza uruchomienie każdej ramki. Usuń program obsługi, gdy nie ma żadnej pracy do wykonania i ponownie wyrejestruj go, gdy będzie potrzebny ponownie.

Korzystanie z biblioteki animacji

Przestrzeń nazw Windows.UI.Xaml.Media.Animation zawiera bibliotekę wysokiej wydajności, gładkich animacji, które mają wygląd i działanie zgodne z innymi animacjami systemu Windows. Odpowiednie klasy mają w nazwie "Theme" i są opisane w sekcji Omówienie animacji. Ta biblioteka obsługuje wiele typowych scenariuszy animacji, takich jak animowanie pierwszego widoku aplikacji oraz tworzenie stanów i przejść zawartości. Zalecamy używanie tej biblioteki animacji, jeśli jest to możliwe, aby zwiększyć wydajność i spójność interfejsu użytkownika platformy UWP.

Uwaga Biblioteka animacji nie może animować wszystkich możliwych właściwości. W przypadku scenariuszy XAML, w których biblioteka animacji nie ma zastosowania, zobacz Animacje scenorysowe.

Animowanie poszczególnych właściwości CompositeTransform3D niezależnie

Można animować każdą właściwość CompositeTransform3D w sposób niezależny, więc zastosuj tylko te animacje, które są potrzebne. Aby uzyskać przykłady i więcej informacji, zobacz UIElement.Transform3D. Aby uzyskać więcej informacji na temat animowania przekształceń, zobacz animacje scenorysowe oraz animacje funkcji key-frame i easing.

Optymalizowanie zasobów multimedialnych

Audio, wideo i obrazy są atrakcyjnymi formami zawartości, z których korzysta większość aplikacji. W miarę zwiększania szybkości przechwytywania multimediów i przenoszenia zawartości ze standardowej definicji do wysokiej definicji ilość zasobów potrzebnych do przechowywania, dekodowania i odtwarzania tej zawartości wzrasta. Platforma XAML bazuje na najnowszych funkcjach dodanych do mechanizmów multimedialnych UWP, co pozwala aplikacjom na uzyskanie tych ulepszeń bez dodatkowych kosztów. W tym miejscu wyjaśnimy kilka dodatkowych sztuczek, które pozwalają uzyskać najwięcej multimediów w aplikacji platformy UWP.

Uwolnij strumienie multimediów

Pliki multimedialne to jedne z najczęściej używanych i najdroższych zasobów, z których korzystają aplikacje. Ze względu na to, że zasoby plików multimedialnych mogą znacznie zwiększyć rozmiar pamięci aplikacji, należy pamiętać, aby zwolnić uchwyt do multimediów zaraz po zakończeniu korzystania z niej przez aplikację.

Jeśli na przykład Twoja aplikacja pracuje z RandomAccessStream lub obiektem IInputStream, upewnij się, że wywołujesz metodę zamknięcia (close) na obiekcie po zakończeniu korzystania z niego, aby zwolnić obiekt bazowy.

Wyświetlanie odtwarzania wideo pełnoekranowego, jeśli jest to możliwe

W aplikacjach platformy UWP zawsze używaj właściwości IsFullWindow w MediaPlayerElement, aby włączyć i wyłączyć renderowanie pełnego okna. To zapewnia, że optymalizacje na poziomie systemu są wykorzystywane podczas odtwarzania multimediów.

Platforma XAML może zoptymalizować wyświetlanie zawartości wideo, gdy jest to jedyna funkcja renderowana, co powoduje, że środowisko zużywa mniej mocy i zapewnia większą szybkość klatek. W przypadku najbardziej wydajnego odtwarzania multimediów ustaw rozmiar MediaPlayerElement jako szerokość i wysokość ekranu i nie wyświetlaj innych elementów XAML

Istnieją uzasadnione powody, aby nakładać elementy XAML na MediaPlayerElement, które zajmują pełną szerokość i wysokość ekranu, na przykład napisy zamknięte lub chwilowe kontrolki transportu. Pamiętaj, aby ukryć te elementy (ustaw Visibility="Collapsed"), gdy nie są potrzebne, aby przywrócić odtwarzanie multimediów do jego najbardziej wydajnego stanu.

Dezaktywacja wyświetlacza i oszczędzanie energii

Aby zapobiec dezaktywowaniu wyświetlania, gdy akcja użytkownika nie jest już wykrywana, na przykład gdy aplikacja odtwarza wideo, możesz wywołać DisplayRequest.RequestActive.

Aby zaoszczędzić energię i żywotność baterii, należy wywołać DisplayRequest.RequestRelease, aby zwolnić żądanie wyświetlania, gdy tylko nie jest już wymagane.

Oto kilka sytuacji, w których należy zwolnić żądanie wyświetlania:

  • Odtwarzanie wideo jest wstrzymane, na przykład przez akcję użytkownika, buforowanie lub korektę z powodu ograniczonej przepustowości.
  • Odtwarzanie zatrzymuje się. Na przykład wideo się skończyło lub prezentacja się skończyła.
  • Wystąpił błąd odtwarzania. Na przykład problemy z łącznością sieciową lub uszkodzony plik.

Umieść inne elementy obok osadzonego nagrania wideo

Często aplikacje oferują osadzony widok, w którym wideo jest odtwarzane na stronie. Teraz oczywiście utracono optymalizację pełnego ekranu, ponieważ MediaPlayerElement nie jest rozmiarem strony i istnieją inne obiekty XAML narysowane. Należy pamiętać o niezamierzonym wejściu do tego trybu, rysując obramowanie wokół MediaPlayerElement.

Nie rysuj elementów XAML na wierzchu filmu wideo, gdy jest odtwarzane w trybie osadzonym. Jeśli to zrobisz, platforma jest zmuszona do wykonania nieco więcej pracy, aby utworzyć scenę. Umieszczenie kontrolek odtwarzania poniżej osadzonego elementu multimedialnego zamiast nad wideo jest dobrym przykładem optymalizacji pod tę sytuację. Na tej ilustracji czerwony pasek wskazuje zestaw elementów sterujących transportem (odtwarzanie, pauza, zatrzymywanie itp.).

MediaPlayerElement z elementami nakładania

Nie umieszczaj tych kontrolek na ekranie, jeśli media nie są w trybie pełnoekranowym. Zamiast tego umieść przyciski nawigacyjne gdzieś poza obszarem, w którym odtwarzane są multimedia. Na następnym obrazie elementy sterujące są umieszczane poniżej nośnika.

MediaPlayerElement z sąsiednimi elementami

Opóźnienie ustawiania źródła elementu MediaPlayerElement

Silniki multimedialne są kosztownymi obiektami, a struktura XAML opóźnia ładowanie bibliotek DLL i tworzenie dużych obiektów tak długo, jak to możliwe. MediaPlayerElement jest zmuszony do wykonania tej pracy po ustawieniu jego źródła za pośrednictwem właściwości Źródło. Ustawienie tego ustawienia, gdy użytkownik jest naprawdę gotowy do odtwarzania multimediów opóźnia większość kosztów związanych z MediaPlayerElement tak długo, jak to możliwe.

Ustawianie elementu MediaPlayerElement.PosterSource

Ustawienie MediaPlayerElement.PosterSource umożliwia programowi XAML zwolnienie niektórych zasobów procesora GPU, które w przeciwnym razie byłyby używane. Ten interfejs API umożliwia aplikacji użycie możliwie najmniejszej ilości pamięci.

Ulepszanie czyszczenia multimediów

Czyszczenie to zawsze trudne zadanie dla platform multimedialnych, aby uczynić je naprawdę responsywnymi. Ludzie ogólnie rzecz biorąc osiągają to, zmieniając wartość suwaka. Oto kilka wskazówek dotyczących tego, jak to zrobić tak wydajne, jak to możliwe:

  • Zaktualizuj wartość Slider na podstawie czasomierza, który wysyła zapytanie do Position na MediaPlayerElement.MediaPlayer. Upewnij się, że używasz rozsądnej częstotliwości aktualizacji dla czasomierza. Właściwość Position aktualizuje tylko co 250 milisekund podczas odtwarzania.
  • Częstotliwość kroków na suwaku musi być skalowana zgodnie z długością wideo.
  • Subskrybuj zdarzenia PointerPressed, PointerMoved, PointerReleased na suwaku, aby ustawić właściwość PlaybackRate na wartość 0, gdy użytkownik przeciąga suwak.
  • W programie obsługi zdarzeń PointerReleased ręcznie ustaw pozycję odtwarzania zgodnie z wartością pozycji suwaka, aby uzyskać optymalne działanie kciuka podczas przewijania.

Dopasowywanie rozdzielczości wideo do rozdzielczości urządzenia

Dekodowanie wideo zajmuje dużo pamięci i cykli procesora GPU, więc wybierz format wideo zbliżony do rozdzielczości, na której będzie wyświetlany. Nie ma sensu używać zasobów do dekodowania wideo 1080, jeśli ma być zmniejszone do znacznie mniejszego rozmiaru. Wiele aplikacji nie ma tego samego wideo zakodowanego w różnych rozdzielczościach; ale jeśli jest dostępny, użyj kodowania zbliżonego do rozdzielczości, w której będzie wyświetlana.

Wybór formatu multimediów może być tematem poufnym i często jest napędzany przez decyzje biznesowe. Z punktu widzenia wydajności platformy UWP zalecamy wideo H.264 jako podstawowy format wideo oraz AAC i MP3 jako preferowane formaty audio. W przypadku odtwarzania plików lokalnych plik MP4 jest preferowanym kontenerem plików dla zawartości wideo. Dekodowanie H.264 jest przyspieszane za pośrednictwem najnowszego sprzętu graficznego. Ponadto, chociaż przyspieszanie sprzętowe dla dekodowania VC-1 jest szeroko dostępne, w przypadku dużego zestawu sprzętu graficznego na rynku przyspieszenie jest w wielu przypadkach ograniczone do poziomu częściowego przyspieszenia sprzętowego (lub poziomu IDCT), a nie na pełnym sprzętowym odciążeniu (tj. w trybie VLD).

Jeśli masz pełną kontrolę nad procesem generowania zawartości wideo, musisz dowiedzieć się, jak zachować dobrą równowagę między wydajnością kompresji a strukturą GOP. Stosunkowo mniejszy rozmiar GOP z obrazami B może zwiększyć wydajność w trybach wyszukiwania lub sztuczki.

W przypadku dodawania krótkich efektów dźwiękowych o małym opóźnieniu, na przykład w grach, używaj plików WAV z nieskompresowanymi danymi PCM, aby zmniejszyć obciążenie przetwarzania charakterystyczne dla skompresowanych formatów audio.

Optymalizowanie zasobów obrazu

Skalowanie obrazów do odpowiedniego rozmiaru

Obrazy są przechwytywane w bardzo wysokiej rozdzielczości, co może prowadzić do korzystania z większej liczby procesorów CPU podczas dekodowania danych obrazu i większej ilości pamięci po załadowaniu z dysku. Nie ma jednak sensu dekodować i zapisywać obraz o wysokiej rozdzielczości w pamięci tylko w celu wyświetlenia go mniejszego niż rozmiar macierzysty. Zamiast tego utwórz wersję obrazu o dokładnym rozmiarze, w jakim zostanie narysowany na ekranie, korzystając z właściwości DecodePixelWidth i DecodePixelHeight.

Nie rób tego:

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

Zamiast tego wykonaj następujące czynności:

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

Jednostki DecodePixelWidth i DecodePixelHeight są domyślnie pikselami fizycznymi. Właściwość DecodePixelType może służyć do zmiany tego zachowania: ustawienie DecodePixelType na Logical powoduje, że rozmiar dekodowania automatycznie uwzględnia bieżący współczynnik skalowania systemu, podobnie jak inne elementy zawartości XAML. W związku z tym ogólnie byłoby odpowiednie ustawić DecodePixelType na Logical, jeśli na przykład chcesz, aby DecodePixelWidth i DecodePixelHeight dopasowywały się do właściwości wysokości i szerokości kontrolki obrazu, w której obraz będzie wyświetlany. W przypadku domyślnego zachowania przy użyciu pikseli fizycznych należy samodzielnie uwzględnić bieżący współczynnik skalowania systemu; i należy nasłuchiwać powiadomień o zmianie skali w przypadku zmiany preferencji użytkownika.

Jeśli DecodePixelWidth/Height są jawnie ustawione większe niż rozmiar, w jakim obraz będzie wyświetlany na ekranie, aplikacja niepotrzebnie będzie używać dodatkowej pamięci — do 4 bajtów na piksel — co szybko staje się kosztowne w przypadku dużych obrazów. Obraz zostanie również zmniejszony przy użyciu skalowania dwuliniowego, co może spowodować rozmycie w przypadku dużych współczynników skalowania.

Jeśli DecodePixelWidth/DecodePixelHeight są jawnie ustawione tak, że są mniejsze niż docelowy rozmiar obrazu na ekranie, to obraz zostanie przeskalowany w górę i może wyglądać na spikselizowany.

W niektórych przypadkach, gdy nie można wcześniej określić odpowiedniego rozmiaru dekodowania, należy polegać na automatycznym dekodowaniu XAML do odpowiedniego rozmiaru, które postara się jak najlepiej zdekodować obraz w odpowiednim rozmiarze, jeśli nie określono jawnych wartości DecodePixelWidth/DecodePixelHeight.

Należy ustawić konkretny rozmiar dekodowania, jeśli znasz rozmiar zawartości obrazu z wyprzedzeniem. Należy również ustawić DecodePixelType na Logiczny, jeśli podany rozmiar dekodowania jest zależny od innych rozmiarów elementów XAML. pl-PL: Na przykład, jeśli jawnie ustawisz rozmiar zawartości za pomocą Image.Width i Image.Height, możesz ustawić DecodePixelType na Logical, aby użyć tych samych logicznych wymiarów pikseli co kontrolka Image, a następnie jawnie użyć BitmapImage.DecodePixelWidth i/lub BitmapImage.DecodePixelHeight, żeby kontrolować rozmiar obrazu i osiągnąć potencjalnie duże oszczędności pamięci.

Należy pamiętać, że podczas określania rozmiaru zdekodowanej zawartości należy wziąć pod uwagę wartość Image.Stretch.

Dekodowanie o odpowiednim rozmiarze

W przypadku, gdy nie ustawisz jawnego rozmiaru dekodowania, kod XAML podejmie najlepszą próbę zapisania pamięci przez dekodowanie obrazu do dokładnego rozmiaru wyświetlanego na ekranie zgodnie z początkowym układem strony zawierającej. Zaleca się napisanie aplikacji w taki sposób, aby w miarę możliwości korzystała z tej funkcji. Ta funkcja zostanie wyłączona, jeśli zostaną spełnione jakiekolwiek z poniższych warunków.

  • BitmapImage jest połączony z dynamicznym drzewem XAML po ustawieniu zawartości za pomocą SetSourceAsync lub UriSource.
  • Obraz jest dekodowany przy użyciu dekodowania synchronicznego, takiego jak SetSource.
  • Obraz jest ukryty poprzez ustawienie Opacity na 0 lub Visibility na Collapsed na elemencie obrazu hosta, pędzlu lub dowolnym elemencie nadrzędnym.
  • Kontrolka obrazu lub szczotka używa StretchNone.
  • Obraz jest używany jako NineGrid.
  • CacheMode="BitmapCache" jest ustawiana na elemencie obrazu lub w dowolnym elemencie nadrzędnym.
  • Pędzel do obrazów nie jest prostokątny (na przykład w przypadku zastosowania do kształtu lub tekstu).

W powyższych scenariuszach ustawienie jawnego rozmiaru dekodowania jest jedynym sposobem osiągnięcia oszczędności pamięci.

Przed ustawieniem źródła należy zawsze dołączyć BitmapImage do drzewa dynamicznego. Za każdym razem, gdy element obrazu lub pędzel zostanie określony w znacznikach, stanie się to automatycznie. Przykłady znajdują się poniżej pod nagłówkiem „Przykłady żywego drzewa”. Zawsze należy unikać używania SetSource i zamiast tego używać SetSourceAsync podczas ustawiania źródła strumienia. Dobrym pomysłem jest unikanie ukrywania zawartości obrazu (z zerową nieprzezroczystością lub zwiniętą widocznością) podczas oczekiwania na zdarzenie ImageOpened, które ma zostać wywołane. Podejmowanie tej decyzji to kwestia osądu: nie będziesz mógł skorzystać z automatycznego dekodowania o dopasowanym rozmiarze, jeśli zostanie to zrobione. Jeśli aplikacja musi początkowo ukryć zawartość obrazu, powinna również jawnie ustawić rozmiar dekodowania, jeśli to możliwe.

przykłady drzewa na żywo

Przykład 1 (dobry) — identyfikator URI (Uniform Resource Identifier) określony w znacznikach.

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

Przykład 2 oznaczenie — identyfikator URI określony w kodzie-behind.

<Image x:Name="myImage"/>

Przykład 2 codebehind (dobry) — połączenie BitmapImage z drzewem przed ustawieniem jego UriSource.

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

Przykład 2 [zły kod za] — ustawienie UriSource w BitmapImage przed połączeniem go z drzewem.

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

Optymalizacje buforowania

Optymalizacje buforowania są wykonywane w przypadku obrazów, które używają UriSource do ładowania zawartości z pakietu aplikacji lub z internetu. Identyfikator URI służy do unikatowego identyfikowania zawartości źródłowej, a wewnętrznie struktura XAML nie będzie pobierać ani dekodować zawartości wiele razy. Zamiast tego będzie używać buforowanego oprogramowania lub zasobów sprzętowych do wielokrotnego wyświetlania zawartości.

Wyjątkiem od tej optymalizacji jest to, że obraz jest wyświetlany wiele razy w różnych rozdzielczościach (które można określić jawnie lub za pomocą automatycznego dekodowania o odpowiednim rozmiarze). Każdy wpis pamięci podręcznej przechowuje również rozdzielczość obrazu, a jeśli kod XAML nie może znaleźć obrazu ze źródłowym identyfikatorem URI zgodnym z wymaganą rozdzielczością, zdekoduje nową wersję o tym rozmiarze. Nie spowoduje to jednak ponownego pobrania zakodowanych danych obrazu.

W związku z tym należy stosować UriSource podczas ładowania obrazów z pakietu aplikacji i unikać używania strumienia plików i SetSourceAsync, gdy nie jest to wymagane.

Obrazy w zwirtualizowanych panelach (na przykład ListView)

Jeśli obraz zostanie usunięty z drzewa — ponieważ aplikacja go jawnie usunęła lub ponieważ znajduje się w nowoczesnym panelu zwirtualizowanym i został niejawnie usunięty po przewinięciu z widoku — XAML zoptymalizuje użycie pamięci, zwalniając zasoby sprzętowe dla obrazu, ponieważ nie są już wymagane. Pamięć nie jest zwalniana natychmiast, lecz podczas aktualizacji klatki, która następuje po jednej sekundzie od momentu, gdy element obrazu nie znajduje się już w drzewie.

W związku z tym należy dążyć do używania nowoczesnych paneli zwirtualizowanych do hostowania list treści graficznych.

Obrazy rasteryzowane programowo

Gdy grafika jest używana dla nieprostokątnego pędzla lub NineGrid, grafika będzie używać ścieżki rasteryzacji programowej, która nie będzie w ogóle skalować obrazów. Ponadto musi przechowywać kopię obrazu zarówno w pamięci oprogramowania, jak i sprzętu. Jeśli na przykład obraz jest używany jako pędzel dla elipsy, potencjalnie duży pełny obraz będzie dwa razy przechowywany wewnętrznie. W przypadku korzystania z NineGrid lub nieprostokątnego pędzla, aplikacja powinna wstępnie skalować obrazy, do rozmiaru, na który będą renderowane.

Ładowanie obrazów w tle wątku

Język XAML ma wewnętrzną optymalizację, która umożliwia dekodowanie zawartości obrazu asynchronicznie na powierzchnię w pamięci sprzętowej bez konieczności pośredniej powierzchni w pamięci oprogramowania. Zmniejsza to maksymalne użycie pamięci i opóźnienie renderowania. Ta funkcja zostanie wyłączona, jeśli zostaną spełnione jakiekolwiek z poniższych warunków.

  • Obraz jest używany jako NineGrid.
  • CacheMode="BitmapCache" jest ustawiana na elemencie obrazu lub w dowolnym elemencie nadrzędnym.
  • Pędzel do obrazów nie jest prostokątny (na przykład w przypadku zastosowania do kształtu lub tekstu).

SoftwareBitmapSource

Klasa SoftwareBitmapSource wymienia nieskompresowane obrazy między różnymi przestrzeniami nazw WinRT, takimi jak BitmapDecoder, API aparatu fotograficznego i XAML. Ta klasa eliminuje potrzebę dodatkowej kopii, która zwykle byłaby wymagana z WriteableBitmap, co pomaga zmniejszyć szczytowe zużycie pamięci i opóźnienie od źródła do ekranu.

SoftwareBitmap, który dostarcza informacje źródłowe, można również skonfigurować do używania niestandardowego IWICBitmap w celu zapewnienia magazynu kopii zapasowej, który umożliwia aplikacji ponowne mapowanie pamięci w miarę dopasowania. Jest to zaawansowany przypadek użycia języka C++.

Aplikacja powinna używać SoftwareBitmap i SoftwareBitmapSource do współdziałania z innymi interfejsami API WinRT, które tworzą i używają obrazów. Aplikacja powinna używać SoftwareBitmapSource do ładowania nieskompresowanych danych obrazu zamiast używania WriteableBitmap.

Użyj metody GetThumbnailAsync dla miniatur

Jednym z przypadków użycia skalowania obrazów jest tworzenie miniatur. Chociaż można użyć DecodePixelWidth i DecodePixelHeight, aby zapewnić małe wersje obrazów, platforma UWP zapewnia jeszcze bardziej wydajne interfejsy API do pobierania miniatur. GetThumbnailAsync udostępnia miniatury obrazów, które mają już buforowany system plików. Zapewnia to jeszcze lepszą wydajność niż interfejsy API XAML, ponieważ obraz nie musi być otwarty ani dekodowany.

FileOpenPicker picker = new FileOpenPicker();
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();
bmp.SetSource(fileThumbnail);

Image img = new Image();
img.Source = bmp;
Dim picker As New FileOpenPicker()
picker.FileTypeFilter.Add(".bmp")
picker.FileTypeFilter.Add(".jpg")
picker.FileTypeFilter.Add(".jpeg")
picker.FileTypeFilter.Add(".png")
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary

Dim file As StorageFile = Await picker.PickSingleFileAsync()

Dim fileThumbnail As StorageItemThumbnail = Await file.GetThumbnailAsync(ThumbnailMode.SingleItem, 64)

Dim bmp As New BitmapImage()
bmp.SetSource(fileThumbnail)

Dim img As New Image()
img.Source = bmp

Dekoduj obrazy raz

Aby zapobiec dekodowaniu obrazów więcej niż raz, przypisz właściwość Image.Source z URI, a nie używaj strumieni pamięci. Struktura XAML może skojarzyć ten sam identyfikator URI w wielu miejscach z jednym zdekodowanym obrazem, ale nie może zrobić tego samego dla wielu strumieni pamięci, które zawierają te same dane i tworzy inny zdekodowany obraz dla każdego strumienia pamięci.