locked
DirectWrite in XAML-DirectX interop

    Question

  • I am trying to create the simplest possible hello world application, which uses DirectWrite to print out some text in a XAML-DirectX interop scenario. I am trying to work based on this guide: http://msdn.microsoft.com/en-us/library/windows/apps/hh825871.aspx, and this sample application: http://code.msdn.microsoft.com/windowsapps/Magazine-Sample-2a657289.

    // This is the OnNavigatedTo event handler of my page.
    
    SurfaceImageSource^ surfaceImageSource = ref new SurfaceImageSource(500, 500);
    ComPtr<ISurfaceImageSourceNative> sisNative;
    IInspectable* sisInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(surfaceImageSource);
    sisInspectable->QueryInterface(__uuidof(ISurfaceImageSourceNative), (void **)&sisNative);
    
    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
    };
    
    ComPtr<ID3D11Device> d3dDevice;
    ComPtr<ID3D11DeviceContext> d3dContext;
    
    UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
    
    D3D11CreateDevice(
    	NULL,
    	D3D_DRIVER_TYPE_HARDWARE,
    	NULL,
    	creationFlags,
    	featureLevels,
    	ARRAYSIZE(featureLevels),
    	D3D11_SDK_VERSION,
    	&d3dDevice,
    	NULL,
    	&d3dContext);
    
    ComPtr<IDXGIDevice> dxgiDevice;
    d3dDevice.As(&dxgiDevice);
    
    D2D1_FACTORY_OPTIONS options;
    ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
    
    ComPtr<ID2D1Factory1> d2dFactory;
    ComPtr<ID2D1Device> d2dDevice;
    ComPtr<ID2D1DeviceContext> d2dContext;
    
    D2D1CreateFactory(
    	D2D1_FACTORY_TYPE_SINGLE_THREADED,
    	__uuidof(ID2D1Factory1),
    	&options,
    	&d2dFactory);
    
    d2dFactory->CreateDevice(dxgiDevice.Get(), &d2dDevice);
    
    d2dDevice->CreateDeviceContext(
    	D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
    	&d2dContext);
    
    d2dContext->SetUnitMode(D2D1_UNIT_MODE_PIXELS);
    d2dContext->SetDpi(DisplayProperties::LogicalDpi, DisplayProperties::LogicalDpi);
    d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
    
    ComPtr<ID2D1SolidColorBrush> solidColorBrush;
    
    d2dContext->CreateSolidColorBrush(
    	D2D1::ColorF(0.2f, 0.2f, 0.2f, 1.0f),
    	D2D1::BrushProperties(),
    	&solidColorBrush);
    
    ComPtr<ID2D1Brush> m_brush;
    m_brush = solidColorBrush;
    
    sisNative->SetDevice(dxgiDevice.Get());
    
    ComPtr<IDXGISurface> surface;
    
    RECT bounds;
    bounds.left = 0;
    bounds.right = 500;
    bounds.top = 0;
    bounds.bottom = 500;
    
    POINT offset;
    
    ComPtr<IDWriteFactory> dwriteFactory;
    
    DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory), &dwriteFactory);
    
    ComPtr<IDWriteTextFormat> textFormat;
    
    dwriteFactory->CreateTextFormat(
    	L"Arial",
    	nullptr,
    	DWRITE_FONT_WEIGHT_NORMAL,
    	DWRITE_FONT_STYLE_NORMAL,
    	DWRITE_FONT_STRETCH_MEDIUM,
    	25.0,
    	L"hu-HU",
    	&textFormat
    	);
    
    ComPtr<IDWriteTextLayout> textLayout;
    
    Platform::String^ str(L"This is a string.");
    
    dwriteFactory->CreateTextLayout(
    	str->Data(),
    	str->Length(),
    	textFormat.Get(),
    	FLT_MAX,
    	FLT_MAX,
    	&textLayout);
    
    sisNative->BeginDraw(bounds, &surface, &offset);
    d2dContext->BeginDraw();
    
    d2dContext->DrawTextLayout(
    	D2D1::Point2F(50, 50),
    	textLayout.Get(),
    	m_brush.Get(),
    	D2D1_DRAW_TEXT_OPTIONS_NONE);
    
    d2dContext->EndDraw();
    sisNative->EndDraw();
    
    ImageBrush^ brush = ref new ImageBrush();
    brush->ImageSource = surfaceImageSource;
    
    // The border is a fixed size (500x500) Border on my Page.
    border->Background = brush;

    I know this is a lot of code, of course I do not expect you to examine it thoroughly, but I couldn't show any smaller code fragment, because I have no clue what I am doing wrong. If someone has experience with DirectDraw, can you take a glance whether you can spot any obvious errors in my code?

    And some concrete questions:

    • Do I need the D3D classes (device, context, etc.) if I only want to use Direct2D (specifically DirectWrite) in my application?
    • What is the best approach to debug these kinds of situations to know what is going on? I tried to examine my variables in the Watch window, but I didn't find any useful information.
    • Could you suggest any DirectWrite or Direct2D tutorials which are easily applicable in the XAML-DirectX scenario?
    Friday, March 09, 2012 4:36 PM

Answers

  • Hi,

    It looks like you need to set the render target for the device context to be your surface, e.g. by adding the following before your BeginDraw call:

    ComPtr<ID2D1Bitmap1> bitmap;
    d2dContext->CreateBitmapFromDxgiSurface(surface.Get(), NULL, &bitmap);
    d2dContext->SetTarget(bitmap.Get());
    	
    d2dContext->BeginDraw();

    That should make the text show up. 

    For your other questions -

    -you generally use D3D here to create a DXGI device.  You could maybe simplify things a little by creating your D2D device via the D2D1CreateDevice(..) method rather than needing to create a D2DFactory first.

    -You can see debug output if you set the D3D11_CREATE_DEVICE_DEBUG flag when creating a device.  Looking at the HRESULT returned by various method calls can also be helpful, e.g. by checking for FAILED(hresult).

    -Here's a basic D2D quickstart which should be easily portable to a SurfaceImageSource:

    http://msdn.microsoft.com/en-us/library/windows/desktop/hh780340(v=vs.85).aspx

    -Jesse

    Monday, March 12, 2012 5:33 PM

All replies

  • Does this thread solved your issue?

    http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/5c983f69-c433-4bf8-9892-5ba5a0390ca3


    NEU_ShieldEdge

    Monday, March 12, 2012 8:07 AM
  • Hi,

    It looks like you need to set the render target for the device context to be your surface, e.g. by adding the following before your BeginDraw call:

    ComPtr<ID2D1Bitmap1> bitmap;
    d2dContext->CreateBitmapFromDxgiSurface(surface.Get(), NULL, &bitmap);
    d2dContext->SetTarget(bitmap.Get());
    	
    d2dContext->BeginDraw();

    That should make the text show up. 

    For your other questions -

    -you generally use D3D here to create a DXGI device.  You could maybe simplify things a little by creating your D2D device via the D2D1CreateDevice(..) method rather than needing to create a D2DFactory first.

    -You can see debug output if you set the D3D11_CREATE_DEVICE_DEBUG flag when creating a device.  Looking at the HRESULT returned by various method calls can also be helpful, e.g. by checking for FAILED(hresult).

    -Here's a basic D2D quickstart which should be easily portable to a SurfaceImageSource:

    http://msdn.microsoft.com/en-us/library/windows/desktop/hh780340(v=vs.85).aspx

    -Jesse

    Monday, March 12, 2012 5:33 PM
  • Thanks!

    That was the last bit of code missing :)

    Tuesday, March 13, 2012 10:14 AM
  • I realise it's an old thread - but just in case it's useful for anyone else, there is a 'gotcha' in this code.  It seems that when you're working with SurfaceImageSource, you need to be careful about which D2D factory you use.  The code sample above does work, but if you extend it to draw more complex things (in my case, using the Geometry APIs) you will get a 'wrong factory' error.  I took a few hours to figure it out (I am no expert with D2D), but the answer is that you should use the factory that created the SurfaceImageSource; you should not create a new factory.  So you will have code like this:

                  ID2D1Factory *pFactory=nullptr;

                  d2dDevice->GetFactory(&pFactory);

                  ID2D1GeometrySink *pSink = NULL;

                  ID2D1PathGeometry *pPathGeometry=NULL;

                  // Create a path geometry.

                  HRESULT hr = pFactory->CreatePathGeometry(&pPathGeometry);

     

                  if (SUCCEEDED(hr))

                  {

                         // Use the geometry sink to write to the path geometry.

                         hr = pPathGeometry->Open(&pSink);

                  }

                  if (SUCCEEDED(hr))

                  {

                         // write your lines and circles to pSink...

                         hr = pSink->Close();

                  }

                  // Draw a filled shape.

                  d2dContext->FillGeometry(pPathGeometry, mBrush.Get());

                  SafeRelease(&pSink);

                  SafeRelease(&pPathGeometry);

                  SafeRelease(&pFactory);


    Jim Chapman

    • Proposed as answer by Lloyd Sunday, February 03, 2013 2:50 PM
    Sunday, October 14, 2012 9:32 AM