--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SharpDX.Direct2D1;
+
+namespace FDK.メディア
+{
+ /// <summary>
+ /// レンダーターゲットとしても描画可能なビットマップを扱うクラス。
+ /// </summary>
+ public class 描画可能画像 : 画像
+ {
+ public 描画可能画像( string 画像ファイルパス )
+ : base( 画像ファイルパス )
+ {
+ }
+
+ public 描画可能画像( SharpDX.Size2 サイズ )
+ : base( null )
+ {
+ this._サイズ = サイズ;
+ }
+
+ protected override void On活性化( グラフィックデバイス gd )
+ {
+ if( this._画像ファイルパス.Nullでも空でもない() )
+ {
+ // (A) ファイルから生成する。
+ this._画像を生成する( gd, new BitmapProperties1() { BitmapOptions = BitmapOptions.Target } );
+ }
+ else
+ {
+ // (B) 空のビットマップを生成する。
+ this._Bitmap?.Dispose();
+ this._Bitmap = new Bitmap1( gd.D2DDeviceContext, this._サイズ, new BitmapProperties1() {
+ PixelFormat = new PixelFormat( gd.D2DDeviceContext.PixelFormat.Format, AlphaMode.Premultiplied ),
+ BitmapOptions = BitmapOptions.Target,
+ } );
+ }
+ }
+
+ /// <summary>
+ /// 生成済み画像(ビットマップ)に対するユーザアクションによる描画を行う。
+ /// </summary>
+ /// <remarks>
+ /// 活性化状態であれば、進行描画() 中でなくても、任意のタイミングで呼び出して良い。
+ /// ユーザアクション内では BeginDraw(), EndDraw() の呼び出しは(呼び出しもとでやるので)不要。
+ /// </remarks>
+ /// <param name="gd">グラフィックデバイス。</param>
+ /// <param name="描画アクション">Bitmap に対して行いたい操作。</param>
+ public void 画像へ描画する( グラフィックデバイス gd, Action<DeviceContext1> 描画アクション )
+ {
+ using( var 旧ターゲット = gd.D2DDeviceContext.Target )
+ {
+ try
+ {
+ gd.D2DDeviceContext.Target = this._Bitmap;
+ gd.D2DBatchDraw( ( dc ) => {
+ 描画アクション( dc );
+ } );
+ }
+ finally
+ {
+ gd.D2DDeviceContext.Target = 旧ターゲット;
+ }
+ }
+ }
+
+
+ private SharpDX.Size2 _サイズ;
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using SharpDX;
+using SharpDX.Direct2D1;
+using SharpDX.DirectWrite;
+using FDK;
+
+namespace FDK.メディア
+{
+ /// <summary>
+ /// DirectWrite を使った Direct2D1ビットマップ。
+ /// </summary>
+ /// <remarks>
+ /// 「表示文字列」メンバを設定/更新すれば、次回の描画時にビットマップが生成される。
+ /// </remarks>
+ public class 文字列画像 : Activity
+ {
+ /// <summary>
+ /// このメンバを set すれば、次回の進行描画時に画像が更新される。
+ /// </summary>
+ public string 表示文字列
+ {
+ get;
+ set;
+ } = null;
+
+ public string フォント名
+ {
+ get;
+ set;
+ } = "メイリオ";
+
+ public float フォントサイズpt
+ {
+ get;
+ set;
+ } = 20.0f;
+
+ public FontWeight フォント幅
+ {
+ get;
+ set;
+ } = FontWeight.Normal;
+
+ public FontStyle フォントスタイル
+ {
+ get;
+ set;
+ } = FontStyle.Normal;
+
+ public InterpolationMode 補正モード
+ {
+ get;
+ set;
+ } = InterpolationMode.Linear;
+
+ public RectangleF? 転送元矩形
+ {
+ get;
+ set;
+ } = null;
+
+ public bool 加算合成
+ {
+ get;
+ set;
+ } = false;
+
+ public Size2F レイアウトサイズ
+ {
+ get;
+ set;
+ } = new Size2F( -1f, -1f );
+
+ public bool 下詰め
+ {
+ get;
+ set;
+ } = false;
+
+
+ public 文字列画像()
+ {
+ }
+
+ public 文字列画像( string 文字列, float フォントサイズpt = 20.0f, string フォント名 = "メイリオ", FontWeight フォント幅 = FontWeight.Normal, FontStyle フォントスタイル = FontStyle.Normal )
+ : this()
+ {
+ this.表示文字列 = 文字列;
+ this.フォント名 = フォント名;
+ this.フォントサイズpt = フォントサイズpt;
+ this.フォント幅 = フォント幅;
+ this.フォントスタイル = フォントスタイル;
+ }
+
+ protected override void On活性化( グラフィックデバイス gd )
+ {
+ this._前回の表示文字列 = null;
+
+ if( this.表示文字列.Nullでも空でもない() )
+ {
+ this.ビットマップレンダーターゲットを生成する( gd );
+ this._前回の表示文字列 = this.表示文字列; // 最初の構築完了。
+ }
+ }
+
+ protected override void On非活性化( グラフィックデバイス gd )
+ {
+ FDKUtilities.解放する( ref this._黒ブラシ );
+ FDKUtilities.解放する( ref this._白ブラシ );
+ FDKUtilities.解放する( ref this.ビットマップレンダーターゲット );
+ FDKUtilities.解放する( ref this._テキストレイアウト );
+ FDKUtilities.解放する( ref this._テキストフォーマット );
+ }
+
+ public void 描画する( グラフィックデバイス gd, float 左位置, float 上位置, float 不透明度0to1 = 1.0f, float X方向拡大率 = 1.0f, float Y方向拡大率 = 1.0f, Matrix? 変換行列3D = null )
+ {
+ var 変換行列2D =
+ Matrix3x2.Scaling( X方向拡大率, Y方向拡大率 ) // スケーリング
+ * Matrix3x2.Translation( 左位置, 上位置 ); // 平行移動
+
+ this.描画する( gd, 変換行列2D, 変換行列3D, 不透明度0to1 );
+ }
+
+ public void 描画する( グラフィックデバイス gd, Matrix3x2? 変換行列2D = null, Matrix? 変換行列3D = null, float 不透明度0to1 = 1.0f )
+ {
+ Debug.Assert( this.活性化している );
+
+ if( this.表示文字列.Nullまたは空である() )
+ return;
+
+ // 表示文字列が変更されているなら、ここで表示ビットマップレンダーターゲットの再構築を行う。
+ if( false == string.Equals( this.表示文字列, this._前回の表示文字列 ) )
+ {
+ this.ビットマップレンダーターゲットを生成する( gd );
+ }
+
+ if( null == this.ビットマップレンダーターゲット )
+ return;
+
+ gd.D2DBatchDraw( ( dc ) => {
+
+ // 変換行列とブレンドモードをD2Dレンダーターゲットに設定する。
+ dc.Transform = ( 変換行列2D ?? Matrix3x2.Identity ) * dc.Transform;
+ dc.PrimitiveBlend = ( 加算合成 ) ? PrimitiveBlend.Add : PrimitiveBlend.SourceOver;
+
+ // D2Dレンダーターゲットに this.Bitmap を描画する。
+ using( var bmp = this.ビットマップレンダーターゲット.Bitmap )
+ {
+ dc.DrawBitmap(
+ bitmap: bmp,
+ destinationRectangle: null,
+ opacity: 不透明度0to1,
+ interpolationMode: this.補正モード,
+ sourceRectangle: this.転送元矩形,
+ erspectiveTransformRef: 変換行列3D );
+ }
+ } );
+ }
+
+
+ protected SharpDX.Direct2D1.BitmapRenderTarget ビットマップレンダーターゲット = null;
+
+ protected void ビットマップレンダーターゲットを生成する( グラフィックデバイス gd )
+ {
+ this._前回の表示文字列 = this.表示文字列;
+
+ // テキストフォーマット/レイアウトを作成し、表示ビットマップのサイズを計算する。
+ if( null == this._テキストフォーマット )
+ {
+ this._テキストフォーマット = new TextFormat( gd.DWriteFactory, this.フォント名, this.フォント幅, this.フォントスタイル, this.フォントサイズpt ) {
+ TextAlignment = TextAlignment.Leading,
+ };
+ }
+
+ this.レイアウトサイズ = new Size2F( gd.設計画面サイズ.Width, gd.設計画面サイズ.Height );
+
+ this._テキストレイアウト?.Dispose();
+ this._テキストレイアウト = new TextLayout(
+ gd.DWriteFactory,
+ this.表示文字列,
+ this._テキストフォーマット,
+ this.レイアウトサイズ.Width,
+ this.レイアウトサイズ.Height );
+
+ var 表示ビットマップのサイズ = new Size2F();
+ var 上マージン = 0.0f;
+
+ if( this.下詰め )
+ {
+ 表示ビットマップのサイズ = new Size2F(
+ this._テキストレイアウト.Metrics.WidthIncludingTrailingWhitespace,
+ this.レイアウトサイズ.Height ); // レイアウトの最大高
+
+ 上マージン = this.レイアウトサイズ.Height - this._テキストレイアウト.Metrics.Height;
+ }
+ else
+ {
+ 表示ビットマップのサイズ = new Size2F(
+ this._テキストレイアウト.Metrics.WidthIncludingTrailingWhitespace,
+ this._テキストレイアウト.Metrics.Height );
+ }
+
+ // 実際のビットマップサイズに更新。
+ this.レイアウトサイズ = 表示ビットマップのサイズ;
+
+ // ビットマップレンダーターゲットを生成する。
+ using( var target = gd.D2DDeviceContext.Target ) // Target を get すると COM参照カウンタが増えるので注意。
+ {
+ // D2DContext1.Target が設定済みであること。さもなきゃ例外も出さずに落ちる。
+ Debug.Assert( null != target );
+ }
+ this.ビットマップレンダーターゲット?.Dispose();
+ this.ビットマップレンダーターゲット = new SharpDX.Direct2D1.BitmapRenderTarget(
+ gd.D2DDeviceContext,
+ CompatibleRenderTargetOptions.None,
+ 表示ビットマップのサイズ );
+
+ // ブラシの作成がまだなら行う。
+ if( null == this._白ブラシ )
+ {
+ this._白ブラシ = new SolidColorBrush( this.ビットマップレンダーターゲット, Color.LightGray );
+ }
+ if( null == this._黒ブラシ )
+ {
+ this._黒ブラシ = new SolidColorBrush( this.ビットマップレンダーターゲット, Color.Black );
+ }
+
+ // ビットマップレンダーターゲットにテキストを描画する。
+ gd.D2DBatchDraw( this.ビットマップレンダーターゲット, ( rt ) => {
+
+ rt.Clear( Color.Transparent );
+
+ rt.DrawTextLayout( // ドロップシャドウ
+ new Vector2( 1.0f, 上マージン + 1.0f ),
+ this._テキストレイアウト,
+ this._黒ブラシ,
+ DrawTextOptions.Clip );
+
+ rt.DrawTextLayout( // 本体
+ new Vector2( 0.0f, 上マージン ),
+ this._テキストレイアウト,
+ this._白ブラシ,
+ DrawTextOptions.Clip );
+ } );
+ }
+
+
+ private string _前回の表示文字列 = null;
+
+ private TextFormat _テキストフォーマット = null;
+
+ private TextLayout _テキストレイアウト = null;
+
+ private SolidColorBrush _白ブラシ = null;
+
+ private SolidColorBrush _黒ブラシ = null;
+ }
+}
/// それが困る場合には、<paramref name="描画先矩形を整数境界に合わせる"/> に true を指定すること。
/// ただし、これを true にした場合、タイルのように並べて描画した場合に1pxずれる場合がある。この場合は false にすること。
/// </remarks>
- public virtual void 描画する(
- グラフィックデバイス gd,
- float 左位置,
- float 上位置,
- float 不透明度0to1 = 1.0f,
- float X方向拡大率 = 1.0f,
- float Y方向拡大率 = 1.0f,
- RectangleF? 転送元矩形 = null,
- bool 描画先矩形を整数境界に合わせる = false,
- Matrix? 変換行列3D = null )
+ public virtual void 描画する( グラフィックデバイス gd, float 左位置, float 上位置, float 不透明度0to1 = 1.0f, float X方向拡大率 = 1.0f, float Y方向拡大率 = 1.0f, RectangleF? 転送元矩形 = null, bool 描画先矩形を整数境界に合わせる = false, Matrix? 変換行列3D = null )
{
Debug.Assert( this.活性化している );
/// <param name="変換行列3D">射影行列。</param>
/// <param name="不透明度0to1">不透明度。(0:透明~1:不透明)</param>
/// <param name="転送元矩形">描画する画像範囲。</param>
- public virtual void 描画する(
- グラフィックデバイス gd,
- Matrix3x2? 変換行列2D = null,
- Matrix? 変換行列3D = null,
- float 不透明度0to1 = 1.0f,
- RectangleF? 転送元矩形 = null )
+ public virtual void 描画する( グラフィックデバイス gd, Matrix3x2? 変換行列2D = null, Matrix? 変換行列3D = null, float 不透明度0to1 = 1.0f, RectangleF? 転送元矩形 = null )
{
Debug.Assert( this.活性化している );
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+
+namespace FDK.メディア
+{
+ /// <summary>
+ /// 任意個の文字を格納した一枚の画像と、それぞれの文字領域の矩形リストから、文字列を連続するD2D画像で表示する。
+ /// </summary>
+ public class 画像フォント : Activity
+ {
+ public 画像フォント( string 文字盤の画像ファイルパス, string 文字矩形リストファイルパス )
+ : this( 文字盤の画像ファイルパス, new 矩形リスト( 文字矩形リストファイルパス ) )
+ {
+ }
+
+ public 画像フォント( string 文字盤の画像ファイルパス, 矩形リスト 文字矩形リスト )
+ {
+ this.子リスト.Add( this._文字盤 = new 画像( 文字盤の画像ファイルパス ) );
+ this._文字矩形リスト = 文字矩形リスト;
+ }
+
+ public void 描画する( グラフィックデバイス gd, float 左位置, float 上位置, string 表示文字列 )
+ {
+ if( 表示文字列.Nullまたは空である() )
+ return;
+
+ // 有効文字(矩形リストに登録されている文字)の矩形、文字数を抽出し、文字列全体のサイズを計算する。
+ var 有効文字矩形リスト =
+ from 文字 in 表示文字列
+ where ( this._文字矩形リスト.文字列to矩形.ContainsKey( new string( new char[] { 文字 } ) ) )
+ select this._文字矩形リスト.文字列to矩形[ new string( new char[] { 文字 } ) ];
+
+ int 有効文字数 = 有効文字矩形リスト.Count();
+ if( 0 == 有効文字数 )
+ return;
+
+ var 文字列全体のサイズ = SharpDX.Size2F.Empty;
+ foreach( var 文字矩形 in 有効文字矩形リスト )
+ {
+ 文字列全体のサイズ.Width += 文字矩形.Width;
+
+ if( 文字列全体のサイズ.Height < 文字矩形.Height )
+ 文字列全体のサイズ.Height = 文字矩形.Height; // 文字列全体の高さは、最大の文字高に一致。
+ }
+
+ // 描画する。
+ for( int i = 0; i < 有効文字数; i++ )
+ {
+ var 文字矩形 = 有効文字矩形リスト.ElementAt( i );
+
+ this._文字盤.描画する( gd, 左位置, 上位置, 転送元矩形: 文字矩形 );
+
+ 左位置 += 文字矩形.Width;
+ }
+ }
+
+
+ private 画像 _文字盤 = null;
+
+ private 矩形リスト _文字矩形リスト = null;
+ }
+}