86494


■概要  
DirectWriteのDrawTextを使って文字だけを描画させてレンダリング対象の背景は透明化したい。

■質問内容  
DrawTextを使って文字列を描画させていますが、文字を描画したときに描画範囲の背景が黒（RGBA=0, 0, 0, 0)や白（RGBA=1, 1, 1,
0)になって文字以外の背景が見えなくなってしまいます。  
期待としては背景は透明化して、文字だけを描画したいのですがどのようにアプローチしたら良いでしょうか？

たとえばCreateWindowでSTATICコントロール生成＋サブクラス化で背景を透過する。  
などが解決案として浮かびますが、Direct2Dによるレンダリングでそれができるのかという懸念と  
単純にコストがかかるので既存のDirect2Dの機能を使ってよりスマートなやり方がないかと考えています。

■サンプルコード

[code]

    #include <windows.h>
    #include <d2d1.h>
    #include <dwrite.h>
    
    #pragma comment(lib, "d2d1.lib")
    #pragma comment(lib, "dwrite.lib")
    
    
    typedef struct tagDWRITE_CONTEXT      // DirectWriteコンテキスト
    {
        ID2D1Factory* pD2DFactory;
        IDWriteFactory* pDWriteFactory;
        ID2D1HwndRenderTarget* pRT;
        ID2D1SolidColorBrush* pBrush;
        IDWriteTextFormat* pTextFormat;
    } DWRITE_CONTEXT, * PDWRITE_CONTEXT;
    
    extern HWND g_hWnd;
    
    static void CALLBACK DirectWriteCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
    {
        CONST D2D1_COLOR_F stBKColor = { 0.0f, 0.0F, 0.0f, 0.0f };
        PDWRITE_CONTEXT pContext;
        D2D1_SIZE_F stTargetSize;
        WCHAR chWork[256];
        static int inC;
    
        pContext = (PDWRITE_CONTEXT)dwUser;
    
        // 描画開始(Direct2D)
        pContext->pRT->BeginDraw();
    
        // 背景クリア
        pContext->pRT->Clear(stBKColor);
    
        wsprintfW(chWork, L"テスト%d", inC++);
    
        stTargetSize = pContext->pRT->GetSize();
        pContext->pRT->DrawText(chWork,
            wcslen(chWork),
            pContext->pTextFormat,
            &D2D1::RectF(0, 0, 167, 155),
            pContext->pBrush);
    
        // 描画終了(Direct2D)
        pContext->pRT->EndDraw();
    }
    
    DWORD WINAPI DirectWriteThreadProc(LPVOID lpParameter)
    {
        DWRITE_CONTEXT stDWriteContext;
    
        RECT rc;
        D2D1_SIZE_U stPixelSize;
    
        UINT uTimerID;
        HRESULT hr;
    
        MSG stMsg;
        BOOL bRinf;
    
    
        //-------------------------------------------------------------------------
        // 変数初期化
        //-------------------------------------------------------------------------
        memset(&stDWriteContext, 0, sizeof(stDWriteContext));
        uTimerID = 0U;
    
        //-------------------------------------------------------------------------
        // D2Dインタフェース生成
        //-------------------------------------------------------------------------
        hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &stDWriteContext.pD2DFactory);
    
        //-------------------------------------------------------------------------
        // DirectWriteインタフェース生成
        //-------------------------------------------------------------------------
        hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown **)&stDWriteContext.pDWriteFactory);
    
        //-------------------------------------------------------------------------
        // ID2D1HwndRenderTarget生成
        //-------------------------------------------------------------------------
        GetClientRect(g_hWnd, &rc);
        stPixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
        hr = stDWriteContext.pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
                                                                 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_UNKNOWN)),
                                                                 D2D1::HwndRenderTargetProperties(g_hWnd, stPixelSize, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
                                                                 &stDWriteContext.pRT);
    
        //-------------------------------------------------------------------------
        // テキストフォーマット作成
        //-------------------------------------------------------------------------
        hr = stDWriteContext.pDWriteFactory->CreateTextFormat(L"Arial",
                                                              NULL,
                                                              DWRITE_FONT_WEIGHT_BOLD,
                                                              DWRITE_FONT_STYLE_NORMAL,
                                                              DWRITE_FONT_STRETCH_ULTRA_EXPANDED,
                                                              72.0f,
                                                              L"ja-jp",
                                                              &stDWriteContext.pTextFormat);
    
        //-------------------------------------------------------------------------
        // ブラシ作成
        //-------------------------------------------------------------------------
        hr = stDWriteContext.pRT->CreateSolidColorBrush(D2D1::ColorF(0xFF0094C8, 1.0F), &stDWriteContext.pBrush);
    
        //-------------------------------------------------------------------------
        // 描画タイマー起動(30fps)
        //-------------------------------------------------------------------------
        uTimerID = timeSetEvent(33, 1, DirectWriteCallback, (DWORD_PTR)&stDWriteContext, TIME_PERIODIC);
    
        //-------------------------------------------------------------------------
        // イベントメッセージループ
        //-------------------------------------------------------------------------
        while (1)
        {
            bRinf = GetMessage(&stMsg, NULL, 0, 0);
            switch (bRinf)
            {
            case 0:                         // プログラム終了
    
                break;
    
            case -1:                        // イベントメッセージ取得エラー
    
                break;
    
            default:
    
                break;
            }
    
            // メッセージループ処理終了判定
            if (bRinf == 0 || bRinf == -1) break;
        }
    
        //-------------------------------------------------------------------------
        // 描画タイマー終了
        //-------------------------------------------------------------------------
        if (uTimerID != 0U)
        {
            timeKillEvent(uTimerID);
        }
    
        //-------------------------------------------------------------------------
        // タイマーコールバックが終了するまで待つ
        //-------------------------------------------------------------------------
        Sleep(500);
    
        //-------------------------------------------------------------------------
        // D2Dリソース解放
        //-------------------------------------------------------------------------
        if (stDWriteContext.pBrush != NULL)
        {
            stDWriteContext.pBrush->Release();
        }
    
        if (stDWriteContext.pRT != NULL)
        {
            stDWriteContext.pRT->Release();
        }
    
        if (stDWriteContext.pTextFormat != NULL)
        {
            stDWriteContext.pTextFormat->Release();
        }
    
        if (stDWriteContext.pDWriteFactory != NULL)
        {
            stDWriteContext.pDWriteFactory->Release();
        }
    
    #if 0
        if (stDWriteContext.pD2DFactory != NULL)
        {
            stDWriteContext.pD2DFactory->Release();
        }
    #endif
    
        ExitThread(0UL);
    }
    
[/code]


CC BY-SA 4.0
2022-02-20T10:46:10.923
86474
2022-02-21T10:44:04.677
20931
question
c++ directx
DirectWriteによる文字列描画で背景色が透明化できない


古典的なやり方ですが、ダイアログまたはCreateWindowで作成したコントロールにWS_EX_LAYEREDを設定してSetLayeredWindowAttributesを使用して  
特定のピクセルのRGBのときに透明化する方法でひとまず課題を解決することができました。  
しかし、SetLayeredWindowAttributesによる透過処理は負荷が高いらしいのでより負荷がかからない方法を将来的に探していきたいと思います。

■解決時のコード

[code]

    INT_PTR CALLBACK DirectWriteDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        // DirectWrite関連
        const D2D1_COLOR_F stBKColor = D2D1::ColorF(0x002B2B2B, 1.0F);      // R=43, G=43, B=43, Alpha=1.0F
        static ID2D1Factory *pD2DFactory;
        static IDWriteFactory *pDWriteFactory;
        static ID2D1HwndRenderTarget *pRT;
        static ID2D1SolidColorBrush *pDWBrush;
        static IDWriteTextFormat *pTextFormat;
    
        RECT rc;
        D2D1_SIZE_U stPixelSize;
        D2D1_SIZE_F stTargetSize;
        HRESULT hr;
    
        // デバコン関連
        HDC                 hdc;
        PAINTSTRUCT         ps;
        static HBRUSH       hBrush;
    
        // タイマー関連
        const UINT uiTimer = 9876;
        static UINT_PTR     uiTimerID;
        DWORD               dwExStyle;
    
        // ワーク領域関連
        WCHAR               chWork[256];
        INT_PTR             inRet;
    
        //-------------------------------------------------------------------------
        // 変数初期化
        //-------------------------------------------------------------------------
        inRet = 0;
    
        //-------------------------------------------------------------------------
        // メッセージ処理開始
        //-------------------------------------------------------------------------
        switch (uMsg)
        {
            case WM_INITDIALOG:
            {
                //-----------------------------------------------------------------
                // タスクバーのウィンドウ非表示設定
                //-----------------------------------------------------------------
                dwExStyle = (DWORD)GetWindowLong(hwndDlg, GWL_EXSTYLE);
                SetWindowLong(hwndDlg, GWL_EXSTYLE, dwExStyle | WS_EX_TOOLWINDOW);
    
                //-----------------------------------------------------------------
                // 定期描画タイマー作成（30fps）
                //-----------------------------------------------------------------
                uiTimerID = SetTimer(hwndDlg, uiTimer, 32, (TIMERPROC)DirectWriteDlgProc);
    
                //-----------------------------------------------------------------
                // ウィンドウ半透明化設定(R=43, G=43, B=43に対して透明化させる)
                //-----------------------------------------------------------------
                SetLayeredWindowAttributes(hwndDlg, RGB(43, 43, 43), 0, LWA_COLORKEY);
    
                //-----------------------------------------------------------------
                // 背景ブラシ（半透明化のため）
                //-----------------------------------------------------------------
                hBrush = CreateSolidBrush(RGB(43, 43, 43));
    
                //-----------------------------------------------------------------
                // D2Dインタフェース生成
                //-----------------------------------------------------------------
                hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
    
                //-----------------------------------------------------------------
                // DirectWriteインタフェース生成
                //-----------------------------------------------------------------
                hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), (IUnknown **)&pDWriteFactory);
    
                //-----------------------------------------------------------------
                // ID2D1HwndRenderTarget生成
                //-----------------------------------------------------------------
                GetClientRect(hwndDlg, &rc);
                stPixelSize = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
                hr = pD2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
                                                                                      D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_UNKNOWN)),
                                                         D2D1::HwndRenderTargetProperties(hwndDlg, stPixelSize),
                                                         &pRT);
    
                //-----------------------------------------------------------------
                // テキストフォーマット作成
                //-----------------------------------------------------------------
                hr = pDWriteFactory->CreateTextFormat(L"Arial",
                                                      NULL,
                                                      DWRITE_FONT_WEIGHT_BOLD,
                                                      DWRITE_FONT_STYLE_NORMAL,
                                                      DWRITE_FONT_STRETCH_NORMAL,
                                                      72.0f,
                                                      L"ja-jp",
                                                      &pTextFormat);
    
                //-----------------------------------------------------------------
                // ブラシ作成
                //-----------------------------------------------------------------
                hr = pRT->CreateSolidColorBrush(D2D1::ColorF(0xFF0094C8, 1.0F), &pDWBrush);
    
                //-----------------------------------------------------------------
                // 描画文字のアンチエイリアスモード
                //-----------------------------------------------------------------
                pRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
    
                inRet = 1;
    
                break;
            }
    
            case WM_TIMER:                      // タイマー通知
            {
                //-----------------------------------------------------------------
                // 再描画要求
                //-----------------------------------------------------------------
                InvalidateRect(hwndDlg, NULL, TRUE);
    
                break;
            }
    
            case WM_ERASEBKGND:                 // ウィンドウ背景消去
            {
                //-----------------------------------------------------------------
                // チラツキを防止するため、システム側で背景消去を行わない
                //-----------------------------------------------------------------
                inRet = 1;
    
                break;
            }
    
            case WM_PAINT:
            {
                hdc = BeginPaint(hwndDlg, &ps);
    
                //-----------------------------------------------------------------
                // 描画開始(Direct2D)
                //-----------------------------------------------------------------
                pRT->BeginDraw();
    
                //-----------------------------------------------------------------
                // 背景クリア（R=43, G=43, B=43, Alpha=1.0の場合は透明化される)
                //-----------------------------------------------------------------
                pRT->Clear(stBKColor);
    
                stTargetSize = pRT->GetSize();
    
                //-----------------------------------------------------------------
                // DirectWriteによる文字列描画
                //-----------------------------------------------------------------
                wsprintfW(&chWork[0], L"テスト");
    
                pRT->DrawText(chWork,
                              wcslen(chWork),
                              pTextFormat,
                              &D2D1::RectF(0, 90, stTargetSize.width, stTargetSize.height),
                              pDWBrush);
    
                //-----------------------------------------------------------------
                // 描画終了(Direct2D)
                //-----------------------------------------------------------------
                pRT->EndDraw();
    
                EndPaint(hwndDlg, &ps);
    
                break;
            }
    
            case WM_CTLCOLORDLG:                // ダイアログの背景色
            {
                return (LRESULT)hBrush;
            }
    
            case WM_CLOSE:
            {
                if (uiTimerID != 0)
                {
                    KillTimer(hwndDlg, uiTimerID);
                    uiTimerID = 0;
                }
    
                if (pDWBrush != NULL)
                {
                    pDWBrush->Release();
                }
    
                // クラッシュする
                //if (pRT != NULL)
                //{
                //    pRT->Release();
                //}
    
                if (pTextFormat != NULL)
                {
                    pTextFormat->Release();
                }
    
                if (pDWriteFactory != NULL)
                {
                    pDWriteFactory->Release();
                }
    
                if (pD2DFactory != NULL)
                {
                    pD2DFactory->Release();
                }
    
                EndDialog(hwndDlg, 0);
                // ダイアログを制御しているスレッドにWM_QUITをポスト
                PostQuitMessage(0);
    
                break;
            }
    
            default:;
        }
    
    
        return inRet;
    }
    
[/code]


CC BY-SA 4.0
2022-02-21T10:44:04.677
86494
2022-02-21T10:44:04.677
20931
86474
answer