#include "pch.h"
#include "D2DBlurEffect.h"
using namespace Light::UI::Effects::Direct2D::BlurEffect;
using namespace Platform;
using namespace concurrency;
using namespace Microsoft::WRL;
using namespace Windows::ApplicationModel;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
using namespace Windows::Storage;
using namespace Windows::UI::Core;
// Initialize hardware-dependent resources.
void BlurEffectImageProcessor::CreateDeviceResources()
{
// This flag adds support for surfaces with a different color channel ordering
// than the API default. It is required for compatibility with Direct2D.
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#if defined(_DEBUG)
// If the project is in a debug build, enable debugging via SDK Layers.
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
// This array defines the set of DirectX hardware feature levels this app will support.
// Note the ordering should be preserved.
// Don't forget to declare your application's minimum required feature level in its
// description. All applications are assumed to support 9.1 unless otherwise stated.
const D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1,
};
// Create the Direct3D 11 API device object.
DX::ThrowIfFailed(
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
creationFlags, // Set debug and Direct2D compatibility flags.
featureLevels, // List of feature levels this app can support.
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
&m_d3dDevice, // Returns the Direct3D device created.
nullptr,
nullptr
)
);
// Get the Direct3D 11.1 API device.
ComPtr dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
// Create the Direct2D device object and a corresponding context.
DX::ThrowIfFailed(
D2D1CreateDevice(
dxgiDevice.Get(),
nullptr,
&m_d2dDevice
)
);
DX::ThrowIfFailed(
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
)
);
}
///
/// Internal method referred from Bing.
/// Convert IBuffer to IStream.
///
///The buffer to convert. IStream* createIStreamFromIBuffer(Streams::IBuffer ^buffer) {
// convert the IBuffer into an IStream to be used with WIC
IStream *fileContentsStream;
HRESULT res = CreateStreamOnHGlobal(NULL, TRUE, &fileContentsStream);
if (FAILED(res) || !fileContentsStream) {
throw ref new FailureException();
}
Streams::DataReader^ dataReader = Streams::DataReader::FromBuffer(buffer);
// read the data into the stream in chunks of 1MB to preserve memory
while (dataReader->UnconsumedBufferLength > 0) {
UINT chunkSize = min(1024 * 1024, dataReader->UnconsumedBufferLength);
auto data = ref new Platform::Array(chunkSize);
dataReader->ReadBytes(data);
ULONG written;
res = fileContentsStream->Write(data->Data, chunkSize, &written);
if (FAILED(res) || written != chunkSize) {
fileContentsStream->Release();
throw ref new FailureException();
}
}
return fileContentsStream;
}
BlurEffectImageProcessor::BlurEffectImageProcessor()
{
IsInitialized = false;
}
///
/// Render image but not get the final image.
/// REMEMBER call DataInitialize method first.
///
///Indicates the the blur amount. ///Indicates the current display's DPI. IAsyncAction^ BlurEffectImageProcessor::RenderImage(float gaussianBlurStDev, float DPI){
return create_async([this, gaussianBlurStDev,DPI]{
if (!IsInitialized){
throw ref new Platform::Exception(1, "The class has not initialized.");
}
// Render it
UINT imageWidth;
UINT imageHeight;
m_wicFormatConverter->GetSize(&imageWidth, &imageHeight);
// Create a Bitmap Source Effect.
DX::ThrowIfFailed(m_d2dContext->CreateEffect(CLSID_D2D1BitmapSource, &m_bitmapSourceEffect));
// Set the BitmapSource Property to the BitmapSource generated earlier.
DX::ThrowIfFailed(
m_bitmapSourceEffect->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, m_wicFormatConverter.Get())
);
// Create the Gaussian Blur Effect.
DX::ThrowIfFailed(m_d2dContext->CreateEffect(CLSID_D2D1GaussianBlur, &m_gaussianBlurEffect));
// Set the input to recieve the bitmap from the BitmapSourceEffect.
m_gaussianBlurEffect->SetInputEffect(0, m_bitmapSourceEffect.Get());
// Set the blur amount.
DX::ThrowIfFailed(m_gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, gaussianBlurStDev));
DX::ThrowIfFailed(m_gaussianBlurEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE, D2D1_BORDER_MODE_HARD));
// Begin drawing.
m_d2dContext->BeginDraw();
m_d2dContext->Clear(D2D1::ColorF(D2D1::ColorF::CornflowerBlue));
// Draw the scaled and blurred image.
m_d2dContext->DrawImage(m_gaussianBlurEffect.Get());
// We ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT hr = m_d2dContext->EndDraw();
if (hr != D2DERR_RECREATE_TARGET)
{
DX::ThrowIfFailed(hr);
}
});
}
///
/// Initializes all device resources and the image.
/// You need to call this method before doing other things.
///
IAsyncAction^ BlurEffectImageProcessor::DataInitialize(IRandomAccessStream^ ImageDataStream,float DPI){
// DirectXBase::Initialize(Window, DPI);
return create_async([this,ImageDataStream, DPI]{
// Initialize Devices
CreateDeviceResources();
DX::ThrowIfFailed(CoCreateInstance(
CLSID_WICImagingFactory1,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicImagingFactory)
)
);
DX::ThrowIfFailed(
CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&m_wicImagingFactory2)
)
);
// Now we have the image source and we can decode it.
ImageBuffer = ref new Buffer(ImageDataStream->Size);
auto op = create_task(ImageDataStream->ReadAsync(ImageBuffer, ImageDataStream->Size, InputStreamOptions::None)).then([this,DPI](IBuffer^ ImageBufferData){
DX::ThrowIfFailed(
m_wicImagingFactory2->CreateDecoderFromStream(createIStreamFromIBuffer(ImageBufferData), nullptr, WICDecodeMetadataCacheOnDemand,
&m_wicDecoder)
);
// Get data ready
DX::ThrowIfFailed(
m_wicDecoder->GetFrame(0, &m_wicFrameDecode)
);
DX::ThrowIfFailed(
m_wicImagingFactory2->CreateFormatConverter(&m_wicFormatConverter)
);
DX::ThrowIfFailed(
m_wicFormatConverter->Initialize(
m_wicFrameDecode.Get(),
GUID_WICPixelFormat32bppBGRA,
WICBitmapDitherTypeNone,
nullptr,
0.0f,
WICBitmapPaletteTypeCustom
)
);
// Create output bitmap & get it ready
UINT Width;
UINT Height;
m_wicFrameDecode->GetSize(&Width, &Height);
m_wicImagingFactory2->CreateBitmap(Width, Height, GUID_WICPixelFormat32bppBGRA, WICBitmapCreateCacheOption::WICBitmapCacheOnDemand, &m_wicBitmap);
D2D1_SIZE_U bitmapSize = D2D1::SizeU(Width, Height);
D2D1_PIXEL_FORMAT bitmapPixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);
D2D1_BITMAP_PROPERTIES1 bitmapProp1 = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,bitmapPixelFormat, DPI, DPI);
m_d2dContext->CreateBitmap(
D2D1::SizeU(Width, Height),
nullptr,
Width * 4, // 4 bytes for B8G8R8A8
bitmapProp1,
&m_d2dBitmap1
);
m_d2dContext->SetTarget(m_d2dBitmap1.Get());
IsInitialized = true;
return;
});
op.wait();
});
}
///
/// Get the final image.
/// REMEMBER call DataInitialize method first.
/// You can call this method before calling RenderImage, but you will get the original image.
///
///Indicates the current display's DPI. IAsyncOperation<IRandomAccessStream^>^ BlurEffectImageProcessor::GetImageAsBitmap(float DPI){
return create_async([this,DPI]{
if (!IsInitialized){
throw ref new Platform::Exception(1, "The class has not initialized.");
}
// Render the bitmap use WIC.
ComPtr m_iwicBitmap;
ComPtr m_iwicStream;
ComPtr m_iwicBitmapEncoder;
ComPtr m_iwicBitmapFrameEncode;
ComPtr m_iwicImageEncoder;
WICImageParameters* m_imageparm = new WICImageParameters();
D2D1_PIXEL_FORMAT m_pixel_format = D2D1_PIXEL_FORMAT();
ComPtr m_iStream;
ID2D1Image* m_id2d1image;
UINT height;
UINT width;
// Since we can't create IStream directly in Windows Runtime, we need creating InMemoryRandomAccessStream and convert it
IRandomAccessStream^ data = ref new InMemoryRandomAccessStream();
DX::ThrowIfFailed(
CreateStreamOverRandomAccessStream(data, IID_PPV_ARGS(&m_iStream))
);
// Get size, we need it later
DX::ThrowIfFailed(
m_wicFrameDecode->GetSize(&width, &height)
);
// Create bitmap
DX::ThrowIfFailed(
m_wicImagingFactory2->CreateBitmap(width, height, GUID_WICPixelFormat32bppBGRA, WICBitmapCreateCacheOption::WICBitmapCacheOnDemand, &m_iwicBitmap)
);
// Create WIC Stream
DX::ThrowIfFailed(
m_wicImagingFactory->CreateStream(&m_iwicStream)
);
// Initialize WIC Stream from IStream that we converted
DX::ThrowIfFailed(
m_iwicStream->InitializeFromIStream(m_iStream.Get())
);
// Create encoder
DX::ThrowIfFailed(
m_wicImagingFactory2->CreateEncoder(GUID_ContainerFormatPng, nullptr, &m_iwicBitmapEncoder)
);
// Create image encoder
DX::ThrowIfFailed(
m_wicImagingFactory2->CreateImageEncoder(m_d2dDevice.Get(), &m_iwicImageEncoder)
);
// Initialize
DX::ThrowIfFailed(
m_iwicBitmapEncoder->Initialize(m_iwicStream.Get(), WICBitmapEncoderCacheOption::WICBitmapEncoderNoCache)
);
// Create new frame for the bitmap
DX::ThrowIfFailed(
m_iwicBitmapEncoder->CreateNewFrame(&m_iwicBitmapFrameEncode,nullptr)
);
// Set properties
m_iwicBitmapFrameEncode->Initialize(nullptr);
m_iwicBitmapFrameEncode->SetSize(width, height);
WICPixelFormatGUID format = GUID_WICPixelFormat32bppBGRA;
m_iwicBitmapFrameEncode->SetPixelFormat(&format);
m_d2dContext->GetTarget(&m_id2d1image);
m_imageparm->DpiX = DPI;
m_imageparm->DpiY = DPI;
m_pixel_format.alphaMode = D2D1_ALPHA_MODE_IGNORE;
m_pixel_format.format = DXGI_FORMAT_B8G8R8A8_UNORM;
m_imageparm->PixelFormat = m_pixel_format;
m_imageparm->PixelHeight = height;
m_imageparm->PixelWidth = width;
// Write frmae
DX::ThrowIfFailed(
m_iwicImageEncoder->WriteFrame(m_id2d1image, m_iwicBitmapFrameEncode.Get(), m_imageparm)
);
// Commit
DX::ThrowIfFailed(
m_iwicBitmapFrameEncode->Commit()
);
DX::ThrowIfFailed(
m_iwicBitmapEncoder->Commit()
);
// Now we successfully got the image
// Convert it to stream.
// Reference: MSDN
Windows::Storage::Streams::IRandomAccessStream^ comRAS;
IUnknown* p11 = reinterpret_cast(comRAS);
static const GUID guidIRandomAccessStream =
{ 0x905a0fe1, 0xbc53, 0x11df, { 0x8c, 0x49, 0x00, 0x1e, 0x4f, 0xc6, 0x86, 0xda } };
DX::ThrowIfFailed(
CreateRandomAccessStreamOverStream(m_iwicStream.Get(), BSOS_DEFAULT, guidIRandomAccessStream, (void**)&p11)
);
// Return result
return reinterpret_cast<IRandomAccessStream^>(p11);
});
}