이미지 파일에 Direct2D 콘텐츠를 저장하는 방법

이 항목에서는 IWICImageEncoder 를 사용하여 ID2D1Image 형식의 콘텐츠를 JPEG와 같은 인코딩된 이미지 파일에 저장하는 방법을 보여 줍니다. Windows 스토어 앱을 작성하는 경우 사용자가 Windows::Storage::P ickers::FileSavePicker를 사용하여 대상 파일을 선택하게 할 수 있습니다.

알아야 하는 작업

기술

사전 요구 사항

지침

1단계: 대상 파일 및 스트림 가져오기

사용자가 대상 파일을 선택할 수 있도록 허용하려면 FileSavePicker를 사용하고, 반환된 파일을 열고, WIC와 함께 사용할 IStream 을 가져올 수 있습니다.

Windows::Storage::P ickers::FileSavePicker를 만들고 이미지 파일에 대한 매개 변수를 설정합니다. 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());

파일 선택 비동기 작업이 반환된 후 실행할 완료 처리기를 선언합니다. GetResults 메서드를 사용하여 파일을 검색하고 파일 스트림 개체를 가져옵니다.

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

파일에서 OpenAsync를 호출하고 비동기 작업의 결과를 가져와 IRandomAccessStream을 가져옵니다.

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

마지막으로 CreateStreamOverRandomAccessStream 메서드를 사용하여 파일 스트림을 변환합니다. Windows 런타임 API는 IRandomAccessStream을 사용하는 스트림을 나타내고 WIC는 IStream을 사용합니다.

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

참고

CreateStreamOverRandomAccessStream 함수를 사용하려면 프로젝트에 shcore.h를 포함해야 합니다.

 

2단계: WIC 비트맵 및 프레임 인코더 가져오기

IWICBitmapEncoderIWICBitmapFrameEncode는 이미징 데이터를 인코딩된 파일 형식으로 저장하는 기능을 제공합니다.

인코더 개체를 만들고 초기화합니다.

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

3단계: IWICImageEncoder 가져오기

IWICImageEncoder는 Windows 8 새로운 인터페이스입니다. IWICImagingFactory를 확장하고 Windows 8 새로운 IWICImagingFactory2에서 만들 수 있습니다.

ComPtr<IWICImagingFactory2> m_wicFactory;

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

IWICImagingFactory2::CreateImageEncoder를 호출합니다. 첫 번째 매개 변수는 ID2D1Device 이며 인코딩하려는 이미지가 만들어진 디바이스여야 합니다. 단일 IWICImageEncoder 내에서 다른 리소스 도메인의 이미지를 혼합할 수 없습니다.

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

4단계: IWICImageEncoder를 사용하여 Direct2D 콘텐츠 작성

IWICImageEncoderDirect2D 이미지를 이미지 프레임, 프레임 축소판 그림 또는 컨테이너 축소판 그림에 쓸 수 있습니다. 그런 다음 IWICBitmapEncoderIWICBitmapFrameEncode 를 사용하여 이미징 데이터를 정상적으로 파일에 인코딩할 수 있습니다.

Direct2D 이미지를 프레임에 씁니다. 이 코드 조각에서는 래스터화된 Direct2D 콘텐츠가 포함된 ID2D1Bitmap 을 작성합니다. 그러나 ID2D1Image를 구현하는 모든 인터페이스를 제공할 수 있습니다.

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

참고

ID2D1Image 매개 변수는 IWICImagingFactory2::CreateImageEncoder에 전달된 ID2D1Device에 만들어졌어야 합니다.

 

WIC 및 스트림 리소스를 커밋하여 작업을 완료합니다.

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

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

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

참고

IStream의 일부 구현에서는 Commit 메서드를 구현하지 않고 E_NOTIMPL 반환합니다.

 

이제 Direct2D 이미지를 포함하는 파일이 있습니다.

전체 예제

다음은 이 예제의 전체 코드입니다.

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