Manipulação de imagem do WinRT

Um amigo e eu passamos a maior parte da noite passada quase arrancando nossos cabelos tentando trabalhar com algumas imagens em um aplicativo do metrô. Temos imagens no aplicativo com o charme de compartilhamento e, em seguida, eu queria fazer outro trabalho com elas, cortar as imagens e salvá-las de volta na pasta appdata. Isso provou ser extremamente frustrante.

Minha pergunta, no final de tudo isso, será: "Qual é a maneira correta de fazer isso, sem sentir que estou martelando um monte de peças de quebra-cabeça que não combinam?"

Ao compartilhar várias imagens com o aplicativo, elas aparecem como uma lista deWindows.Storage.StorageFiles. Aqui está algum código usado para lidar com isso.

<code>var storageItems = await _shareOperation.Data.GetStorageItemsAsync();

foreach (StorageFile item in storageItems)
{
    var stream = await item.OpenReadAsync();
    var properties = await item.Properties.GetImagePropertiesAsync();

    var image = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    image.SetSource(stream);

    images.Add(image);
}
</code>

Algumas pesquisas online indicaram que atualmente,Windows.UI.Xaml.Media.Imaging.WriteableBitmap é a única coisa capaz de permitir que você acesse os dados de pixel na imagem.Essa questão inclui uma resposta útil cheia de métodos de extensão para salvar imagens em um arquivo, por isso usamos essas.

Nossos problemas foram os piores quando tentei abrir os arquivos novamente mais tarde. Eu fiz algo parecido com antes:

<code>var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();

foreach (var file in files)
{
    var fileStream = await file.OpenReadAsync();
    var properties = await file.Properties.GetImagePropertiesAsync();
    var bitmap = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height);
    bitmap.SetSource(fileStream);

    System.IO.Stream stream = bitmap.PixelBuffer.AsStream();
</code>

Aqui vem um problema. Quanto tempo é esse fluxo, se eu quiser os bytes de fora?

<code>    // CRASH! Length isn't supported on an IRandomAccessStream.
    var pixels = new byte[fileStream.Length];
</code>

Ok, tente novamente.

<code>    var pixels = new byte[stream.Length];
</code>

Isso funciona, exceto ... se a imagem for compactada, o fluxo será menor do que o esperado, portanto, você obterá uma exceção fora dos limites. Por agora, finja que é um bitmap descompactado.

<code>    await _stream.ReadAsync(pixels, 0, pixels.Length);
</code>

Bem, adivinhe. Mesmo que eu tenha ditobitmap.SetSource(fileStream); Para ler os dados, minha matriz de bytes ainda está cheia de zeros. Eu não tenho ideia do porquê. Se eu passar esse mesmobitmap em minha interface do usuário através do grupo de dados de amostra, a imagem aparece bem. Por isso, tem claramente os dados de pixel nesse bitmap em algum lugar, mas não consigo lerbitmap.PixelBuffer? Por que não?

Finalmente, aqui está o que realmente acabou funcionando.

<code>    var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, fileStream);
    var data = await decoder.GetPixelDataAsync();
    var bytes = data.DetachPixelData();

    /* process my data, finally */

} // end of that foreach I started a while ago
</code>

Então agora eu tenho por dados de imagem, mas ainda tenho um grande problema. Para fazer qualquer coisa com isso, tenho que fazer suposições sobre seu formato. Eu não tenho idéia se é rgba, rgb, abgr, bgra, o que quer que eles possam ser. Se eu acho errado meu processamento simplesmente falha. Eu tive dúzias de execuções de teste cuspindo zero bytes e imagens corrompidas, imagens de cabeça para baixo (???), cores erradas, etc. Eu teria esperado encontrar algumas dessas informações noproperties que recebi de chamarawait file.Properties.GetImagePropertiesAsync();mas sem sorte. Isso contém apenas a largura e a altura da imagem, além de algumas outras coisas inúteis. Documentação mínimaAqui.

Então, por que esse processo é tão doloroso? Isso está apenas refletindo a imaturidade das bibliotecas agora, e posso esperar que melhore? Ou já existe alguma maneira padrão de fazer isso? Eu queria que fosse tão fácil quanto emSystem.Drawing. Isso deu a você todos os dados que você precisava, e carregou qualquer tipo de imagem corretamente, sem fazer com que você lide com streams.

questionAnswers(1)

yourAnswerToTheQuestion