2

First, I want to say that I am a newbie in direct2d usage.

My final purpose is to have per pixel transparent window. I have found this nice article :

https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/june/windows-with-c-high-performance-window-layering-using-the-windows-composition-engine

and it seems that it answers my question. Before writing my per-pixel transparent window, I just tried to test the code in that link. But when I run the program, the disk that should be displayed in the window is not displayed at all.

Here is the complete code that I have written, using just C++ win api, without the COM stuff added by the author Kenny Kerr. There are few functions in it, mainly :

  • d2d_init() : it sets up Direct2D
  • render() : it displays the scene
  • main() : it creates and display the window, calls d2d_init()
  • the window procedure which calls render() in WM_PAINT
#include <iostream>

#ifdef _WIN32_WINNT
# undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00

#include <guiddef.h>
#include <d2d1_1.h>
#include <d3d11.h>
#include <dxgi1_3.h>
#include <dcomp.h>

static HINSTANCE instance = NULL;
static HWND win = NULL;

LRESULT CALLBACK _window_procedure(HWND   window,
                                   UINT   message,
                                   WPARAM window_param,
                                   LPARAM data_param);

typedef struct
{
    ID3D11Device *d3d_device;
    IDXGIDevice *dxgi_device;
    IDXGIFactory2 *dxgi_factory;
    ID2D1Factory1 *d2d_factory;
    ID2D1Device *d2d_device;
    ID2D1DeviceContext *d2d_device_ctx;
    IDCompositionDevice *dcomp_device;
    IDCompositionVisual *dcomp_visual;
    IDCompositionTarget *dcomp_target;
    IDXGISwapChain1 *dxgi_swapchain;
} D2d;

int d2d_init(HWND win, D2d *d2d)
{
    const D3D_FEATURE_LEVEL levels[] =
    {
        D3D_FEATURE_LEVEL_12_1,
        D3D_FEATURE_LEVEL_12_0,
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0
    };
    D2D1_FACTORY_OPTIONS opt;
    DXGI_SWAP_CHAIN_DESC1 desc;
    RECT r;
    HRESULT res;

    /* direct3d device */
    res = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT |
                            D3D11_CREATE_DEVICE_DEBUG,
                            levels, sizeof(levels) / sizeof(D3D_FEATURE_LEVEL),
                            D3D11_SDK_VERSION, &d2d->d3d_device, NULL, NULL);
    if (FAILED(res))
        return 0;

    /* dxgi device */
    res = d2d->d3d_device->QueryInterface(&d2d->dxgi_device);
    if (FAILED(res))
        goto release_d3d_device;

    /* dxgi factory */
    res = CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG,
                             __uuidof(d2d->dxgi_factory),
                             (void **)&d2d->dxgi_factory);
    if (FAILED(res))
        goto release_dxgi_device;

    /* d2d factory */
    opt.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
    res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
                            __uuidof(d2d->d2d_factory), &opt,
                            (void **)&d2d->d2d_factory);
    if (FAILED(res))
        goto release_dxgi_factory;

    /* d2d device */

    res = d2d->d2d_factory->CreateDevice(d2d->dxgi_device, &d2d->d2d_device);
    if (FAILED(res))
        goto release_d2d_factory;

    /* d2d device context */

    // FIXME : D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS
    res = d2d->d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
                                               &d2d->d2d_device_ctx);
    if (FAILED(res))
        goto release_d2d_device;

    /* dcomp device */
    res = DCompositionCreateDevice(d2d->dxgi_device,
                                   __uuidof(d2d->dcomp_device),
                                   (void **)&d2d->dcomp_device);
    if (FAILED(res))
        goto release_d2d_device_ctx;

    /* dcomp visual */
    res = d2d->dcomp_device->CreateVisual(&d2d->dcomp_visual);
    if (FAILED(res))
        goto release_dcomp_device;

    /* dcomp target */
    res = d2d->dcomp_device->CreateTargetForHwnd(win, TRUE,
                                                 &d2d->dcomp_target);
    if (FAILED(res))
        goto release_dcomp_visual;

    /* dxgi swapchain */
    if (!GetClientRect(win, &r))
        goto release_dcomp_target;

    desc.Width = r.right - r.left; /* width of client area */
    desc.Height = r.bottom - r.top; /* height of client area */
    desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    desc.Stereo = FALSE;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc.BufferCount = 2;
    desc.Scaling = DXGI_SCALING_STRETCH;
    desc.SwapEffect= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
    desc.Flags = 0;

    res = d2d->dxgi_factory->CreateSwapChainForComposition(d2d->dxgi_device,
                                                           &desc,
                                                           NULL,
                                                           &d2d->dxgi_swapchain);
    if (FAILED(res))
        goto release_dcomp_target;

    return 1;

  release_dcomp_target:
    d2d->dcomp_target->Release();
  release_dcomp_visual:
    d2d->dcomp_visual->Release();
  release_dcomp_device:
    d2d->dcomp_device->Release();
  release_d2d_device_ctx:
    d2d->d2d_device_ctx->Release();
  release_d2d_device:
    d2d->d2d_device->Release();
  release_d2d_factory:
    d2d->d2d_factory->Release();
  release_dxgi_factory:
    d2d->dxgi_factory->Release();
  release_dxgi_device:
    d2d->dxgi_device->Release();
  release_d3d_device:
    d2d->d3d_device->Release();

    return 0;
}

void d2d_shutdown(D2d *d2d)
{
    d2d->dxgi_swapchain->Release();
    d2d->dcomp_target->Release();
    d2d->dcomp_visual->Release();
    d2d->dcomp_device->Release();
    d2d->d2d_device_ctx->Release();
    d2d->d2d_device->Release();
    d2d->d2d_factory->Release();
    d2d->dxgi_factory->Release();
    d2d->dxgi_device->Release();
    d2d->d3d_device->Release();
}

void render(D2d *d2d)
{
    IDXGISurface *surface;
    ID2D1Bitmap1 *bitmap;
    D2D1_BITMAP_PROPERTIES1 properties;
    D2D1_COLOR_F c;
    HRESULT res;

    std::cout << "render" << std::endl;

    /* swapchain buffer in a IDXGISurface */
    res = d2d->dxgi_swapchain->GetBuffer(0, __uuidof(surface),
                                         (void **)&surface);
    if (FAILED(res))
        return;

    properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
    properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
    properties.dpiX = 0;
    properties.dpiY = 0;
    properties.bitmapOptions = (D2D1_BITMAP_OPTIONS)(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW);
    properties.colorContext = NULL;

    res = d2d->d2d_device_ctx->CreateBitmapFromDxgiSurface(surface,
                                                           &properties,
                                                           &bitmap);
    if (FAILED(res))
        goto release_surface;

    d2d->d2d_device_ctx->SetTarget(bitmap);
    d2d->d2d_device_ctx->BeginDraw();

    c.r = 0.0f;
    c.g = 0.0f;
    c.b = 0.0f;
    c.a = 0.0f;
    d2d->d2d_device_ctx->Clear(&c);

    ID2D1SolidColorBrush *brush;
    D2D1_COLOR_F brushColor;
    D2D1_BRUSH_PROPERTIES bp;

    brushColor.r = 0.18f;
    brushColor.g = 0.55f;
    brushColor.b = 0.340f;
    brushColor.a = 0.75f;

    bp.opacity = 0.0f;
    bp.transform.m11 = 1.0f;
    bp.transform.m12 = 0.0f;
    bp.transform.m21 = 0.0f;
    bp.transform.m22 = 1.0f;
    bp.transform.dx = 0.0f;
    bp.transform.dy = 0.0f;
    res = d2d->d2d_device_ctx->CreateSolidColorBrush(&brushColor, &bp, &brush);
    if (FAILED(res))
    {
          std::cout << "CreateSolidColorBrush failed" << std::endl;
          goto release_bitmap;
    }

    D2D1_ELLIPSE ellipse;
    ellipse.point.x = 320.0f;
    ellipse.point.y = 240.0f;
    ellipse.radiusX = 100.0f;
    ellipse.radiusY = 100.0f;
    d2d->d2d_device_ctx->FillEllipse(ellipse, brush);
    brush->Release();

    d2d->d2d_device_ctx->EndDraw(NULL, NULL);

    d2d->dxgi_swapchain->Present(1, 0);
    d2d->dcomp_visual->SetContent(d2d->dxgi_swapchain);
    d2d->dcomp_target->SetRoot(d2d->dcomp_visual);
    d2d->dcomp_device->Commit();

  release_bitmap:
    bitmap->Release();
  release_surface:
    surface->Release();
}


LRESULT CALLBACK
_window_procedure(HWND   window,
                  UINT   message,
                  WPARAM window_param,
                  LPARAM data_param)
{
  switch (message)
    {
    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;
    case WM_KEYUP:
      if (window_param == 'Q')
        {
          PostQuitMessage(0);
        }
      if (window_param == 'T')
        {
            std::cout << "transp" << std::endl;
        }
      return 0;
      /* GDI notifications */
    case WM_CREATE:
        return 0;
    case WM_PAINT:
      {
        RECT rect;
        D2d *d2d;

        d2d = (D2d *)GetWindowLongPtr(window, GWLP_USERDATA);

        std::cout << "paint" << std::endl;

        if (GetUpdateRect(window, &rect, FALSE))
          {
            PAINTSTRUCT ps;
            BeginPaint(window, &ps);

            render(d2d);

            EndPaint(window, &ps);
          }
        return 0;
      }
    default:
      return DefWindowProc(window, message, window_param, data_param);
    }
}

int main()
{
    /* class */
    WNDCLASS wc;

    instance = GetModuleHandle(NULL);
    if (!instance)
        return 1;

    memset (&wc, 0, sizeof (WNDCLASS));
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = _window_procedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = instance;
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "D2D";

    if(!RegisterClass(&wc))
        goto free_library;

    /* Window */
    int w;
    int h;
    RECT r;
    DWORD style;
    DWORD exstyle;

    w = 640;
    h = 480;

    style = WS_OVERLAPPEDWINDOW | WS_SIZEBOX;
    exstyle = WS_EX_NOREDIRECTIONBITMAP;

    r.left = 0;
    r.top = 0;
    r.right = w;
    r.bottom = h;
    if (!AdjustWindowRectEx(&r, style, FALSE, exstyle))
        goto unregister_class;

    win = CreateWindowEx(exstyle,
                         "D2D", "Test",
                         style,
                         100, 100,
                         r.right - r.left,
                         r.bottom - r.top,
                         NULL,
                         NULL, instance, NULL);
    if (!win)
        goto unregister_class;

    /* d2d init */
    D2d d2d;

    if (!d2d_init(win, &d2d))
    {
        std::cout << "d2d init failed" << std::endl;
        goto destroy_win;
    }

    SetWindowLongPtr(win, GWLP_USERDATA, (LONG_PTR)&d2d);
    ShowWindow(win, SW_SHOWNORMAL);
    UpdateWindow(win);

    /* msg loop */
    while(1)
    {
        MSG msg;
        BOOL ret;

        ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
        if (ret)
        {
            do
            {
                if (msg.message == WM_QUIT)
                  goto beach;
                TranslateMessage(&msg);
                DispatchMessageW(&msg);
            } while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
        }
    }

 beach:
    d2d_shutdown(&d2d);
    DestroyWindow(win);
    UnregisterClass("D2D", instance);
    FreeLibrary(instance);

    std::cout << "exiting..." << std::endl;

    return 0;

  destroy_win:
    DestroyWindow(win);
  unregister_class:
    UnregisterClass("D2D", instance);
  free_library:
    FreeLibrary(instance);

    std::cout << "exiting 2..." << std::endl;

    return 1;
}

Now, I am blocked. I've searched a lot on Internet the last week, but i have not seen any code tha could help me.

Does someone see where my error is ?

thank you

vtorri
  • 166
  • 13
  • [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – IInspectable Nov 23 '20 at 11:28
  • i don't know how this site can help me. I've tested the program with the debug layer of D2D and other debug flags possible, and nothing arises. I've checked all the returned values of the functions and none fail. I have written the code that someone claimed to work and it does not work. This program is the minimal program i can write to test what Kerr is writing. If you have any hint, I would be glad to read it. thank you – vtorri Nov 23 '20 at 12:21
  • 1
    What's your problem? If it's the fact you don't see the green ellipse, you set it's color opacity to 0. Just set it to 1 for example. – Simon Mourier Nov 23 '20 at 12:24
  • @simon arg, just that :( thank you. in Kerr article, CreateSolidColorBrush was called without the brush properties, and i diddn't know what to set there. Thank you very much – vtorri Nov 23 '20 at 12:29

1 Answers1

3

Just set a non zero opacity for the ellipse brush, for example:

bp.opacity = 1.0f;
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298