none
DirectWriteでフォントの幅を指定する方法はありますでしょうか?

    質問

  • Direct2Dでフォントを指定する場合にはIDWriteTextFormatを構築して
    テキスト描画関数ID2D1RenderTarget::DrawText()にIDWriteTextFormatを渡すことによりフォントを指定できます。

    IDWriteTextFormatの構築にはIDWriteFactory::CreateTextFormat()を使うのですが、
    この第5引数であるDWRITE_FONT_STRETCH型に何を渡してもフォントの幅が変わりません。
    フォントの1文字の幅を狭くしたいのですが、どの様な方法があるでしょうか。
    というのが質問です。
    レンダはID2D1HwndRenderTargetを使ってます。
    ちなみにGDIでは可能でした(フォントフェースは"Lucida Console"を使用)。

    皆さん特にお困りでないのか、または自分が見当違いの事をしているのか、
    バージョンによっては可能なのか、色々とを検索してみたのですが情報が見つかりませんでした。

    できない場合は代替案として、

    (a)GDIとDirectWriteを同居させる。
      ID2D1DCRenderTarget、か又はID2D1BitmapRenderTargetを使う方法。
      仕方ないので図形はDirect2Dで、テキストはGDIで描画するというわかりづらい実装。

    (b)テキストを一旦ID2D1Bitmap等に描画してからメインのレンダにストレッチする
      まぁできるでしょうが遠回り感があり、あまりやりたくありません。

    を考えました。
    詳しい方のお知恵を拝借したくよろしくお願いいたします。

    VS2013 on Win7(+SP1)、Xeon E3-1240、NVIDIA Quadro K600
    Direct2D ver1.1

    2018年1月10日 6:26

回答

  • CreateTextFormat 関数の第 5 引数で指定する DWRITE_FONT_STRETCH 型 (フォント幅) は、第 1 引数で指定するフォントファミリーの中から最も近いフォント幅のフォントが選択されるということのようです。

    フォントファミリー "Lucida Console" のフォント幅を確認したところ Semi Condensed の 1 つしかないため、他のフォント幅を指定しても変化が起こらなかったようです。(こちらのプログラムで確認)

    思いつく回避方法ですが、(b) 案に近いのですが ID2D1RenderTarget::DrawText 関数の直前で ID2D1HwndRenderTarget::SetTransform にて拡縮(座標変換)を行って描画し、描画の直後に座標変換を戻してあげるのはどうでしょうか?

    float fScaleY = 4.0F; // 縦方向に 4 倍引き伸ばす
    WCHAR szText[] = L"Hello, World!";
    D2D1_SIZE_F renderTargetSize = pRenderTarget->GetSize();
    pRenderTarget->BeginDraw();
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    D2D1_MATRIX_3X2_F matrix;
    pRenderTarget->GetTransform(&matrix);
    pRenderTarget->SetTransform(D2D1::Matrix3x2F::Scale(1.0F, fScaleY));
    pRenderTarget->DrawText(szText, lstrlen(szText), pTextFormat, D2D1::RectF(0, 0, renderTargetSize.width, (1.0F / fScaleY) * renderTargetSize.height), m_pBlackBrush);
    pRenderTarget->SetTransform(&matrix);
    pRenderTarget->EndDraw();
    2018年1月11日 8:17

すべての返信

  • CreateTextFormat 関数の第 5 引数で指定する DWRITE_FONT_STRETCH 型 (フォント幅) は、第 1 引数で指定するフォントファミリーの中から最も近いフォント幅のフォントが選択されるということのようです。

    フォントファミリー "Lucida Console" のフォント幅を確認したところ Semi Condensed の 1 つしかないため、他のフォント幅を指定しても変化が起こらなかったようです。(こちらのプログラムで確認)

    思いつく回避方法ですが、(b) 案に近いのですが ID2D1RenderTarget::DrawText 関数の直前で ID2D1HwndRenderTarget::SetTransform にて拡縮(座標変換)を行って描画し、描画の直後に座標変換を戻してあげるのはどうでしょうか?

    float fScaleY = 4.0F; // 縦方向に 4 倍引き伸ばす
    WCHAR szText[] = L"Hello, World!";
    D2D1_SIZE_F renderTargetSize = pRenderTarget->GetSize();
    pRenderTarget->BeginDraw();
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    D2D1_MATRIX_3X2_F matrix;
    pRenderTarget->GetTransform(&matrix);
    pRenderTarget->SetTransform(D2D1::Matrix3x2F::Scale(1.0F, fScaleY));
    pRenderTarget->DrawText(szText, lstrlen(szText), pTextFormat, D2D1::RectF(0, 0, renderTargetSize.width, (1.0F / fScaleY) * renderTargetSize.height), m_pBlackBrush);
    pRenderTarget->SetTransform(&matrix);
    pRenderTarget->EndDraw();
    2018年1月11日 8:17
  • コメントありがとうございます。
    思い至りませんでした、なるほどです。

    一応、文字の幅を小さくすることには成功しました。
    一応というのも、

    (1)描画位置というか、出力先矩形の位置もストレッチされてしまう(予想通り)。
    (2)同じ理由で、正しく計算された矩形を渡すと幅が足りないという判定がなされて、
     文字列が途中で折り返されてしまう(やれやれ)。
    (3)あまり判然としませんが、GDIの出力よりややスムージングが下手に見える場合があるようだ。

    等の問題もあるようです。

    IDWriteTextRenderer から派生してカスタムのテキストレンダを
    自前で実装すればちゃんとしたものができるのかもしれませんが、結構面倒そうです。
    悩ましいですねぇ。
    自前のレンダターゲットのベースクラスに文字幅のストレッチ値を考慮しつつ、正しい位置に描画できる様な
    メンバ関数を追加することにしてみます。
    ありがとうございました。

    また、他のアイデアなどお持ちの方がありましたら引き続きご意見頂けますようお願いいたします。

    2018年1月11日 9:16