Criar, editar e salvar imagens de bitmapCreate, edit, and save bitmap images

Este artigo explica como carregar e salvar arquivos de imagem usando BitmapDecoder e BitmapEncoder e como usar o objeto SoftwareBitmap para representar imagens de bitmap.This article explains how to load and save image files using BitmapDecoder and BitmapEncoder and how to use the SoftwareBitmap object to represent bitmap images.

A classe SoftwareBitmap é uma API versátil que pode ser criada a partir de várias origens incluindo arquivos de imagem, objetos WriteableBitmap, superfícies do Direct3D e código.The SoftwareBitmap class is a versatile API that can be created from multiple sources including image files, WriteableBitmap objects, Direct3D surfaces, and code. SoftwareBitmap permite que você converta facilmente entre modos alfa e formatos de pixel diferentes e permite acesso de baixo nível a dados de pixel.SoftwareBitmap allows you to easily convert between different pixel formats and alpha modes, and allows low-level access to pixel data. Além disso, o SoftwareBitmap é uma interface comum usada por vários recursos do Windows, incluindo:Also, SoftwareBitmap is a common interface used by multiple features of Windows, including:

  • CapturedFrame permite que você obtenha os quadros capturados pela câmera como um SoftwareBitmap.CapturedFrame allows you to get frames captured by the camera as a SoftwareBitmap.

  • VideoFrame permite que você obtenha uma representação SoftwareBitmap de um VideoFrame.VideoFrame allows you to get a SoftwareBitmap representation of a VideoFrame.

  • FaceDetector permite que você detecte rostos em um SoftwareBitmap.FaceDetector allows you to detect faces in a SoftwareBitmap.

O código de exemplo neste artigo usa APIs dos namespaces a seguir.The sample code in this article uses APIs from the following namespaces.

using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.Graphics.Imaging;
using Windows.UI.Xaml.Media.Imaging;

Criar um SoftwareBitmap de um arquivo de imagem com BitmapDecoderCreate a SoftwareBitmap from an image file with BitmapDecoder

Para criar um SoftwareBitmap de um arquivo, obtenha uma instância de StorageFile que contenha os dados da imagem.To create a SoftwareBitmap from a file, get an instance of StorageFile containing the image data. Este exemplo usa um FileOpenPicker para permitir que o usuário selecione um arquivo de imagem.This example uses a FileOpenPicker to allow the user to select an image file.

FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;

var inputFile = await fileOpenPicker.PickSingleFileAsync();

if (inputFile == null)
{
    // The user cancelled the picking operation
    return;
}

Chame o método OpenAsync do objeto StorageFile para obter um fluxo de acesso aleatório que contém os dados da imagem.Call the OpenAsync method of the StorageFile object to get a random access stream containing the image data. Chame o método estático BitmapDecoder.CreateAsync para obter uma instância da classe BitmapDecoder para o fluxo especificado.Call the static method BitmapDecoder.CreateAsync to get an instance of the BitmapDecoder class for the specified stream. Chame GetSoftwareBitmapAsync para obter um objeto SoftwareBitmap que contém a imagem.Call GetSoftwareBitmapAsync to get a SoftwareBitmap object containing the image.

SoftwareBitmap softwareBitmap;

using (IRandomAccessStream stream = await inputFile.OpenAsync(FileAccessMode.Read))
{
    // Create the decoder from the stream
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);

    // Get the SoftwareBitmap representation of the file
    softwareBitmap = await decoder.GetSoftwareBitmapAsync();
}

Salvar um SoftwareBitmap em um arquivo com BitmapEncoderSave a SoftwareBitmap to a file with BitmapEncoder

Para salvar um SoftwareBitmap em um arquivo, obtenha uma instância de StorageFile na qual a imagem será salva.To save a SoftwareBitmap to a file, get an instance of StorageFile to which the image will be saved. Este exemplo usa um FileSavePicker para permitir que o usuário selecione um arquivo de saída.This example uses a FileSavePicker to allow the user to select an output file.

FileSavePicker fileSavePicker = new FileSavePicker();
fileSavePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileSavePicker.FileTypeChoices.Add("JPEG files", new List<string>() { ".jpg" });
fileSavePicker.SuggestedFileName = "image";

var outputFile = await fileSavePicker.PickSaveFileAsync();

if (outputFile == null)
{
    // The user cancelled the picking operation
    return;
}

Chame o método OpenAsync do objeto StorageFile para obter um fluxo de acesso aleatório no qual a imagem será gravada.Call the OpenAsync method of the StorageFile object to get a random access stream to which the image will be written. Chame o método estático BitmapEncoder.CreateAsync para obter uma instância da classe BitmapEncoder para o fluxo especificado.Call the static method BitmapEncoder.CreateAsync to get an instance of the BitmapEncoder class for the specified stream. O primeiro parâmetro para CreateAsync é um GUID que representa o codec que deve ser usado para codificar a imagem.The first parameter to CreateAsync is a GUID representing the codec that should be used to encode the image. A classe BitmapEncoder expõe uma propriedade que contém a ID de cada codec compatível com o codificador, como JpegEncoderId.BitmapEncoder class exposes a property containing the ID for each codec supported by the encoder, such as JpegEncoderId.

Use o método SetSoftwareBitmap para definir a imagem que será codificada.Use the SetSoftwareBitmap method to set the image that will be encoded. Você pode definir valores da propriedade BitmapTransform para aplicar transformações básicas à imagem enquanto ela está sendo codificada.You can set values of the BitmapTransform property to apply basic transforms to the image while it is being encoded. A propriedade IsThumbnailGenerated determina se uma miniatura é gerada pelo codificador.The IsThumbnailGenerated property determines whether a thumbnail is generated by the encoder. Observe que nem todos os formatos de arquivo suportam miniaturas. Portanto, se você usar esse recurso, deverá capturar o erro de operação não suportada que será gerado se não houver suporte para miniaturas.Note that not all file formats support thumbnails, so if you use this feature, you should catch the unsupported operation error that will be thrown if thumbnails are not supported.

Chame FlushAsync para fazer com que o codificador grave os dados de imagem no arquivo especificado.Call FlushAsync to cause the encoder to write the image data to the specified file.

private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
    using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        // Create an encoder with the desired format
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);

        // Set the software bitmap
        encoder.SetSoftwareBitmap(softwareBitmap);

        // Set additional encoding parameters, if needed
        encoder.BitmapTransform.ScaledWidth = 320;
        encoder.BitmapTransform.ScaledHeight = 240;
        encoder.BitmapTransform.Rotation = Windows.Graphics.Imaging.BitmapRotation.Clockwise90Degrees;
        encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
        encoder.IsThumbnailGenerated = true;

        try
        {
            await encoder.FlushAsync();
        }
        catch (Exception err)
        {
            const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
            switch (err.HResult)
            {
                case WINCODEC_ERR_UNSUPPORTEDOPERATION: 
                    // If the encoder does not support writing a thumbnail, then try again
                    // but disable thumbnail generation.
                    encoder.IsThumbnailGenerated = false;
                    break;
                default:
                    throw;
            }
        }

        if (encoder.IsThumbnailGenerated == false)
        {
            await encoder.FlushAsync();
        }


    }
}

Você pode especificar opções de codificação adicionais ao criar o BitmapEncoder, criando um novo objeto BitmapPropertySet e populando-o com um ou mais objetos BitmapTypedValue que representam as configurações de codificador.You can specify additional encoding options when you create the BitmapEncoder by creating a new BitmapPropertySet object and populating it with one or more BitmapTypedValue objects representing the encoder settings. Para obter uma lista de opções de codificador com suporte, consulte Referência de opções de BitmapEncoder.For a list of supported encoder options, see BitmapEncoder options reference.

var propertySet = new Windows.Graphics.Imaging.BitmapPropertySet();
var qualityValue = new Windows.Graphics.Imaging.BitmapTypedValue(
    1.0, // Maximum quality
    Windows.Foundation.PropertyType.Single
    );

propertySet.Add("ImageQuality", qualityValue);

await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(
    Windows.Graphics.Imaging.BitmapEncoder.JpegEncoderId,
    stream,
    propertySet
);

Usar SoftwareBitmap com um controle de imagem XAMLUse SoftwareBitmap with a XAML Image control

Para exibir uma imagem em uma página XAML usando o controle Image, defina primeiro um controle Image em sua página XAML.To display an image within a XAML page using the Image control, first define an Image control in your XAML page.

<Image x:Name="imageControl"/>

Atualmente, o controle Image só dá suporte a imagens que usam a codificação BGRA8 e canal alfa pré-multiplicado ou nenhum.Currently, the Image control only supports images that use BGRA8 encoding and pre-multiplied or no alpha channel. Antes de tentar exibir uma imagem, teste para verificar se ele tem o formato correto e, se não tiver, use o método Convert estático de SoftwareBitmap para converter a imagem no formato com suporte.Before attempting to display an image, test to make sure it has the correct format, and if not, use the SoftwareBitmap static Convert method to convert the image to the supported format.

Crie um novo objeto SoftwareBitmapSource.Create a new SoftwareBitmapSource object. Defina o conteúdo dos objetos de origem chamando SetBitmapAsync e passando um SoftwareBitmap.Set the contents of the source object by calling SetBitmapAsync, passing in a SoftwareBitmap. Em seguida, você pode definir a propriedade Source do controle Image para o SoftwareBitmapSource recém-criado.Then you can set the Source property of the Image control to the newly created SoftwareBitmapSource.

if (softwareBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8 ||
    softwareBitmap.BitmapAlphaMode == BitmapAlphaMode.Straight)
{
    softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}

var source = new SoftwareBitmapSource();
await source.SetBitmapAsync(softwareBitmap);

// Set the source of the Image control
imageControl.Source = source;

Você também pode usar SoftwareBitmapSource para definir um SoftwareBitmap como o ImageSource para um ImageBrush.You can also use SoftwareBitmapSource to set a SoftwareBitmap as the ImageSource for an ImageBrush.

Criar um SoftwareBitmap de um WriteableBitmapCreate a SoftwareBitmap from a WriteableBitmap

Você pode criar um SoftwareBitmap de um WriteableBitmap existente chamando SoftwareBitmap.CreateCopyFromBuffer e fornecendo a propriedade PixelBuffer do WriteableBitmap para definir os dados de pixel.You can create a SoftwareBitmap from an existing WriteableBitmap by calling SoftwareBitmap.CreateCopyFromBuffer and supplying the PixelBuffer property of the WriteableBitmap to set the pixel data. O segundo argumento permite que você solicite um formato de pixel para o WriteableBitmap recém-criado.The second argument allows you to request a pixel format for the newly created WriteableBitmap. Você pode usar as propriedades PixelWidth e PixelHeight do WriteableBitmap para especificar as dimensões da nova imagem.You can use the PixelWidth and PixelHeight properties of the WriteableBitmap to specify the dimensions of the new image.

SoftwareBitmap outputBitmap = SoftwareBitmap.CreateCopyFromBuffer(
    writeableBitmap.PixelBuffer,
    BitmapPixelFormat.Bgra8,
    writeableBitmap.PixelWidth,
    writeableBitmap.PixelHeight
);

Criar ou editar um SoftwareBitmap programaticamenteCreate or edit a SoftwareBitmap programmatically

Até agora este tópico mostrou como trabalhar com arquivos de imagem.So far this topic has addressed working with image files. Você também pode criar um novo SoftwareBitmap programaticamente no código e usar a mesma técnica para acessar e modificar os dados de pixel do SoftwareBitmap.You can also create a new SoftwareBitmap programatically in code and use the same technique to access and modify the SoftwareBitmap's pixel data.

SoftwareBitmap usa interoperabilidade COM para expor o buffer bruto que contém os dados de pixel.SoftwareBitmap uses COM interop to expose the raw buffer containing the pixel data.

Para usar interoperabilidade COM, você deve incluir uma referência ao namespace System.Runtime.InteropServices em seu projeto.To use COM interop, you must include a reference to the System.Runtime.InteropServices namespace in your project.

using System.Runtime.InteropServices;

Inicialize a interface COM IMemoryBufferByteAccess adicionando o seguinte código em seu namespace.Initialize the IMemoryBufferByteAccess COM interface by adding the following code within your namespace.

[ComImport]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Crie um novo SoftwareBitmap com o formato de pixel e o tamanho desejados.Create a new SoftwareBitmap with pixel format and size you want. Ou use um SoftwareBitmap existente do qual você deseja editar os dados de pixel.Or, use an existing SoftwareBitmap for which you want to edit the pixel data. Chame SoftwareBitmap.LockBuffer para obter uma instância da classe BitmapBuffer que representa o buffer de dados de pixel.Call SoftwareBitmap.LockBuffer to obtain an instance of the BitmapBuffer class representing the pixel data buffer. Converta BitmapBuffer em IMemoryBufferByteAccess na interface COM e chame IMemoryBufferByteAccess.GetBuffer para popular uma matriz de bytes com os dados.Cast the BitmapBuffer to the IMemoryBufferByteAccess COM interface and then call IMemoryBufferByteAccess.GetBuffer to populate a byte array with data. Use o método BitmapBuffer.GetPlaneDescription para obter um objeto BitmapPlaneDescription que ajudará você a calcular o deslocamento para o buffer de cada pixel.Use the BitmapBuffer.GetPlaneDescription method to get a BitmapPlaneDescription object that will help you calculate the offset into the buffer for each pixel.

softwareBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8, 100, 100, BitmapAlphaMode.Premultiplied);

using (BitmapBuffer buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Write))
{
    using (var reference = buffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacity;
        ((IMemoryBufferByteAccess)reference).GetBuffer(out dataInBytes, out capacity);

        // Fill-in the BGRA plane
        BitmapPlaneDescription bufferLayout = buffer.GetPlaneDescription(0);
        for (int i = 0; i < bufferLayout.Height; i++)
        {
            for (int j = 0; j < bufferLayout.Width; j++)
            {

                byte value = (byte)((float)j / bufferLayout.Width * 255);
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 0] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 1] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 2] = value;
                dataInBytes[bufferLayout.StartIndex + bufferLayout.Stride * i + 4 * j + 3] = (byte)255;
            }
        }
    }
}

Como esse método acessa o buffer bruto subjacente aos tipos do Windows Runtime, ele deve ser declarado usando a palavra-chave unsafe.Because this method accesses the raw buffer underlying the Windows Runtime types, it must be declared using the unsafe keyword. Você também deve configurar seu projeto no Microsoft Visual Studio para permitir a compilação de código não seguro. Para fazer isso, abra a página Propriedades do projeto, clique na página de propriedade Build e selecione a caixa de seleção Permitir Código Não Seguro.You must also configure your project in Microsoft Visual Studio to allow the compilation of unsafe code by opening the project's Properties page, clicking the Build property page, and selecting the Allow Unsafe Code checkbox.

Criar um SoftwareBitmap de uma superfície de Direct3DCreate a SoftwareBitmap from a Direct3D surface

Para criar um objeto SoftwareBitmap de uma superfície de Direct3D, você deve incluir o namespace Windows.Graphics.DirectX.Direct3D11 em seu projeto.To create a SoftwareBitmap object from a Direct3D surface, you must include the Windows.Graphics.DirectX.Direct3D11 namespace in your project.

using Windows.Graphics.DirectX.Direct3D11;

Chame CreateCopyFromSurfaceAsync para criar um novo SoftwareBitmap da superfície.Call CreateCopyFromSurfaceAsync to create a new SoftwareBitmap from the surface. Como o nome indica, o novo SoftwareBitmap tem uma cópia separada dos dados de imagem.As the name indicates, the new SoftwareBitmap has a separate copy of the image data. As modificações no SoftwareBitmap não terão nenhum efeito na superfície do Direct3D.Modifications to the SoftwareBitmap will not have any effect on the Direct3D surface.

private async void CreateSoftwareBitmapFromSurface(IDirect3DSurface surface)
{
    softwareBitmap = await SoftwareBitmap.CreateCopyFromSurfaceAsync(surface);
}

Converter um SoftwareBitmap em um formato de pixel diferenteConvert a SoftwareBitmap to a different pixel format

A classe SoftwareBitmap fornece o método estático Convert que permite criar facilmente um novo SoftwareBitmap que usa o formato de pixel e o modo alfa especificados de um SoftwareBitmap existente.The SoftwareBitmap class provides the static method, Convert, that allows you to easily create a new SoftwareBitmap that uses the pixel format and alpha mode you specify from an existing SoftwareBitmap. Observe que o bitmap recém-criado tem uma cópia separada dos dados de imagem.Note that the newly created bitmap has a separate copy of the image data. As modificações no novo bitmap não afetarão o bitmap de origem.Modifications to the new bitmap will not affect the source bitmap.

SoftwareBitmap bitmapBGRA8 = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);

Transcodificar um arquivo de imagemTranscode an image file

Você pode transcodificar um arquivo de imagem diretamente de um BitmapDecoder para um BitmapEncoder.You can transcode an image file directly from a BitmapDecoder to a BitmapEncoder. Crie um IRandomAccessStream do arquivo a ser transcodificado.Create a IRandomAccessStream from the file to be transcoded. Crie um novo BitmapDecoder do fluxo de entrada.Create a new BitmapDecoder from the input stream. Crie um novo InMemoryRandomAccessStream para o codificador a ser gravado e chame BitmapEncoder.CreateForTranscodingAsync passando o fluxo de memória e o objeto de decodificador.Create a new InMemoryRandomAccessStream for the encoder to write to and call BitmapEncoder.CreateForTranscodingAsync, passing in the in-memory stream and the decoder object. As opções de codificação não são compatíveis na transcodificação; em vez disso, você deve usar CreateAsync.Encode options are not supported when transcoding; instead you should use CreateAsync. Todas as propriedades de arquivo de imagem de entrada que não são definidas especificamente no codificador são gravadas no arquivo de saída inalteradas.Any properties in the input image file that you do not specifically set on the encoder, will be written to the output file unchanged. Chame FlushAsync para fazer com que o codificador codifique o fluxo de memória.Call FlushAsync to cause the encoder to encode to the in-memory stream. Por fim, procure o fluxo do arquivo e o fluxo de memória no início e chame CopyAsync para gravar o fluxo de memória no fluxo de arquivo.Finally, seek the file stream and the in-memory stream to the beginning and call CopyAsync to write the in-memory stream out to the file stream.

private async void TranscodeImageFile(StorageFile imageFile)
{


    using (IRandomAccessStream fileStream = await imageFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);

        var memStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
        BitmapEncoder encoder = await BitmapEncoder.CreateForTranscodingAsync(memStream, decoder);

        encoder.BitmapTransform.ScaledWidth = 320;
        encoder.BitmapTransform.ScaledHeight = 240;

        await encoder.FlushAsync();

        memStream.Seek(0);
        fileStream.Seek(0);
        fileStream.Size = 0;
        await RandomAccessStream.CopyAsync(memStream, fileStream);

        memStream.Dispose();
    }
}