FreeTypeSharpを使った文字列描画（MonoGame用ラッパー）

# 1.前回の内容
https://qiita.com/fumifumi0453/items/2d2aadd15b42e2b4f2c3

# 2.目標
　日本語文字列をMonoGameのTexture2Dへ変換してDrawメソッドで描画する。

# 3.ソース
https://github.com/fumifumi0453/SimpleFreeTypeSharp

# 4.説明
### 4.1.前回部分の変更点
#### 4.1.1.static変数をメンバ変数へ変更
　ImageFontの一部のstatic変数をメンバ変数に変更しました。
　これにより、１種類のフォント、サイズしか使えなかった問題を解決しました。
~~~C#:ImageFont (before)
        private static FreeTypeLibrary _Library;
        private static FreeTypeFaceFacade _FaceFacade;
        private static float _Size;
~~~

~~~C#:ImageFont (after)
        private static FreeTypeLibrary FTL_Library;
        private const int INT_FontSize = 12;

        private FreeTypeFaceFacade _FaceFacade;
        private float _Size;
~~~

#### 4.1.2.その他細かな修正
　無駄な処理とかを消したり書き直しました。

### 4.2. 追加したクラス
#### 4,2,1. TextTextureクラス
　Texture2Dを管理するクラス。Texture2Dは継承してないので名付けは失敗かも。
　GetTextureメソッドでTexture2Dを取得して描画する。
　Texture2Dオブジェクトは不要になったらDisposeが必要だが、不要になるタイミングがちょっと掴みにくい＆描画毎に生成するのって違うよねっていうところからTextTextureは管理クラスという扱いになりました。
　その代わり、文字列の変更も受け付けられるようになったし良いよね…？
~~~C#:TextTexture (GetTexture()メソッド周り)
        public Texture2D GetTexture()
        {
            if (string.Equals(_ImageData.Value, Text) == false || Color.Equals(_PrevColor, Color) == false || _PrevViewLength != _ViewLength)
            {
                if (string.Equals(_ImageData.Value, Text) == false)
                {
                    _ImageData = _Font.Render(Text);
                }

                _Texture?.Dispose();
                _Texture = CreateTexture2D(_Device, _ImageData.ToImageData(0, _ViewLength), Color);
                _PrevColor = Color;
                _PrevViewLength = _ViewLength;
            }

            return _Texture;
        }
        public static Texture2D CreateTexture2D(GraphicsDevice device, ImageData image, Color color)
        {
            var texture = new Texture2D(device, image.Width, image.Height);
            var array = new Color[image.Width * image.Height];

            var clrTable = new Dictionary<int, Color>(256);
            for (int i = 0; i < array.Length; i++)
            {
                var alpha = image.Datas[i];
                if (alpha == 0) continue;

                Color clr;

                if (clrTable.TryGetValue(alpha, out clr) == false)
                {
                    //clr = new Color(color, color.A * alpha / 255);
                    clr = Color.Multiply(color, alpha / 255f);
                    clrTable[alpha] = clr;
                }

                array[i] = clr;
            }

            texture.SetData(array);
            return texture;
        }
~~~
　ちょっと納得いかないのがColorの扱い
　前回の呼出メソッドでは"System.Drawing.Color"構造体、今回は"Microsoft.Xna.Framework.Color"構造体なので違うのが当然と言えばそうなのだが、なにか納得いかない。

　以下が２つの比較である。
~~~C#:System.Drawing.Color構造体の場合
            var baseColor = Color.DarkGreen;
            var image = new Bitmap(data.Width, data.Height);

            for (int i = 0; i < data.Width; i++)
            {
                for (int j = 0; j < data.Height; j++)
                {
                    var tmp = data.GetData(i, j);
                    if (tmp == 0) continue;
                    image.SetPixel(i, j, Color.FromArgb(tmp, baseColor));
                }
            }
~~~
　テストコードなので実行速度とかは気にしないで欲しい。
　それよりも、System.Drawing.ColorではColor.FromArgb(int, color)を呼んでいるところに注目して欲しい。
　ここではベースカラーに対してAlpha値を変更する方法で色を決定している。
~~~C#:Microsoft.Xna.Framework.Color構造体の場合
            var clrTable = new Dictionary<int, Color>(256);
            for (int i = 0; i < array.Length; i++)
            {
                var alpha = image.Datas[i];
                if (alpha == 0) continue;

                Color clr;

                if (clrTable.TryGetValue(alpha, out clr) == false)
                {
                    //clr = new Color(color, color.A * alpha / 255);
                    clr = Color.Multiply(color, alpha / 255f);
                    clrTable[alpha] = clr;
                }

                array[i] = clr;
            }
~~~
　そしてこちらはMicrosoft.Xna.Framework.Colorの場合だ。
　Color.Multiply(Color, int)を呼んでいる。そう乗算しているのだ!!
　これには理由があって、比較的System.Drawing.Colorに近いnew Color(Color, int)との実行結果の比較画像が以下のとおりです。
　■new Color(Color, int)の場合
![TestApp2_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741278/3182a626-e216-6fc8-9ad0-69d5a8f6a276.png)
　■Color.Multiply(Color, int)の場合
![TestApp2_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741278/6bf8ad24-bd28-fd00-e996-5a4d39d4e1ee.png)

………なんでこうなるんでしょうね？
MonoGameさんは何か余計なことしてるのかな？調べてないのでわからないよ。

まぁとにかく挙動が違うので注意が必要。

#### 4.2.2. TextTextureFactoryクラス
　TextTextureを作成するクラス
　Textureを生成するためにGraphicsDeviceが必要だったり、ImageFontの生成を一元化するために作成。

# 5.呼び出し側ソース
~~~C#:Game1
        TextTexture textTexture; // メンバ変数

        // (………省略)

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here

            TextTextureFactory.Parent = this;
            TextTextureFactory.LoadFont("Default", @"JF-Dot-MPlusH12.ttf", 24);
            textTexture = TextTextureFactory.Create("Default", "日本語でおｋ", Color.Violet);
        }
        
        // (………省略)

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            _spriteBatch.Begin();

            _spriteBatch.Draw(textTexture.GetTexture(), new Vector2(10, 40), Color.White);
            _spriteBatch.End();

            base.Draw(gameTime);
        }
~~~

# 6. 終わりに
　とりあえず日本語表示できるようになったのでよかったですね。
　これでやっとゲーム作成の第一歩…

# 7. 続き
https://qiita.com/fumifumi0453/items/de9b79b9feed23d0d71d