最佳化動畫、媒體及影像Optimize animations, media, and images

建立具有流暢的動畫、高畫面播放速率和高效能媒體擷取和播放功能的通用 Windows 平台 (UWP) app。Create Universal Windows Platform (UWP) apps with smooth animations, high frame rate, and high-performance media capture and playback.

建立流暢的動畫Make animations smooth

流暢的互動是 UWP app 最主要的特性。A key aspect of UWP apps is smooth interactions. 這包含與手指緊密結合的觸控操作、流暢的切換效果和動畫,以及提供輸入回饋的小動作。This includes touch manipulations that "stick to your finger," smooth transitions and animations, and small motions that provide input feedback. XAML 架構有一個執行緒 (稱為合成執行緒) 專門在應用程式視覺元素中進行合成和製作動畫。In the XAML framework there is a thread called the composition thread that is dedicated to the composition and animation of an app’s visual elements. 因為合成執行緒和 UI 執行緒 (執行架構和開發人員程式碼的執行緒) 是分開的,所以無論是複雜的版面配置階段或延伸的計算,app 都可以實現一致的畫面播放速率和流暢的動畫。Because the composition thread is separate from UI thread (the thread that runs framework and developer code), apps can achieve a consistent frame rate and smooth animations regardless of complicated layout passes or extended calculations. 本節說明如何使用合成執行緒讓 app 的動畫更加流暢。This section shows how to use the composition thread to keep an app’s animations buttery smooth. 如需動畫的詳細資訊,請參閱動畫概觀For more info about animations, see Animations overview. 若要了解如何在執行大量計算時增加 app 的回應性,請參閱讓 UI 執行緒保持回應To learn about increasing an app’s responsiveness while performing intensive computations, see Keep the UI thread responsive.

使用獨立式動畫,而非相依式動畫Use independent instead of dependent animations

獨立式動畫可以在建立時從開始到結束計算,因為要製作成動畫之屬性的變更不會影響場景中的其餘物件。Independent animations can be calculated from beginning to end at the time of creation because changes to the property being animated don't affect rest of the objects in a scene. 因此,獨立式動畫可在合成執行緒上執行,而不是在 UI 執行緒上執行。Independent animations can therefore run on the composition thread instead of the UI thread. 這可以確保他們的流暢度,因為合成執行緒會持續執行更新。This guarantees that they remain smooth because the composition thread is updated at a consistent cadence.

下列動畫類型一定是獨立式動畫:All of these types of animations are guaranteed to be independent:

相依式動畫會影響配置,因此需要有來自 UI 執行緒的額外輸入才能進行計算。Dependent animations affect layout, which therefore cannot be calculated without extra input from the UI thread. 相依式動畫包含對 WidthHeight 這類屬性的修改。Dependent animations include modifications to properties like Width and Height. 相依式動畫預設不會執行,且需要由 app 開發人員選擇加入。By default, dependent animations are not run and require an opt-in from the app developer. 如果 UI 執行緒保持未封鎖狀態,則相依式動畫啟用後便可以很流暢地執行,但是如果架構或 app 正在 UI 執行緒上執行大量的其他工作,就會發生斷斷續續的現象。When enabled, they run smoothly if the UI thread remains unblocked, but they begin to stutter if the framework or app is doing a lot of other work on the UI thread.

XAML 架構中幾乎所有的動畫皆預設為獨立式,但是您可以執行一些動作來停用這個最佳化功能。Almost all animations in the XAML framework are independent by default, but there are some actions that you can take to disable this optimization. 請注意這些情況,尤其是:Beware of these scenarios particularly:

  • EnableDependentAnimation 屬性設為允許在 UI 執行緒執行相依式動畫。Setting the EnableDependentAnimation property to allow a dependent animation to run on the UI thread. 然後,將這些動畫轉換為獨立式版本。Convert these animations into an independent version. 例如,讓物件的 ScaleTransform.ScaleXScaleTransform.ScaleY 產生動畫,而不是 WidthHeightFor example animate ScaleTransform.ScaleX and ScaleTransform.ScaleY instead of the Width and Height of an object. 不要害怕縮放影像和文字這類物件。Don’t be afraid to scale objects like images and text. 架構只會在 ScaleTransform 產生動畫時套用雙線性縮放。The framework applies bilinear scaling only while the ScaleTransform is being animated. 為了確保清晰度,影像/文字會在最終大小進行點陣化處理。The image/text will be rerasterized at the final size to ensure it’s always clear.
  • 進行每個畫面的更新,這是有效的相依式動畫。Making per frame updates, which are effectively dependent animations. 這種情況的例子是在 CompositonTarget.Rendering 事件處理常式套用轉換。An example of this is applying transformations in the handler of the CompositonTarget.Rendering event.
  • 執行元素 (CacheMode 屬性設為 BitmapCache) 中被視為獨立式動畫的任何動畫。Running any animation considered independent in an element with the CacheMode property set to BitmapCache. 這可視為相依式動畫,因為必須針對每個畫面將快取重新點陣化。This is considered dependent because the cache must be re-rasterized for each frame.

不要讓 WebView 或 MediaPlayerElement 產生動畫Don't animate a WebView or MediaPlayerElement

WebView 控制項內的網頁內容不是由 XAML 架構直接呈現,並且需要執行額外的工作才能與其他場景合成。Web content within a WebView control is not directly rendered by the XAML framework and it requires extra work to be composed with the rest of the scene. 在畫面上產生控制項動畫會增加這項額外的工作,而這項工作可能會帶來同步化的問題 (例如,HTML 內容可能不會與頁面上的其餘 XAML 內容同步移動)。This extra work adds up when animating the control around the screen and can potentially introduce synchronization issues (for example, the HTML content might not move in sync with the rest of the XAML content on the page). 當您需要讓 WebView 控制項產生動畫時,請在動畫期間將它交換為 WebViewBrushWhen you need to animate a WebView control, swap it with a WebViewBrush for the duration of the animation.

MediaPlayerElement 產生動畫同樣也不是很好的做法。Animating a MediaPlayerElement is a similarly bad idea. 除了效能變差,還會造成播放的視訊內容產生裂紋或其他殘影。Beyond the performance detriment, it can cause tearing or other artifacts in the video content being played.

注意   這篇文章對於 MediaPlayerElement 的建議也適用於 MediaElementNote   The recommendations in this article for MediaPlayerElement also apply to MediaElement. MediaPlayerElement 只能在 Windows 10 版本 1607 取得,因此如果您是針對先前版本的 Windows 建立 App,則需要使用 MediaElementMediaPlayerElement is only available in Windows 10, version 1607, so if you are creating an app for a previous version of Windows you need to use MediaElement.

謹慎使用無限動畫Use infinite animations sparingly

大部分動畫會有指定的執行時間,但是將 Timeline.Duration 設定為永久會讓動畫無限期地執行下去。Most animations execute for a specified amount of time, but setting the Timeline.Duration property to Forever allows an animation to run indefinitely. 建議您儘量不要使用無限動畫,因為這些動畫會不斷耗用 CPU 資源,並且可能會讓 CPU 無法進入低電源或閒置狀態,進而導致電力快速耗盡。We recommend minimizing the use of infinite animations because they continually consume CPU resources and can prevent the CPU from going into a low power or idle state, causing it to run out of power more quickly.

新增 CompositionTarget.Rendering 處理常式與執行無限動畫類似。Adding a handler for CompositionTarget.Rendering is similar to running an infinite animation. 通常 UI 執行緒只會在有工作時啟用,但是新增這個事件處理常式會強制執行緒執行每個畫面。Normally the UI thread is active only when there is work to do, but adding handler for this event forces it to run every frame. 沒有需要完成的工作時請移除處理常式,然後於需要時再重新登錄。Remove the handler when there is no work to be done and reregister it when it’s needed again.

使用動畫庫Use the animation library

Windows.UI.Xaml.Media.Animation 命名空間包含高效能且順暢的動畫庫,這些動畫的外觀及操作與其他 Windows 動畫相同。The Windows.UI.Xaml.Media.Animation namespace includes a library of high-performance, smooth animations that have a look and feel consistent with other Windows animations. 相關類別的名稱中會包含「佈景主題」,其相關說明請見動畫概觀The relevant classes have "Theme" in their name, and are described in Animations overview. 這個動畫庫支援許多常見的動畫案例,例如將應用程式的第一個檢視以動畫顯示,並建立狀態與內容轉換。This library supports many common animation scenarios, such as animating the first view of the app and creating state and content transitions. 建議您儘量使用這個動畫庫來提升效能以及與 UWP UI 的一致性。We recommend using this animation library whenever possible to increase performance and consistency for UWP UI.

注意   動畫庫無法讓所有可能的屬性產生動畫效果。Note   The animation library can't animate all possible properties. 如需無法套用動畫庫的 XAML 案例,請參閱腳本動畫For XAML scenarios where the animation library doesn't apply, see Storyboarded animations.

個別使 CompositeTransform3D 屬性產生動畫效果Animate CompositeTransform3D properties independently

您可以分別為 CompositeTransform3D 的每個屬性產生動畫效果,如此一來就只會套用您所需的動畫。You can animate each property of a CompositeTransform3D independently, so apply only the animations you need. 如需範例和詳細資訊,請參閱 UIElement.Transform3DFor examples and more info, see UIElement.Transform3D. 如需產生動畫效果的轉換詳細資訊,請參閱 腳本動畫主要畫面格和 Easing 函式動畫For more info about animating transforms, see Storyboarded animations and Key-frame and easing function animations.

最佳化媒體資源Optimize media resources

音訊、視訊及影像是大部分 app 都會使用的精彩內容格式。Audio, video, and images are compelling forms of content that the majority of apps use. 隨著媒體擷取速率增加以及內容從標準畫質進步到高畫質,儲存、解碼和播放這種內容所需的資源數量亦隨之增加。As media capture rates increase and content moves from standard definition to high definition the amount of resources need to store, decode, and play back this content increases. UWP 媒體引擎中已加入使用最新功能建立的 XAML 架構,因此,app 可以免費使用這些改善的功能。The XAML framework builds on the latest features added to the UWP media engines so apps get these improvements for free. 本文說明的一些額外技巧可以讓您在 UWP 中有效使用媒體。Here we explain some additional tricks that allow you to get the most out media in your UWP app.

釋放媒體串流Release media streams

媒體檔案是應用程式經常使用的一些最常見且成本較高的資源。Media files are some of the most common and expensive resources apps typically use. 因為媒體檔案資源可能會大幅增加應用程式的磁碟使用量大小,所以在應用程式使用完媒體後,您必須記得立即釋放媒體的控制代碼。Because media file resources can greatly increase the size of your app's memory footprint, you must remember to release the handle to media as soon as the app is finished using it.

例如,如果您的 app 使用 RandomAccessStreamIInputStream 物件,請確定您的 app 完成使用時呼叫物件的 close 方法,以釋放基礎物件。For example, if your app working with a RandomAccessStream or an IInputStream object, be sure to call the close method on the object when your app has finished using it, to release the underlying object.

盡可能顯示全螢幕視訊播放Display full screen video playback when possible

在 UWP App 中,一律在 MediaPlayerElement 上使用 IsFullWindow 屬性,以啟用和停用完整視窗轉譯。In UWP apps, always use the IsFullWindow property on the MediaPlayerElement to enable and disable full window rendering. 這可確保在媒體播放時會使用系統層級最佳化。This insures system level optimizations are used during media playback.

當視訊內容是唯一要轉譯的內容時,XAML 架構可以最佳化顯示的視訊內容,因此可以使用較少的電力,並產生較高的畫面播放速率。The XAML framework can optimize the display of video content when it is the only thing being rendered, resulting in an experience that uses less power and yields higher frame rates. 最有效的媒體播放,是將 MediaPlayerElement 物件的大小設為畫面的寬度和長度,並且不顯示其他 XAML 元素。For most efficient media playback set the size of a MediaPlayerElement to be the width and height of the screen and don’t display other XAML elements

基於某些正當理由,您可以在佔據了螢幕全長和全寬的 MediaPlayerElement 上重疊 XAML 元素,例如隱藏式輔助字幕或短暫的傳輸控制項。There are legitimate reasons to overlay XAML elements on a MediaPlayerElement that takes up the full width and height of the screen, for example closed captions or momentary transport controls. 不需要這些元素時,請務必隱藏它們 (設定 Visibility="Collapsed"),讓媒體撥放返回最有效的狀態。Make sure to hide these elements (set Visibility="Collapsed") when they are not needed to put media playback back into its most efficient state.

顯示器停用與省電Display deactivation and conserving power

若要防止顯示器在未偵測到使用者動作時停用 (例如 app 播放視訊時),您可以呼叫 DisplayRequest.RequestActiveTo prevent the display from be deactivating when user action is no longer detected, such as when an app is playing video, you can call DisplayRequest.RequestActive.

若要省電並延長電池續航力,您應該在不再需要顯示器要求時呼叫 DisplayRequest.RequestRelease 以便立即釋放顯示器要求。To conserve power and battery life, you should call DisplayRequest.RequestRelease to release the display request as soon as it is no longer required.

以下是一些您應該釋放顯示器要求的情況:Here are some situations when you should release the display request:

  • 視訊播放暫停時,例如因使用者動作、緩衝或因為頻寬有限而進行調整。Video playback is paused, for example by user action, buffering, or adjustment due to limited bandwidth.
  • 播放停止時。Playback stops. 例如,視訊播放完畢或簡報結束。For example, the video is done playing or the presentation is over.
  • 發生播放錯誤時。A playback error has occurred. 例如,網路連線問題或檔案損毀。For example, network connectivity issues or a corrupted file.

將其他元素放到內嵌視訊的旁邊Put other elements to the side of embedded video

App 通常會提供在頁面中播放視訊的內嵌檢視。Often apps offer an embedded view where video is played within a page. 由於 MediaPlayerElement 不是頁面大小,再加上其他繪製的 XAML 物件,您很明顯地無法使用全螢幕播放。Now you obviously lost the full screen optimization because the MediaPlayerElement is not the size of the page and there are other XAML objects drawn. 請注意不要在 周圍繪製框線,以避免不慎進入這個模式。Beware of unintentionally entering this mode by drawing a border around a MediaPlayerElement.

當視訊是內嵌模式時,請勿在視訊上繪製 XAML 元素。Don’t draw XAML elements on top of video when it’s in embedded mode. 如果您這樣做,架構會強制執行一些額外工作來建構場景。If you do, the framework is forced to do a little extra work to compose the scene. 將傳輸控制項放在內嵌媒體元素下方 (而非視訊上),是這個狀況最佳化的最佳範例。Placing transport controls below an embedded media element instead of on top of the video is a good example of optimizing for this situation. 在這個影像中,紅色列代表一組傳輸控制項 (播放、暫停及停止等)。In this image, the red bar indicates a set of transport controls (play, pause, stop, etc.).

含有重疊元素的 MediaPlayerElement

請勿在非全螢幕的媒體上方放置這些控制項。Don’t place these controls on top of media that is not full screen. 反之,將這些傳輸控制項放在媒體轉譯區域外部的位置。Instead place the transport controls somewhere outside of the area where the media is being rendered. 在下一個影像中,控制項放在媒體下方。In the next image, the controls are placed below the media.

含有相鄰元素的 MediaPlayerElement

延遲設定 MediaPlayerElement 的來源Delay setting the source for a MediaPlayerElement

媒體引擎是耗費大量資源的物件,XAML 架構會盡可能延遲載入 dll 和建立大型物件。Media engines are expensive objects and the XAML framework delays loading dlls and creating large objects as long as possible. 在透過 Source 屬性設定來源後,MediaPlayerElement 會被強制執行這個工作。The MediaPlayerElement is forced to do this work after its source is set via the Source property. 在使用者真的準備好播放媒體時這樣設定,就能盡量延遲與 MediaElement 關聯的大多數資源消耗。Setting this when the user is really ready to play media delays the majority of the cost associated with the MediaPlayerElement as long as possible.

設定 MediaPlayerElement.PosterSourceSet MediaPlayerElement.PosterSource

設定 MediaPlayerElement.PosterSource 可以讓 XAML 釋放一些原先可能會被使用的 GPU 資源。Setting MediaPlayerElement.PosterSource enables XAML to release some GPU resources that would have otherwise been used. 這個 API 可以讓 App 盡可能使用最少的記憶體。This API allows an app to use as little memory as possible.

改善媒體清除Improve media scrubbing

為了使媒體平台可以立即回應,清除永遠是一項困難的工作。Scrubbing is always a tough task for media platforms to make really responsive. 我們通常是透過變更滑桿的值來進行這項工作。Generally people accomplish this by changing the value of a Slider. 下列是一些如何讓這項工作更有效率的提示:Here are a couple tips on how to make this as efficient as possible:

  • 根據會查詢 MediaPlayerElement.MediaPlayerPosition 的計時器來更新 Slider 的值。Update the value of Slider based on a timer that queries the Position on the MediaPlayerElement.MediaPlayer. 請務必為您的計時器使用合理的更新頻率。Make sure to use a reasonable update frequency for your timer. 在播放期間,Position 只會每 250 毫秒更新一次。The Position property only updates every 250 millisecond during playback.
  • 滑桿上的步階頻率大小必須隨視訊長度調整。The size of the step frequency on the Slider must scale with the length of the video.
  • 訂閱滑桿上的 PointerPressedPointerMoved 以及 PointerReleased 事件,以在使用者拖曳滑桿的指標時將 PlaybackRate 屬性設定為 0。Subscribe to the PointerPressed, PointerMoved, PointerReleased events on the slider to set the PlaybackRate property to 0 when the user drags the thumb of the slider.
  • PointerReleased 事件處理常式中,將媒體位置手動設定為滑桿位置值,以在清除時獲得最佳貼齊效果。In the PointerReleased event handler, manually set the media position to the slider position value to achieve optimal thumb snapping while scrubbing.

採用與裝置解析度相符的視訊解析度Match video resolution to device resolution

解碼視訊會耗用相當多記憶體和 GPU 週期,因此最好選擇與顯示時所採用的解析度相近的視訊格式。Decoding video takes a lot of memory and GPU cycles, so choose a video format close to the resolution it will be displayed at. 如果 1080 視訊將縮減成很小的大小,那麼使用資源將它解碼就沒有任何意義。There is no point in using the resources to decode 1080 video if it’s going to get scaled down to a much smaller size. 許多應用程式不會根據不同的解析度來編碼相同的視訊,不過如果有這項功能,請使用與顯示時所採用的解析度相近的編碼方式。Many apps don’t have the same video encoded at different resolutions; but if it is available, use an encoding that is close to the resolution at which it will be displayed.

媒體格式選擇有可能是一個敏感的話題,而且通常是由企業決策所驅動。Media format selection can be a sensitive topic and is often driven by business decisions. 從 UWP 效能觀點來看,我們建議使用 H.264 視訊做為主要的視訊格式,以及使用 AAC 與 MP3 做為慣用的音訊格式。From a UWP performance perspective, we recommend H.264 video as the primary video format and AAC and MP3 as the preferred audio formats. 就本機檔案播放而言,MP4 是視訊內容的慣用檔案容器。For local file playback, MP4 is the preferred file container for video content. H.264 解碼會透過最新的圖形硬體來加速。H.264 decoding is accelerated through most recent graphics hardware. 此外,雖然 VC-1 解碼的硬體加速非常普及,但對於市場上許多圖形硬體而言,這個加速在許多情況下都被限制成部分加速等級 (或是 IDCT 等級),而不是全速等級的硬體卸載 (也就是 VLD 模式)。Also, although hardware acceleration for VC-1 decoding is broadly available, for a large set of graphics hardware on the market, the acceleration is limited in many cases to a partial acceleration level (or IDCT level), rather than a full-steam level hardware offload (i.e. VLD mode).

如果您對於視訊內容產生程序有完整的控制權,您應該思考如何在壓縮效率與 GOP 結構之間保持良好的平衡。If you have full control of the video content generation process, you must figure out how to keep a good balance between compression efficiency and GOP structure. 含 B 圖片且相對較小的 GOP 大小可在搜尋或策略模式中提升效能。Relatively smaller GOP size with B pictures can increase the performance in seeking or trick modes.

包含簡短且低延遲的音訊效果時 (例如在遊戲中),請使用含未壓縮 PCM 資料的 WAV 檔案,以減輕壓縮音訊格式時常會發生的額外處理負荷。When including short, low-latency audio effects, for example in games, use WAV files with uncompressed PCM data to reduce processing overhead that is typical for compressed audio formats.

最佳化影像資源Optimize image resources

將影像按比例調整為適當大小Scale images to the appropriate size

擷取影像時是以非常高的解析度擷取,這可能會導致 App 在將影像資料解碼時使用較多 CPU,以及在從磁碟載入影像之後使用較多記憶體。Images are captured at very high resolutions, which can lead to apps using more CPU when decoding the image data and more memory after it’s loaded from disk. 但是如果只為了顯示比原生大小還小的影像,而將高解析度影像解碼並儲存在記憶體中,實在不太合理。But there’s no sense decoding and saving a high-resolution image in memory only to display it smaller than its native size. 因此會改為使用 DecodePixelWidthDecodePixelHeight 屬性,以繪製在螢幕上的實際大小建立一個影像版本。Instead, create a version of the image at the exact size it will be drawn on-screen using the DecodePixelWidth and DecodePixelHeight properties.

不要這樣做:Don't do this:

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

改為這樣做:Instead, do this:

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

DecodePixelWidthDecodePixelHeight 的單位預設是實體像素。The units for DecodePixelWidth and DecodePixelHeight are by default physical pixels. DecodePixelType 屬性可以用來變更這個行為:將 DecodePixelType 設定為 Logical 會導致解碼大小自動採用系統目前的縮放比例,類似於其他的 XAML 內容。The DecodePixelType property can be used to change this behavior: setting DecodePixelType to Logical results in the decode size automatically accounting for the system’s current scale factor, similar to other XAML content. 因此,如果您希望 DecodePixelWidthDecodePixelHeight 符合影像顯示所在之 Image 控制項的 Height 和 Width 屬性,將 DecodePixelType 設定為 Logical 會比較適當。It would therefore be generally appropriate to set DecodePixelType to Logical if, for instance, you want DecodePixelWidth and DecodePixelHeight to match the Height and Width properties of the Image control the image will be displayed in. 使用實體像素的預設行為,您必須自行考量系統目前的縮放比例;而且您必須接聽縮放比例變更通知,以免使用者變更其顯示喜好設定。With the default behavior of using physical pixels, you must account for the system’s current scale factor yourself; and you should listen for scale change notifications in case the user changes their display preferences.

如果 DecodePixelWidth/Height 明確設定超過要在螢幕上顯示的影像大小,則 app 會不必要地使用額外的記憶體—每像素最多 4 個位元組—大型影像就會變得非常耗費資源。If DecodePixelWidth/Height are explicitly set larger than the image will be displayed on-screen then the app will unnecessarily use extra memory—up to 4 bytes per pixel—which quickly becomes expensive for large images. 影像也會使用雙線性縮放比例縮小,而當縮放比例變大時可能會導致影像模糊。The image will also be scaled down using bilinear scaling which could cause it to appear blurry for large scale factors.

如果 DecodePixelWidth/DecodePixelHeight 明確設定低於要在螢幕上顯示的影像大小,則影像會被放大而變得不太美觀。If DecodePixelWidth/DecodePixelHeight are explicitly set smaller than the image will be displayed on screen then it will be scaled up and could appear pixelated.

在無法預先決定適當解碼大小的某些情況下,您應該遵從 XAML 的自動以正確大小解碼,它會在未指定明確的 DecodePixelWidth/DecodePixelHeight 時,盡可能嘗試以適當大小將影像解碼。In some cases where an appropriate decode size cannot be determined ahead of time, you should defer to XAML’s automatic right-size-decoding which will make a best effort attempt to decode the image at the appropriate size if an explicit DecodePixelWidth/DecodePixelHeight is not specified.

如果您預先知道影像內容的大小,就應該設定明確的解碼大小。You should set an explicit decode size if you know the size of the image content ahead of time. 如果提供的解碼大小是相對其他 XAML 元素的大小,您也應該一併將 DecodePixelType 設定為 LogicalYou should also in conjunction set DecodePixelType to Logical if the supplied decode size is relative to other XAML element sizes. 例如,如果您明確使用 Image.Width 和 Image.Height 設定內容的大小,可以將 DecodePixelType 設定為 DecodePixelType.Logical 以使用與 Image 控制項相同的邏輯像素維度,然後明確使用 BitmapImage.DecodePixelWidth 和/或 BitmapImage.DecodePixelHeight 控制影像的大小來節省大量記憶體。For example, if you explicitly set the content size with Image.Width and Image.Height, you could set DecodePixelType to DecodePixelType.Logical to use the same logical pixel dimensions as an Image control and then explicitly use BitmapImage.DecodePixelWidth and/or BitmapImage.DecodePixelHeight to control the size of the image to achieve potentially large memory savings.

請注意,決定解碼內容的大小時,應考量 Image.Stretch。Note that Image.Stretch should be considered when determining the size of the decoded content.

以正確大小解碼Right-sized decoding

如果您沒有明確設定解碼大小,XAML 會根據內含頁面的初始配置,將影像解碼成要在螢幕上顯示的確切大小,盡可能嘗試節省記憶體。In the event that you don't set an explicit decode size, XAML will make a best effort attempt to save memory by decoding an image to the exact size it will appear on-screen according to the containing page’s initial layout. 建議您盡可能以這種方式撰寫您的應用程式以便使用這項功能。You're advised to write your application in such a way as to make use of this feature when possible. 如果符合下列任何一個條件,則會停用此功能。This feature will be disabled if any of the following conditions are met.

  • 使用 SetSourceAsyncUriSource 設定內容之後,BitmapImage 連接到動態的 XAML 樹狀結構。The BitmapImage is connected to the live XAML tree after setting the content with SetSourceAsync or UriSource.
  • 影像使用同步解碼 (例如 SetSource) 來解碼。The image is decoded using synchronous decoding such as SetSource.
  • 影像在主機影像元素或筆刷或任何父元素上透過將 [不透明度] 設定為 0,或將 [可見度] 設定為 [收合] 而隱藏。The image is hidden via setting Opacity to 0 or Visibility to Collapsed on the host image element or brush or any parent element.
  • 影像控制項或筆刷使用設定為 NoneStretchThe image control or brush uses a Stretch of None.
  • 影像用來做為 NineGridThe image is used as a NineGrid.
  • 已在影像元素或任何父元素上設定 CacheMode="BitmapCache"CacheMode="BitmapCache" is set on the image element or on any parent element.
  • 影像筆刷是非矩形 (例如套用至圖形或文字時)。The image brush is non-rectangular (such as when applied to a shape or to text).

在上述案例中,設定明確的解碼大小是節省記憶體的唯一方法。In the above scenarios, setting an explicit decode size is the only way to achieve memory savings.

您在設定來源之前,應該一律將 BitmapImage 附加到動態樹狀結構。You should always attach a BitmapImage to the live tree before setting the source. 每當在標記中指定影像元素或筆刷時,就自動會是這種情況。Any time an image element or brush is specified in markup, this will automatically be the case. 「動態樹狀結構範例」標題之下會提供範例。Examples are provided below under the heading "Live tree examples". 您在設定串流來源時應該一律避免使用 SetSource,改為使用 SetSourceAsyncYou should always avoid using SetSource and instead use SetSourceAsync when setting a stream source. 而且最好避免在等待 ImageOpened 事件引發時隱藏影像內容 (無論是不透明度設定為零,或是摺疊的可見性)。And it's a good idea to avoid hiding image content (either with zero opacity or with collapsed visibility) while waiting for the ImageOpened event to be raised. 這麼做只是為了判斷:如果完成,您也無法從自動以正確大小解碼獲得好處。Doing this is a judgment call: you won't benefit from automatic right-sized decoding if it's done. 如果您的 app 必須一開始就隱藏影像內容,則可能的話也應該明確設定解碼大小。If your app must hide image content initially then it should also set the decode size explicitly if possible.

動態樹狀結構範例Live tree examples

範例 1 (好)—在標記中指定的統一資源識別元 (URI)。Example 1 (good)—Uniform Resource Identifier (URI) specified in markup.

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

範例 2 標記—在程式碼後置中指定的 URI。Example 2 markup—URI specified in code-behind.

<Image x:Name="myImage"/>

範例 2 程式碼後置 (好)—將 BitmapImage 連接到樹狀結構之後才設定其 UriSource。Example 2 code-behind (good)—connecting the BitmapImage to the tree before setting its UriSource.

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

範例 2 程式碼後置 (壞)—將 BitmapImage 連接到樹狀結構之前先設定其 UriSource。Example 2 code-behind (bad)—setting the BitmapImage's UriSource before connecting it to the tree.

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

快取最佳化Caching optimizations

快取最佳化適用於使用 UriSource 從應用程式套件或從網路載入內容的影像。Caching optimizations are in effect for images that use UriSource to load content from an app package or from the web. URI 是用來唯一識別相關的內容,因此就不需要內部多次下載 XAML 架構或解碼內容。The URI is used to uniquely identify the underlying content, and internally the XAML framework will not download or decode the content multiple times. 相反地會使用快取的軟體或硬體資源來多次顯示內容。Instead, it will use the cached software or hardware resources to display the content multiple times.

這個最佳化的例外是如果影像以不同解析度顯示多次 (這可以明確指定或透過自動以正確大小解碼)。The exception to this optimization is if the image is displayed multiple times at different resolutions (which can be specified explicitly or through automatic right-sized decoding). 每個快取項目也會儲存影像的解析度,如果 XAML 找不到有來源 URI 的影像符合需要的解析度,則會以該大小解碼新的版本,Each cache entry also stores the resolution of the image, and if XAML cannot find an image with a source URI that matches the required resolution then it will decode a new version at that size. 但是不會再下載編碼的影像資料。It will not, however, download the encoded image data again.

因此,您必須在從應用程式套件載入影像時包含使用 UriSource,以及避免使用檔案串流和 SetSourceAsync (如果不必要)。Consequently, you should embrace using UriSource when loading images from an app package, and avoid using a file stream and SetSourceAsync when it's not required.

虛擬化面板中的影像 (例如 ListView)Images in virtualized panels (ListView, for instance)

如果影像從樹狀結構移除—因為 app 明確將影像移除,或者因為影像位於新式虛擬化面板中而在捲動到檢視之外時以隱含方式移除—則 XAML 會為影像釋放已經不再需要的硬體資源來最佳化記憶體使用量。If an image is removed from the tree—because the app explicitly removed it, or because it’s in a modern virtualized panel and was implicitly removed when scrolled out of view—then XAML will optimize memory usage by releasing the hardware resources for the image since they are no longer required. 記憶體不會立即釋放,而是在影像元素從樹狀結構移除之後發生畫面更新期間時釋放。The memory is not released immediately, but rather is released during the frame update that occurs after one second of the image element no longer being in the tree.

因此,您應該盡量使用新式虛擬化面板來裝載影像內容的清單。Consequently, you should strive to use modern virtualized panels to host lists of image content.

軟體點陣化的影像Software-rasterized images

當影像用於非矩形的筆刷或用於 NineGrid 時,影像會使用軟體點陣化路徑,完全不會縮放影像。When an image is used for a non-rectangular brush or for a NineGrid, the image will use a software rasterization path, which will not scale images at all. 此外,它也必須在軟體和硬體記憶體中都儲存一份影像。Additionally, it must store a copy of the image in both software and hardware memory. 例如,如果有一張影像用來做為橢圓形的筆刷,則內部會儲存兩次可能很大的完整影像。For instance, if an image is used as a brush for an ellipse then the potentially large full image will be stored twice internally. 使用 NineGrid 或非矩形的筆刷時,您的 app 應該會將影像預先縮放到接近要呈現的大小。When using NineGrid or a non-rectangular brush, then, your app should pre-scale its images to approximately the size they will be rendered at.

背景執行緒影像載入Background thread image-loading

XAML 具備內部最佳化的功能,允許以非同步方式將影像的內容解碼到硬體記憶體中的表層,而不需要軟體記憶體中的中繼層。XAML has an internal optimization that allows it to decode the contents of an image asynchronously to a surface in hardware memory without requiring an intermediate surface in software memory. 這樣可以減少記憶體的尖峰使用量與轉譯延遲。This reduces peak memory usage and rendering latency. 如果符合下列任何一個條件,則會停用此功能。This feature will be disabled if any of the following conditions are met.

  • 影像用來做為 NineGridThe image is used as a NineGrid.
  • 已在影像元素或任何父元素上設定 CacheMode="BitmapCache"CacheMode="BitmapCache" is set on the image element or on any parent element.
  • 影像筆刷是非矩形 (例如套用至圖形或文字時)。The image brush is non-rectangular (such as when applied to a shape or to text).

SoftwareBitmapSourceSoftwareBitmapSource

SoftwareBitmapSource 類別交換不同 WinRT 命名空間 (例如 BitmapDecoder、相機 API 以及 XAML) 之間的互通性未壓縮映像。The SoftwareBitmapSource class exchanges interoperable uncompressed images between different WinRT namespaces such as BitmapDecoder, camera APIs, and XAML. 這個類別會排除通常是使用 WriteableBitmap 才需要的額外複本,以及協助減少尖峰記憶體和來源到螢幕的延遲。This class obviates an extra copy that would typically be necessary with WriteableBitmap, and that helps reduce peak memory and source-to-screen latency.

提供來源資訊的 SoftwareBitmap 也可以設定為使用自訂 IWICBitmap,提供可重新載入的備份存放區,讓 app 需要時就可以重新對應記憶體。The SoftwareBitmap that supplies source information can also be configured to use a custom IWICBitmap to provide a reloadable backing store that allows the app to re-map memory as it sees fit. 這是進階的 C++ 使用案例。This is an advanced C++ use case.

您的 app 應該使用 SoftwareBitmapSoftwareBitmapSource 與其他製作和使用影像的 WinRT API 相互溝通。Your app should use SoftwareBitmap and SoftwareBitmapSource to interoperate with other WinRT APIs that produce and consume images. 您的 app 也應該在載入未壓縮的影像資料時使用 SoftwareBitmapSource 而不是使用 WriteableBitmapAnd your app should use SoftwareBitmapSource when loading uncompressed image data instead of using WriteableBitmap.

使用 GetThumbnailAsync 以取得縮圖Use GetThumbnailAsync for thumbnails

按比例調整影像的一個使用案例是建立縮圖。One use case for scaling images is creating thumbnails. 雖然您可以使用 DecodePixelWidthDecodePixelHeight 來提供較小的影像版本,但 UWP 提供更有效的 API 供您擷取縮圖。Although you could use DecodePixelWidth and DecodePixelHeight to provide small versions of images, UWP provides even more efficient APIs for retrieving thumbnails. GetThumbnailAsync 會為已快取檔案系統的影像提供縮圖。GetThumbnailAsync provides the thumbnails for images that have the file system already cached. 這樣做的效能比使用 XAML API 更好,因為不需要開啟或解碼影像。This provides even better performance than the XAML APIs because the image doesn’t need to be opened or decoded.

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

解碼影像一次Decode images once

為避免影像解碼超過一次,請從 URI 指派 Image.Source 屬性,而非使用記憶體串流。To prevent images from being decoded more than once, assign the Image.Source property from an Uri rather than using memory streams. XAML 架構可以將多個位置中的同一個 URI 與一個解碼的影像建立關聯,但無法為包含相同資料的多個記憶體串流執行相同動作,而是會為每個記憶體串流建立一個不同的解碼影像。The XAML framework can associate the same Uri in multiple places with one decoded image, but it cannot do the same for multiple memory streams that contain the same data and creates a different decoded image for each memory stream.