How to load an image into Direct2D effects using the FilePicker
Shows how to use the Windows::Storage::Pickers::FileOpenPicker to load an image into Direct2D effects. If you want to let the user select an image file from storage in a Windows Store app, we recommend that you use the FileOpenPicker.
What you need to know
Technologies
Prerequisites
- You need an ID2D1DeviceContext object for creating effects.
- You need an IWICImagingFactory object for creating WIC objects.
Instructions
Step 1: Open the file picker
Create a FileOpenPicker object and set the ViewMode, SuggestedStartLocation, and the FileTypeFilter for selecting images. Call the PickSingleFileAsync method.
FileOpenPicker^ openPicker = ref new FileOpenPicker();
openPicker->ViewMode = PickerViewMode::Thumbnail;
openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;
openPicker->FileTypeFilter->Append(".jpg");
auto pickOperation = openPicker->PickSingleFileAsync();
After the PickSingleFileAsync completes, you get a file stream from the IAsyncOperation interface it returns.
Step 2: Get a file stream
Declare a completion handler to run after the file picker async operation returns. Use the GetResults method to retrieve the file and to get the file stream object.
pickOperation->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>(
[=](IAsyncOperation<StorageFile^> ^operation, AsyncStatus status)
{
auto file = operation->GetResults();
if (file) // If file == nullptr, the user did not select a file.
{
auto openOperation = file->OpenAsync(FileAccessMode::Read);
openOperation->Completed = ref new
AsyncOperationCompletedHandler<IRandomAccessStream^>(
[=](IAsyncOperation<IRandomAccessStream^> ^operation, AsyncStatus status)
{
auto fileStream = operation->GetResults();
// Pass IRandomAccessStream^ into DirectXApp for decoding/processing.
OpenFile(fileStream);
});
}
});
In the next step you convert the IRandomAccessStream object to an IStream that you can pass to WIC.
Step 3: Convert the file stream
Use the CreateStreamOverRandomAccessStream function to convert the file stream. Windows Runtime APIs represent streams with IRandomAccessStream, while WIC consumes IStream.
ComPtr<IStream> istream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(
reinterpret_cast<IUnknown*>(fileStream),
IID_PPV_ARGS(&istream)
)
);
Note
To use the CreateStreamOverRandomAccessStream function, you should include shcore.h in your project.
Step 4: Create a WIC decoder and get the frame
Create an IWICBitmapDecoder object using the IWICImagingFactory::CreateDecoderFromStream method.
ComPtr<IWICBitmapDecoder> decoder;
DX::ThrowIfFailed(
m_wicFactory->CreateDecoderFromStream(
istream.Get(),
nullptr,
WICDecodeMetadataCacheOnDemand,
&decoder
)
);
Get the first frame of the image from the decoder using the IWICBitmapDecoder::GetFrame method.
ComPtr<IWICBitmapFrameDecode> frame;
DX::ThrowIfFailed(
decoder->GetFrame(0, &frame)
);
Step 5: Create a WIC converter and initialize
Convert the image to the BGRA color format using WIC. IWICBitmapFrameDecode will return the native pixel format of the image, like JPEGs are stored in GUID_WICPixelFormat24bppBGR. However, as a performance optimization with Direct2D we recommend that you convert to WICPixelFormat32bppPBGRA.
Create a IWICFormatConverter object using the IWICImagingFactory::CreateFormatConverter method.
ComPtr<IWICFormatConverter> converter; DX::ThrowIfFailed( m_wicFactory->CreateFormatConverter(&converter) );
Initialize the format converter to use the WICPixelFormat32bppPBGRA and pass in the bitmap frame.
DX::ThrowIfFailed( converter->Initialize( frame.Get(), GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom // premultiplied BGRA has no paletting, so this is ignored ) );
The IWICFormatConverter interface is derived from the IWICBitmapSource interface, so you can pass the converter to the bitmap source effect.
Step 6: Create effect and pass in an IWICBitmapSource
Use the CreateEffect method to create a bitmap source ID2D1Effect object using the Direct2D device context.
Use the ID2D1Effect::SetValue method to set the D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE property to the WIC format converter.
Note
The bitmap source effect doesn't take an input from the SetInput method like many Direct2D effects. Instead, the IWICBitmapSource object is specified as a property.
ComPtr<ID2D1Effect> bitmapSourceEffect;
DX::ThrowIfFailed(
m_d2dContext->CreateEffect(CLSID_D2D1BitmapSource, &bitmapSourceEffect)
);
DX::ThrowIfFailed(
bitmapSourceEffect->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, converter.Get())
);
// Insert code using the bitmap source in an effect graph.
Now that you have the bitmap source effect, you can use it as input to any ID2D1Effect and create an effect graph.
Complete example
Here is the full code for this example.
ComPtr<ID2D1Effect> bitmapSourceEffect;
void OpenFilePicker()
{
FileOpenPicker^ openPicker = ref new FileOpenPicker();
openPicker->ViewMode = PickerViewMode::Thumbnail;
openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;
openPicker->FileTypeFilter->Append(".jpg");
auto pickOperation = openPicker->PickSingleFileAsync();
pickOperation->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>(
[=](IAsyncOperation<StorageFile^> ^operation, AsyncStatus status)
{
auto file = operation->GetResults();
if (file)
{
auto openOperation = file->OpenAsync(FileAccessMode::Read);
openOperation->Completed = ref new
AsyncOperationCompletedHandler<IRandomAccessStream^>(
[=](IAsyncOperation<IRandomAccessStream^> ^operation, AsyncStatus status)
{
auto fileStream = operation->GetResults();
// Pass IRandomAccessStream^ into DirectXApp for decoding/processing.
OpenFile(fileStream);
});
}
});
}
void OpenFile(Windows::Storage::Streams::IRandomAccessStream^ fileStream)
{
ComPtr<IStream> istream;
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(
reinterpret_cast<IUnknown*>(fileStream),
IID_PPV_ARGS(&istream)
)
);
ComPtr<IWICBitmapDecoder> decoder;
DX::ThrowIfFailed(
m_wicFactory->CreateDecoderFromStream(
istream.Get(),
nullptr,
WICDecodeMetadataCacheOnDemand,
&decoder
)
);
ComPtr<IWICBitmapFrameDecode> frame;
DX::ThrowIfFailed(
decoder->GetFrame(0, &frame)
);
ComPtr<IWICFormatConverter> converter;
DX::ThrowIfFailed(
m_wicFactory->CreateFormatConverter(&converter)
);
DX::ThrowIfFailed(
converter->Initialize(
frame.Get(),
GUID_WICPixelFormat32bppPBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom // premultiplied BGRA has no paletting, so this is ignored
)
);
ComPtr<ID2D1Effect> bitmapSourceEffect;
DX::ThrowIfFailed(
m_d2dContext->CreateEffect(CLSID_D2D1BitmapSource, &bitmapSourceEffect)
);
DX::ThrowIfFailed(
bitmapSourceEffect->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, converter.Get())
);
// Insert code using the bitmap source in an effect graph.
}