none
Managed Direct X での画面サイズの変更 RRS feed

  • 質問

  • こんばんは。

     Form 上の PictureBox に MDX で描画しているのですが、フォームのサイズ変更に合わせて描画範囲を
    変更する手段はあるのでしょうか?

     ViewPort のサイズを PictureBox の ClientRectangle のサイズにしても変化が無く、バックバッファのサイズ
    が変更されていなのが原因かと思い、デバイスの PresentParameters のバックバッファサイズだけ変更し、
    Reset を行ってみましたが、何も描画されなくなってしまいました。

     対処法をご存知の方、いらっしゃいましたらアドバイスをいただけたら幸いです。
    2008年11月19日 8:58

回答

  • こんばんは!(^^)!ふ~です。

    取り合えず、私の考えていたピクチャーボックスの描画部分です。

    using System;
    using System.Drawing;
    using System.Windows.Forms;

    using System.Runtime.InteropServices; // Win32

    using Microsoft.DirectX;
    using Microsoft.DirectX.DirectDraw;

    namespace DirectXTest2
    {
     public partial class Form1 : Form
     {
      private Surface imageSurface;

      // ディスプレイサイズ
      int displayWidth  = 120;
      int displayHeight = 160;

      public Form1()
      {
       InitializeComponent();
       pictureBox1.Paint +=new PaintEventHandler(pictureBox1_Paint);
       pictureBox1.Resize += new EventHandler(pictureBox1_Resize);
      }

      private void Form1_Load(object sender, EventArgs e)
      {
       // タイマーの初期化処理
       InitializeTimer();
      }

     

    Code Snippet

      // pictureBox1のペイント
      void pictureBox1_Paint(object sender, PaintEventArgs e)
      {
       // DirectXの初期化
       InitializeDirectDraw();

       Graphics g, gd;
       g = e.Graphics;

       // 適当なビットマップを作成する。
       Bitmap bb = new Bitmap(displayWidth, displayHeight, g);
       gd = Graphics.FromImage(bb);

       // DirextXの画像のハンドル
       IntPtr srcHDC = imageSurface.GetDc();
       IntPtr destHDC = gd.GetHdc();

       // DirextXの画像をビットマップへ転送する
       BitBlt(destHDC, 0, 0, bb.Width, bb.Height, srcHDC, 0, 0, 0xCC0020);

       gd.ReleaseHdc(destHDC);

       // ビットマップをピクチャーボックスに表示する。
       e.Graphics.DrawImage(bb, 0, 0, pictureBox1.Width, pictureBox1.Height);

       bb.Dispose();
      }

      // pictureBox1のサイズ変更ハンドラ
      void pictureBox1_Resize(object sender, EventArgs e)
      {
       Refresh();
      }

     


      ///
      /// DirectDraw関連の初期化を行います。
      ///
      private Device device;
      private Surface primarySurface;
      private Surface secondarySurface;
      private Clipper clipper;

      private void InitializeDirectDraw()
      {
       // DirectDrawデバイスを作成
       device = new Device();

       // デバイスの協調レベルをNormalに設定
       device.SetCooperativeLevel(this, CooperativeLevelFlags.Normal);

       // サーフェスを作成するためのパラメータとなる構造体
       SurfaceDescription desc = new SurfaceDescription();
       SurfaceCaps caps = new SurfaceCaps();

       // プライマリサーフェスを作成
       caps.PrimarySurface = true;
       desc.SurfaceCaps = caps;
       primarySurface = new Surface(desc, device);

       // 構造体をリセット
       caps.Clear();

       // セカンダリサーフェスを作成
       desc.Width = displayWidth;
       desc.Height = displayHeight;
       desc.SurfaceCaps = caps;
       secondarySurface = new Surface(desc, device);

       // クリッパーを作成
       clipper = new Clipper(device);
       clipper.Window = this;

       // プライマリサーフェスに作成したクリッパーを指定
       primarySurface.Clipper = clipper;

       desc.Clear();

       // Image.bmpという画像ファイルを読み込んでサーフェスを作成
       // "bin\debug\XXXXX.bmp" ビットマップ以外は不可
       imageSurface = new Surface("xxxxxxxxxx.BMP", desc, device);
      }
      
      ///
      /// タイマーの初期化を行います。
      ///
      private Timer timer;

      private void InitializeTimer()
      {
       // タイマーを作成
       timer = new Timer();

       // インターバルを30[ms]に設定
       timer.Interval = 30;

       // イベントハンドラを指定
       timer.Tick += new EventHandler(this.Render);

       // タイマーを起動
       timer.Start();
      }

      ///
      /// timerのTickイベント用のハンドラです。 ここでDirectDrawの描画処理を行います。
      ///
      private void Render(object sender, EventArgs e)
      {
       // フォームが表示されていない場合は描画しない
       if (!this.Visible) return;

       // フォームが最小化されている場合は描画しない
       if (this.WindowState == FormWindowState.Minimized) return;
      
       try
       {
        // 背景をブルー
        secondarySurface.ColorFill(Color.Blue);
        // 楕円の中を赤で塗る
        secondarySurface.FillColor = Color.Red;
        // 楕円を描画する
        secondarySurface.DrawEllipse( 10, 30, 50, 100);

        // 描画先領域を指定
        Rectangle destRectangle = new Rectangle();

        destRectangle.Size = this.ClientSize;
        destRectangle.Location = this.PointToScreen(this.ClientRectangle.Location);

        // プライマリサーフェスにセカンダリサーフェスを描画
        primarySurface.Draw(destRectangle, secondarySurface, DrawFlags.Wait);
       }
       catch( SurfaceLostException error )
       {
        InitializeDirectDraw();
       }
      }

      //BitBlt複写処理
      [DllImport("gdi32.dll")]
      public static extern bool BitBlt(
       IntPtr hdcDest,
       int xDest,
       int yDest,
       int width,
       int height,
       IntPtr hdcSrc,
       int xSrc,
       int ySrc,
       System.Int32 rasterOp
       ); 
     }

     // メイン処理
     public class ManagedDX
     {
      public static void Main(string[] args)
      {
       Form1 FormTest = new Form1();

       // アプリケーションを起動する
       Application.Run(FormTest);
      }
     }
    }

    <作成方法>
    参照設定
    Microsoft.DirectX;
    Microsoft.DirectX.DirectDraw;
    System;
    System.Drawing;
    System.Windows.Forms;
    は最低必要です。

    "bin\debug\XXXXX.bmp"に小さめの軽いビットマップを用意します。
    サイズが大きいとエラーになります。
     
    使用したManaged DirectX バージョン
    DirectX 9.0c Redistributable for Software Developers - Multilingual - 日本語
    http://www.microsoft.com/downloads/details.aspx?familyid=143EF8C6-CFF7-4AF0-8209-1CDF49C58BC0&displaylang=ja
    インストール後、「スタート」->「ファイル名を指定して実行」->「DXDiag」で動作確認が行えます。

    参考URL
    Managed DirectX ゲーム画面のキャプチャ
    http://d.hatena.ne.jp/TAKAO/20050722

    Managed DirectX (その 1 DirectDraw)
    http://www.microsoft.com/japan/msdn/directx/japan/dx9/mxd1.aspx
     

    2008年11月24日 14:22

すべての返信

  • こんばんは!(^^)!ふ~です。

    前回で、Managed DirectXからビットマップを作成するところは
    完成していると思います。

    pictureBoxのサイズに合わせて、ビットマップを拡大縮小するには
    DrawImage(bmp, 0, 0, pictureBox1.Width, pictureBox1.Height)をPaintの
    タイミングで表示するだけです。

    フォームサイズを変更すると、pictureBoxのサイズも変更するようにDock=Fillに設定しています。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    namespace PictureBoxResize
    {
     public partial class Form1 : Form
     {
      // ここにManaged DirectXからのデータを格納する
      Bitmap bmp;

      public Form1()
      {
       InitializeComponent();

       // フォームサイズと同じにする
       pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
       pictureBox1.Paint += new PaintEventHandler(pictureBox1_Paint);
      }

      private void Form1_Load(object sender, EventArgs e)
      {
       // テスト用、ビットマップを設定する(Managed DirectXからのビットマップを設定する)<-適当に修正する
       bmp = (Bitmap)Image.FromFile(@"C:\My Pictures\hasu01.jpg");
      }

      // サイズの更新、再描画などでピクチャーボックスを描画する
      void pictureBox1_Paint(object sender, PaintEventArgs e)
      {
       // ピクチャーボックスのサイズに合わせてビットマップを拡大縮小する
       e.Graphics.DrawImage(bmp, 0, 0, pictureBox1.Width, pictureBox1.Height);
      }
     }
    }

    予想では、こんな感じかなぁと思ってます。やはり、正確には、サンプルが欲しい所です。
    2008年11月20日 12:42
  • Managed DirectXの経験がないため、ずばっと指摘することはできません。

    問題を起こすための簡単なサンプルを書いてみて、情報を募ってみてはいかがでしょうか。

    2008年11月20日 14:17
    モデレータ
  • お返事ありがとうございました。

    最初にサンプル用意すべきでした。 すいません。



    ---------------------------------------------------------------------------------------
    こちらは MDX 管理クラスの関数です。
    こちらの device は MDX の Device です。
            public void ViewPort_Set(int x, int y, int width, int height)
            {
                Viewport vp = new Viewport();
                vp = device.Viewport;
                vp.X = x;
                vp.Y = y;
                vp.Width = width;
                vp.Height = height;
                device.Viewport = vp;
                PresentParameters pp = new PresentParameters(device.PresentationParameters);
                pp.BackBufferWidth = width;
                pp.BackBufferHeight = height;
                device.Reset(pp);
            }

    こちらは上記関数の呼び出しです。
    こちらの device は、上記クラスのインスタンスです。
            private void Form1_SizeChanged(object sender, EventArgs e)
            {
                device.ViewPort_Set(0, 0, pictureBox1.ClientRectangle.Width, pictureBox1.ClientRectangle.Height);
            }
    ---------------------------------------------------------------------------------------



    ビットマップを拡大すると、画像が荒くなってしまうため、出来れば MDX での描画範囲を大きくしたいです。
    2008年11月21日 2:07
  • こんにちは!(^^)!ふ~です。

    >ビットマップを拡大すると、画像が荒くなってしまうため、出来れば MDX での描画範囲を大きくしたいです。
    2つの問題に分けますと、最初は、「ビットマップを拡大すると、画像が荒くなる」ですね。

     

     pictureBox1.Resize += new EventHandler(pictureBox1_Resize);を追加し、このイベントハンドラの中で

     pictureBox1.サイズに合わせたビットマップを設定し、再描画する事で対応できます。

     

     // ピクチャーボックスのサイズの更新処理
      void pictureBox1_Resize(object sender, EventArgs e)
      {
       // Managed DirectXからピクチャーボックスのサイズに合わせたビットマップ用意する
       //bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height); <--ここを作成する
     
       // 再描画する。
       Refresh();       
      }

     

    2番目の問題で「MDX での描画範囲を大きくしたい」

    サンプルを見ますと、pictureBoxサイズ変更で描画されるタイミングは、Form1_SizeChangedとは描画タイミングが異なります。

    タイミングを合わせれば上手く描画ができるのではと思います。

     

    やはり、全体が分かる、動作するサンプルがあれば、短時間で検討できると思います。

    2008年11月21日 3:53
  • お返事ありがとうございます。

     !(^^)!ふ~ さんからの引用

     

    やはり、全体が分かる、動作するサンプルがあれば、短時間で検討できると思います。



    ソースコード4つ程なんですが、zip にしてアップロードしたり出来るのでしょうか?
    それともそのまま貼り付けても大丈夫なんでしょうか?
    2008年11月21日 7:44
  • こんばんは!(^^)!ふ~です。

     

    > ソースコード4つ程なんですが、zip にしてアップロードしたり出来るのでしょうか?
    > それともそのまま貼り付けても大丈夫なんでしょうか?

    アップロードはできないようです。既に、ピクチャーボックスにビットマップを表示されていっらしゃるようですので、Managed Dirext XでどのようにC#のピクチャーボックスを制御して描画しているのかが理解できれば良いと思います。

     

    どうしても、切り出せない状況であれば、ご参考資料と言うことで、URLで、リンクするような事が良いと思われます。

    100行程度までなら、貼り付けても大丈夫ではないでしょうか?

    2008年11月21日 8:58
  • こんばんは!(^^)!ふ~です。

    取り合えず、私の考えていたピクチャーボックスの描画部分です。

    using System;
    using System.Drawing;
    using System.Windows.Forms;

    using System.Runtime.InteropServices; // Win32

    using Microsoft.DirectX;
    using Microsoft.DirectX.DirectDraw;

    namespace DirectXTest2
    {
     public partial class Form1 : Form
     {
      private Surface imageSurface;

      // ディスプレイサイズ
      int displayWidth  = 120;
      int displayHeight = 160;

      public Form1()
      {
       InitializeComponent();
       pictureBox1.Paint +=new PaintEventHandler(pictureBox1_Paint);
       pictureBox1.Resize += new EventHandler(pictureBox1_Resize);
      }

      private void Form1_Load(object sender, EventArgs e)
      {
       // タイマーの初期化処理
       InitializeTimer();
      }

     

    Code Snippet

      // pictureBox1のペイント
      void pictureBox1_Paint(object sender, PaintEventArgs e)
      {
       // DirectXの初期化
       InitializeDirectDraw();

       Graphics g, gd;
       g = e.Graphics;

       // 適当なビットマップを作成する。
       Bitmap bb = new Bitmap(displayWidth, displayHeight, g);
       gd = Graphics.FromImage(bb);

       // DirextXの画像のハンドル
       IntPtr srcHDC = imageSurface.GetDc();
       IntPtr destHDC = gd.GetHdc();

       // DirextXの画像をビットマップへ転送する
       BitBlt(destHDC, 0, 0, bb.Width, bb.Height, srcHDC, 0, 0, 0xCC0020);

       gd.ReleaseHdc(destHDC);

       // ビットマップをピクチャーボックスに表示する。
       e.Graphics.DrawImage(bb, 0, 0, pictureBox1.Width, pictureBox1.Height);

       bb.Dispose();
      }

      // pictureBox1のサイズ変更ハンドラ
      void pictureBox1_Resize(object sender, EventArgs e)
      {
       Refresh();
      }

     


      ///
      /// DirectDraw関連の初期化を行います。
      ///
      private Device device;
      private Surface primarySurface;
      private Surface secondarySurface;
      private Clipper clipper;

      private void InitializeDirectDraw()
      {
       // DirectDrawデバイスを作成
       device = new Device();

       // デバイスの協調レベルをNormalに設定
       device.SetCooperativeLevel(this, CooperativeLevelFlags.Normal);

       // サーフェスを作成するためのパラメータとなる構造体
       SurfaceDescription desc = new SurfaceDescription();
       SurfaceCaps caps = new SurfaceCaps();

       // プライマリサーフェスを作成
       caps.PrimarySurface = true;
       desc.SurfaceCaps = caps;
       primarySurface = new Surface(desc, device);

       // 構造体をリセット
       caps.Clear();

       // セカンダリサーフェスを作成
       desc.Width = displayWidth;
       desc.Height = displayHeight;
       desc.SurfaceCaps = caps;
       secondarySurface = new Surface(desc, device);

       // クリッパーを作成
       clipper = new Clipper(device);
       clipper.Window = this;

       // プライマリサーフェスに作成したクリッパーを指定
       primarySurface.Clipper = clipper;

       desc.Clear();

       // Image.bmpという画像ファイルを読み込んでサーフェスを作成
       // "bin\debug\XXXXX.bmp" ビットマップ以外は不可
       imageSurface = new Surface("xxxxxxxxxx.BMP", desc, device);
      }
      
      ///
      /// タイマーの初期化を行います。
      ///
      private Timer timer;

      private void InitializeTimer()
      {
       // タイマーを作成
       timer = new Timer();

       // インターバルを30[ms]に設定
       timer.Interval = 30;

       // イベントハンドラを指定
       timer.Tick += new EventHandler(this.Render);

       // タイマーを起動
       timer.Start();
      }

      ///
      /// timerのTickイベント用のハンドラです。 ここでDirectDrawの描画処理を行います。
      ///
      private void Render(object sender, EventArgs e)
      {
       // フォームが表示されていない場合は描画しない
       if (!this.Visible) return;

       // フォームが最小化されている場合は描画しない
       if (this.WindowState == FormWindowState.Minimized) return;
      
       try
       {
        // 背景をブルー
        secondarySurface.ColorFill(Color.Blue);
        // 楕円の中を赤で塗る
        secondarySurface.FillColor = Color.Red;
        // 楕円を描画する
        secondarySurface.DrawEllipse( 10, 30, 50, 100);

        // 描画先領域を指定
        Rectangle destRectangle = new Rectangle();

        destRectangle.Size = this.ClientSize;
        destRectangle.Location = this.PointToScreen(this.ClientRectangle.Location);

        // プライマリサーフェスにセカンダリサーフェスを描画
        primarySurface.Draw(destRectangle, secondarySurface, DrawFlags.Wait);
       }
       catch( SurfaceLostException error )
       {
        InitializeDirectDraw();
       }
      }

      //BitBlt複写処理
      [DllImport("gdi32.dll")]
      public static extern bool BitBlt(
       IntPtr hdcDest,
       int xDest,
       int yDest,
       int width,
       int height,
       IntPtr hdcSrc,
       int xSrc,
       int ySrc,
       System.Int32 rasterOp
       ); 
     }

     // メイン処理
     public class ManagedDX
     {
      public static void Main(string[] args)
      {
       Form1 FormTest = new Form1();

       // アプリケーションを起動する
       Application.Run(FormTest);
      }
     }
    }

    <作成方法>
    参照設定
    Microsoft.DirectX;
    Microsoft.DirectX.DirectDraw;
    System;
    System.Drawing;
    System.Windows.Forms;
    は最低必要です。

    "bin\debug\XXXXX.bmp"に小さめの軽いビットマップを用意します。
    サイズが大きいとエラーになります。
     
    使用したManaged DirectX バージョン
    DirectX 9.0c Redistributable for Software Developers - Multilingual - 日本語
    http://www.microsoft.com/downloads/details.aspx?familyid=143EF8C6-CFF7-4AF0-8209-1CDF49C58BC0&displaylang=ja
    インストール後、「スタート」->「ファイル名を指定して実行」->「DXDiag」で動作確認が行えます。

    参考URL
    Managed DirectX ゲーム画面のキャプチャ
    http://d.hatena.ne.jp/TAKAO/20050722

    Managed DirectX (その 1 DirectDraw)
    http://www.microsoft.com/japan/msdn/directx/japan/dx9/mxd1.aspx
     

    2008年11月24日 14:22
  • お返事ありがとうございます。

    これだと、フォームを横に伸ばした時に一緒に画像も伸びてしまうため、私が行いたい処理とは少し違います。



    http://sorceryforce.com/index.html
    こちらのページの
    http://sorceryforce.com/programing/mdx/direct3d/stepup/polygon3d.html
    こちらのサンプルにバックバッファ取得関数を追加してテストしています。

    変更点
     ・ MainForm のメンバにビットマップ変数を追加。

     ・デバイス初期化時のハンドルを PictureBox のものに切り替えてあります。

     ・SaveBackBuffer を

            public Bitmap SaveBackBuffer()
            {
                Surface sf = _device.GetBackBuffer(0, 0, BackBufferType.Mono); //バックバッファの取得

                Graphics g, gd;
                g = sf.GetGraphics(); //Graphicsの取得
                Bitmap bb = new Bitmap(sf.Description.Width, sf.Description.Height, g);
                gd = Graphics.FromImage(bb);
                IntPtr srcHDC = g.GetHdc();  //Hdcの取得
                IntPtr destHDC = gd.GetHdc();
                BitBlt(destHDC, 0, 0, bb.Width, bb.Height, srcHDC, 0, 0, 0xCC0020);
                gd.ReleaseHdc(destHDC);
                g.ReleaseHdc(srcHDC);

                sf.ReleaseGraphics();
                g.Dispose();

                return bb;
            }

    このように変更。

     ・ MainLoop 内の EndScene 後に SaveBackBuffer の返り値を MainForm のビットマップ変数に追加し
    PictureBox の Paint イベントで drawImage して描画します。



    上記のソースでテストしていたのですが、サイズ変更時にデバイスを破棄して再構築したら上手くいきました。
    しかし、いちいちデバイスを再構築しなければならないのでしょうか? フォームのサイズ変更のイベントは
    細かく発生するので、これだと少し処理が重い気がします。
    しかし他に方法が無いのであれば、これで解決としたいと思います。
    2008年11月25日 3:26
  • こんにちは。中川俊輔 です。

     

    !(^^)!ふ~さん、Azuleanさん、回答ありがとうございます。

     

    七篠さん、フォーラムのご利用ありがとうございます。

    七篠さんの問題解決になる回答ではなかったようですが、

    有用な情報と思われたため、!(^^)!ふ~さんの回答へ回答済みチェックをつけさせていただきました。

     

    今後ともフォーラムをよろしくお願いします。

    それでは!

    2009年1月7日 5:19