Click here to Skip to main content
15,885,767 members
Articles / Multimedia / DirectX

SurfaceImageSource Manager: Connecting C# and DirectX/Direct2D using the WinRT/Metro SurfaceImageSource class, through a small C++ component

Rate me:
Please Sign up or sign in to vote.
4.88/5 (6 votes)
21 Mar 2012CPOL2 min read 44.1K   1.3K   14  
SurfaceImageSource Manager is a C++ WinRT component making it easy to produce ready-to-use SurfaceImageSource instances, and then use DirectX or Direct2D to define their aspect in C# Metro applications.
#include "pch.h"
#include "KSurfaceImageSourceManager.h"

using namespace SurfaceImageSourceManager;
using namespace Platform;

KSurfaceImageSourceManager::KSurfaceImageSourceManager()
{
    // This flag adds support for surfaces with a different color channel ordering than the API default.
    // It is recommended usage, and is required for compatibility with Direct2D.
    UINT creationFlags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if defined(_DEBUG)
    // If the project is in a debug build, enable debugging via SDK Layers with this flag.
    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.
    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 D3D11 API device object, and get a corresponding context.
    ComPtr<ID3D11DeviceContext> d3dContext;
    DX::ThrowIfFailed(
        D3D11CreateDevice(
            nullptr,                    // specify null to use the default adapter
            D3D_DRIVER_TYPE_HARDWARE,
            0,                          // leave as 0 unless software device
            creationFlags,              // optionally set debug and Direct2D compatibility flags
            _featureLevels,              // list of feature levels this app can support
            ARRAYSIZE(_featureLevels),   // number of entries in above list
            D3D11_SDK_VERSION,          // always set this to D3D11_SDK_VERSION for Metro style apps
            &m_pID3D11Device,                 // returns the Direct3D device created
            &m_featureLevel,            // returns feature level of device created
            &m_d3dContext                 // returns the device immediate context
            )
        );
	m_d3dDevice = ComPtr<ID3D11Device>(m_pID3D11Device);

    // Obtain the underlying DXGI device of the Direct3D11.1 device.
    DX::ThrowIfFailed(m_d3dDevice.As(&m_dxgiDevice));

	// m_d2dFactory
	{
		D2D1_FACTORY_OPTIONS _options;
		ZeroMemory(&_options, sizeof(D2D1_FACTORY_OPTIONS));
		DX::ThrowIfFailed(D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory1), &_options, &m_d2dFactory));
	}

    // Obtain the Direct2D device for 2-D rendering.
    DX::ThrowIfFailed(m_d2dFactory->CreateDevice(m_dxgiDevice.Get(), &m_d2dDevice));

	// m_d2dDeviceContext
    DX::ThrowIfFailed(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dDeviceContext));

}

int KSurfaceImageSourceManager::Get_ID3D11Device()
{
	if (m_d3dDevice == nullptr) return 0;
	return (int)m_d3dDevice.Get();
}

int KSurfaceImageSourceManager::Get_ID3D11DeviceContext()
{
	if (m_d3dContext == nullptr) return 0;
	return (int)m_d3dContext.Get();
}

int KSurfaceImageSourceManager::Get_IDXGIDevice()
{
	if (m_dxgiDevice==nullptr) return 0;
	return (int)m_dxgiDevice.Get();
}

int KSurfaceImageSourceManager::Get_ID2D1Factory1()
{
	if (m_d2dFactory==nullptr) return 0;
	return (int)m_d2dFactory.Get();
}

int KSurfaceImageSourceManager::Get_ID2D1Device()
{
	if (m_d2dDevice==nullptr) return 0;
	return (int)m_d2dDevice.Get();
}

int KSurfaceImageSourceManager::Get_ID2D1DeviceContext()
{
	if (m_d2dDeviceContext==nullptr) return 0;
	return (int)m_d2dDeviceContext.Get();
}

SurfaceImageSource^ KSurfaceImageSourceManager::NewSurfaceImageSource(int pixelWidth, int pixelHeight)
{
	SurfaceImageSource^ vret = nullptr;

    // Native interface of the virtual surface image source
    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;

    // Create an image source at the initial pixel size.
    vret = ref new SurfaceImageSource(pixelWidth, pixelHeight, false);

    ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(vret));
    unknown.As(&_imageSourceNative);

	_imageSourceNative->SetDevice(m_dxgiDevice.Get());

	m_std_Id_Width.insert(std::pair<int,int>(vret->GetHashCode(), pixelWidth));
	m_std_Id_Height.insert(std::pair<int,int>(vret->GetHashCode(), pixelHeight));

	int _id = vret->GetHashCode();
	int _width = m_std_Id_Width.find(_id)->second;
	int _height = m_std_Id_Height.find(_id)->second;

	return vret;
}

void KSurfaceImageSourceManager::DeleteSurfaceImageSource(SurfaceImageSource^ pSurfaceImageSource)
{
	if (pSurfaceImageSource==nullptr) return;
	int _id = pSurfaceImageSource->GetHashCode();
	m_std_Id_Width.erase(_id);
	m_std_Id_Height.erase(_id);
}

int KSurfaceImageSourceManager::GetSurfaceImageSourceSize(SurfaceImageSource^ pSurfaceImageSource, int* pSurfaceWidth, int* pSurfaceHeight)
{
	if (pSurfaceWidth != NULL) *pSurfaceWidth = 0;
	if (pSurfaceHeight != NULL) *pSurfaceHeight = 0;

	if (pSurfaceImageSource == nullptr) return 1;
	if (pSurfaceWidth == NULL) return 2;
	if (pSurfaceHeight == NULL) return 3;

	int _id = pSurfaceImageSource->GetHashCode();

	std::map<int,int>::iterator _it = m_std_Id_Width.find(_id);
	if (_it != m_std_Id_Width.end())
	{
		*pSurfaceWidth = _it->second;
	}
	else
	{
		return 4;
	}

	_it = m_std_Id_Height.find(_id);
	if (_it != m_std_Id_Height.end())
	{
		*pSurfaceHeight = _it->second;
	}
	else
	{
		return 5;
	}

	return 0;
}

int KSurfaceImageSourceManager::ClearSurfaceImageSource(SurfaceImageSource^ pSurfaceImageSource, float R, float G, float B, float A)
{
	if (pSurfaceImageSource == nullptr) return 1;

	int _width = 0;
	int _height = 0;
	int _retcode = GetSurfaceImageSourceSize(pSurfaceImageSource, &_width, &_height);
	if (_retcode != 0) return 2;

    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;
    ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(pSurfaceImageSource));
    unknown.As(&_imageSourceNative);
	if (_imageSourceNative==nullptr) return 3;

	HRESULT hr;

	ComPtr<IDXGISurface> _dxgiSurface;
	POINT _surfaceOffset = {0};
	RECT _drawingBounds = {0, 0, _width, _height};
	// BeginDraw
	hr = _imageSourceNative->BeginDraw(_drawingBounds, &_dxgiSurface, &_surfaceOffset);
	if (FAILED(hr)) return 2;

		IDXGISurface* _pIDXGISurface = _dxgiSurface.Get();

		ComPtr<ID2D1Bitmap1> _bitmap;
		hr = m_d2dDeviceContext->CreateBitmapFromDxgiSurface(_pIDXGISurface, nullptr, &_bitmap);

		if (FAILED(hr)) return 4;

		// Begin the drawing batch
		m_d2dDeviceContext->BeginDraw();

			// Set the render target to surface given by the framework
			m_d2dDeviceContext->SetTarget(_bitmap.Get());

				//
				m_d2dDeviceContext->Clear(D2D1::ColorF(R, G, B, A));

			//
			m_d2dDeviceContext->SetTarget(nullptr);

		//
		m_d2dDeviceContext->EndDraw();

	// EndDraw
	hr = _imageSourceNative->EndDraw();
	if (FAILED(hr)) return 5;

	return 0;
}

int KSurfaceImageSourceManager::BeginDraw2D(SurfaceImageSource^ pSurfaceImageSource, int* pOffsetX, int* pOffsetY, int* pSurfaceWidth, int* pSurfaceHeight)
{
	if (pSurfaceImageSource == nullptr) return 0;
	if (pOffsetX==nullptr || pOffsetY==nullptr || pSurfaceWidth==nullptr || pSurfaceHeight==nullptr) return 0;

	*pOffsetX = *pOffsetY = *pSurfaceWidth = *pSurfaceHeight = 0;

	int _width = 0;
	int _height = 0;
	int _retcode = GetSurfaceImageSourceSize(pSurfaceImageSource, &_width, &_height);
	if (_retcode != 0) return 0;

    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;
	{
		ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(pSurfaceImageSource));
		unknown.As(&_imageSourceNative);
	}
	if (_imageSourceNative==nullptr) return 0;

	HRESULT hr;

	ComPtr<IDXGISurface> _dxgiSurface;
	{
		POINT _surfaceOffset = {0};
		RECT _drawingBounds = {0, 0, _width, _height};
		// BeginDraw -> _dxgiSurface
		hr = _imageSourceNative->BeginDraw(_drawingBounds, &_dxgiSurface, &_surfaceOffset);
		if (FAILED(hr)) return 0;
		*pOffsetX = _surfaceOffset.x;
		*pOffsetY = _surfaceOffset.y;
	}

	{
		DXGI_SURFACE_DESC _surfacedesc;
		ZeroMemory(&_surfacedesc, sizeof(DXGI_SURFACE_DESC));
		_dxgiSurface->GetDesc(&_surfacedesc);
		*pSurfaceWidth =_surfacedesc.Width;
		*pSurfaceHeight = _surfacedesc.Height;
	}

	IDXGISurface* _pIDXGISurface = _dxgiSurface.Get();
	if (_pIDXGISurface==nullptr) return 0;

	{
		// Create the DXGI Surface Render Target.
		FLOAT dpiX;
		FLOAT dpiY;
		m_d2dFactory->GetDesktopDpi(&dpiX, &dpiY);

		D2D1_RENDER_TARGET_PROPERTIES _props =
			D2D1::RenderTargetProperties(
				D2D1_RENDER_TARGET_TYPE_DEFAULT,
				D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
				dpiX,
				dpiY
				);

		hr = m_d2dFactory->CreateDxgiSurfaceRenderTarget(_pIDXGISurface, &_props, &m_pRenderTarget_Draw2D);
		if (FAILED(hr)) return 0;

		return (int)m_pRenderTarget_Draw2D.Get();
	}

}

void KSurfaceImageSourceManager::EndDraw2D(SurfaceImageSource^ pSurfaceImageSource)
{
	if (pSurfaceImageSource == nullptr) return;

    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;
    ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(pSurfaceImageSource));
    unknown.As(&_imageSourceNative);
	if (_imageSourceNative != nullptr)
	{
		HRESULT hr = _imageSourceNative->EndDraw();
	}

	m_pRenderTarget_Draw2D = nullptr;
}

int KSurfaceImageSourceManager::BeginDraw3D(SurfaceImageSource^ pSurfaceImageSource, int* pOffsetX, int* pOffsetY, int* pSurfaceWidth, int* pSurfaceHeight)
{
	if (pSurfaceImageSource == nullptr) return 0;
	if (pOffsetX==nullptr || pOffsetY==nullptr || pSurfaceWidth==nullptr || pSurfaceHeight==nullptr) return 0;

	*pOffsetX = *pOffsetY = *pSurfaceWidth = *pSurfaceHeight = 0;

	int _width = 0;
	int _height = 0;
	int _retcode = GetSurfaceImageSourceSize(pSurfaceImageSource, &_width, &_height);
	if (_retcode != 0) return 0;

    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;
	{
		ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(pSurfaceImageSource));
		unknown.As(&_imageSourceNative);
	}
	if (_imageSourceNative==nullptr) return 0;

	HRESULT hr;

	ComPtr<IDXGISurface> _dxgiSurface;
	{
		POINT _surfaceOffset = {0};
		RECT _drawingBounds = {0, 0, _width, _height};
		// BeginDraw -> _dxgiSurface
		hr = _imageSourceNative->BeginDraw(_drawingBounds, &_dxgiSurface, &_surfaceOffset);
		if (FAILED(hr)) return 0;
		*pOffsetX = _surfaceOffset.x;
		*pOffsetY = _surfaceOffset.y;
	}

	{
		DXGI_SURFACE_DESC _surfacedesc;
		ZeroMemory(&_surfacedesc, sizeof(DXGI_SURFACE_DESC));
		_dxgiSurface->GetDesc(&_surfacedesc);
		*pSurfaceWidth =_surfacedesc.Width;
		*pSurfaceHeight = _surfacedesc.Height;
	}

	IDXGISurface* _pIDXGISurface = _dxgiSurface.Get();
	if (_pIDXGISurface == nullptr) return 0;

	hr = _pIDXGISurface->QueryInterface(IID_ID3D11Texture2D, &m_pTexture2D_Draw3D);
	if (FAILED(hr)) return 0;

	return (int)m_pTexture2D_Draw3D.Get();
}

void KSurfaceImageSourceManager::EndDraw3D(SurfaceImageSource^ pSurfaceImageSource)
{
	if (pSurfaceImageSource == nullptr) return;

    ComPtr<ISurfaceImageSourceNative> _imageSourceNative;
    ComPtr<IUnknown> unknown(reinterpret_cast<IUnknown*>(pSurfaceImageSource));
    unknown.As(&_imageSourceNative);
	if (_imageSourceNative != nullptr)
	{
		HRESULT hr = _imageSourceNative->EndDraw();
	}

	m_pTexture2D_Draw3D = nullptr;

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
France France
I am a freelance software engineer living in Paris, France.

Comments and Discussions