OSDN Git Service

FDK.メディア 名前空間を実装。
authorくまかみ工房 <kumakamikoubou@gmail.com>
Sat, 17 Sep 2016 16:58:45 +0000 (01:58 +0900)
committerくまかみ工房 <kumakamikoubou@gmail.com>
Sat, 17 Sep 2016 16:58:45 +0000 (01:58 +0900)
FDK24/FDK24.csproj
FDK24/メディア/XML.cs [new file with mode: 0644]
FDK24/メディア/テクスチャ.cs
FDK24/メディア/動画.cs [new file with mode: 0644]
FDK24/メディア/描画可能画像.cs [new file with mode: 0644]
FDK24/メディア/文字列画像.cs [new file with mode: 0644]
FDK24/メディア/画像.cs [new file with mode: 0644]
FDK24/メディア/矩形リスト.cs [new file with mode: 0644]

index 005ce23..0e4f2b6 100644 (file)
     <Compile Include="カウンタ\単純増加後反復カウンタ.cs" />
     <Compile Include="カウンタ\定間隔進行.cs" />
     <Compile Include="フォルダ.cs" />
+    <Compile Include="メディア\XML.cs" />
+    <Compile Include="メディア\動画.cs" />
+    <Compile Include="メディア\描画可能画像.cs" />
+    <Compile Include="メディア\文字列画像.cs" />
+    <Compile Include="メディア\画像.cs" />
+    <Compile Include="メディア\矩形リスト.cs" />
     <Compile Include="メディア\サウンド\WASAPI排他\ExclusiveDevice.cs" />
     <Compile Include="メディア\サウンド\WASAPI排他\MFAsyncCallback.cs" />
     <Compile Include="メディア\サウンド\WASAPI排他\Mixer.cs" />
diff --git a/FDK24/メディア/XML.cs b/FDK24/メディア/XML.cs
new file mode 100644 (file)
index 0000000..2deebb6
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+
+namespace FDK.メディア
+{
+       /// <summary>
+       /// この名前空間で使用するXML用の各種文字列定数。nameof() と一緒に使う。
+       /// </summary>
+       enum XML
+       {
+               // 要素・属性名
+               Root,
+               SubImage,
+               Name,
+               Rectangle,
+       }
+}
index 80b8c91..8c58bf7 100644 (file)
@@ -7,117 +7,6 @@ namespace FDK.メディア
 {
        public class テクスチャ : FDK.Activity
        {
-               // 全インスタンス共通項目(static)
-
-               public static void 共有リソースを作成する( FDK.メディア.デバイスリソース dr )
-               {
-                       var d3dDevice = (SharpDX.Direct3D11.Device) null;
-                       using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( dr.DXGIDeviceManager, out d3dDevice ) )
-                       {
-                               #region " 頂点シェーダ "
-                               //----------------
-                               var シェーダコンパイルのオプション =
-                               SharpDX.D3DCompiler.ShaderFlags.Debug |
-                               SharpDX.D3DCompiler.ShaderFlags.SkipOptimization |
-                               SharpDX.D3DCompiler.ShaderFlags.EnableStrictness |
-                               SharpDX.D3DCompiler.ShaderFlags.PackMatrixColumnMajor;
-
-                               // シェーダコードをコンパイルする。
-                               using( var code = SharpDX.D3DCompiler.ShaderBytecode.Compile(
-                                       FDK.Properties.Resources.テクスチャ用シェーダコード,
-                                       "VS", "vs_4_0", シェーダコンパイルのオプション ) )
-                               {
-                                       // 頂点シェーダを生成する。
-                                       テクスチャ.VertexShader = new SharpDX.Direct3D11.VertexShader( d3dDevice, code );
-                               }
-                               //----------------
-                               #endregion
-                               #region " ピクセルシェーダ "
-                               //----------------
-                               // シェーダコードをコンパイルする。
-                               using( var code = SharpDX.D3DCompiler.ShaderBytecode.Compile(
-                                       FDK.Properties.Resources.テクスチャ用シェーダコード,
-                                       "PS", "ps_4_0", シェーダコンパイルのオプション ) )
-                               {
-                                       // ピクセルシェーダを作成する。
-                                       テクスチャ.PixelShader = new SharpDX.Direct3D11.PixelShader( d3dDevice, code );
-                               }
-                               //----------------
-                               #endregion
-                               #region " ブレンドステート "
-                               //----------------
-                               var BlendState = new SharpDX.Direct3D11.BlendStateDescription() {
-                                       AlphaToCoverageEnable = false,  // アルファマスクで透過する(するならZバッファ必須)
-                                       IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
-                               };
-                               BlendState.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
-                               BlendState.RenderTarget[ 0 ].RenderTargetWriteMask = SharpDX.Direct3D11.ColorWriteMaskFlags.All;        // RGBA の書き込みマスク。
-
-                               // アルファ値のブレンディング設定 ... 特になし
-                               BlendState.RenderTarget[ 0 ].SourceAlphaBlend = SharpDX.Direct3D11.BlendOption.One;
-                               BlendState.RenderTarget[ 0 ].DestinationAlphaBlend = SharpDX.Direct3D11.BlendOption.Zero;
-                               BlendState.RenderTarget[ 0 ].AlphaBlendOperation = SharpDX.Direct3D11.BlendOperation.Add;
-
-                               // 色値のブレンディング設定 ... アルファ強度に応じた透明合成(テクスチャのアルファ値は、テクスチャのアルファ×ピクセルシェーダでの全体アルファとする(HLSL参照))
-                               BlendState.RenderTarget[ 0 ].SourceBlend = SharpDX.Direct3D11.BlendOption.SourceAlpha;
-                               BlendState.RenderTarget[ 0 ].DestinationBlend = SharpDX.Direct3D11.BlendOption.InverseSourceAlpha;
-                               BlendState.RenderTarget[ 0 ].BlendOperation = SharpDX.Direct3D11.BlendOperation.Add;
-
-                               // ブレンドステートを作成する。
-                               テクスチャ.BlendState = new SharpDX.Direct3D11.BlendState( d3dDevice, BlendState );
-                               //----------------
-                               #endregion
-                               #region " ラスタライザステート "
-                               //----------------
-                               var RSDesc = new SharpDX.Direct3D11.RasterizerStateDescription() {
-                                       FillMode = SharpDX.Direct3D11.FillMode.Solid,   // 普通に描画する
-                                       CullMode = SharpDX.Direct3D11.CullMode.None,    // 両面を描画する
-                                       IsFrontCounterClockwise = false,    // 時計回りが表面
-                                       DepthBias = 0,
-                                       DepthBiasClamp = 0,
-                                       SlopeScaledDepthBias = 0,
-                                       IsDepthClipEnabled = true,
-                                       IsScissorEnabled = false,
-                                       IsMultisampleEnabled = false,
-                                       IsAntialiasedLineEnabled = false,
-                               };
-                               テクスチャ.RasterizerState = new SharpDX.Direct3D11.RasterizerState( d3dDevice, RSDesc );
-                               //----------------
-                               #endregion
-                               #region " サンプラーステート "
-                               //----------------
-                               var descSampler = new SharpDX.Direct3D11.SamplerStateDescription() {
-                                       Filter = SharpDX.Direct3D11.Filter.Anisotropic,
-                                       AddressU = SharpDX.Direct3D11.TextureAddressMode.Wrap,
-                                       AddressV = SharpDX.Direct3D11.TextureAddressMode.Wrap,
-                                       AddressW = SharpDX.Direct3D11.TextureAddressMode.Wrap,
-                                       MipLodBias = 0.0f,
-                                       MaximumAnisotropy = 2,
-                                       ComparisonFunction = SharpDX.Direct3D11.Comparison.Never,
-                                       BorderColor = new SharpDX.Mathematics.Interop.RawColor4( 0f, 0f, 0f, 0f ),
-                                       MinimumLod = float.MinValue,
-                                       MaximumLod = float.MaxValue,
-                               };
-                               テクスチャ.SamplerState = new SharpDX.Direct3D11.SamplerState( d3dDevice, descSampler );
-                               //----------------
-                               #endregion
-                       }
-               }
-               public static void 共有リソースを解放する()
-               {
-                       FDK.Utilities.解放する( ref テクスチャ.SamplerState );
-                       FDK.Utilities.解放する( ref テクスチャ.RasterizerState );
-                       FDK.Utilities.解放する( ref テクスチャ.BlendState );
-                       FDK.Utilities.解放する( ref テクスチャ.PixelShader );
-                       FDK.Utilities.解放する( ref テクスチャ.VertexShader );
-               }
-
-               protected static SharpDX.Direct3D11.VertexShader VertexShader = null;
-               protected static SharpDX.Direct3D11.PixelShader PixelShader = null;
-               protected static SharpDX.Direct3D11.BlendState BlendState = null;
-               protected static SharpDX.Direct3D11.RasterizerState RasterizerState = null;
-               protected static SharpDX.Direct3D11.SamplerState SamplerState = null;
-
                // 個別項目
 
                /// <summary>
@@ -263,5 +152,116 @@ namespace FDK.メディア
                protected SharpDX.Direct3D11.Buffer ConstantBuffer = null;
                protected SharpDX.Direct3D11.ShaderResourceView ShaderResourceView = null;
                protected SharpDX.Size2F ShaderResourceViewSize;
+
+               // 全インスタンス共通項目(static)
+
+               public static void 共有リソースを作成する( FDK.メディア.デバイスリソース dr )
+               {
+                       var d3dDevice = (SharpDX.Direct3D11.Device) null;
+                       using( var d3dLock = new FDK.同期.AutoD3DDeviceLock( dr.DXGIDeviceManager, out d3dDevice ) )
+                       {
+                               #region " 頂点シェーダ "
+                               //----------------
+                               var シェーダコンパイルのオプション =
+                               SharpDX.D3DCompiler.ShaderFlags.Debug |
+                               SharpDX.D3DCompiler.ShaderFlags.SkipOptimization |
+                               SharpDX.D3DCompiler.ShaderFlags.EnableStrictness |
+                               SharpDX.D3DCompiler.ShaderFlags.PackMatrixColumnMajor;
+
+                               // シェーダコードをコンパイルする。
+                               using( var code = SharpDX.D3DCompiler.ShaderBytecode.Compile(
+                                       FDK.Properties.Resources.テクスチャ用シェーダコード,
+                                       "VS", "vs_4_0", シェーダコンパイルのオプション ) )
+                               {
+                                       // 頂点シェーダを生成する。
+                                       テクスチャ.VertexShader = new SharpDX.Direct3D11.VertexShader( d3dDevice, code );
+                               }
+                               //----------------
+                               #endregion
+                               #region " ピクセルシェーダ "
+                               //----------------
+                               // シェーダコードをコンパイルする。
+                               using( var code = SharpDX.D3DCompiler.ShaderBytecode.Compile(
+                                       FDK.Properties.Resources.テクスチャ用シェーダコード,
+                                       "PS", "ps_4_0", シェーダコンパイルのオプション ) )
+                               {
+                                       // ピクセルシェーダを作成する。
+                                       テクスチャ.PixelShader = new SharpDX.Direct3D11.PixelShader( d3dDevice, code );
+                               }
+                               //----------------
+                               #endregion
+                               #region " ブレンドステート "
+                               //----------------
+                               var BlendState = new SharpDX.Direct3D11.BlendStateDescription() {
+                                       AlphaToCoverageEnable = false,  // アルファマスクで透過する(するならZバッファ必須)
+                                       IndependentBlendEnable = false, // 個別設定。false なら BendStateDescription.RenderTarget[0] だけが有効で、[1~7] は無視される。
+                               };
+                               BlendState.RenderTarget[ 0 ].IsBlendEnabled = true; // true ならブレンディングが有効。
+                               BlendState.RenderTarget[ 0 ].RenderTargetWriteMask = SharpDX.Direct3D11.ColorWriteMaskFlags.All;        // RGBA の書き込みマスク。
+
+                               // アルファ値のブレンディング設定 ... 特になし
+                               BlendState.RenderTarget[ 0 ].SourceAlphaBlend = SharpDX.Direct3D11.BlendOption.One;
+                               BlendState.RenderTarget[ 0 ].DestinationAlphaBlend = SharpDX.Direct3D11.BlendOption.Zero;
+                               BlendState.RenderTarget[ 0 ].AlphaBlendOperation = SharpDX.Direct3D11.BlendOperation.Add;
+
+                               // 色値のブレンディング設定 ... アルファ強度に応じた透明合成(テクスチャのアルファ値は、テクスチャのアルファ×ピクセルシェーダでの全体アルファとする(HLSL参照))
+                               BlendState.RenderTarget[ 0 ].SourceBlend = SharpDX.Direct3D11.BlendOption.SourceAlpha;
+                               BlendState.RenderTarget[ 0 ].DestinationBlend = SharpDX.Direct3D11.BlendOption.InverseSourceAlpha;
+                               BlendState.RenderTarget[ 0 ].BlendOperation = SharpDX.Direct3D11.BlendOperation.Add;
+
+                               // ブレンドステートを作成する。
+                               テクスチャ.BlendState = new SharpDX.Direct3D11.BlendState( d3dDevice, BlendState );
+                               //----------------
+                               #endregion
+                               #region " ラスタライザステート "
+                               //----------------
+                               var RSDesc = new SharpDX.Direct3D11.RasterizerStateDescription() {
+                                       FillMode = SharpDX.Direct3D11.FillMode.Solid,   // 普通に描画する
+                                       CullMode = SharpDX.Direct3D11.CullMode.None,    // 両面を描画する
+                                       IsFrontCounterClockwise = false,    // 時計回りが表面
+                                       DepthBias = 0,
+                                       DepthBiasClamp = 0,
+                                       SlopeScaledDepthBias = 0,
+                                       IsDepthClipEnabled = true,
+                                       IsScissorEnabled = false,
+                                       IsMultisampleEnabled = false,
+                                       IsAntialiasedLineEnabled = false,
+                               };
+                               テクスチャ.RasterizerState = new SharpDX.Direct3D11.RasterizerState( d3dDevice, RSDesc );
+                               //----------------
+                               #endregion
+                               #region " サンプラーステート "
+                               //----------------
+                               var descSampler = new SharpDX.Direct3D11.SamplerStateDescription() {
+                                       Filter = SharpDX.Direct3D11.Filter.Anisotropic,
+                                       AddressU = SharpDX.Direct3D11.TextureAddressMode.Wrap,
+                                       AddressV = SharpDX.Direct3D11.TextureAddressMode.Wrap,
+                                       AddressW = SharpDX.Direct3D11.TextureAddressMode.Wrap,
+                                       MipLodBias = 0.0f,
+                                       MaximumAnisotropy = 2,
+                                       ComparisonFunction = SharpDX.Direct3D11.Comparison.Never,
+                                       BorderColor = new SharpDX.Mathematics.Interop.RawColor4( 0f, 0f, 0f, 0f ),
+                                       MinimumLod = float.MinValue,
+                                       MaximumLod = float.MaxValue,
+                               };
+                               テクスチャ.SamplerState = new SharpDX.Direct3D11.SamplerState( d3dDevice, descSampler );
+                               //----------------
+                               #endregion
+                       }
+               }
+               public static void 共有リソースを解放する()
+               {
+                       FDK.Utilities.解放する( ref テクスチャ.SamplerState );
+                       FDK.Utilities.解放する( ref テクスチャ.RasterizerState );
+                       FDK.Utilities.解放する( ref テクスチャ.BlendState );
+                       FDK.Utilities.解放する( ref テクスチャ.PixelShader );
+                       FDK.Utilities.解放する( ref テクスチャ.VertexShader );
+               }
+
+               protected static SharpDX.Direct3D11.VertexShader VertexShader = null;
+               protected static SharpDX.Direct3D11.PixelShader PixelShader = null;
+               protected static SharpDX.Direct3D11.BlendState BlendState = null;
+               protected static SharpDX.Direct3D11.RasterizerState RasterizerState = null;
+               protected static SharpDX.Direct3D11.SamplerState SamplerState = null;
        }
 }
diff --git a/FDK24/メディア/動画.cs b/FDK24/メディア/動画.cs
new file mode 100644 (file)
index 0000000..aa64134
--- /dev/null
@@ -0,0 +1,721 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using FDK.同期;
+
+namespace FDK.メディア
+{
+       /// <remarks>
+       /// 活性化後の最初の進行描画時に、再生が開始される。
+       /// </remarks>
+       public unsafe class 動画 : Activity
+       {
+               public enum 再生状態ID { 未再生, 再生中, 再生終了済み }
+               public FDK.同期.RWLock<再生状態ID> 再生状態
+               {
+                       get;
+               } = new RWLock<再生状態ID>( 再生状態ID.未再生 );
+               public bool 加算合成
+               {
+                       get;
+                       set;
+               } = false;
+               public float 不透明度0to1
+               {
+                       get;
+                       set;
+               } = 1.0f;
+               public bool ループ再生する
+               {
+                       get;
+                       set;
+               } = false;
+
+               public 動画( string 動画ファイルパス )
+               {
+                       this.動画ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 動画ファイルパス );
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       this.再生状態.Value = 再生状態ID.未再生;
+                       this.前フレームの時刻 = FDK.カウンタ.QPCTimer.未使用;
+                       this.ループした際の先頭時刻 = 0;
+
+                       string 変数付きファイルパス = フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス );
+                       var result = SharpDX.Result.Ok;
+                       try
+                       {
+                               this.動画の生成に成功した = false;
+
+                               // 動画ファイルパスを基に、SourceReaderEx, MediaType, フレームサイズ, 描画先WICビットマップを準備する。
+
+                               #region " 動画ファイルパスの有効性を確認する。"
+                               //-----------------
+                               if( string.IsNullOrEmpty( this.動画ファイルパス ) )
+                               {
+                                       Log.ERROR( $"動画ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               if( false == System.IO.File.Exists( this.動画ファイルパス ) )
+                               {
+                                       Log.ERROR( $"動画ファイルが存在しません。[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 動画のファイルパス(URI扱い)から SourceReaderEx を作成する。"
+                               //-----------------
+                               try
+                               {
+                                       using( var 属性 = new SharpDX.MediaFoundation.MediaAttributes() )
+                                       {
+                                               // DXVAに対応しているGPUの場合にはそれをデコードに利用するよう指定する。
+                                               属性.Set<SharpDX.ComObject>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.D3DManager, dr.DXGIDeviceManager );
+
+                                               // 追加のビデオプロセッシングを有効にする。
+                                               属性.Set<bool>( SharpDX.MediaFoundation.SourceReaderAttributeKeys.EnableAdvancedVideoProcessing, true );  // bool だったり
+
+                                               // 追加のビデオプロセッシングを有効にしたら、こちらは無効に。
+                                               属性.Set<int>( SharpDX.MediaFoundation.SinkWriterAttributeKeys.ReadwriteDisableConverters, 0 );           // int だったり
+
+                                               // 属性を使って、SourceReaderEx を生成。
+                                               using( var sourceReader = new SharpDX.MediaFoundation.SourceReader( this.動画ファイルパス, 属性 ) )
+                                               {
+                                                       this.SourceReaderEx = sourceReader.QueryInterface<SharpDX.MediaFoundation.SourceReaderEx>();
+                                               }
+                                       }
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"SourceReaderEx の作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 最初のビデオストリームを選択し、その他のすべてのストリームを非選択にする。"
+                               //-----------------
+                               try
+                               {
+                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.AllStreams, false );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"すべてのストリームの選択解除に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               try
+                               {
+                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"最初のビデオストリームの選択に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " ARGB32 フォーマットに合わせたメディアタイプを作成し、SourceReaderEx に登録してデコーダを準備する。"
+                               //-----------------
+                               try
+                               {
+                                       using( var mediaType = new SharpDX.MediaFoundation.MediaType() )
+                                       {
+                                               // フォーマットを部分メディアタイプの属性に設定。
+                                               mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.MajorType, SharpDX.MediaFoundation.MediaTypeGuids.Video );
+                                               mediaType.Set<Guid>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.Subtype, SharpDX.MediaFoundation.VideoFormatGuids.Argb32 ); // ARGB32 フォーマットで固定。
+
+                                               // 部分メディアタイプを SourceReaderEx にセットする。SourceReaderEx は、必要なデコーダをロードするだろう。
+                                               this.SourceReaderEx.SetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, mediaType );
+                                       }
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"MediaType (Video, ARGB32) の設定または必要なデコーダの読み込みに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " ビデオストリームが選択されていることを再度保証する。"
+                               //-----------------
+                               try
+                               {
+                                       this.SourceReaderEx.SetStreamSelection( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream, true );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"最初のビデオストリームの選択に失敗しました(MediaType 設定後)。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " デコーダの読み込みにより完成した完全メディアタイプを取得する。"
+                               //-----------------
+                               try
+                               {
+                                       this.MediaType = this.SourceReaderEx.GetCurrentMediaType( SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"完全メディアタイプの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " フレームサイズを取得する。動画の途中でのサイズ変更には対応しない。"
+                               //-----------------
+                               try
+                               {
+                                       var packedFrameSize = this.MediaType.Get<long>( SharpDX.MediaFoundation.MediaTypeAttributeKeys.FrameSize );
+                                       this.サイズdpx.Width = (int) ( ( packedFrameSize >> 32 ) & 0xFFFFFFFF );
+                                       this.サイズdpx.Height = (int) ( ( packedFrameSize ) & 0xFFFFFFFF );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"フレームサイズの取得に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 描画先WICビットマップを作成する。"
+                               //-----------------
+                               try
+                               {
+                                       this.WICビットマップ = new SharpDX.WIC.Bitmap(
+                                               dr.WicImagingFactory2,
+                                               this.サイズdpx.Width,
+                                               this.サイズdpx.Height,
+                                               SharpDX.WIC.PixelFormat.Format32bppBGR,
+                                               SharpDX.WIC.BitmapCreateCacheOption.CacheOnLoad );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"描画先 WIC ビットマップの作成に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       result = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+
+                               this.動画の生成に成功した = true;
+                               Log.Info( $"{Utilities.現在のメソッド名}: 動画を生成しました。[{変数付きファイルパス}]" );
+
+                               #region " デコードの前処理を行う。"
+                               //----------------
+                               this.デコードキャッシング();
+                               Log.Info( $"{Utilities.現在のメソッド名}: 動画のセットアップを行いました。[{変数付きファイルパス}]" );
+                               //----------------
+                               #endregion
+
+                               // 準備完了。以降は、サンプルを読み込みたいときに、適宜 ReadSample() する。
+                               // コールバックを使っていないので、IMFSourceReader::ReadSample() はサンプルが用意できるまでブロックすることに注意。
+                       }
+                       finally
+                       {
+                               if( result.Failure )
+                                       this.全リソースを解放する();
+
+                               // 失敗しても、一応活性化は成功とする。(進行描画はされない。)
+                       }
+               }
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       this.全リソースを解放する();
+                       Log.Info( $"{Utilities.現在のメソッド名}: 動画を解放しました。[{フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( this.動画ファイルパス )}]" );
+               }
+               public void 進行描画する( デバイスリソース dr, SharpDX.RectangleF 描画先矩形dpx, float 不透明度0to1 = 1.0f, bool ループ再生する = false )
+               {
+                       // Direct2D の行列は、設計単位じゃなく物理単位で計算するので注意。
+                       var 変換行列2Dpx = SharpDX.Matrix3x2.Identity
+                               * SharpDX.Matrix3x2.Scaling( 描画先矩形dpx.Width / this.サイズdpx.Width, 描画先矩形dpx.Height / this.サイズdpx.Height )  // スケーリング。
+                               * dr.行列を単位変換するDPXtoPX( SharpDX.Matrix3x2.Translation( 描画先矩形dpx.Left, 描画先矩形dpx.Top ) );  // 平行移動(物理単位)、
+
+                       // 描画する。
+                       this.進行描画する( dr, 変換行列2Dpx );
+               }
+               public void 進行描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列 )
+               {
+                       Debug.Assert( this.活性化している );
+
+                       // 活性化でエラーが発生している場合は、何もしない(何も描画しない)。
+                       if( false == this.動画の生成に成功した )
+                               return;
+
+                       // 以下、再生状態とデコーダ状態に応じて処理分岐。
+                       switch( this.再生状態.Value )
+                       {
+                               case 再生状態ID.未再生:
+                                       // (A) 未再生なら、デコードタスクを開始する。
+                                       this.再生状態.Value = 再生状態ID.再生中;
+                                       this.デコードタスクを開始する();
+                                       break;
+
+                               case 再生状態ID.再生終了済み:
+                                       // (B) 再生が終了している場合は、何もしない(何も描画しない)。
+                                       break;
+
+                               case 再生状態ID.再生中:
+                                       // (C) 再生中なら、D2Dビットマップを描画する。
+                                       switch( this.デコーダ状態.Value )
+                                       {
+                                               case デコーダ状態ID.デコード中:
+                                                       // (C-a) タスクが次のサンプルフレームをデコード中なら、現在のD2Dビットマップ(直前のフレーム画像が入ってる)をもう一度描画する。
+                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
+                                                       break;
+
+                                               case デコーダ状態ID.完了:
+                                                       // (C-b) タスクの次のサンプルフレームのデコード処理が完了しているなら、サンプルをD2Dビットマップに転送し、それを描画し、デコードタスクを再び起動する。
+                                                       this.サンプルをD2Dビットマップに転送する( dr );
+                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
+                                                       this.デコードタスクを開始する();
+                                                       break;
+
+                                               case デコーダ状態ID.スキップ:
+                                                       // (C-c) タスクの次のサンプルフレームをスキップしたなら、現在のD2Dビットマップ(直前のフレーム画像が入ってる)をもう一度描画し、デコードタスクを再び起動する。
+                                                       this.D2Dビットマップを描画する( dr, 変換行列 );
+                                                       this.デコードタスクを開始する();
+                                                       break;
+                                       }
+                                       break;
+                       }
+               }
+
+               private enum デコーダ状態ID { 完了, デコード中, スキップ }
+               private readonly FDK.同期.RWLock<デコーダ状態ID> デコーダ状態 = new RWLock<デコーダ状態ID>( デコーダ状態ID.完了 );
+               private string 動画ファイルパス = "";
+               private bool 動画の生成に成功した = false;    // 活性化時に成功の成否を設定。
+               private SharpDX.Result LastSharpDXResult = SharpDX.Result.Ok;
+               private SharpDX.MediaFoundation.Sample Sample = null;
+               private SharpDX.MediaFoundation.SourceReaderEx SourceReaderEx = null;
+               private SharpDX.WIC.Bitmap WICビットマップ = null;    // MediaFoundation は WICBitmap に出力する。
+               private SharpDX.Direct2D1.Bitmap D2Dビットマップ = null;  // WICBitmap をこれに転送して D2D に Drawする。
+               private SharpDX.MediaFoundation.MediaType MediaType = null;
+               private SharpDX.Size2 サイズdpx = new SharpDX.Size2( 1, 1 );  // CopyMemory()で使うので、Size2F じゃなく Size2 を使う。
+               private FDK.カウンタ.QPCTimer タイマ = new FDK.カウンタ.QPCTimer();
+               private long ループした際の先頭時刻 = 0;
+               private long 前フレームの時刻 = FDK.カウンタ.QPCTimer.未使用;  // 未使用 = 先頭のフレームである
+
+               private unsafe void サンプルをD2Dビットマップに転送する( デバイスリソース dr )
+               {
+                       var buffer = (SharpDX.MediaFoundation.MediaBuffer) null;
+                       var buffer2d2 = (SharpDX.MediaFoundation.Buffer2D2) null;
+                       try
+                       {
+                               #region " サンプルからサンプルバッファ (MediaBuffer) を取得する。"
+                               //-----------------
+                               try
+                               {
+                                       buffer = this.Sample.ConvertToContiguousBuffer();
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"サンプルバッファの取得に失敗しました。(0x{e.HResult:x8})" );
+                                       this.LastSharpDXResult = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " サンプルバッファを Buffer2D2 にキャストする。"
+                               //-----------------
+                               try
+                               {
+                                       buffer2d2 = buffer.QueryInterface<SharpDX.MediaFoundation.Buffer2D2>();
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"サンプルバッファから Buffer2D2 へのキャストに失敗しました。(0x{e.HResult:x8})" );
+                                       this.LastSharpDXResult = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " サンプルバッファをロックする。"
+                               //-----------------
+                               byte[] scanLine0_bp = new byte[ 8 ];   // 生ポインタが格納される。32bitなら[0~3]、64bitなら[0~7]が有効。(CPUではなく.NETに依存)
+                               int pitch = 0;
+                               byte[] bufferStart_bp = new byte[ 8 ];  // 生ポインタが格納される。詳細は同上だが、ここでは受け取るだけで何もしない。
+                               int bufferLength;
+                               try
+                               {
+                                       buffer2d2.Lock2DSize(
+                                               SharpDX.MediaFoundation.Buffer2DLockFlags.Read,
+                                               scanLine0_bp,
+                                               out pitch,
+                                               bufferStart_bp,
+                                               out bufferLength );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"サンプルバッファのロックに失敗しました。(0x{e.HResult:x8})" );
+                                       this.LastSharpDXResult = e.ResultCode;
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               try
+                               {
+                                       #region " サンプルバッファのネイティブ先頭アドレスを取得する。"
+                                       //-----------------
+                                       byte* scanLine0 = null;
+                                       try
+                                       {
+                                               scanLine0 = (byte*) this.生ポインタを格納したbyte配列からポインタを取得して返す( scanLine0_bp );
+                                       }
+                                       catch( SharpDX.SharpDXException e )
+                                       {
+                                               Log.ERROR( $"サンプルバッファのアドレスの取得に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = e.ResultCode;
+                                               return;
+                                       }
+                                       //-----------------
+                                       #endregion
+                                       #region " サンプルバッファから描画先WICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピーする。"
+                                       //-----------------
+                                       try
+                                       {
+                                               // 描画先WICビットマップをロックする。
+                                               using( var bitmapLock = this.WICビットマップ.Lock(
+                                                       new SharpDX.Rectangle( 0, 0, this.WICビットマップ.Size.Width, this.WICビットマップ.Size.Height ),
+                                                       SharpDX.WIC.BitmapLockFlags.Write ) )
+                                               {
+                                                       // サンプルバッファからWICビットマップへ、ARGB32 を G8B8R8X8 に変換しながらコピー。
+                                                       int bitmapStride = bitmapLock.Stride;
+                                                       byte* src = scanLine0;
+                                                       byte* dest = (byte*) bitmapLock.Data.DataPointer.ToPointer();
+
+                                                       for( int y = 0; y < this.サイズdpx.Height; y++ )
+                                                       {
+                                                               // ARGB32 to G8B8R8X8 ではデータ変換が不要なので、一行を一気にコピー。
+                                                               動画.CopyMemory( dest, src, this.サイズdpx.Width * 4 );   // ARGB=4バイト。
+                                                               src += pitch;
+                                                               dest += bitmapStride;   // bitmapStride は byte 単位
+                                                       }
+                                               }
+                                       }
+                                       catch( SharpDX.SharpDXException e )
+                                       {
+                                               Log.ERROR( $"WICビットマップの Lock に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = e.ResultCode;
+                                               return;
+                                       }
+                                       catch( Exception e )
+                                       {
+                                               Log.ERROR( $"サンプルバッファから WIC ビットマップへのデータの転送に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = SharpDX.Result.GetResultFromException( e );
+                                               return;
+                                       }
+                                       //-----------------
+                                       #endregion
+                                       #region " 描画先WICビットマップからD2Dビットマップを生成する。 "
+                                       //-----------------
+                                       try
+                                       {
+                                               this.D2Dビットマップ?.Dispose();
+                                               this.D2Dビットマップ = SharpDX.Direct2D1.Bitmap.FromWicBitmap(
+                                                       dr.D2DContext1,
+                                                       this.WICビットマップ );
+                                       }
+                                       catch( SharpDX.SharpDXException e )
+                                       {
+                                               Log.ERROR( $"D2Dビットマップの作成に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = e.ResultCode;
+                                               return;
+                                       }
+                                       //-----------------
+                                       #endregion
+                               }
+                               finally
+                               {
+                                       #region " サンプルバッファのロックを解除する。"
+                                       //-----------------
+                                       buffer2d2.Unlock2D();
+                                       //-----------------
+                                       #endregion
+                               }
+                       }
+                       finally
+                       {
+                               FDK.Utilities.解放する( ref buffer2d2 );
+                               FDK.Utilities.解放する( ref buffer );
+                               if( this.LastSharpDXResult.Failure )
+                               {
+                                       // 描画に失敗したら、全リソースを解放して再生終了。
+                                       this.再生状態.Value = 再生状態ID.再生終了済み;
+                                       this.全リソースを解放する();
+                               }
+                       }
+               }
+               private void D2Dビットマップを描画する( デバイスリソース dr, SharpDX.Matrix3x2 変換行列2Dpx )
+               {
+                       if( null == this.D2Dビットマップ )
+                               return;
+
+                       Utilities.D2DBatchDraw( dr.D2DContext1, () => {
+
+                               // 変換行列とブレンドモードを設定する。
+                               dr.D2DContext1.Transform = 変換行列2Dpx;
+                               dr.D2DContext1.PrimitiveBlend = SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
+
+                               // D2Dビットマップを描画する。
+                               dr.D2DContext1.DrawBitmap( this.D2Dビットマップ, this.不透明度0to1, SharpDX.Direct2D1.InterpolationMode.Linear );
+                       } );
+               }
+               private void デコードタスクを開始する()
+               {
+                       // 再生中なら、デコードタスクをタスクとして起動する(タスクの完了を待たず、このメソッドはすぐ抜ける)。
+                       if( this.再生状態.Value == 再生状態ID.再生中 )
+                       {
+                               Task.Run( () => {
+                                       this.デコードタスク( this.ループ再生する );
+                               } );
+                       }
+               }
+               private void デコードタスク( bool ループ再生する )
+               {
+                       //  サンプル(フレーム)を1つ取得するタスク。
+
+                       this.デコーダ状態.Value = デコーダ状態ID.デコード中;
+                       this.LastSharpDXResult = SharpDX.Result.Ok;
+                       try
+                       {
+                               var streamFlags = SharpDX.MediaFoundation.SourceReaderFlags.None;
+                               long サンプルの時刻 = 0;
+
+                               #region " SourceReader から次のサンプル(フレーム)を1つ取得する。"
+                               //-----------------
+                               {
+                                       int actualStreamIndex = 0;
+                                       try
+                                       {
+                                               this.Sample?.Dispose();
+                                               this.Sample = this.SourceReaderEx.ReadSample(
+                                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
+                                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
+                                                       out actualStreamIndex,
+                                                       out streamFlags,
+                                                       out サンプルの時刻 );
+                                       }
+                                       catch( SharpDX.SharpDXException e )
+                                       {
+                                               Log.ERROR( $"SourceReaderEx.ReadSample() に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = e.ResultCode;
+                                               return;
+                                       }
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 取得結果フラグに応じて、必要な処理があれば行なう。"
+                               //---------------------------------------------------
+                               if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Endofstream ) )  // BOX化コストとか気にしない
+                               {
+                                       #region " ストリーム終了 "
+                                       //----------------
+                                       if( ループ再生する )   // メンバじゃなくパラメータのほう。
+                                       {
+                                               // (A) ループ再生する場合
+                                               FDK.Log.Info( "動画をループ再生します。" );
+                                               this.ループした際の先頭時刻 = this.前フレームの時刻;   // このフラグがセットされるときは、ReadSample() で返されるサンプル時刻は 0 になってるので使えない。
+                                               this.SourceReaderEx.SetCurrentPosition( 0 );  // ストリーム再生位置を先頭へ。
+                                               this.デコードタスク( ループ再生する ); // 再帰で先頭サンプルを取得して終了。
+                                               return;
+                                       }
+                                       else
+                                       {
+                                               // (B) ループ再生しない場合
+                                               FDK.Log.Info( "動画の再生を終了します。" );
+                                               this.再生状態.Value = 再生状態ID.再生終了済み;
+                                               this.LastSharpDXResult = SharpDX.Result.Ok; // SharpDX のエラーによる終了じゃないよと。
+                                               return;
+                                       }
+                                       //----------------
+                                       #endregion
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Newstream ) )
+                               {
+                                       // 未対応。
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Nativemediatypechanged ) )
+                               {
+                                       // 未対応。
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Currentmediatypechanged ) )
+                               {
+                                       // 未対応。
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.StreamTick ) )
+                               {
+                                       // 未対応。
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.AllEffectsremoved ) )
+                               {
+                                       // 未対応。
+                               }
+                               else if( streamFlags.HasFlag( SharpDX.MediaFoundation.SourceReaderFlags.Error ) )
+                               {
+                                       #region " エラー "
+                                       //----------------
+                                       FDK.Log.Info( "エラーが発生したので、動画の再生を終了します。" );
+                                       this.再生状態.Value = 再生状態ID.再生終了済み;
+                                       this.LastSharpDXResult = SharpDX.Result.Fail;
+                                       return;
+                                       //----------------
+                                       #endregion
+                               }
+                               //---------------------------------------------------
+                               #endregion
+                               #region " 前フレームの時刻を、先頭時刻+サンプルの時刻 に設定する。"
+                               //-----------------
+                               if( FDK.カウンタ.QPCTimer.未使用 == 前フレームの時刻 )    // 最初のフレームである場合(ループ時は除く)
+                               {
+                                       // 最初のフレームの取得には時間がかかることが多いので、最初のフレームを取得した場合のみ、タイマをサンプルの時刻にリセットする。
+                                       this.タイマ.リセットする( サンプルの時刻 );
+                               }
+
+                               サンプルの時刻 += this.ループした際の先頭時刻;
+                               this.前フレームの時刻 = サンプルの時刻;
+                               //-----------------
+                               #endregion
+                               #region " サンプルの表示時刻までの時刻調整を行う。"
+                               //-----------------
+                               var 現在時刻 = this.タイマ.現在のリアルタイムカウント100ns単位;
+                               if( 現在時刻 > サンプルの時刻 )
+                               {
+                                       // (A) サンプルの時刻が過去である → このサンプルとタスクは捨てる(さっさと次のサンプルへ移る)。
+                                       this.デコーダ状態.Value = デコーダ状態ID.スキップ;
+                                       return;
+                               }
+                               else
+                               {
+                                       // (B) サンプルの時刻が未来である → その時刻を過ぎるまで Sleep する。
+                                       while( 現在時刻 < サンプルの時刻 )
+                                       {
+                                               System.Threading.Thread.Sleep( 2 );      // 500fps の動画まで対応(理論上(汗
+                                               現在時刻 = タイマ.現在のリアルタイムカウント100ns単位;
+                                       }
+                               }
+                               //-----------------
+                               #endregion
+                       }
+                       finally
+                       {
+                               #region " 失敗した、または EndOfStream に達した場合は、Sample を無効化する。"
+                               //----------------
+                               if( this.LastSharpDXResult.Failure )
+                               {
+                                       FDK.Log.Info( "SharpDX でエラーが発生したので、動画の再生を終了します。" );
+                                       this.再生状態.Value = 再生状態ID.再生終了済み;
+                                       FDK.Utilities.解放する( ref this.Sample );
+                               }
+                               //----------------
+                               #endregion
+
+                               this.デコーダ状態.Value = デコーダ状態ID.完了;
+                       }
+               }
+               private void デコードキャッシング()
+               {
+                       // 最初のほうのサンプルのデコードには 100~200 ms ほどかかってしまうので、あらかじめ少しデコードしてメモリにキャッシュさせることでこれを緩和する。
+
+                       this.デコーダ状態.Value = デコーダ状態ID.デコード中;
+                       try
+                       {
+                               // 先頭から複数のフレームを読み込む。
+                               for( int i = 0; i < 60; i++ )   // 60フレームもあれば、2つめのキーフレームくらいには届くだろう……
+                               {
+                                       #region " SourceReader から次のサンプル(フレーム)を1つ取得しては解放する。"
+                                       //-----------------
+                                       var sample = (SharpDX.MediaFoundation.Sample) null;
+                                       int actualStreamIndex = 0;
+                                       var streamFlags = SharpDX.MediaFoundation.SourceReaderFlags.None;
+                                       long サンプルの時刻 = 0;
+                                       try
+                                       {
+                                               sample = this.SourceReaderEx.ReadSample(
+                                                       SharpDX.MediaFoundation.SourceReaderIndex.FirstVideoStream,
+                                                       SharpDX.MediaFoundation.SourceReaderControlFlags.None,
+                                                       out actualStreamIndex,
+                                                       out streamFlags,
+                                                       out サンプルの時刻 );
+                                       }
+                                       catch( SharpDX.SharpDXException e )
+                                       {
+                                               Log.ERROR( $"SourceReaderEx.ReadSample() に失敗しました。(0x{e.HResult:x8})" );
+                                               this.LastSharpDXResult = e.ResultCode;
+                                               return;
+                                       }
+                                       finally
+                                       {
+                                               // すぐ解放。
+                                               sample?.Dispose();
+                                       }
+                                       //-----------------
+                                       #endregion
+                               }
+                               // SourceReader を先頭へリセット。
+                               this.SourceReaderEx.SetCurrentPosition( 0 );
+                       }
+                       finally
+                       {
+                               this.デコーダ状態.Value = デコーダ状態ID.完了;
+                       }
+               }
+               private void 全リソースを解放する()
+               {
+                       FDK.Utilities.解放する( ref this.D2Dビットマップ );
+                       FDK.Utilities.解放する( ref this.WICビットマップ );
+                       FDK.Utilities.解放する( ref this.Sample );
+                       FDK.Utilities.解放する( ref this.MediaType );
+                       FDK.Utilities.解放する( ref this.SourceReaderEx );
+               }
+               private unsafe void* 生ポインタを格納したbyte配列からポインタを取得して返す( byte[] 生ポインタ )
+               {
+                       if( ( 4 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
+                       {
+                               // (A) 32bit, リトルエンディアン
+                               int 生アドレス32bit = 0;
+                               for( int i = 0; i < 4; i++ )
+                                       生アドレス32bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
+                               return new IntPtr( 生アドレス32bit ).ToPointer();
+                       }
+                       else if( ( 8 == IntPtr.Size ) && System.BitConverter.IsLittleEndian )
+                       {
+                               // (B) 64bit, リトルエンディアン
+                               long 生アドレス64bit = 0;
+                               for( int i = 0; i < 8; i++ )
+                                       生アドレス64bit += ( (int) 生ポインタ[ i ] ) << ( i * 8 );
+                               return new IntPtr( 生アドレス64bit ).ToPointer();
+                       }
+                       else if( ( 4 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
+                       {
+                               // (C) 32bit, ビッグエンディアン
+                               int 生アドレス32bit = 0;
+                               for( int i = 0; i < 4; i++ )
+                                       生アドレス32bit += ( (int) 生ポインタ[ 4 - i ] ) << ( i * 8 );
+                               return new IntPtr( 生アドレス32bit ).ToPointer();
+                       }
+                       else if( ( 8 == IntPtr.Size ) && ( false == System.BitConverter.IsLittleEndian ) )
+                       {
+                               // (D) 64bit, ビッグエンディアン
+                               long 生アドレス64bit = 0;
+                               for( int i = 0; i < 8; i++ )
+                                       生アドレス64bit += ( (int) 生ポインタ[ 8 - i ] ) << ( i * 8 );
+                               return new IntPtr( 生アドレス64bit ).ToPointer();
+                       }
+
+                       throw new SharpDX.SharpDXException( SharpDX.Result.NotImplemented, "この .NET アーキテクチャには対応していません。" );
+               }
+
+               #region " Win32 API "
+               //-----------------
+               [System.Runtime.InteropServices.DllImport( "kernel32.dll", SetLastError = true )]
+               private static extern unsafe void CopyMemory( void* dst, void* src, int size );
+               //-----------------
+               #endregion
+       }
+}
diff --git a/FDK24/メディア/描画可能画像.cs b/FDK24/メディア/描画可能画像.cs
new file mode 100644 (file)
index 0000000..5641871
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace FDK.メディア
+{
+       /// <summary>
+       /// 画像ファイルから生成され、レンダーターゲットとしても描画可能な Bitmap を扱うクラス。
+       /// </summary>
+       public class 描画可能画像 : FDK.メディア.画像
+       {
+               public 描画可能画像( string 画像ファイルパス ) : base( 画像ファイルパス )
+               {
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       Debug.Assert( this.活性化していない );
+
+                       this.画像を活性化する(
+                               dr,
+                               new SharpDX.Direct2D1.BitmapProperties1() {
+                                       BitmapOptions = SharpDX.Direct2D1.BitmapOptions.Target
+                               } );
+
+                       base.活性化する( dr );
+               }
+
+               /// <summary>
+               /// 生成済み画像(ビットマップ)に対するユーザアクションによる描画を行う。
+               /// </summary>
+               /// <remarks>
+               /// 活性化状態であれば、進行描画() 中でなくても、任意のタイミングで呼び出して良い。
+               /// ユーザアクション内では BeginDraw(), EndDraw() の呼び出しは(呼び出しもとでやるので)不要。
+               /// </remarks>
+               /// <param name="dr">デバイスリソース。</param>
+               /// <param name="描画アクション">Bitmap に対して行いたい操作。</param>
+               public void 画像へ描画する( FDK.メディア.デバイスリソース dr, Action<SharpDX.Direct2D1.DeviceContext1, SharpDX.Direct2D1.Bitmap1> 描画アクション )
+               {
+                       var 旧ターゲット = dr.D2DContext1.Target;
+                       try
+                       {
+                               dr.D2DContext1.Target = this.Bitmap;
+                               FDK.Utilities.D2DBatchDraw( dr.D2DContext1, () => {
+                                       描画アクション( dr.D2DContext1, this.Bitmap );
+                               } );
+                       }
+                       finally
+                       {
+                               dr.D2DContext1.Target = 旧ターゲット;
+                       }
+               }
+       }
+}
diff --git a/FDK24/メディア/文字列画像.cs b/FDK24/メディア/文字列画像.cs
new file mode 100644 (file)
index 0000000..b9f805b
--- /dev/null
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace FDK.メディア
+{
+       /// <summary>
+       /// DirectWrite を使った Direct2D1ビットマップ。
+       /// </summary>
+       /// <remarks>
+       /// 「表示文字列」メンバを設定/更新すれば、次回の描画時にビットマップが生成される。
+       /// </remarks>
+       public class 文字列画像 : FDK.Activity
+       {
+               /// <summary>
+               /// このメンバを set すれば、次回の進行描画時に画像が更新される。
+               /// </summary>
+               public string 表示文字列
+               {
+                       get;
+                       set;
+               } = null;
+               public string フォント名
+               {
+                       get;
+                       set;
+               } = "メイリオ";
+               public float フォントサイズpt
+               {
+                       get;
+                       set;
+               } = 20.0f;
+               public SharpDX.Direct2D1.InterpolationMode 補正モード
+               {
+                       get;
+                       set;
+               } = SharpDX.Direct2D1.InterpolationMode.Linear;
+               public SharpDX.RectangleF? 転送元矩形dpx
+               {
+                       get;
+                       set;
+               } = null;
+               public bool 加算合成
+               {
+                       get;
+                       set;
+               } = false;
+               public SharpDX.Size2F レイアウトサイズdpx
+               {
+                       get;
+                       set;
+               } = new SharpDX.Size2F( -1f, -1f );
+               public bool 下詰め
+               {
+                       get;
+                       set;
+               } = false;
+
+               public 文字列画像()
+               {
+               }
+               public 文字列画像( string 文字列, float フォントサイズpt = 20.0f, string フォント名 = "メイリオ" )
+               {
+                       this.表示文字列 = 文字列;
+                       this.フォント名 = フォント名;
+                       this.フォントサイズpt = フォントサイズpt;
+               }
+               protected override void On活性化( デバイスリソース dr )
+               {
+                       FDK.Log.Info( $"{FDK.Utilities.現在のメソッド名}: 文字列ビットマップを生成します。" );
+                       this.前回の表示文字列 = null;
+                       if( this.表示文字列.Nullでも空でもない() )
+                       {
+                               this.ビットマップを再構築する( dr );
+                               this.前回の表示文字列 = this.表示文字列; // 最初の構築完了。
+                       }
+               }
+               protected override void On非活性化( デバイスリソース dr )
+               {
+                       FDK.Utilities.解放する( ref this.ビットマップレンダーターゲット );
+                       FDK.Utilities.解放する( ref this.テキストレイアウト );
+                       FDK.Utilities.解放する( ref this.テキストフォーマット );
+                       FDK.Utilities.解放する( ref this.白ブラシ );
+                       FDK.Utilities.解放する( ref this.黒ブラシ );
+
+                       FDK.Log.Info( $"{FDK.Utilities.現在のメソッド名}: 文字列ビットマップを解放しました。" );
+               }
+               public void 進行描画する(
+                       デバイスリソース dr,
+                       float 左位置dpx,
+                       float 上位置dpx,
+                       float 不透明度0to1 = 1.0f,
+                       float X方向拡大率 = 1.0f,
+                       float Y方向拡大率 = 1.0f,
+                       SharpDX.Matrix? 変換行列3Dpx = null )
+               {
+                       var 変換行列2Dpx = SharpDX.Matrix3x2.Identity
+                               * SharpDX.Matrix3x2.Scaling( X方向拡大率, Y方向拡大率 )   // スケーリング
+                               * dr.行列を単位変換するDPXtoPX( SharpDX.Matrix3x2.Translation( 左位置dpx, 上位置dpx ) );  // 平行移動(物理単位)。
+
+                       this.進行描画する( dr, 変換行列2Dpx, 変換行列3Dpx, 不透明度0to1 );
+               }
+
+               public void 進行描画する(
+                       デバイスリソース dr,
+                       SharpDX.Matrix3x2? 変換行列2Dpx = null,
+                       SharpDX.Matrix? 変換行列3Dpx = null,
+                       float 不透明度0to1 = 1.0f )
+               {
+                       Debug.Assert( this.活性化している );
+
+                       if( this.表示文字列.Nullまたは空である() )
+                               return;
+
+                       // 表示文字列が変更されているなら、ここで表示ビットマップの再構築を行う。
+                       if( false == string.Equals( this.表示文字列, this.前回の表示文字列 ) )
+                               this.ビットマップを再構築する( dr );
+
+                       if( null == this.ビットマップレンダーターゲット )
+                               return;
+
+                       Utilities.D2DBatchDraw( dr.D2DContext1, () => {
+
+                               // 変換行列とブレンドモードをD2Dレンダーターゲットに設定する。
+                               dr.D2DContext1.Transform = 変換行列2Dpx ?? SharpDX.Matrix3x2.Identity;
+                               dr.D2DContext1.PrimitiveBlend = ( 加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
+
+                               // D2Dレンダーターゲットに this.Bitmap を描画する。
+                               dr.D2DContext1.DrawBitmap(
+                                       bitmap: this.ビットマップレンダーターゲット.Bitmap,
+                                       destinationRectangle: null,
+                                       opacity: 不透明度0to1,
+                                       interpolationMode: this.補正モード,
+                                       sourceRectangle: this.転送元矩形dpx,
+                                       erspectiveTransformRef: 変換行列3Dpx );
+                       } );
+               }
+
+               private string 前回の表示文字列 = null;
+               private SharpDX.Direct2D1.BitmapRenderTarget ビットマップレンダーターゲット = null;
+               private SharpDX.DirectWrite.TextFormat テキストフォーマット = null;
+               private SharpDX.DirectWrite.TextLayout テキストレイアウト = null;
+               private SharpDX.Direct2D1.SolidColorBrush 白ブラシ = null;
+               private SharpDX.Direct2D1.SolidColorBrush 黒ブラシ = null;
+
+               private void ビットマップを再構築する( デバイスリソース dr )
+               {
+                       this.前回の表示文字列 = this.表示文字列;
+
+                       // テキストフォーマット/レイアウトを作成し、表示ビットマップのサイズを計算する。
+                       if( null == this.テキストフォーマット )
+                       {
+                               this.テキストフォーマット = new SharpDX.DirectWrite.TextFormat( dr.DWriteFactory, this.フォント名, this.フォントサイズpt ) {
+                                       TextAlignment = SharpDX.DirectWrite.TextAlignment.Leading,
+                               };
+                       }
+
+                       if( ( 0.0f >= this.レイアウトサイズdpx.Width ) || ( 0.0f >= this.レイアウトサイズdpx.Height ) )
+                               this.レイアウトサイズdpx = dr.設計画面サイズdpx;
+
+                       this.テキストレイアウト?.Dispose();
+                       this.テキストレイアウト = new SharpDX.DirectWrite.TextLayout(
+                               dr.DWriteFactory,
+                               this.表示文字列,
+                               this.テキストフォーマット,
+                               this.レイアウトサイズdpx.Width,
+                               this.レイアウトサイズdpx.Height );
+
+                       var 表示ビットマップのサイズdpx = new SharpDX.Size2F();
+                       var 上マージン = 0.0f;
+                       if( this.下詰め )
+                       {
+                               表示ビットマップのサイズdpx = new SharpDX.Size2F(
+                                       this.テキストレイアウト.Metrics.WidthIncludingTrailingWhitespace,
+                                       this.レイアウトサイズdpx.Height );       // レイアウトの最大高
+
+                               上マージン = this.レイアウトサイズdpx.Height - this.テキストレイアウト.Metrics.Height;
+                       }
+                       else
+                       {
+                               表示ビットマップのサイズdpx = new SharpDX.Size2F(
+                                       this.テキストレイアウト.Metrics.WidthIncludingTrailingWhitespace,
+                                       this.テキストレイアウト.Metrics.Height );
+                       }
+
+                       // ビットマップレンダーターゲットを生成する。
+                       this.ビットマップレンダーターゲット?.Dispose();
+                       this.ビットマップレンダーターゲット = new SharpDX.Direct2D1.BitmapRenderTarget(
+                               dr.D2DContext1,
+                               SharpDX.Direct2D1.CompatibleRenderTargetOptions.None,
+                               表示ビットマップのサイズdpx );
+
+                       // ブラシの作成がまだなら行う。
+                       if( null == this.白ブラシ )
+                               this.白ブラシ = new SharpDX.Direct2D1.SolidColorBrush( this.ビットマップレンダーターゲット, SharpDX.Color.LightGray );
+                       if( null == this.黒ブラシ )
+                               this.黒ブラシ = new SharpDX.Direct2D1.SolidColorBrush( this.ビットマップレンダーターゲット, SharpDX.Color.Black );
+
+                       // ビットマップレンダーターゲットにテキストを描画する。
+                       Utilities.D2DBatchDraw( this.ビットマップレンダーターゲット, () => {
+                               this.ビットマップレンダーターゲット.DrawTextLayout(    // ドロップシャドウ
+                                       new SharpDX.Vector2( 1.0f, 上マージン + 1.0f ),
+                                       this.テキストレイアウト,
+                                       this.黒ブラシ,
+                                       SharpDX.Direct2D1.DrawTextOptions.Clip );
+                       } );
+                       Utilities.D2DBatchDraw( this.ビットマップレンダーターゲット, () => {
+                               this.ビットマップレンダーターゲット.DrawTextLayout(    // 本体
+                                       new SharpDX.Vector2( 0.0f, 上マージン ),
+                                       this.テキストレイアウト,
+                                       this.白ブラシ,
+                                       SharpDX.Direct2D1.DrawTextOptions.Clip );
+                       } );
+               }
+       }
+}
diff --git a/FDK24/メディア/画像.cs b/FDK24/メディア/画像.cs
new file mode 100644 (file)
index 0000000..791a4eb
--- /dev/null
@@ -0,0 +1,207 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace FDK.メディア
+{
+       public class 画像 : Activity
+       {
+               public bool 生成成功 => ( null != this.Bitmap );
+               public bool 生成失敗 => ( null == this.Bitmap );
+
+               /// <summary>
+               /// 画像は、設計単位で作成される。
+               /// </summary>
+               public SharpDX.Size2F サイズdpx => new SharpDX.Size2F( (float) this.Bitmap.PixelSize.Width, (float) this.Bitmap.PixelSize.Height );
+
+               // あまり頻繁に変更されないであろう描画パラメータは、メソッド引数ではなくプロパティにしておく。
+               public SharpDX.Direct2D1.InterpolationMode 補正モード { get; set; } = SharpDX.Direct2D1.InterpolationMode.Linear;
+               public bool 加算合成 { get; set; } = false;
+
+               public 画像( string 画像ファイルパス )
+               {
+                       this.画像ファイルパス = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( 画像ファイルパス );
+               }
+               protected override void On活性化( FDK.メディア.デバイスリソース dr )
+               {
+                       this.画像を活性化する( dr );
+               }
+               protected override void On非活性化( FDK.メディア.デバイスリソース dr )
+               {
+                       FDK.Utilities.解放する( ref this.Bitmap );
+                       Log.Info( $"画像を解放しました。[{フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.画像ファイルパス )}]" );
+               }
+
+               /// <summary>
+               /// 画像を描画する。
+               /// </summary>
+               /// <param name="dr">デバイスリソース。</param>
+               /// <param name="左位置dpx">画像の描画先範囲の左上隅X座標。</param>
+               /// <param name="上位置dpx">画像の描画先範囲の左上隅Y座標。</param>
+               /// <param name="不透明度0to1">不透明度。(0:透明~1:不透明)</param>
+               /// <param name="X方向拡大率">画像の横方向の拡大率。</param>
+               /// <param name="Y方向拡大率">画像の縦方向の拡大率。</param>
+               /// <param name="転送元矩形dpx">画像の転送元範囲。画像は設計単位で作成されている。</param>
+               /// <param name="変換行列3Dpx">射影行列。Direct2D の仕様により、設計単位ではなく物理単位で構築すること。</param>
+               public virtual void 進行描画する(
+                       FDK.メディア.デバイスリソース dr,
+                       float 左位置dpx,
+                       float 上位置dpx,
+                       float 不透明度0to1 = 1.0f,
+                       float X方向拡大率 = 1.0f,
+                       float Y方向拡大率 = 1.0f,
+                       SharpDX.RectangleF? 転送元矩形dpx = null,
+                       SharpDX.Matrix? 変換行列3Dpx = null )
+               {
+                       var 変換行列2Dpx = SharpDX.Matrix3x2.Identity
+                               * SharpDX.Matrix3x2.Scaling( X方向拡大率, Y方向拡大率 )   // スケーリング。
+                               * dr.行列を単位変換するDPXtoPX( SharpDX.Matrix3x2.Translation( 左位置dpx, 上位置dpx ) );   // 平行移動(物理単位)。
+
+                       this.進行描画する( dr, 変換行列2Dpx, 変換行列3Dpx, 不透明度0to1, 転送元矩形dpx );
+               }
+
+               /// <summary>
+               /// 画像を描画する。
+               /// </summary>
+               /// <param name="dr">デバイスリソース。</param>
+               /// <param name="変換行列2Dpx">Transform に適用する行列。Direct2D の仕様により、設計単位ではなく物理単位で構築すること。</param>
+               /// <param name="変換行列3Dpx">射影行列。Direct2D の仕様により、設計単位ではなく物理単位で構築すること。</param>
+               /// <param name="不透明度0to1">不透明度。(0:透明~1:不透明)</param>
+               /// <param name="転送元矩形dpx">描画する画像範囲。画像は設計単位で作成されている。</param>
+               public virtual void 進行描画する(
+                       FDK.メディア.デバイスリソース dr,
+                       SharpDX.Matrix3x2? 変換行列2Dpx = null,
+                       SharpDX.Matrix? 変換行列3Dpx = null,
+                       float 不透明度0to1 = 1.0f,
+                       SharpDX.RectangleF? 転送元矩形dpx = null )
+               {
+                       Debug.Assert( this.活性化している );
+
+                       if( null == this.Bitmap )
+                               return;
+
+                       Utilities.D2DBatchDraw( dr.D2DContext1, () => {
+
+                               // 変換行列とブレンドモードをD2Dレンダーターゲットに設定する。
+                               dr.D2DContext1.Transform = 変換行列2Dpx ?? SharpDX.Matrix3x2.Identity;
+                               dr.D2DContext1.PrimitiveBlend = ( this.加算合成 ) ? SharpDX.Direct2D1.PrimitiveBlend.Add : SharpDX.Direct2D1.PrimitiveBlend.SourceOver;
+
+                               // D2Dレンダーターゲットに this.Bitmap を描画する。
+                               dr.D2DContext1.DrawBitmap(
+                                       bitmap: this.Bitmap,
+                                       destinationRectangle: null,
+                                       opacity: 不透明度0to1,
+                                       interpolationMode: this.補正モード,
+                                       sourceRectangle: 転送元矩形dpx,  // this.Bitmap は設計単位で作成されている。
+                                       erspectiveTransformRef: 変換行列3Dpx ); // null 指定可。
+                       } );
+               }
+
+               protected string 画像ファイルパス = null;
+               protected SharpDX.Direct2D1.Bitmap1 Bitmap = null;
+
+               protected void 画像を活性化する( FDK.メディア.デバイスリソース dr, SharpDX.Direct2D1.BitmapProperties1 bitmapProperties1 = null )
+               {
+                       var decoder = (SharpDX.WIC.BitmapDecoder) null;
+                       var sourceFrame = (SharpDX.WIC.BitmapFrameDecode) null;
+                       var converter = (SharpDX.WIC.FormatConverter) null;
+
+                       string 変数付きファイルパス = フォルダ.絶対パスをフォルダ変数付き絶対パスに変換して返す( this.画像ファイルパス );
+                       try
+                       {
+                               #region " 画像ファイルパスの有効性を確認する。"
+                               //-----------------
+                               if( string.IsNullOrEmpty( this.画像ファイルパス ) )
+                               {
+                                       Log.ERROR( $"画像ファイルパスが null または空文字列です。[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               if( false == System.IO.File.Exists( this.画像ファイルパス ) )
+                               {
+                                       Log.ERROR( $"画像ファイルが存在しません。[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 画像ファイルに対応できるデコーダを見つける。"
+                               //-----------------
+                               try
+                               {
+                                       decoder = new SharpDX.WIC.BitmapDecoder(
+                                               dr.WicImagingFactory2,
+                                               this.画像ファイルパス,
+                                               SharpDX.IO.NativeFileAccess.Read,
+                                               SharpDX.WIC.DecodeOptions.CacheOnLoad );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"画像ファイルに対応するコーデックが見つかりません。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 最初のフレームをデコードし、取得する。"
+                               //-----------------
+                               try
+                               {
+                                       sourceFrame = decoder.GetFrame( 0 );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"画像ファイルの最初のフレームのデコードに失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " 32bitPBGRA へのフォーマットコンバータを生成する。"
+                               //-----------------
+                               try
+                               {
+                                       // WICイメージングファクトリから新しいコンバータを生成。
+                                       converter = new SharpDX.WIC.FormatConverter( dr.WicImagingFactory2 );
+
+                                       // コンバータに変換元フレームや変換後フォーマットなどを設定。
+                                       converter.Initialize(
+                                               sourceRef: sourceFrame,
+                                               dstFormat: SharpDX.WIC.PixelFormat.Format32bppPBGRA,
+                                               dither: SharpDX.WIC.BitmapDitherType.None,
+                                               paletteRef: null,
+                                               alphaThresholdPercent: 0.0,
+                                               paletteTranslate: SharpDX.WIC.BitmapPaletteType.MedianCut );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"32bitPBGRA へのフォーマットコンバータの生成または初期化に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+                               #region " コンバータを使って、フレームを WICビットマップ経由で D2D ビットマップに変換する。"
+                               //-----------------
+                               try
+                               {
+                                       // WIC ビットマップを D2D ビットマップに変換する。
+                                       this.Bitmap = SharpDX.Direct2D1.Bitmap1.FromWicBitmap(
+                                               dr.D2DContext1,
+                                               converter,
+                                               bitmapProperties1 );
+                               }
+                               catch( SharpDX.SharpDXException e )
+                               {
+                                       Log.ERROR( $"Direct2D1.Bitmap1 への変換に失敗しました。(0x{e.HResult:x8})[{変数付きファイルパス}]" );
+                                       return;
+                               }
+                               //-----------------
+                               #endregion
+
+                               Log.Info( $"{Utilities.現在のメソッド名}: 画像を生成しました。[{変数付きファイルパス}]" );
+                       }
+                       finally
+                       {
+                               converter?.Dispose();
+                               sourceFrame?.Dispose();
+                               decoder?.Dispose();
+                       }
+               }
+       }
+}
diff --git a/FDK24/メディア/矩形リスト.cs b/FDK24/メディア/矩形リスト.cs
new file mode 100644 (file)
index 0000000..225bb04
--- /dev/null
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Xml.Linq;
+
+namespace FDK.メディア
+{
+       /// <summary>
+       /// 任意の文字列から任意の矩形を引き当てるための辞書。
+       /// 辞書の内容は、ファイルから読み込むすることができる。
+       /// </summary>
+       public class 矩形リスト
+       {
+               public Dictionary<string, SharpDX.RectangleF> 文字列to矩形
+               {
+                       get;
+               } = new Dictionary<string, SharpDX.RectangleF>();
+
+               /// <summary>
+               /// キー(文字列)に対応する矩形を返す。
+               /// キーがなければ null を返す。
+               /// </summary>
+               /// <param name="文字列">キー文字列。</param>
+               /// <returns>キー文字列に対応する矩形。Null許容型であり、キーが存在していなければ null を返す。</returns>
+               public SharpDX.RectangleF? this[ string 文字列 ]
+               {
+                       get
+                       {
+                               return this.文字列to矩形.ContainsKey( 文字列 ) ?
+                                       this.文字列to矩形[ 文字列 ] :
+                                       (SharpDX.RectangleF?) null;
+                       }
+               }
+
+               public 矩形リスト()
+               {
+               }
+               public 矩形リスト( string ファイルパス ) : this()
+               {
+                       this.矩形リストXmlファイルを読み込む( ファイルパス );
+               }
+               public void 矩形リストXmlファイルを読み込む( string ファイルパス )
+               {
+                       this.文字列to矩形.Clear();
+
+                       string 変数なしファイル名 = FDK.フォルダ.絶対パスに含まれるフォルダ変数を展開して返す( ファイルパス );
+                       if( false == File.Exists( 変数なしファイル名 ) )
+                               throw new FDKException( $"矩形リストXmlファイルが存在しません。[{ファイルパス}]" );
+
+                       try
+                       {
+                               var xml文書 = XDocument.Load( 変数なしファイル名 );
+
+                               // <Root>
+                               var Root要素 = xml文書.Element( nameof( XML.Root ) );
+                               {
+                                       // <SubImage Name="..." Rectangle="x,y,w,h" />*
+                                       foreach( var SubImage要素 in Root要素.Elements( nameof( XML.SubImage ) ) )
+                                       {
+                                               // Name 属性を取得。なかったら例外発出。
+                                               var Name属性 = SubImage要素.Attribute( nameof( XML.Name ) );
+                                               if( null == Name属性 )
+                                                       throw new FDKException( $"{nameof( XML.Name )} 属性が存在しません。[{ファイルパス}]" );
+
+                                               // 同じ名前の Name 属性が指定されたらこの要素を無視。
+                                               if( this.文字列to矩形.ContainsKey( Name属性.Value ) )
+                                               {
+                                                       FDK.Log.WARNING( $"{nameof( XML.SubImage )} 要素の {nameof( XML.Name )} 属性が一意ではありません。これをスキップします。[{nameof( XML.Name )}={Name属性.Value}][{ファイルパス}]" );
+                                                       continue;
+                                               }
+
+                                               // Rectangle 属性を取得。なかったら例外発出。
+                                               var Rectangle属性 = SubImage要素.Attribute( nameof( XML.Rectangle ) );
+                                               if( null == Rectangle属性 )
+                                               {
+                                                       FDK.Log.WARNING( $"{nameof( XML.SubImage )} 要素の {nameof( XML.Rectangle )} 属性が存在しません。これをスキップします。[{ファイルパス}]" );
+                                                       continue;
+                                               }
+
+                                               // Rectangle 属性値を正規表現に照らし合わせて、x, y, width, height を取得する。
+                                               var match = Regex.Match( Rectangle属性.Value, @"^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*$" );
+                                               if( ( false == match.Success ) || ( 5 != match.Groups.Count ) )
+                                               {
+                                                       FDK.Log.WARNING( $"{nameof( XML.SubImage )} 要素の {nameof( XML.Rectangle )} 属性の値が不正です。x,y,w,h の順に、整数で指定します。[{ファイルパス}]" );
+                                                       continue;
+                                               }
+
+                                               float x = float.Parse( match.Groups[ 1 ].Value );
+                                               float y = float.Parse( match.Groups[ 2 ].Value );
+                                               float width = float.Parse( match.Groups[ 3 ].Value );
+                                               float height = float.Parse( match.Groups[ 4 ].Value );
+
+                                               // 辞書に追加。
+                                               this.文字列to矩形.Add( Name属性.Value, new SharpDX.RectangleF( x, y, width, height ) );
+                                       }
+                               }
+                       }
+                       catch( Exception e )
+                       {
+                               FDK.Log.ERROR( $"矩形リストXmlファイルの読み込みに失敗しました。{e.Message}[{ファイルパス}]" );
+                       }
+               }
+       }
+}