Como salvar o conteúdo do Direct2D em um arquivo de imagem
Este tópico mostra como usar IWICImageEncoder para salvar conteúdo na forma de uma ID2D1Image em um arquivo de imagem codificado, como JPEG. Se você estiver escrevendo um aplicativo da Windows Store, poderá fazer com que o usuário selecione um arquivo de destino usando Windows::Storage::P ickers::FileSavePicker.
O que você precisa saber
Tecnologias
Pré-requisitos
- Você precisa de um objeto ID2D1DeviceContext e um objeto que contenha Direct2D conteúdo que implementa ID2D1Image, como ID2D1Effect ou ID2D1Bitmap1.
Instruções
Etapa 1: Obter um arquivo de destino e um fluxo
Se você quiser permitir que o usuário selecione um arquivo de destino, use FileSavePicker, abra o arquivo retornado e obtenha um IStream para usar com WIC.
Crie um Windows::Storage::P ickers::FileSavePicker e defina seus parâmetros para arquivos de imagem. Chame o método PickSaveFileAsync .
Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
pngExtensions->Append(".png");
savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
jpgExtensions->Append(".jpg");
savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
savePicker->DefaultFileExtension = ".jpg";
savePicker->SuggestedFileName = "SaveScreen";
savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
Declare um manipulador de conclusão a ser executado após o retorno da operação assíncrona do seletor de arquivos. Use o método GetResults para recuperar o arquivo e obter o objeto de fluxo de arquivo.
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
fileTask.then([=](StorageFile^ file) {
if (file != nullptr)
{
// User selects a file.
GUID wicFormat = GUID_ContainerFormatPng;
if (file->FileType == ".jpg")
{
wicFormat = GUID_ContainerFormatJpeg;
}
Obtenha um IRandomAccessStream chamando OpenAsync no arquivo e obtendo o resultado da operação assíncrona.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
Por fim, use o método CreateStreamOverRandomAccessStream para converter o fluxo de arquivos. Windows Runtime APIs representam fluxos com IRandomAccessStream, enquanto o WIC consome IStream.
ComPtr<IStream> stream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
);
Observação
Para usar a função CreateStreamOverRandomAccessStream , você deve incluir shcore.h em seu projeto.
Etapa 2: Obter o bitmap wic e o codificador de quadros
IWICBitmapEncoder e IWICBitmapFrameEncode fornecem a funcionalidade para salvar dados de geração de imagens em um formato de arquivo codificado.
Crie e inicialize os objetos do codificador.
ComPtr<IWICBitmapEncoder> wicBitmapEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateEncoder(
wicFormat,
nullptr, // No preferred codec vendor.
&wicBitmapEncoder
)
);
DX::ThrowIfFailed(
wicBitmapEncoder->Initialize(
stream,
WICBitmapEncoderNoCache
)
);
ComPtr<IWICBitmapFrameEncode> wicFrameEncode;
DX::ThrowIfFailed(
wicBitmapEncoder->CreateNewFrame(
&wicFrameEncode,
nullptr // No encoder options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Initialize(nullptr)
);
Etapa 3: Obter um IWICImageEncoder
IWICImageEncoder é uma nova interface no Windows 8. Ele pode ser criado a partir de IWICImagingFactory2, que estende IWICImagingFactory e também é novo para Windows 8.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
Chame IWICImagingFactory2::CreateImageEncoder. O primeiro parâmetro é um ID2D1Device e deve ser o dispositivo no qual a imagem que você deseja codificar foi criada – você não pode misturar imagens de domínios de recursos diferentes em um único IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
Etapa 4: Gravar o conteúdo Direct2D usando IWICImageEncoder
IWICImageEncoder pode gravar uma imagem Direct2D em um quadro de imagem, em uma miniatura de quadro ou na miniatura do contêiner. Em seguida, você pode usar IWICBitmapEncoder e IWICBitmapFrameEncode para codificar os dados de imagem em um arquivo normalmente.
Escreva a imagem Direct2D no quadro. Neste snippet, gravamos um ID2D1Bitmap que contém conteúdo de Direct2D rasterizado. No entanto, você pode fornecer qualquer interface que implemente ID2D1Image.
DX::ThrowIfFailed(
imageEncoder->WriteFrame(
d2dBitmap.Get(),
wicFrameEncode.Get(),
nullptr // Use default WICImageParameter options.
)
);
Observação
O parâmetro ID2D1Image deve ter sido criado no ID2D1Device que foi passado para IWICImagingFactory2::CreateImageEncoder.
Confirme o WIC e os recursos de fluxo para finalizar a operação.
DX::ThrowIfFailed(
wicFrameEncode->Commit()
);
DX::ThrowIfFailed(
wicBitmapEncoder->Commit()
);
// Flush all memory buffers to the next-level storage object.
DX::ThrowIfFailed(
stream->Commit(STGC_DEFAULT)
);
Agora você tem um arquivo que contém a imagem Direct2D.
Exemplo completo
Aqui o código completo para este exemplo.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
void SaveAsImageFile::SaveBitmapToFile()
{
// Prepare a file picker for customers to input image file name.
Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
auto pngExtensions = ref new Platform::Collections::Vector<Platform::String^>();
pngExtensions->Append(".png");
savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
jpgExtensions->Append(".jpg");
savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
auto bmpExtensions = ref new Platform::Collections::Vector<Platform::String^>();
bmpExtensions->Append(".bmp");
savePicker->FileTypeChoices->Insert("BMP file", bmpExtensions);
savePicker->DefaultFileExtension = ".png";
savePicker->SuggestedFileName = "SaveScreen";
savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;
task<StorageFile^> fileTask(savePicker->PickSaveFileAsync());
fileTask.then([=](StorageFile^ file) {
if (file != nullptr)
{
// User selects a file.
m_imageFileName = file->Name;
GUID wicFormat = GUID_ContainerFormatPng;
if (file->FileType == ".bmp")
{
wicFormat = GUID_ContainerFormatBmp;
}
else if (file->FileType == ".jpg")
{
wicFormat = GUID_ContainerFormatJpeg;
}
// Retrieve a stream from the file.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
// Convert the RandomAccessStream to an IStream.
ComPtr<IStream> stream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
);
SaveBitmapToStream(m_d2dTargetBitmap, m_wicFactory, m_d2dContext, wicFormat, stream.Get());
});
}
});
}
// Save render target bitmap to a stream using WIC.
void SaveAsImageFile::SaveBitmapToStream(
_In_ ComPtr<ID2D1Bitmap1> d2dBitmap,
_In_ ComPtr<IWICImagingFactory2> wicFactory2,
_In_ ComPtr<ID2D1DeviceContext> d2dContext,
_In_ REFGUID wicFormat,
_In_ IStream* stream
)
{
// Create and initialize WIC Bitmap Encoder.
ComPtr<IWICBitmapEncoder> wicBitmapEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateEncoder(
wicFormat,
nullptr, // No preferred codec vendor.
&wicBitmapEncoder
)
);
DX::ThrowIfFailed(
wicBitmapEncoder->Initialize(
stream,
WICBitmapEncoderNoCache
)
);
// Create and initialize WIC Frame Encoder.
ComPtr<IWICBitmapFrameEncode> wicFrameEncode;
DX::ThrowIfFailed(
wicBitmapEncoder->CreateNewFrame(
&wicFrameEncode,
nullptr // No encoder options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Initialize(nullptr)
);
// Retrieve D2D Device.
ComPtr<ID2D1Device> d2dDevice;
d2dContext->GetDevice(&d2dDevice);
// Create IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
DX::ThrowIfFailed(
imageEncoder->WriteFrame(
d2dBitmap.Get(),
wicFrameEncode.Get(),
nullptr // Use default WICImageParameter options.
)
);
DX::ThrowIfFailed(
wicFrameEncode->Commit()
);
DX::ThrowIfFailed(
wicBitmapEncoder->Commit()
);
// Flush all memory buffers to the next-level storage object.
DX::ThrowIfFailed(
stream->Commit(STGC_DEFAULT)
);
}