Partilhar via


Otimize animações, mídia e imagens

Crie aplicativos da Plataforma Universal do Windows (UWP) com animações suaves, alta taxa de quadros e captura e reprodução de mídia de alto desempenho.

Torne as animações suaves

Um aspeto fundamental dos aplicativos UWP são as interações suaves. Isso inclui manipulações de toque que "mantêm-se no seu dedo", transições e animações suaves, e movimentos subtis que fornecem feedback de entrada. Na estrutura XAML, há um thread chamado thread de composição dedicado à composição e animação dos elementos visuais de um aplicativo. Como o encadeamento de composição é separado do encadeamento da interface do utilizador (o encadeamento que executa o framework e o código do desenvolvedor), as aplicações podem obter uma taxa de quadros consistente e animações suaves, independentemente de processos de layout complexos ou cálculos prolongados. Esta seção mostra como usar o thread de composição para manter as animações de um aplicativo perfeitamente suaves. Para saber mais sobre animações, veja Visão geral de animações. Para saber mais sobre como aumentar a capacidade de resposta de um aplicativo durante a execução de cálculos intensivos, consulte Manter o thread da interface do usuário responsivo.

Use animações independentes em vez de dependentes

As animações independentes podem ser calculadas do início ao fim no momento da sua criação, porque as alterações na propriedade animada não afetam o restante dos objetos numa cena. Portanto, animações independentes podem ser executadas no thread de composição em vez do thread da interface do usuário. Isso garante que eles permaneçam suaves porque o thread de composição é atualizado a um ritmo consistente.

Todos estes tipos de animações são garantidamente independentes:

As animações dependentes afetam o layout, que, portanto, não pode ser calculado sem entrada extra do thread da interface do usuário. As animações dependentes incluem modificações em propriedades como Width e Height. Por padrão, as animações dependentes não são executadas e exigem uma aceitação do desenvolvedor do aplicativo. Quando habilitados, eles são executados sem problemas se o thread da interface do usuário permanecer desbloqueado, mas começam a gaguejar se a estrutura ou o aplicativo estiver fazendo muito outro trabalho no thread da interface do usuário.

Quase todas as animações na estrutura XAML são independentes por padrão, mas há algumas ações que você pode tomar para desabilitar essa otimização. Cuidado com estes cenários, em particular:

  • Definindo a propriedade EnableDependentAnimation para permitir que uma animação dependente seja executada no thread da interface do usuário. Converta essas animações em uma versão independente. Por exemplo, animar ScaleTransform.ScaleX e ScaleTransform.ScaleY em vez da Largura e Altura de um objeto. Não tenha medo de dimensionar objetos como imagens e texto. A estrutura aplica escalonamento bilinear somente enquanto o ScaleTransform está sendo animado. A imagem e o texto serão rerrasterizados para o tamanho final para garantir que estejam sempre claros.
  • Fazer atualizações por quadro, que são animações efetivamente dependentes. Um exemplo disso é aplicar transformações no manipulador do evento CompositionTarget.Rendering.
  • Executar qualquer animação considerada independente em um elemento com a propriedade CacheMode definida como BitmapCache. Isso é considerado dependente porque o cache deve ser rasterizado novamente para cada quadro.

Não animes um WebView ou MediaPlayerElement

O conteúdo da Web dentro de um WebView controle não é renderizado diretamente pela estrutura XAML, e requer trabalho extra para ser integrado com o resto da cena. Esse trabalho extra é adicionado ao animar o controle ao redor da tela e pode potencialmente introduzir problemas de sincronização (por exemplo, o conteúdo HTML pode não se mover em sincronia com o restante do conteúdo XAML na página). Quando precisar animar um controle WebView, troque-o por um WebViewBrush durante a animação.

Animar um MediaPlayerElement é uma ideia igualmente má. Além do prejuízo de desempenho, ele pode causar rasgos ou outros artefatos no conteúdo de vídeo que está sendo reproduzido.

Nota As recomendações neste artigo para o MediaPlayerElement aplicam-se também ao MediaElement. MediaPlayerElement só está disponível no Windows 10, versão 1607, portanto, se você estiver criando um aplicativo para uma versão anterior do Windows, você precisa usar MediaElement.

Use animações infinitas com moderação

A maioria das animações é executada por um período de tempo especificado, mas definir a propriedade Timeline.Duration como Forever permite que uma animação seja executada indefinidamente. Recomendamos minimizar o uso de animações infinitas porque elas consomem continuamente recursos da CPU e podem impedir que a CPU entre em um estado de baixa energia ou ociosidade, fazendo com que ela fique sem energia mais rapidamente.

Adicionar um manipulador para CompositionTarget.Rendering é semelhante a executar uma animação infinita. Normalmente, o thread da interface do usuário fica ativo somente quando há trabalho a fazer, mas adicionar manipulador para esse evento força a execução de todos os quadros. Remova o manipulador quando não houver trabalho a ser feito e registre-o novamente quando for necessário novamente.

Usar a biblioteca de animações

O namespace Windows.UI.Xaml.Media.Animation inclui uma biblioteca de animações suaves e de alto desempenho que têm uma aparência consistente com outras animações do Windows. As classes relevantes têm "Tema" em seu nome e são descritas em Visão geral de animações. Esta biblioteca suporta muitos cenários de animação comuns, como a animação da primeira vista da aplicação e a criação de transições de estado e conteúdo. Recomendamos usar essa biblioteca de animações sempre que possível para aumentar o desempenho e a consistência da interface do usuário da UWP.

Observação A biblioteca de animação não pode animar todas as propriedades possíveis. Para cenários XAML em que a biblioteca de animações não se aplica, consulte Animações com guião gráfico.

Animar de forma independente as propriedades do CompositeTransform3D

Você pode animar cada propriedade de um CompositeTransform3D independentemente, portanto, aplique apenas as animações necessárias. Para obter exemplos e mais informações, consulte UIElement.Transform3D. Para saber mais sobre como animar transformações, veja Animações com storyboard e Animações de quadro-chave e função de atenuação.

Otimize os recursos de mídia

Áudio, vídeo e imagens são formas atraentes de conteúdo que a maioria dos aplicativos usa. À medida que as taxas de captura de mídia aumentam e o conteúdo passa da definição padrão para a alta definição, a quantidade de recursos necessários para armazenar, decodificar e reproduzir esse conteúdo aumenta. A estrutura XAML se baseia nos recursos mais recentes adicionados aos mecanismos de mídia UWP para que os aplicativos obtenham essas melhorias gratuitamente. Aqui explicamos alguns truques adicionais que permitem que você aproveite ao máximo a mídia em seu aplicativo UWP.

Liberar fluxos de mídia

Os arquivos de mídia são alguns dos recursos mais comuns e caros que os aplicativos normalmente usam. Como os recursos de arquivo de mídia podem aumentar consideravelmente o tamanho do espaço de memória do seu aplicativo, lembre-se de liberar o identificador para a mídia assim que o aplicativo terminar de usá-lo.

Por exemplo, se a sua aplicação estiver a trabalhar com um objeto RandomAccessStream ou um objeto IInputStream, certifique-se de chamar o método close no objeto quando a aplicação terminar de utilizá-lo, para liberar o objeto subjacente.

Exibir reprodução de vídeo em tela cheia quando possível

Em aplicativos UWP, sempre use a propriedade IsFullWindow no MediaPlayerElement para habilitar e desabilitar a renderização de janela inteira. Isso garante que otimizações no nível do sistema sejam usadas durante a reprodução de mídia.

A estrutura XAML pode otimizar a exibição de conteúdo de vídeo quando é a única coisa que está sendo renderizada, resultando em uma experiência que usa menos energia e produz taxas de quadros mais altas. Para uma reprodução de mídia mais eficiente, ajuste o tamanho de um MediaPlayerElement para ser a largura e a altura da tela e não exiba outros elementos XAML.

Há motivos legítimos para sobrepor elementos XAML em um MediaPlayerElement que ocupa toda a largura e altura da tela, por exemplo, legendas ocultas ou controles de transporte momentâneos. Certifique-se de ocultar esses elementos (definir Visibility="Collapsed") quando eles não forem necessários para colocar a reprodução de mídia de volta em seu estado mais eficiente.

Desativação e conservação de energia do ecrã

Para evitar que a exibição seja desativada quando a ação do usuário não for mais detetada, como quando um aplicativo está reproduzindo vídeo, você pode chamar DisplayRequest.RequestActive.

Para economizar energia e a vida útil da bateria, você deve ligar para DisplayRequest.RequestRelease para liberar a solicitação de exibição assim que ela não for mais necessária.

Aqui estão algumas situações em que você deve liberar a solicitação de exibição:

  • A reprodução de vídeo é pausada, por exemplo, por ação do usuário, buffering ou ajuste devido à largura de banda limitada.
  • A reprodução é interrompida. Por exemplo, a reprodução do vídeo terminou ou a apresentação chegou ao fim.
  • Ocorreu um erro de reprodução. Por exemplo, problemas de conectividade de rede ou um arquivo corrompido.

Coloque outros elementos ao lado do vídeo incorporado

Muitas vezes, os aplicativos oferecem uma visualização incorporada onde o vídeo é reproduzido dentro de uma página. Agora você obviamente perdeu a otimização de tela cheia porque o MediaPlayerElement não é do tamanho da página e há outros objetos XAML desenhados. Cuidado para não entrar involuntariamente neste modo ao desenhar uma borda à volta de um MediaPlayerElement.

Não desenhe elementos XAML sobre o vídeo quando ele estiver no modo incorporado. Se você fizer isso, a estrutura é forçada a fazer um pouco de trabalho extra para compor a cena. Colocar controles de transporte abaixo de um elemento de mídia incorporado em vez de sobre o vídeo é um bom exemplo de otimização para essa situação. Nesta imagem, a barra vermelha indica um conjunto de controlos de transporte (reproduzir, pausar, parar, etc.).

MediaPlayerElement com elementos de sobreposição

Não coloque esses controles em cima de mídia que não esteja em tela cheia. Em vez disso, coloque os controles de transporte em algum lugar fora da área onde a mídia está sendo renderizada. Os controlos são colocados abaixo do conteúdo multimédia na imagem seguinte.

MediaPlayerElement com elementos vizinhos

Atrasar a definição da origem de um MediaPlayerElement

Os mecanismos de mídia são objetos caros e a estrutura XAML atrasa o carregamento de dlls e a criação de objetos grandes o maior tempo possível. O MediaPlayerElement é forçado a fazer esse trabalho depois que sua origem é definida por meio da propriedade Source. Definir isso quando o usuário está realmente pronto para reproduzir mídia atrasa a maior parte do custo associado ao MediaPlayerElement o maior tempo possível.

Definir MediaPlayerElement.PosterSource

A configuração de MediaPlayerElement.PosterSource permite que o XAML libere alguns recursos de GPU que teriam sido usados de outra forma. Essa API permite que um aplicativo use o mínimo de memória possível.

Melhorar a depuração de mídia

Esfregar é sempre uma tarefa difícil para as plataformas de mídia tornarem realmente responsivo. Geralmente, as pessoas conseguem isso alterando o valor de um *slider*. Aqui estão algumas dicas sobre como tornar isso o mais eficiente possível:

  • Atualize o valor de Slider com base em um temporizador que consulta o Posição no MediaPlayerElement.MediaPlayer. Certifique-se de usar uma frequência de atualização razoável para o seu temporizador. A propriedade Position só é atualizada a cada 250 milissegundos durante a reprodução.
  • O tamanho da frequência de passo no controle deslizante deve ser dimensionado de acordo com a duração do vídeo.
  • Assine os eventos PointerPressed, PointerMoved, PointerReleased no controle deslizante para definir a propriedade PlaybackRate como 0 quando o usuário arrasta o polegar do controle deslizante.
  • No manipulador de eventos PointerReleased, defina manualmente a posição da mídia para o valor da posição do controle deslizante para obter o encaixe ideal do polegar durante a depuração.

Fazer corresponder a resolução de vídeo à resolução do dispositivo

A descodificação de vídeo requer muita memória e ciclos de GPU, por isso escolha um formato de vídeo próximo da resolução em que será apresentado. Não adianta usar os recursos para decodificar o vídeo 1080 se ele for reduzido para um tamanho muito menor. Muitas aplicações não têm o mesmo vídeo codificado em resoluções diferentes; mas se estiver disponível, use uma codificação próxima à resolução em que será exibida.

A seleção de formatos de mídia pode ser um tópico sensível e muitas vezes é impulsionada por decisões de negócios. Do ponto de vista do desempenho da UWP, recomendamos o vídeo H.264 como o formato de vídeo principal e AAC e MP3 como os formatos de áudio preferidos. Para reprodução de arquivos locais, MP4 é o contêiner de arquivo preferido para conteúdo de vídeo. A descodificação H.264 é acelerada através do hardware gráfico mais recente. Além disso, embora a aceleração de hardware para decodificação VC-1 esteja amplamente disponível, para um grande conjunto de hardware gráfico no mercado, a aceleração é limitada em muitos casos a um nível de aceleração parcial (ou nível IDCT), em vez de um descarregamento de hardware de nível de vapor total (ou seja, modo VLD).

Se você tem controle total do processo de geração de conteúdo de vídeo, você deve descobrir como manter um bom equilíbrio entre a eficiência de compressão e a estrutura GOP. Tamanho GOP relativamente menor com imagens B pode aumentar o desempenho em modos de busca ou truque.

Ao incluir efeitos de áudio curtos e de baixa latência, por exemplo, em jogos, use arquivos WAV com dados PCM não compactados para reduzir a sobrecarga de processamento típica de formatos de áudio compactado.

Otimize os recursos de imagem

Dimensionar imagens para o tamanho apropriado

As imagens são capturadas em resoluções muito altas, o que pode levar os aplicativos a usar mais CPU ao decodificar os dados da imagem e mais memória depois que ela é carregada do disco. Mas não faz sentido decodificar e salvar uma imagem de alta resolução na memória apenas para exibi-la menor do que seu tamanho nativo. Em vez disso, crie uma versão da imagem no tamanho exato em que ela será exibida na tela usando as propriedades DecodePixelWidth e DecodePixelHeight.

Não faças isto:

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

Em vez disso, faça o seguinte:

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

As unidades para DecodePixelWidth e DecodePixelHeight são, por padrão, pixels físicos. A propriedade DecodePixelType pode ser usada para alterar esse comportamento: definir DecodePixelType como Logical resulta no tamanho da decodificação contabilizando automaticamente o fator de escala atual do sistema, semelhante a outro conteúdo XAML. Portanto, seria geralmente apropriado definir DecodePixelType como lógico se, por exemplo, você quiser que DecodePixelWidth e DecodePixelHeight correspondam às propriedades Height e Width do controle Image no qual a imagem será exibida. Com o comportamento padrão de usar pixels físicos, você mesmo deve levar em conta o fator de escala atual do sistema; E você deve ouvir as notificações de alteração de escala caso o usuário altere suas preferências de exibição.

Se DecodePixelWidth/Height forem explicitamente definidos para valores maiores do que o tamanho em que a imagem será exibida no ecrã, o aplicativo usará desnecessariamente memória extra—até 4 bytes adicionais por pixel—o que rapidamente se torna dispendioso para imagens grandes. A imagem também será reduzida usando escala bilinear, o que pode fazer com que pareça desfocada para fatores de grande escala.

Se DecodePixelWidth/DecodePixelHeight forem explicitamente definidos como menores do que a imagem será exibida na tela, ela será ampliada e poderá parecer pixelada.

Em alguns casos em que não se pode determinar um tamanho adequado de decodificação com antecedência, deve-se confiar na decodificação automática do XAML, que fará um esforço para decodificar a imagem no tamanho apropriado se não for especificado um DecodePixelWidth/DecodePixelHeight explícito.

Você deve definir um tamanho de decodificação explícito se souber o tamanho do conteúdo da imagem com antecedência. Você deve também definir em conjunto DecodePixelType como Logical se o tamanho de decodificação fornecido for relativo a outros tamanhos de elemento XAML. Por exemplo, se você definir explicitamente o tamanho do conteúdo com Image.Width e Image.Height, poderá definir DecodePixelType como DecodePixelType.Logical para usar as mesmas dimensões lógicas de pixels de um controle Image e, em seguida, usar explicitamente BitmapImage.DecodePixelWidth e/ou BitmapImage.DecodePixelHeight para controlar o tamanho da imagem para obter economias de memória potencialmente grandes.

Observe que Image.Stretch deve ser considerado ao determinar o tamanho do conteúdo decodificado.

Descodificação no tamanho certo

Caso você não defina um tamanho de decodificação explícito, o XAML fará um esforço para salvar memória decodificando uma imagem para o tamanho exato em que ela aparecerá na tela de acordo com o layout inicial da página que a contém. Aconselhamo-lo a escrever a sua candidatura de forma a fazer uso desta funcionalidade sempre que possível. Este recurso será desativado se qualquer uma das seguintes condições for atendida.

  • O BitmapImage está ligado à árvore XAML ao vivo após definir o conteúdo usando SetSourceAsync ou UriSource.
  • A imagem é decodificada usando decodificação síncrona, como SetSource.
  • A imagem é ocultada por meio da configuração Opacidade para 0 ou Visibilidade para Recolhido no elemento de imagem host ou pincel, ou em qualquer elemento pai.
  • O controle de imagem ou pincel usa um Stretch de Nenhum.
  • A imagem é usada como um NineGrid.
  • CacheMode="BitmapCache" é definido no elemento de imagem ou em qualquer elemento pai.
  • O pincel da imagem não é retangular (como quando aplicado a uma forma ou ao texto).

Nos cenários acima, definir um tamanho de decodificação explícito é a única maneira de obter economia de memória.

Você sempre deve anexar um BitmapImage à árvore ativa antes de definir a origem. Sempre que um elemento de imagem ou pincel for especificado na marcação, esse será automaticamente o caso. Exemplos são fornecidos abaixo sob o título "Exemplos de árvores vivas". Você deve sempre evitar usar SetSource e, em vez disso, usar SetSourceAsync ao definir uma fonte de fluxo. E é uma boa ideia evitar ocultar o conteúdo da imagem (com opacidade zero ou com visibilidade recolhida) enquanto aguarda o evento ImageOpened ser gerado. Fazer isso é uma decisão de julgamento: não te beneficiarás da decodificação automática adequada se for feita. Se o seu aplicativo precisar ocultar o conteúdo da imagem inicialmente, ele também deverá definir o tamanho da decodificação explicitamente, se possível.

Exemplos de árvores vivas

Exemplo 1 (bom) — URI (Uniform Resource Identifier) especificado na marcação.

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

Exemplo de marcação 2 — URI especificada em code-behind.

<Image x:Name="myImage"/>

Exemplo 2 de code-behind (bom) — ligando o BitmapImage à árvore de elementos antes de definir o seu UriSource.

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

Exemplo 2 código subjacente (inadequado) — ao definir o UriSource da BitmapImage antes de conectá-lo à árvore.

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

Otimizações de cache

As otimizações de cache estão em vigor para imagens que usam UriSource para carregar conteúdo de um pacote de aplicativo ou da Web. O URI é usado para identificar exclusivamente o conteúdo subjacente e, internamente, a estrutura XAML não baixará ou decodificará o conteúdo várias vezes. Em vez disso, ele usará os recursos de software ou hardware armazenados em cache para exibir o conteúdo várias vezes.

A exceção a essa otimização é se a imagem for exibida várias vezes em resoluções diferentes (que podem ser especificadas explicitamente ou por meio de decodificação automática de tamanho certo). Cada entrada de cache também armazena a resolução da imagem e, se o XAML não conseguir encontrar uma imagem com um URI de origem que corresponda à resolução necessária, ele decodificará uma nova versão nesse tamanho. No entanto, ele não baixará os dados de imagem codificados novamente.

Consequentemente, você deve adotar o uso de UriSource ao carregar imagens de um pacote de aplicações e evitar o uso de um fluxo de ficheiros e SetSourceAsync quando não for necessário.

Imagens em painéis virtualizados (ListView, por exemplo)

Se uma imagem for removida da árvore — porque o aplicativo a removeu explicitamente ou porque ela está em um painel virtualizado moderno e foi implicitamente removida quando rolada para fora da exibição — o XAML otimizará o uso de memória liberando os recursos de hardware para a imagem, já que eles não são mais necessários. A memória não é libertada imediatamente, mas sim durante a atualização do quadro que ocorre após um segundo de o elemento de imagem deixar de estar na árvore.

Consequentemente, você deve se esforçar para usar painéis virtualizados modernos para hospedar listas de conteúdo de imagem.

Imagens rasterizadas por software

Quando uma imagem é usada para um pincel não retangular ou para um NineGrid, a imagem usará um caminho de rasterização de software, que não dimensionará as imagens. Além disso, ele deve armazenar uma cópia da imagem na memória de software e hardware. Por exemplo, se uma imagem for usada como pincel para uma elipse, a imagem completa, potencialmente grande, será armazenada duas vezes internamente. Ao usar o NineGrid ou um pincel não retangular, seu aplicativo deve pré-dimensionar suas imagens para aproximadamente o tamanho em que serão renderizadas.

Carregamento de imagens de thread de plano de fundo

O XAML tem uma otimização interna que permite decodificar o conteúdo de uma imagem de forma assíncrona para uma superfície na memória de hardware sem exigir uma superfície intermediária na memória de software. Isso reduz o pico de uso de memória e a latência de renderização. Este recurso será desativado se qualquer uma das seguintes condições for atendida.

  • A imagem é usada como um NineGrid.
  • CacheMode="BitmapCache" é definido no elemento de imagem ou em qualquer elemento pai.
  • O pincel da imagem não é retangular (como quando aplicado a uma forma ou ao texto).

SoftwareBitmapSource

A classe SoftwareBitmapSource troca imagens não compactadas interoperáveis entre diferentes namespaces WinRT, como BitmapDecoder, APIs de câmera e XAML. Essa classe dispensa uma cópia extra que normalmente seria necessária com WriteableBitmap e isso ajuda a reduzir o pico de memória e a latência da fonte até à tela.

O SoftwareBitmap que fornece informações de origem também pode ser configurado para usar um IWICBitmap personalizado para fornecer um armazenamento subjacente recarregável que permite que o aplicativo remapeie a memória como achar melhor. Este é um caso de uso C++ avançado.

Seu aplicativo deve usar SoftwareBitmap e SoftwareBitmapSource para interoperar com outras APIs do WinRT que produzem e consomem imagens. E seu aplicativo deve usar SoftwareBitmapSource ao carregar dados de imagem não compactados em vez de usar WriteableBitmap.

Utilize GetThumbnailAsync para miniaturas

Um caso de uso para dimensionar imagens é a criação de miniaturas. Embora você possa usar DecodePixelWidth e DecodePixelHeight para fornecer versões pequenas de imagens, a UWP fornece APIs ainda mais eficientes para recuperar miniaturas. GetThumbnailAsync fornece as miniaturas de imagens que já estão armazenadas em cache no sistema de arquivos. Isso fornece um desempenho ainda melhor do que as APIs XAML porque a imagem não precisa ser aberta ou decodificada.

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

Decodificar imagens uma vez

Para evitar que as imagens sejam decodificadas mais de uma vez, atribua a propriedade Image.Source de um Uri em vez de usar fluxos de memória. A estrutura XAML pode associar o mesmo Uri em vários locais a uma imagem decodificada, mas não pode fazer o mesmo para vários fluxos de memória que contêm os mesmos dados e cria uma imagem decodificada diferente para cada fluxo de memória.