이미지 파일에 Direct2D 콘텐츠를 저장하는 방법
이 항목에서는 IWICImageEncoder 를 사용하여 ID2D1Image 형식의 콘텐츠를 JPEG와 같은 인코딩된 이미지 파일에 저장하는 방법을 보여 줍니다. Windows 스토어 앱을 작성하는 경우 사용자가 Windows::Storage::P ickers::FileSavePicker를 사용하여 대상 파일을 선택하게 할 수 있습니다.
알아야 하는 작업
기술
사전 요구 사항
- ID2D1DeviceContext 개체와 ID2D1Effect 또는 ID2D1Bitmap1과 같은 ID2D1Image를 구현하는 Direct2D 콘텐츠가 포함된 개체가 필요합니다.
지침
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 비트맵 및 프레임 인코더 가져오기
IWICBitmapEncoder 및 IWICBitmapFrameEncode는 이미징 데이터를 인코딩된 파일 형식으로 저장하는 기능을 제공합니다.
인코더 개체를 만들고 초기화합니다.
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 콘텐츠 작성
IWICImageEncoder 는 Direct2D 이미지를 이미지 프레임, 프레임 축소판 그림 또는 컨테이너 축소판 그림에 쓸 수 있습니다. 그런 다음 IWICBitmapEncoder 및 IWICBitmapFrameEncode 를 사용하여 이미징 데이터를 정상적으로 파일에 인코딩할 수 있습니다.
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)
);
이제 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)
);
}