OSDN Git Service

92b678883cfebf51541f0500185aeb606c6b87ef
[dtxmania/dtxmania.git] / DTXMania / コード / 全体 / CPrivateFont.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.IO;
5 using System.Runtime.InteropServices;
6 using System.Drawing;
7 using System.Drawing.Drawing2D;
8 using System.Diagnostics;
9 using SharpDX;
10 using FDK;
11
12 using Rectangle = System.Drawing.Rectangle;
13 using Color = System.Drawing.Color;
14 using Point = System.Drawing.Point;
15
16 namespace DTXMania
17 {
18         /// <summary>
19         /// プライベートフォントでの描画を扱うクラス。
20         /// </summary>
21         /// <exception cref="FileNotFoundException">フォントファイルが見つからない時に例外発生</exception>
22         /// <exception cref="ArgumentException">スタイル指定不正時に例外発生</exception>
23         /// <remarks>
24         /// 簡単な使い方
25         /// CPrivateFont prvFont = new CPrivateFont( CSkin.Path( @"Graphics\fonts\mplus-1p-bold.ttf" ), 36 );   // プライベートフォント
26         /// とか
27         /// CPrivateFont prvFont = new CPrivateFont( new FontFamily("MS UI Gothic"), 36, FontStyle.Bold );              // システムフォント
28         /// とかした上で、
29         /// Bitmap bmp = prvFont.DrawPrivateFont( "ABCDE", Color.White, Color.Black );                                                  // フォント色=白、縁の色=黒の例。縁の色は省略可能
30         /// とか
31         /// Bitmap bmp = prvFont.DrawPrivateFont( "ABCDE", Color.White, Color.Black, Color.Yellow, Color.OrangeRed ); // 上下グラデーション(Yellow→OrangeRed)
32         /// とかして、
33         /// CTexture ctBmp = TextureFactory.tテクスチャの生成( bmp, false );
34         /// ctBMP.t2D描画( ~~~ );
35         /// で表示してください。
36         /// 
37         /// 注意点
38         /// 任意のフォントでのレンダリングは結構負荷が大きいので、なるべくなら描画フレーム毎にフォントを再レンダリングするようなことはせず、
39         /// 一旦レンダリングしたものを描画に使い回すようにしてください。
40         /// また、長い文字列を与えると、返されるBitmapも横長になります。この横長画像をそのままテクスチャとして使うと、
41         /// 古いPCで問題を発生させやすいです。これを回避するには、一旦Bitmapとして取得したのち、256pixや512pixで分割して
42         /// テクスチャに定義するようにしてください。
43         /// </remarks>
44         public class CPrivateFont : IDisposable
45         {
46                 #region [ コンストラクタ ]
47                 public CPrivateFont(FontFamily fontfamily, int pt, FontStyle style)
48                 {
49                         Initialize(null, null, fontfamily, pt, style);
50                 }
51                 public CPrivateFont(FontFamily fontfamily, int pt)
52                 {
53                         Initialize(null, null, fontfamily, pt, FontStyle.Regular);
54                 }
55                 public CPrivateFont(string fontpath, FontFamily fontfamily, int pt, FontStyle style)
56                 {
57                         Initialize(fontpath, null, fontfamily, pt, style);
58                 }
59                 public CPrivateFont(string fontpath, int pt, FontStyle style)
60                 {
61                         Initialize(fontpath, null, null, pt, style);
62                 }
63                 public CPrivateFont(string fontpath, int pt)
64                 {
65                         Initialize(fontpath, null, null, pt, FontStyle.Regular);
66                 }
67                 public CPrivateFont()
68                 {
69                         //throw new ArgumentException("CPrivateFont: 引数があるコンストラクタを使用してください。");
70                 }
71                 #endregion
72
73                 protected void Initialize(string fontpath, string baseFontPath, FontFamily fontfamily, int pt, FontStyle style)
74                 {
75                         this._pfc = null;
76                         this._fontfamily = null;
77                         this._font = null;
78                         this._pt = pt;
79                         this._rectStrings = new Rectangle(0, 0, 0, 0);
80                         this._ptOrigin = new Point(0, 0);
81                         this.bDispose完了済み = false;
82                         this._baseFontname = baseFontPath;
83
84                         if (fontfamily != null)
85                         {
86                                 this._fontfamily = fontfamily;
87                         }
88                         else
89                         {
90                                 try
91                                 {
92                                         this._pfc = new System.Drawing.Text.PrivateFontCollection();    //PrivateFontCollectionオブジェクトを作成する
93                                         this._pfc.AddFontFile(fontpath);                                                                //PrivateFontCollectionにフォントを追加する
94                                         _fontfamily = _pfc.Families[0];
95                                 }
96                                 catch (Exception e) when (e is System.IO.FileNotFoundException || e is System.Runtime.InteropServices.ExternalException)
97                                 {
98                                         Trace.TraceWarning(e.Message);
99                                         Trace.TraceWarning("プライベートフォントの追加に失敗しました({0})。代わりにMS PGothicの使用を試みます。", fontpath);
100                                         //throw new FileNotFoundException( "プライベートフォントの追加に失敗しました。({0})", Path.GetFileName( fontpath ) );
101                                         //return;
102
103                                         _fontfamily = null;
104                                 }
105
106                                 //foreach ( FontFamily ff in _pfc.Families )
107                                 //{
108                                 //      Debug.WriteLine( "fontname=" + ff.Name );
109                                 //      if ( ff.Name == Path.GetFileNameWithoutExtension( fontpath ) )
110                                 //      {
111                                 //              _fontfamily = ff;
112                                 //              break;
113                                 //      }
114                                 //}
115                                 //if ( _fontfamily == null )
116                                 //{
117                                 //      Trace.TraceError( "プライベートフォントの追加後、検索に失敗しました。({0})", fontpath );
118                                 //      return;
119                                 //}
120                         }
121
122                         // 指定されたフォントスタイルが適用できない場合は、フォント内で定義されているスタイルから候補を選んで使用する
123                         // 何もスタイルが使えないようなフォントなら、例外を出す。
124                         if (_fontfamily != null)
125                         {
126                                 if (!_fontfamily.IsStyleAvailable(style))
127                                 {
128                                         FontStyle[] FS = { FontStyle.Regular, FontStyle.Bold, FontStyle.Italic, FontStyle.Underline, FontStyle.Strikeout };
129                                         style = FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout;  // null非許容型なので、代わりに全盛をNGワードに設定
130                                         foreach (FontStyle ff in FS)
131                                         {
132                                                 if (this._fontfamily.IsStyleAvailable(ff))
133                                                 {
134                                                         style = ff;
135                                                         Trace.TraceWarning("フォント{0}へのスタイル指定を、{1}に変更しました。", Path.GetFileName(fontpath), style.ToString());
136                                                         break;
137                                                 }
138                                         }
139                                         if (style == (FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout))
140                                         {
141                                                 Trace.TraceWarning("フォント{0}は適切なスタイル{1}を選択できませんでした。", Path.GetFileName(fontpath), style.ToString());
142                                         }
143                                 }
144                                 //this._font = new Font(this._fontfamily, pt, style);                   //PrivateFontCollectionの先頭のフォントのFontオブジェクトを作成する
145                                 float emSize = pt * 96.0f / 72.0f;
146                                 this._font = new Font(this._fontfamily, emSize, style, GraphicsUnit.Pixel); //PrivateFontCollectionの先頭のフォントのFontオブジェクトを作成する
147                                                                                                                                                                                                                                                                                                                                                 //HighDPI対応のため、pxサイズで指定
148                         }
149                         else
150                         // フォントファイルが見つからなかった場合 (MS PGothicを代わりに指定する)
151                         {
152                                 float emSize = pt * 96.0f / 72.0f;
153                                 this._font = new Font("MS PGothic", emSize, style, GraphicsUnit.Pixel); //MS PGothicのFontオブジェクトを作成する
154                                 FontFamily[] ffs = new System.Drawing.Text.InstalledFontCollection().Families;
155                                 int lcid = System.Globalization.CultureInfo.GetCultureInfo("en-us").LCID;
156                                 foreach (FontFamily ff in ffs)
157                                 {
158                                         // Trace.WriteLine( lcid ) );
159                                         if (ff.GetName(lcid) == "MS PGothic")
160                                         {
161                                                 this._fontfamily = ff;
162                                                 Trace.TraceInformation("MS PGothicを代わりに指定しました。");
163                                                 return;
164                                         }
165                                 }
166                                 throw new FileNotFoundException("プライベートフォントの追加に失敗し、MS PGothicでの代替処理にも失敗しました。({0})", Path.GetFileName(fontpath));
167                         }
168                 }
169
170                 [Flags]
171                 protected enum DrawMode
172                 {
173                         Normal,
174                         Edge,
175                         Gradation
176                 }
177
178                 #region [ DrawPrivateFontのオーバーロード群 ]
179                 /// <summary>
180                 /// 文字列を描画したテクスチャを返す
181                 /// </summary>
182                 /// <param name="drawstr">描画文字列</param>
183                 /// <param name="fontColor">描画色</param>
184                 /// <returns>描画済テクスチャ</returns>
185                 public Bitmap DrawPrivateFont(string drawstr, Color fontColor)
186                 {
187                         return DrawPrivateFont(drawstr, DrawMode.Normal, fontColor, Color.White, Color.White, Color.White);
188                 }
189
190                 /// <summary>
191                 /// 文字列を描画したテクスチャを返す
192                 /// </summary>
193                 /// <param name="drawstr">描画文字列</param>
194                 /// <param name="fontColor">描画色</param>
195                 /// <param name="edgeColor">縁取色</param>
196                 /// <returns>描画済テクスチャ</returns>
197                 public Bitmap DrawPrivateFont(string drawstr, Color fontColor, Color edgeColor)
198                 {
199                         return DrawPrivateFont(drawstr, DrawMode.Edge, fontColor, edgeColor, Color.White, Color.White);
200                 }
201
202                 /// <summary>
203                 /// 文字列を描画したテクスチャを返す
204                 /// </summary>
205                 /// <param name="drawstr">描画文字列</param>
206                 /// <param name="fontColor">描画色</param>
207                 /// <param name="gradationTopColor">グラデーション 上側の色</param>
208                 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
209                 /// <returns>描画済テクスチャ</returns>
210                 //public Bitmap DrawPrivateFont( string drawstr, Color fontColor, Color gradationTopColor, Color gradataionBottomColor )
211                 //{
212                 //    return DrawPrivateFont( drawstr, DrawMode.Gradation, fontColor, Color.White, gradationTopColor, gradataionBottomColor );
213                 //}
214
215                 /// <summary>
216                 /// 文字列を描画したテクスチャを返す
217                 /// </summary>
218                 /// <param name="drawstr">描画文字列</param>
219                 /// <param name="fontColor">描画色</param>
220                 /// <param name="edgeColor">縁取色</param>
221                 /// <param name="gradationTopColor">グラデーション 上側の色</param>
222                 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
223                 /// <returns>描画済テクスチャ</returns>
224                 public Bitmap DrawPrivateFont(string drawstr, Color fontColor, Color edgeColor, Color gradationTopColor, Color gradataionBottomColor)
225                 {
226                         return DrawPrivateFont(drawstr, DrawMode.Edge | DrawMode.Gradation, fontColor, edgeColor, gradationTopColor, gradataionBottomColor);
227                 }
228
229 #if こちらは使わない // (Bitmapではなく、CTextureを返す版)
230                 /// <summary>
231                 /// 文字列を描画したテクスチャを返す
232                 /// </summary>
233                 /// <param name="drawstr">描画文字列</param>
234                 /// <param name="fontColor">描画色</param>
235                 /// <returns>描画済テクスチャ</returns>
236                 public CTexture DrawPrivateFont( string drawstr, Color fontColor )
237                 {
238                         Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Normal, fontColor, Color.White, Color.White, Color.White );
239                         return TextureFactory.tテクスチャの生成( bmp, false );
240                 }
241
242                 /// <summary>
243                 /// 文字列を描画したテクスチャを返す
244                 /// </summary>
245                 /// <param name="drawstr">描画文字列</param>
246                 /// <param name="fontColor">描画色</param>
247                 /// <param name="edgeColor">縁取色</param>
248                 /// <returns>描画済テクスチャ</returns>
249                 public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color edgeColor )
250                 {
251                         Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Edge, fontColor, edgeColor, Color.White, Color.White );
252                         return TextureFactory.tテクスチャの生成( bmp, false );
253                 }
254
255                 /// <summary>
256                 /// 文字列を描画したテクスチャを返す
257                 /// </summary>
258                 /// <param name="drawstr">描画文字列</param>
259                 /// <param name="fontColor">描画色</param>
260                 /// <param name="gradationTopColor">グラデーション 上側の色</param>
261                 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
262                 /// <returns>描画済テクスチャ</returns>
263                 //public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color gradationTopColor, Color gradataionBottomColor )
264                 //{
265                 //    Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Gradation, fontColor, Color.White, gradationTopColor, gradataionBottomColor );
266                 //        return TextureFactory.tテクスチャの生成( bmp, false );
267                 //}
268
269                 /// <summary>
270                 /// 文字列を描画したテクスチャを返す
271                 /// </summary>
272                 /// <param name="drawstr">描画文字列</param>
273                 /// <param name="fontColor">描画色</param>
274                 /// <param name="edgeColor">縁取色</param>
275                 /// <param name="gradationTopColor">グラデーション 上側の色</param>
276                 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
277                 /// <returns>描画済テクスチャ</returns>
278                 public CTexture DrawPrivateFont( string drawstr, Color fontColor, Color edgeColor,  Color gradationTopColor, Color gradataionBottomColor )
279                 {
280                         Bitmap bmp = DrawPrivateFont( drawstr, DrawMode.Edge | DrawMode.Gradation, fontColor, edgeColor, gradationTopColor, gradataionBottomColor );
281                         return TextureFactory.tテクスチャの生成( bmp, false );
282                 }
283 #endif
284                 #endregion
285
286                 /// <summary>
287                 /// 文字列を描画したテクスチャを返す(メイン処理)
288                 /// </summary>
289                 /// <param name="rectDrawn">描画された領域</param>
290                 /// <param name="ptOrigin">描画文字列</param>
291                 /// <param name="drawstr">描画文字列</param>
292                 /// <param name="drawmode">描画モード</param>
293                 /// <param name="fontColor">描画色</param>
294                 /// <param name="edgeColor">縁取色</param>
295                 /// <param name="gradationTopColor">グラデーション 上側の色</param>
296                 /// <param name="gradationBottomColor">グラデーション 下側の色</param>
297                 /// <returns>描画済テクスチャ</returns>
298                 protected Bitmap DrawPrivateFont(string drawstr, DrawMode drawmode, Color fontColor, Color edgeColor, Color gradationTopColor, Color gradationBottomColor)
299                 {
300                         if (this._fontfamily == null || drawstr == null || drawstr == "")
301                         {
302                                 // nullを返すと、その後bmp→texture処理や、textureのサイズを見て・・の処理で全部例外が発生することになる。
303                                 // それは非常に面倒なので、最小限のbitmapを返してしまう。
304                                 // まずはこの仕様で進めますが、問題有れば(上位側からエラー検出が必要であれば)例外を出したりエラー状態であるプロパティを定義するなり検討します。
305                                 if (drawstr != "")
306                                 {
307                                         Trace.TraceWarning("DrawPrivateFont()の入力不正。最小値のbitmapを返します。");
308                                 }
309                                 _rectStrings = new Rectangle(0, 0, 0, 0);
310                                 _ptOrigin = new Point(0, 0);
311                                 return new Bitmap(1, 1);
312                         }
313                         bool bEdge = ((drawmode & DrawMode.Edge) == DrawMode.Edge);
314                         bool bGradation = ((drawmode & DrawMode.Gradation) == DrawMode.Gradation);
315
316                         // 縁取りの縁のサイズは、とりあえずフォントの大きさの1/4とする
317                         int nEdgePt = (bEdge) ? _pt / 4 : 0;
318
319                         // 描画サイズを測定する
320                         Size stringSize = System.Windows.Forms.TextRenderer.MeasureText(drawstr, this._font, new Size(int.MaxValue, int.MaxValue),
321                                 System.Windows.Forms.TextFormatFlags.NoPrefix |
322                                 System.Windows.Forms.TextFormatFlags.NoPadding
323                         );
324
325                         //取得した描画サイズを基に、描画先のbitmapを作成する
326                         Bitmap bmp = new Bitmap(stringSize.Width + nEdgePt * 2, stringSize.Height + nEdgePt * 2);
327                         bmp.MakeTransparent();
328
329                         using (Graphics g = Graphics.FromImage(bmp))
330                         {
331                                 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
332
333                                 using (StringFormat sf = new StringFormat())
334                                 {
335                                         // 画面下部(垂直方向位置)
336                                         sf.LineAlignment = StringAlignment.Far;
337                                         // 画面中央(水平方向位置)
338                                         sf.Alignment = StringAlignment.Center;
339                                         sf.FormatFlags = StringFormatFlags.NoWrap;
340
341                                         // レイアウト枠
342                                         Rectangle r = new Rectangle(0, 0, stringSize.Width + nEdgePt * 2, stringSize.Height + nEdgePt * 2);
343
344                                         // 縁取り有りの描画
345                                         if (bEdge)
346                                         {
347                                                 // DrawPathで、ポイントサイズを使って描画するために、DPIを使って単位変換する
348                                                 // (これをしないと、単位が違うために、小さめに描画されてしまう)
349                                                 float sizeInPixels = _font.SizeInPoints * g.DpiY / 72;  // 1 inch = 72 points
350
351                                                 using (System.Drawing.Drawing2D.GraphicsPath gp = new System.Drawing.Drawing2D.GraphicsPath())
352                                                 {
353                                                         gp.AddString(drawstr, this._fontfamily, (int)this._font.Style, sizeInPixels, r, sf);
354
355                                                         // 縁取りを描画する
356                                                         using (Pen p = new Pen(edgeColor, nEdgePt))
357                                                         {
358                                                                 p.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
359                                                                 g.DrawPath(p, gp);
360
361                                                                 // 塗りつぶす
362                                                                 using (Brush br = bGradation ?
363                                                                         new LinearGradientBrush(r, gradationTopColor, gradationBottomColor, LinearGradientMode.Vertical) as Brush :
364                                                                         new SolidBrush(fontColor) as Brush)
365                                                                 {
366                                                                         g.FillPath(br, gp);
367                                                                 }
368                                                         }
369                                                 }
370                                         }
371                                         else
372                                         {
373                                                 // 縁取りなしの描画
374                                                 using (Brush br = new SolidBrush(fontColor))
375                                                 {
376                                                         g.DrawString(drawstr, _font, br, 0f, 0f);
377                                                 }
378                                                 // System.Windows.Forms.TextRenderer.DrawText(g, drawstr, _font, new Point(0, 0), fontColor);
379                                         }
380 #if debug表示
381                         g.DrawRectangle( new Pen( Color.White, 1 ), new Rectangle( 1, 1, stringSize.Width-1, stringSize.Height-1 ) );
382                         g.DrawRectangle( new Pen( Color.Green, 1 ), new Rectangle( 0, 0, bmp.Width - 1, bmp.Height - 1 ) );
383 #endif
384                                         _rectStrings = new Rectangle(0, 0, stringSize.Width, stringSize.Height);
385                                         _ptOrigin = new Point(nEdgePt * 2, nEdgePt * 2);
386                                 }
387                         }
388
389                         return bmp;
390                 }
391
392                 /// <summary>
393                 /// 最後にDrawPrivateFont()した文字列の描画領域を取得します。
394                 /// </summary>
395                 public Rectangle RectStrings
396                 {
397                         get
398                         {
399                                 return _rectStrings;
400                         }
401                         protected set
402                         {
403                                 _rectStrings = value;
404                         }
405                 }
406                 public Point PtOrigin
407                 {
408                         get
409                         {
410                                 return _ptOrigin;
411                         }
412                         protected set
413                         {
414                                 _ptOrigin = value;
415                         }
416                 }
417
418                 #region [ IDisposable 実装 ]
419                 //-----------------
420                 public void Dispose()
421                 {
422                         if (!this.bDispose完了済み)
423                         {
424                                 if (this._font != null)
425                                 {
426                                         this._font.Dispose();
427                                         this._font = null;
428                                 }
429                                 if (this._pfc != null)
430                                 {
431                                         this._pfc.Dispose();
432                                         this._pfc = null;
433                                 }
434                 if (this._fontfamily != null)
435                 {
436                     this._fontfamily.Dispose();
437                     this._fontfamily = null;
438                 }
439
440                 this.bDispose完了済み = true;
441                         }
442                 }
443                 //-----------------
444                 #endregion
445
446                 #region [ private ]
447                 //-----------------
448                 protected bool bDispose完了済み;
449                 protected Font _font;
450
451                 private System.Drawing.Text.PrivateFontCollection _pfc;
452                 private FontFamily _fontfamily;
453                 private int _pt;
454                 private Rectangle _rectStrings;
455                 private Point _ptOrigin;
456                 private string _baseFontname = null;
457                 //-----------------
458                 #endregion
459         }
460 }