none
メモリデバイスコンテキストでの描画により画像のシフト方法について RRS feed

  • 質問

  • お世話になります、鏑木です。
    現在、Visual Studio 2005にてMFCのアプリケーションを開発しております。
    今回質問させて頂きたいことは、メモリデバイスを使用して、ピクチャーコントロール上に描画した図(グラフ)をボタンまたはキー操作によって
    左右にシフトさせたいと思っており、その処理の方法で何かよいアイディアを頂けたらと思い質問しました。

    初めに、私は左右移動の操作命令(ボタンまたはキー操作によるイベントで)でその都度、メモリデバイスの背景を塗りつぶし、メモリデバイスに全ての点を
    プロットして、描画するという処理をしていました。
    そうすれば確かに描画は出来るのですが、処理が重く、同じダイアログの画面上にグラフを表示するピクチャーコントロール以外に、データを波形で表示しているピクチャーコントロール
    があるのですが、その波形の描画更新が影響を受け、描画更新がすごく遅くなります。一度ボタンやキーを操作した場合には影響はそれほどでも無いのですが、
    操作キーを押しっぱなしにすると連続でグラフがシフトされるため、完全に波形の描画の処理更新が停止してしまいます。

    考えられることは描画の更新処理をOnTimerの処理にしているため、シフト後のグラフのプロット点の描画処理の重さの影響で、正常にタイマーイベントが発生しなくなるためでは
    ないかと考えています。しかし、できれば波形の描画方法の変更ではなく、グラフの描画の処理方法をもっと処理を重くしない(すべてのプロット点を一から設定していかない)
    で描画できればなと思っております。

    ご意見がありましたら、どうか宜しくお願い致します。

    Visual Studio 2005
    Windows CE 6.0
    2009年7月16日 7:09

回答

  • 初めに、私は左右移動の操作命令(ボタンまたはキー操作によるイベントで)でその都度、メモリデバイスの背景を塗りつぶし、メモリデバイスに全ての点を
    プロットして、描画するという処理をしていました。
    ここでいう「全ての点」というのは、「表示に必要な点すべて」でしょうか?
    それとも、「表示したい領域に関わらず、データの全て」でしょうか?

    もし、前者なら
    メモリ上のDCは、表示領域と同じ大きさにする必要がないので
    「グラフすべてを描画できる大きさ」でつくり、そこに全部書いておく。
    そのDCから、BilBltするときに「コピー元のX,Y座標」をうまく設定することで、
    必要な部分だけを画面に表示する。
    こうすれば、かなり処理は軽くなります。

    後者で、
    すでに上のことをやっていて、なお問題なら・・・
    たしか、かなり早いスパンでタイマー更新されてたと思うで
    タイマーで描画中は、「ボタン操作」によるX,Yの値を変えるだけで
    描画作業は、タイマーでの呼び出しによる描画だけに任せるというのはどうでしょうか?
    • 回答としてマーク 鏑木肆星 2009年7月17日 2:45
    2009年7月16日 11:58
  • 考えられることは描画の更新処理をOnTimerの処理にしているため、シフト後のグラフのプロット点の描画処理の重さの影響で、正常にタイマーイベントが発生しなくなるためでは
    ないかと考えています。しかし、できれば波形の描画方法の変更ではなく、グラフの描画の処理方法をもっと処理を重くしない(すべてのプロット点を一から設定していかない)
    で描画できればなと思っております。

    以下のような処理の流れということでしょうか?
    OnKeyDown()でメモリデバイスコンテキストに(シフト後の状態の)波形をすべて描画し、ピクチャーコントロールへの描画処理は行わない。
    OnTimer()内でメモリデバイスコンテキストに選択していたビットマップをピクチャーコントロールに渡して描画させる。

    これが正しいと仮定してですが、
    WM_TIMERは基本的に他のメッセージがメッセージキューにある場合には、GetMessage()やPeekMessage()で取得されませんので、
    キーを押しっぱなしの場合にはOnTimer()が呼び出されることはありません。
    描画更新が行われないのはそのためなので、処理を早くしたとしても描画処理を変更しなければ状況は変わらないと思います。

    そもそも、ピクチャーコントロールを使うのはどうしてでしょうか?
    こういう用途には向いていないような気がします。
    普通のWindowのデバイスコンテキストに描画するのではだめなのでしょうか?

    一般的には左右移動のキーでScrollWindow()を使ってスクロールさせて、
    無効になった領域だけをメモリデバイスコンテキストからコピーするということをするのだと思います。
    • 回答としてマーク 鏑木肆星 2009年7月17日 2:45
    2009年7月17日 0:30

すべての返信

  • 初めに、私は左右移動の操作命令(ボタンまたはキー操作によるイベントで)でその都度、メモリデバイスの背景を塗りつぶし、メモリデバイスに全ての点を
    プロットして、描画するという処理をしていました。
    ここでいう「全ての点」というのは、「表示に必要な点すべて」でしょうか?
    それとも、「表示したい領域に関わらず、データの全て」でしょうか?

    もし、前者なら
    メモリ上のDCは、表示領域と同じ大きさにする必要がないので
    「グラフすべてを描画できる大きさ」でつくり、そこに全部書いておく。
    そのDCから、BilBltするときに「コピー元のX,Y座標」をうまく設定することで、
    必要な部分だけを画面に表示する。
    こうすれば、かなり処理は軽くなります。

    後者で、
    すでに上のことをやっていて、なお問題なら・・・
    たしか、かなり早いスパンでタイマー更新されてたと思うで
    タイマーで描画中は、「ボタン操作」によるX,Yの値を変えるだけで
    描画作業は、タイマーでの呼び出しによる描画だけに任せるというのはどうでしょうか?
    • 回答としてマーク 鏑木肆星 2009年7月17日 2:45
    2009年7月16日 11:58
  • yominet様、いつもご回答ありがとうございます。

    全ての点は、yominet様のおっしゃる「表示に必要な点すべて」です。
    私もフォーラムに質問させて頂いてから、yominet様のおっしゃるような

    >「グラフすべてを描画できる大きさ」でつくり、そこに全部書いておく。
    そのDCから、BilBltするときに「コピー元のX,Y座標」をうまく設定することで、
    必要な部分だけを画面に表示する。

    を思いつきまして、昨日から悪戦苦闘しているところです。
    しかし、グラフの点すべてをメモリデバイスコンテキストに書くという発想までは
    いたらなかった為、yominet様のおっしゃる手法でチャレンジしてみたいと思います。
    2009年7月17日 0:03
  • 考えられることは描画の更新処理をOnTimerの処理にしているため、シフト後のグラフのプロット点の描画処理の重さの影響で、正常にタイマーイベントが発生しなくなるためでは
    ないかと考えています。しかし、できれば波形の描画方法の変更ではなく、グラフの描画の処理方法をもっと処理を重くしない(すべてのプロット点を一から設定していかない)
    で描画できればなと思っております。

    以下のような処理の流れということでしょうか?
    OnKeyDown()でメモリデバイスコンテキストに(シフト後の状態の)波形をすべて描画し、ピクチャーコントロールへの描画処理は行わない。
    OnTimer()内でメモリデバイスコンテキストに選択していたビットマップをピクチャーコントロールに渡して描画させる。

    これが正しいと仮定してですが、
    WM_TIMERは基本的に他のメッセージがメッセージキューにある場合には、GetMessage()やPeekMessage()で取得されませんので、
    キーを押しっぱなしの場合にはOnTimer()が呼び出されることはありません。
    描画更新が行われないのはそのためなので、処理を早くしたとしても描画処理を変更しなければ状況は変わらないと思います。

    そもそも、ピクチャーコントロールを使うのはどうしてでしょうか?
    こういう用途には向いていないような気がします。
    普通のWindowのデバイスコンテキストに描画するのではだめなのでしょうか?

    一般的には左右移動のキーでScrollWindow()を使ってスクロールさせて、
    無効になった領域だけをメモリデバイスコンテキストからコピーするということをするのだと思います。
    • 回答としてマーク 鏑木肆星 2009年7月17日 2:45
    2009年7月17日 0:30
  • Atsushi777様、ご回答ありがとうございます。

    >以下のような処理の流れということでしょうか?
    OnKeyDown()でメモリデバイスコンテキストに(シフト後の状態の)波形をすべて描画し、ピクチャーコントロールへの描画処理は行わない。
    OnTimer()内でメモリデバイスコンテキストに選択していたビットマップをピクチャーコントロールに渡して描画させる。

    今回質問させて頂いています描画にはOnTimer()は使用していません。なのでOnKeyDown()でメモリデバイスコンテキストに描画領域の
    全ての点を描画し、ピクチャーコントロール上にBitBltしています。

    >そもそも、ピクチャーコントロールを使うのはどうしてでしょうか?
    こういう用途には向いていないような気がします。
    普通のWindowのデバイスコンテキストに描画するのではだめなのでしょうか?

    これには深い理由は特に無く、経験不足から、画像を描画するばあいはピクチャーコントロールがいい、と思っているだけです(^^;)
    デバイスコンテキスト上(ダイアログ上に)に直接描画したほうがいいのでしょうか?

    >一般的には左右移動のキーでScrollWindow()を使ってスクロールさせて、
    無効になった領域だけをメモリデバイスコンテキストからコピーするということをするのだと思います。

    ScrollWindow()は初めて聞きました。この関数はyominet様がおっしゃるように、描画する全ての点を始めにメモリデバイスコンテキストに
    描画したら、あとはScrollWindow()を呼び出すことで自動でシフト描画更新処理をしてくれるものなのでしょうか?

    質問責めで申し訳ございません。。
    2009年7月17日 1:30
  • yominet様

    こんにちは、鏑木です。
    yominet様のおっしゃる方法で表示させたところ、無事波形の描画にも全く影響を与えずに処理することが出来ました。
    大変参考となるご意見、本当にありがとうございました。
    2009年7月17日 2:45
  • 今回質問させて頂いています描画にはOnTimer()は使用していません。なのでOnKeyDown()でメモリデバイスコンテキストに描画領域の
    全ての点を描画し、ピクチャーコントロール上にBitBltしています。
    ということでしたら、WM_TIMERうんぬんは関係ないようです。すみません。
    また、CStatic::SetBitmap()をつかっているのではなくてBitBlt()しているのであれば、ピクチャーコントロールを使っていても問題ないです。

    ScrollWindow()は初めて聞きました。この関数はyominet様がおっしゃるように、描画する全ての点を始めにメモリデバイスコンテキストに
    描画したら、あとはScrollWindow()を呼び出すことで自動でシフト描画更新処理をしてくれるものなのでしょうか?
    ScrollWindowEx()はクライアント領域の内容をスクロールするものですので、この場合だとピクチャーコントロールの中身をスクロールします。
    スクロールするとスクロールした分無効な領域ができますから、その分だけメモリデバイスコンテキストからBitBlt()すればいいので、
    BitBlt()する領域が減り、理論的にはちらつきなどが減るはずですが、今回のケースではあまり影響ないかもしれません。

    すでにボトルネックとなっていた部分は改善できたようですので、この投稿も含めて蛇足だったということで。m(_ _)m
    2009年7月17日 3:21
  • Atsushi777様、ご回答ありがとうございます。

    >すでにボトルネックとなっていた部分は改善できたようですので、この投稿も含めて蛇足だったということで。m(_ _)m

    いえいえ、大変参考となるご意見ありがとうございました。ScrollWindowEx()という関数の存在を知ることが出来ただけでも、
    とても勉強になりました。
    丁寧に返答して頂き、本当にありがとうございました。
    2009年7月17日 3:28