Guide pratique pour enregistrer le contenu Direct2D dans un fichier image
Cette rubrique montre comment utiliser IWICImageEncoder pour enregistrer du contenu sous la forme d’un ID2D1Image dans un fichier image codé tel que JPEG. Si vous écrivez une application du Windows Store, l’utilisateur peut sélectionner un fichier de destination à l’aide de Windows::Storage::P ickers::FileSavePicker.
Bon à savoir
Technologies
Prérequis
- Vous avez besoin d’un objet ID2D1DeviceContext et d’un objet contenant du contenu Direct2D qui implémente ID2D1Image tel que ID2D1Effect ou ID2D1Bitmap1.
Instructions
Étape 1 : Obtenir un fichier de destination et un flux
Si vous souhaitez autoriser l’utilisateur à sélectionner un fichier de destination, vous pouvez utiliser FileSavePicker, ouvrir le fichier retourné et obtenir un IStream à utiliser avec WIC.
Créez un windows::Storage::P ickers::FileSavePicker et définissez ses paramètres pour les fichiers image. Appelez la méthode 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());
Déclarez un gestionnaire d’achèvement à exécuter après le retour de l’opération asynchrone du sélecteur de fichiers. Utilisez la méthode GetResults pour récupérer le fichier et obtenir l’objet de flux de fichier.
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;
}
Obtenez un IRandomAccessStream en appelant OpenAsync sur le fichier et en obtenant le résultat de l’opération asynchrone.
task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {
Enfin, utilisez la méthode CreateStreamOverRandomAccessStream pour convertir le flux de fichiers. Windows Runtime API représentent des flux avec IRandomAccessStream, tandis que WIC consomme IStream.
ComPtr<IStream> stream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
);
Notes
Pour utiliser la fonction CreateStreamOverRandomAccessStream , vous devez inclure shcore.h dans votre projet.
Étape 2 : Obtenir la bitmap WIC et l’encodeur de trame
IWICBitmapEncoder et IWICBitmapFrameEncode fournissent les fonctionnalités permettant d’enregistrer les données d’imagerie dans un format de fichier encodé.
Créez et initialisez les objets encodeur.
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)
);
Étape 3 : Obtenir un IWICImageEncoder
IWICImageEncoder est une nouvelle interface dans Windows 8. Il peut être créé à partir de IWICImagingFactory2, qui étend IWICImagingFactory et est également nouveau pour Windows 8.
ComPtr<IWICImagingFactory2> m_wicFactory;
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicFactory)
)
);
Appelez IWICImagingFactory2::CreateImageEncoder. Le premier paramètre est un ID2D1Device et doit être l’appareil sur lequel l’image que vous souhaitez encoder a été créée. Vous ne pouvez pas mélanger des images provenant de différents domaines de ressources au sein d’un seul IWICImageEncoder.
ComPtr<IWICImageEncoder> imageEncoder;
DX::ThrowIfFailed(
wicFactory2->CreateImageEncoder(
d2dDevice.Get(),
&imageEncoder
)
);
Étape 4 : Écrire le contenu Direct2D à l’aide de IWICImageEncoder
IWICImageEncoder peut écrire une image Direct2D dans un cadre d’image, une miniature de cadre ou la miniature du conteneur. Vous pouvez ensuite utiliser IWICBitmapEncoder et IWICBitmapFrameEncode pour encoder normalement les données d’imagerie dans un fichier.
Écrivez l’image Direct2D dans le cadre. Dans cet extrait de code, nous écrivons un ID2D1Bitmap qui contient du contenu Direct2D rastérisé. Toutefois, vous pouvez fournir n’importe quelle interface qui implémente ID2D1Image.
DX::ThrowIfFailed(
imageEncoder->WriteFrame(
d2dBitmap.Get(),
wicFrameEncode.Get(),
nullptr // Use default WICImageParameter options.
)
);
Notes
Le paramètre ID2D1Image doit avoir été créé sur l’id2D1Device qui a été passé à IWICImagingFactory2::CreateImageEncoder.
Validez le WIC et streamez les ressources pour finaliser l’opération.
DX::ThrowIfFailed(
wicFrameEncode->Commit()
);
DX::ThrowIfFailed(
wicBitmapEncoder->Commit()
);
// Flush all memory buffers to the next-level storage object.
DX::ThrowIfFailed(
stream->Commit(STGC_DEFAULT)
);
Notes
Certaines implémentations d’IStream n’implémentent pas la méthode Commit et retournent E_NOTIMPL.
Vous disposez maintenant d’un fichier contenant l’image Direct2D .
Exemple complet
Voici le code complet de cet exemple.
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)
);
}