6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

.NET GraphicsPath.AddStringで縁取り文字を描きたい

Last updated at Posted at 2019-10-17

GraphicsPathクラスで文字のパスを追加して、縁取りを追加してから文字を描きたい。

// ※using、Dispose文は省略
var bitmap = new Bitmap(1280, 200);
// 透過
bitmap.MakeTransparent();
// BitmapからGraphicsを生成
var graphics = Graphics.FromImage(bitmap);

var stringFormat = new StringFormat();
// どんなに長くて単語の区切りが良くても改行しない
stringFormat.FormatFlags = StringFormatFlags.NoWrap;
// どんなに長くてもトリミングしない
stringFormat.Trimming = StringTrimming.None;
// ハイクオリティレンダリング
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
// アンチエイリアスをかける
graphics.SmoothingMode = SmoothingMode.HighQuality;

// GraphicsPathを生成
var gp = new GraphicsPath();

// パスに文字を追加
gp.AddString("山路を登りながら、智に働けば角が立つ。", new FontFamily("メイリオ"), 0, 46, new Point(8, 8), stringFormat);

// 縁取りをする。
graphics.DrawPath(new Pen(Color.Black, 16), gp);
// 文字を塗りつぶす。
graphics.FillPath(new SolidBrush(Color.White), gp);

// テスト用にBitmapの内容をD:\result.pngに出力
bitmap.Save("D:\\result.png", System.Drawing.Imaging.ImageFormat.Png);

注意して欲しいのが、縁取りする場合の文字の座標。文字のパスを追加した座標を基準に、中央から太さが変わっていくので文字の座標をを縁取りのサイズの半分ずらす必要がある。上のサンプルでは、縁取りのサイズが16なので文字自体をnew Point(8, 8)としている。

レンダリング結果

result.png

「智に働けば"角が"立つ」、ってね!

やかましいわ! うおー! なんかおかしい!

文字自体のパスは正常に追加されてるが、GraphicsPath.DrawPathによる縁取りの描画が破綻しているっぽい。

フォントによって結果が異なりますが(ハミングとかスーラとか、角丸系のフォントは大丈夫)、角張ってるフォントはこうなりやすい……。

PenクラスのLineJoinプロパティとMiterLimitプロパティ

マイクロソフトのドキュメントの登場だー! Penクラスのプロパティ弄れば解決できるかな、と思いとりあえずこのページを観てみる。すると「LineJoin - この Pen で描画された連続する 2 本の直線の終点の接合スタイルを取得または設定します。」といういかにもそれっぽいプロパティを見つけたのでこの列挙型を変更してみる。

LineJoin.Bevel

graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.Bevel }, gp);

result.png

斜めの縁取りが生成される感じ。3DCGのベベルエッジと同じ感じですね。

LineJoin.Miter

graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.Miter }, gp);

result.png

LineJoin.Miter = 0なのでこれがデフォルト。

LineJoin.MiterClipped

graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.MiterClipped }, gp);

result.png

これもLineJoin.Miterとほとんど同じなんだけど、MiterLimitの値を上回ったときの処理方法が違うっぽい。

graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.Miter, MiterLimit = 2 }, gp);
graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.MiterClipped, MiterLimit = 2 }, gp);

result.png

違うはず……なんだけど違いがよく分かんねぇわ。フォトショで減算したらたしかに違うって事はわかるけど。

LineJoin.Round

graphics.DrawPath(new Pen(Color.Black, 16) { LineJoin = LineJoin.Round} , gp);

result.png

個人的に1番好き。角張っていないので他のと比べて優しい感じ。

まとめ

  • カクカクした縁取りを行うなら、LineJoin.Miterを指定して適切なMiterLimitを指定する。
  • 丸い縁取りを行うなら、LineJoin.Roundを指定する。
6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?