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 encodé comme JPEG. si vous écrivez une application Windows Store, vous pouvez demander à l’utilisateur de sélectionner un fichier de destination à l’aide de Windows :: Stockage ::P ickers :: FileSavePicker.

Bon à savoir

Technologies

Prérequis

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 :: Stockage ::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 Async 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 fichier. Windows Les API de Runtime 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’acquisition d’images dans un format de fichier encodé.

Créez et initialisez les objets de l’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 même 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 frame d’image, une miniature de cadre ou la miniature de conteneur. Vous pouvez ensuite utiliser IWICBitmapEncoder et IWICBitmapFrameEncode pour encoder les données d’acquisition d’images dans un fichier comme d’habitude.

Écrivez l’image Direct2D dans le frame. Dans cet extrait de code, nous écrivons un ID2D1Bitmap qui contient le contenu Direct2D pixellisé. Toutefois, vous pouvez fournir une 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 le ID2D1Device qui a été passé dans IWICImagingFactory2 :: CreateImageEncoder.

Validez le WIC et les ressources de flux 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 de IStream n’implémentent pas la méthode Commit et retournent E _ NOTIMPL.

Vous avez maintenant un fichier qui contient 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)
        );
}