none
Graphics.DrawString()で文字毎の位置制御 RRS feed

  • 質問

  • 皆様お世話になります。

    C++BuilderからC#.NETへ移行中の初心者です。

    Win32APIのExtTextOut()の第8引数(lpDx)と同じ機能は、C#.NETでありますでしょうか?

    【Win32APIと.NETとの対応表】
    https://docs.microsoft.com/ja-jp/previous-versions/msdn/architecture-center/cc440130(v=vs.71)?redirectedfrom=MSDN

    によればDrawString()でよいはずですが、6個のオーバーロード関数、StringFormat を見ても同じ働きをするパラメータが見つかりません。

    特殊な文字配置をしたいわけではなく、等幅フォントを等幅で並べたいだけなのですが。

    1文字ずつDrawString()してもパフォーマンス上の問題はありませんでしたが、文字列として描画した方がコード上スマートです。

    よろしくお願いします。

    2021年2月7日 3:43

回答

  • GDI+ を使わずに、TextRender.DrawText や、TextOut を呼び出す方がいい気がします。
    • 回答としてマーク M14Cluster 2021年2月12日 21:47
    2021年2月9日 0:01
  • 外池です。ざっと小生の過去の経験(自作ソフトの例)を調べてみたんですが、すいません、等幅フォントの文字インターバルを任意で指定することはやったことないですし、ざっとMicrosoftのドキュメントを調べても該当する機能が見つからないようです。

    例えば、Courier Newを使って、「abc」ではなく「a b c 」というような、ゆったりしたインターバルを実現されたいんだと理解していますが・・・。

    ただ、疑似的な効果は実現できるかもしれません。任意のインターバルになると思いますが、ただし、文字そのものも細身になったり太っちょになったりするハズ。DrawStringの対象であるGraphicsクラスのオブジェクトで、座標変換を適用する方法です。これもうろ覚えで・・・、すいません。

    GDI+における座標系と変換の説明


    • 編集済み 外池 2021年2月7日 6:38
    • 回答としてマーク M14Cluster 2021年2月7日 14:30
    2021年2月7日 6:37

すべての返信

  • 外池と申します。ひとつ確認させて下さい。等幅フォントを、そのフォント標準の文字インターバルではなくて、ご自身で指定したい、ということですね? (小生の経験では、そのフォント標準の文字インターバルのままで描画して特に不自由はなかったのですが・・・。)

    うろ覚えなので、回答を提示できるかお約束できないのですが、ちょっと調べてみます・・・。

    2021年2月7日 6:24
  • 外池です。ざっと小生の過去の経験(自作ソフトの例)を調べてみたんですが、すいません、等幅フォントの文字インターバルを任意で指定することはやったことないですし、ざっとMicrosoftのドキュメントを調べても該当する機能が見つからないようです。

    例えば、Courier Newを使って、「abc」ではなく「a b c 」というような、ゆったりしたインターバルを実現されたいんだと理解していますが・・・。

    ただ、疑似的な効果は実現できるかもしれません。任意のインターバルになると思いますが、ただし、文字そのものも細身になったり太っちょになったりするハズ。DrawStringの対象であるGraphicsクラスのオブジェクトで、座標変換を適用する方法です。これもうろ覚えで・・・、すいません。

    GDI+における座標系と変換の説明


    • 編集済み 外池 2021年2月7日 6:38
    • 回答としてマーク M14Cluster 2021年2月7日 14:30
    2021年2月7日 6:37
  • 外池様、コメントありがとうございます。

    >等幅フォントを、そのフォント標準の文字インターバルではなくて、ご自身で指定したい、ということですね?

    積極的に変えたいわけではありません。フォントの幅は昔と違って一般に整数ではなく、8.783332といった端数をもち、ピクセル画像にマップされた幅が8ピクセルだったり9ピクセルだったり不均等で見苦しいので、整数に丸めたピッチできっちり整列したいということです。

    端数のフォント幅が整数になるように座標変換すると、そもそもの座標が整数でなくなってしまい、それもうれしくありません。

    なお、実数で扱った方が印刷との相性は良く、ディスプレイ上でもスムージングにより不均等さは目立たなくなりますが、スムージングの効かないフォントもありますし、印刷を成果物としないCUIツール類は整数ピクセルの文化のはずです。

    とりあえず.NETの特性、文化の違い、ということで理解しました。

    ありがとうございました。

    2021年2月7日 14:30
  • 「.NET の特性・文化の違い」は誤解されていると思います。

    System.Drawing.Graphics はあくまで GDI+ のラッパー です。
    あえて同じような表現をとるなら「GDI と GDI+ の違い」でしょう。

    2021年2月7日 15:16
    モデレータ
  • 外池です。意図、理解しました。そうしますと、Azuleanさんのご指摘もありますが、GDI+の動作について誤解があるようにも思われます。

    小生も、実装の細部は承知していないので、最終的に個々の文字が画面に描画されるときのドット単位の色のつきかたまでは追い切れていないのですが・・・、

    GraphicsクラスのオブジェクトにDraw~の類のメソッドで描く段階では、座標の値は基本的にfloat型です。ですので、フォント巾が小数点数でも気にせず、「等幅」で文字が配置され「よう」とする、と期待して良いと思います。(小生は、そのような期待で、プログラミングしていて、不都合を生じたことがありません。)

    その上で、Graphicsクラスのオブジェクトが、実際に表示デバイスに描かれる段階で、ある文字の部分部分が表示デバイスのドットごとに配置されていくわけですが、仰るとおり中途半端な位置に色を塗ろうにも塗れないから、1ドット分ずらして色を置く、あるいは、中間的な色を置いて中途半端な位置への描画を表現する、等の工夫が行われると理解しています。この辺りの制御は、.Net Frameworkの場合は、GraphicsクラスのCompositeQuality、SmoothingMode、TextContrast、TextRnedringHintあたりでやることになるんじゃないかと。
    2021年2月7日 22:00
  • なお、実数で扱った方が印刷との相性は良く、ディスプレイ上でもスムージングにより不均等さは目立たなくなりますが、スムージングの効かないフォントもありますし、印刷を成果物としないCUIツール類は整数ピクセルの文化のはずです。
    High DPI、DPIスケーリングが一般的となっている現代のWindowsにおいて、整数ピクセルに何の意味があるのでしょうか…? 1px単位で制御しているつもりでも225%等の拡大を受けて表示されますよ?
    2021年2月7日 22:14
  • 確かに.NETではなくGDI+の話ですね。それは失礼しました。

    ただ、実際問題として、私の心がゆがんでいるのか、次に示す左側が等間隔に見えないのです。

     

    どちらから選択しろと言われたら、私は迷うことなく右側を選びます。

    「次に示す」の後に、空の img タグが 2 つ並んでいるようで…?

    どんな画像を貼ろうとしたのかは分からないですが、直近で投稿された画像だとこのあたりでしょうか。(M14Cluster さんが投稿された画像では無いかも知れません)

    164494816449551644956

    2021年2月8日 14:24
  • 昨晩、アップした瞬間に画像が消え、編集で再挿入しても消えたのでコメントは削除済です。

    今朝、アップし直してもダメでしたので諦めます。

    なお、自宅で使っているGoogleChromeがおかしいのか、家を出る前には無かったはずの魔界の仮面弁士様のコメントは、会社のIEで発見しました。画像は見えませんが。

    2021年2月8日 22:16
  • GDI+ を使わずに、TextRender.DrawText や、TextOut を呼び出す方がいい気がします。
    • 回答としてマーク M14Cluster 2021年2月12日 21:47
    2021年2月9日 0:01
  • なんと! 小生、これ、知りませんでした。TextRendererクラスのドキュメントを読んでも、背景にある事情が全然わからなかったのですが、

    .Net FrameworkでGDIとGDI+を使い分ける場合のやりかた

    こんなところに解説があった・・・。

    で、

    ひとつのアプリケーション全体で、一括して選ぶなら

    これだと。

    2021年2月9日 10:49
  • 参考までに、.NET 5 の Windows Forms ではパフォーマンス向上のため、GDI の活用が進んでいるそうです。

    https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-runtime-in-net-5-0/#performance-improvements

    たとえば、PaintEventArgs クラスの実現対象に IDeviceContext インターフェースが増え、HDC へのアクセスがさらに容易になっています。

    2021年2月9日 11:00
    モデレータ