Speichern von Direct2D-Inhalten in einer Bilddatei

In diesem Thema wird gezeigt, wie Sie mit IWICImageEncoder Inhalte in Form eines ID2D1Image in einer codierten Bilddatei wie JPEG speichern. Wenn Sie eine Windows Store-App schreiben, können Sie den Benutzer mithilfe von Windows::Storage::P ickers::FileSavePicker eine Zieldatei auswählen lassen.

Wichtige Informationen

Technologien

Voraussetzungen

Anweisungen

Schritt 1: Abrufen einer Zieldatei und eines Datenstroms

Wenn Sie dem Benutzer erlauben möchten, eine Zieldatei auszuwählen, können Sie FileSavePicker verwenden, die zurückgegebene Datei öffnen und einen IStream abrufen, der mit WIC verwendet werden kann.

Erstellen Sie einen Windows::Storage::P ickers::FileSavePicker , und legen Sie die Parameter für Bilddateien fest. Rufen Sie die PickSaveFileAsync-Methode auf.

    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());

Deklarieren Sie einen Vervollständigungshandler, der ausgeführt werden soll, nachdem der asynchrone Vorgang der Dateiauswahl zurückgegeben wird. Verwenden Sie die GetResults-Methode , um die Datei abzurufen und das Dateistreamobjekt abzurufen.

    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;
            }

Rufen Sie einen IRandomAccessStream ab, indem Sie OpenAsync für die Datei aufrufen und das Ergebnis des asynchronen Vorgangs abrufen.

    task<Streams::IRandomAccessStream^> createStreamTask(file->OpenAsync(FileAccessMode::ReadWrite));
    createStreamTask.then([=](Streams::IRandomAccessStream^ randomAccessStream) {

Verwenden Sie schließlich die CreateStreamOverRandomAccessStream-Methode , um den Dateistream zu konvertieren. Windows-Runtime-APIs stellen Streams mit IRandomAccessStream dar, während WIC IStream nutzt.

    ComPtr<IStream> stream;
    DX::ThrowIfFailed(
        CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
        );

Hinweis

Um die CreateStreamOverRandomAccessStream-Funktion zu verwenden, sollten Sie shcore.h in Ihr Projekt einschließen.

 

Schritt 2: Abrufen der WIC-Bitmap und des Frame-Encoders

IWICBitmapEncoder und IWICBitmapFrameEncode bieten die Funktionalität zum Speichern von Bilddaten in einem codierten Dateiformat.

Erstellen und initialisieren Sie die Encoderobjekte.

    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)
        );

Schritt 3: Abrufen eines IWICImageEncoders

IWICImageEncoder ist eine neue Schnittstelle in Windows 8. Es kann aus IWICImagingFactory2 erstellt werden, die IWICImagingFactory erweitert und auch für Windows 8 neu ist.

ComPtr<IWICImagingFactory2> m_wicFactory;

DX::ThrowIfFailed(
    CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&m_wicFactory)
        )
    );

Rufen Sie IWICImagingFactory2::CreateImageEncoder auf. Der erste Parameter ist ein ID2D1Device und muss das Gerät sein, auf dem das Image erstellt wurde, das Sie codieren möchten. Sie können keine Bilder aus verschiedenen Ressourcendomänen in einem einzelnen IWICImageEncoder mischen.

    ComPtr<IWICImageEncoder> imageEncoder;
    DX::ThrowIfFailed(
        wicFactory2->CreateImageEncoder(
            d2dDevice.Get(),
            &imageEncoder
            )
       );

Schritt 4: Schreiben des Direct2D-Inhalts mithilfe von IWICImageEncoder

IWICImageEncoder kann ein Direct2D-Bild in einen Bildrahmen, eine Frameminiaturansicht oder die Containerminiatur schreiben. Anschließend können Sie IWICBitmapEncoder und IWICBitmapFrameEncode verwenden, um die Bilddaten wie gewohnt in eine Datei zu codieren.

Schreiben Sie das Direct2D-Bild in den Frame. In diesem Codeausschnitt schreiben wir eine ID2D1Bitmap , die gerasterte Direct2D-Inhalte enthält. Sie können jedoch eine beliebige Schnittstelle bereitstellen, die ID2D1Image implementiert.

    DX::ThrowIfFailed(
        imageEncoder->WriteFrame(
            d2dBitmap.Get(),
            wicFrameEncode.Get(),
            nullptr     // Use default WICImageParameter options.
            )
        );

Hinweis

Der ID2D1Image-Parameter muss für das ID2D1Device erstellt worden sein, das an IWICImagingFactory2::CreateImageEncoder übergeben wurde.

 

Committen der WIC- und Streamressourcen, um den Vorgang abzuschließen.

    DX::ThrowIfFailed(
        wicFrameEncode->Commit()
        );

    DX::ThrowIfFailed(
        wicBitmapEncoder->Commit()
        );

    // Flush all memory buffers to the next-level storage object.
    DX::ThrowIfFailed(
        stream->Commit(STGC_DEFAULT)
        );

Hinweis

Einige Implementierungen von IStream implementieren die Commit-Methode nicht und geben E_NOTIMPL zurück.

 

Jetzt verfügen Sie über eine Datei, die das Direct2D-Image enthält.

Vollständiges Beispiel

Hier der vollständige Code für dieses Beispiel.

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)
        );
}