Como salvar conteúdo Direct2D em um arquivo de imagem
Este tópico mostra como usar IWICImageEncoder para salvar conteúdo na forma de um ID2D1Image em um arquivo de imagem codificado, como JPEG. Se estiver a escrever uma aplicação da Loja Windows, pode pedir ao utilizador que selecione um ficheiro de destino utilizando Windows::Storage::P ickers::FileSavePicker.
O que precisa de saber
Tecnologias
Pré-requisitos
- Você precisa de um objeto ID2D1DeviceContext e um objeto que contenha conteúdo Direct2D que implemente ID2D1Image, como ID2D1Effect ou ID2D1Bitmap1.
Instruções
Etapa 1: Obter um arquivo de destino e transmitir
Se quiser permitir que o usuário selecione um arquivo de destino, você pode usar FileSavePicker, abrir o arquivo retornado e obter um IStream para usar com o WIC.
Crie um Windows::Storage::Pickers::FileSavePicker e configure os 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 arquivos.
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 ficheiro e obtendo o resultado da operação assíncrona.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
Finalmente, use o CreateStreamOverRandomAccessStream método para converter o fluxo de arquivos. As APIs do Tempo de Execução do Windows 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 , deve incluir shcore.h no seu projeto.
Etapa 2: Obter o bitmap WIC e o codificador de frames
IWICBitmapEncoder e IWICBitmapFrameEncode fornecem a funcionalidade para guardar dados de imagem em um formato de ficheiro 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 no Windows 8.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
Chamar IWICImagingFactory2::CreateImageEncoder. O primeiro parâmetro é um ID2D1Device e deve ser o dispositivo no qual a imagem que você deseja codificar foi criada – não é possível misturar imagens de diferentes domínios de recursos em um único IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
Etapa 4: Escrever o conteúdo Direct2D usando IWICImageEncoder
IWICImageEncoder pode gravar uma imagem Direct2D em um quadro de imagem, uma miniatura de quadro ou a miniatura do contêiner. Em seguida, poderá usar IWICBitmapEncoder e IWICBitmapFrameEncode para codificar os dados de imagem normalmente para um ficheiro.
Escreva a imagem Direct2D para o quadro. Neste trecho, escrevemos um ID2D1Bitmap que contém conteúdo 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 o IWICImagingFactory2::CreateImageEncoder.
Aloque o WIC e os recursos de transmissão 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)
);
Observação
Algumas implementações de IStream não implementam o método Commit e retornam E_NOTIMPL.
Agora tens 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)
);
}