メモリデバイスコンテキストのビューポートの内容を表示する方法

[戻る]
2010/04/23(金) 15:11:37
1000×1000の論理空間に線などを描画した内容を
100×100ピクセルに縮小して表示させたいのですが
うまくいきません。以下のやり方は間違っているのでしょうか?

まずメモリデバイスコンテキストを用意します。
次にボタン押下などのイベントのときにこのメモリデバイスコンテキスト
に対して以下のマッピング設定を行い、LintTo()などで描画処理をします。
・SetMapMode(MM_ISOTROPIC)
・SetWindowExt(1000, 1000)
・SetViewportExt(100, 100)

後は、OnDraw()のところでBitBlt()でメモリデバイスコンテキストの
内容をコピーして表示させます。

しかし、実際に表示するのは1000×1000のエリアに描画した
左上の100×100のエリアの内容のみです。
尚、開発環境はVC++6.0 でMFCを使用します。

以下のコードを記載します。

////////////////////////////////
// ボタン押下イベント
////////////////////////////////
{
    // マッピングモードセット
    m_memdc_emb.SetMapMode(MM_ISOTROPIC);

    // 論理座標で表示させたい領域
    m_memdc_emb.SetWindowExt(1000, 1000);

    // 実際に表示するピクセル領域
    m_memdc_emb.SetViewportExt(100, 100);

    // 背景描画
    m_memdc_emb.FillSolidRect(0,0,1000,1000,RGB(222,219,222));

    // 各種描画処理(省略)

    // 再描画
    Invalidate(FALSE);
}

////////////////////////////////
// OnDraw再描画処理
////////////////////////////////
void CxxxView::OnDraw(CDC* pDC) 
{
    pDC->BitBlt(5, 5, 100, 100, &m_memdc_emb, 0, 0, SRCCOPY);
}

aetos 2010/04/23(金) 16:31:40
BitBlt の第3、第4引数は、コピー元DC、コピー先DC双方について論理単位となるから
です。
このコードは、メモリ DC の左上の 100x100 を、ビューの (5,5) からの 100x100 にコ
ピーします。

もちろん、第3、第4引数を 1000x1000 にすればいいというものでもありません。
それではメモリ DC 上の内容は正しく取得されても、描画先に余分な描画をしてしまう
ことになります。

根幹は第3、第4引数が複数の意味を持つことにあるので、(デバイス座標系で見た大
きさが同じであっても)座標を別々に指定できる StretchBlt を使う必要があるでしょ
う。

仲澤@失業者 2010/04/23(金) 17:18:22
aetosさん発言の通りですが、

1.OnDraw()内で使用している pDC にマップモード等が設定されていない

のが一番の問題かもしれません。

思ったようにならないときは
BitBlt()のデバイスコンテキストでのマップモード、
ウインドウエクステント、ビューポートエクステントなどは
そろえてからデバッグしてみましょう。

2010/04/23(金) 17:58:08
aetos様、仲澤@失業者様ご回答ありがとうございます。

aetos様のおっしゃるとおりですね。
BitBlt()の第3、第4引数の意味を理解できていませんでした。
BitBlt()をStretchBlt()にすれば思ったとおりの表示になりました。
この場合、メモリDCのマッピング設定は意味がなくなりますよね?

仲澤@失業者様のご指摘どおり、BitBlt()のデバイスコンテキストでの
マップモード、ウインドウエクステント、ビューポートエクステントを
あわせることでも思ったとおりの表示になりました。

ただ、どちらの場合もメモリDCの論理領域が大きくなると
(1000×1000から例えば10000×10000など)OnDraw()処理が重くなって
しまいますよね?

最終的に表示するデバイス領域は常に100×100なので、できれば
メモリDC上でデバイス領域100×100のビットマップを作っておき、OnDraw()内では
BitBlt()で転送するのみにしたいのですが無理なんですかね?

無理だとすると、上記のStrecBlt()を使う方法とマップモードをあわせたBitBlt()を
使う方法とどちらがセオリーだと思いますか?

ご意見を聞かせてください。

aetos 2010/04/23(金) 18:22:06
> この場合、メモリDCのマッピング設定は意味がなくなりますよね?

そんなことはないのでは。

確かに StretchBlt を使うと縮小しながら描画できますから、メモリ DC の方も 
MM_TEXT で作っておいて、描画時に縮小することはできます。
でも、その場合、メモリ DC に選択しておくビットマップも 1000x1000 のものが必要に
なりますよね。
懸念されているパフォーマンスの問題が起きるのでは。
一方で、SetWindowExt と SetViewportExt を使えば、ビットマップの大きさは 
100x100 で済みますね。

念のため書いておきますと、メモリ DC に選択するビットマップは 
CBitmap::CreateCompatibleBitmap で作っていると思いますが、この引数の単位はピク
セル単位=デバイス単位=MM_TEXT単位です。
デバイスコンテキストの論理座標単位がなんであろうと、その数字と同じにする必要は
ありませんよ。

aetos 2010/04/23(金) 18:24:22
> OnDraw()内で使用している pDC にマップモード等が設定されていない

StretchBlt ではなく BitBlt にこだわるなら、それでもいいでしょう。
わかりやすい方を選べばいいですね。

2010/04/23(金) 21:43:01
aetos様ご回答ありがとうございます。

申し訳ありませんが、もう少し教えてください。

StretchBlt()を使って思ったとおりの表示をさせたときは以下のコード記述しました。

void CxxxView::OnDraw(CDC* pDC) 
{
    pDC->SetStretchBltMode(HALFTONE);
    pDC->StretchBlt(5, 5, 100, 100, &m_memdc_emb, 0, 0, 1000, 1000, SRCCOPY);
}

メモリDC の論理領域は1000,1000を指定しています。
このときメモリDCがMM_TEXTで、1000×1000のビットマップである場合、
縮小描画となりパフォーマンスが悪くなり、
MM_ISOTROPIC、SetWindowExt(1000, 1000)、SetViewportExt(100, 100)で
100×100のビットマップである場合、縮小描画とならずパフォーマンスが悪くないと
いうことでしょうか?

つまり、先にも書きました、私のやりたいこと
>最終的に表示するデバイス領域は常に100×100なので、できれば
>メモリDC上でデバイス領域100×100のビットマップを作っておき、OnDraw()内では
>BitBlt()で転送するのみにしたいのですが無理なんですかね?
を実現しているということでしょうか?

何度も申し訳ありませんが、よろしければ教えてください。

aetos 2010/04/24(土) 17:15:33
話を整理します。

パターンA:
MM_ISOTROPIC、SetWindowExt(1000, 1000)、SetViewportExt(100, 100) で 100x100 の
ビットマップ

パターンB:
MM_TEXT で 1000x1000 のビットマップ

Aでは描画時に縮小されていることになります。
Bでは転送時に縮小します。

どちら縮小描画と言えば縮小描画です。
転送スピードはおそらくAの方が速いでしょう。
ただし、Aではひょっとすると描画が遅いかもしれません。

確実に言えるのは、Bはビットマップの大きさが 100 倍になるということで、つまりは
使用メモリ量も 100 倍になるということです。

A:
描画スピード:遅い?
転送スピード:速い
メモリ使用量:少ない

B:
描画スピード:速い
転送スピード:遅い?
メモリ使用量:多い

トータルでどっちがパフォーマンスがいいかは、実験してみないとわかりません。

2010/04/24(土) 22:50:11
aetos様 ご回答ありがとうございます。

パターンA・Bのどちらの場合でも、結局OnDraw()内でStretchBlt()を呼び出し
メモリDCの内容を転送して描画を行わなければならないと思います。

つまりどちらの場合もOnDraw()で縮小・転送・描画の処理行われると思います。

(パターンAでも、ボタン押下イベントのメモリDCに対しての場合、
 描画を行う訳ではないので結局StretchBlt()で縮小・転送すると
 理解しています。)

私としては、縮小まではボタン押下イベントの中で行い、OnDraw()では転送・描画のみに
したかったのですが、無理なんですかね?

aetos 2010/04/25(日) 02:02:58
パターンAでStretchBltを使ったのは、転送元と転送先のサイズを別々に渡せるためで
あって、縮小するためではありません。物理サイズは同じですよね。
StretchBltを使おうとも、物理サイズが同じ場合と違う場合とでは、スピードも違うの
ではないかと想像します。実験していないので確信はありませんが。

> (パターンAでも、ボタン押下イベントのメモリDCに対しての場合、
>  描画を行う訳ではないので結局StretchBlt()で縮小・転送すると
>  理解しています。)

ここの意味はよくわかりませんでした。

まぁ、どうしても StretchBlt を避け BitBlt を使いたいというのであれば、仲澤さん
のおっしゃるように、View にも SetMapMode / SetWindowExt / SetViewportExt をすれ
ば BitBlt で済むでしょう。

ところで、どうしてそこまでこだわるのでしょう?
実験してみたら深刻なパフォーマンスの差が出たというのであれば、実験していない俺
に言えることは何もありません。
別に俺の方法が一番パフォーマンスがいいというつもりはありませんが、そこまでこだ
わるのであれば実験してみる価値はあるのでは?

2010/04/25(日) 22:13:08
[[解決]]
aetos様 ご回答ありがとうございました。

> パターンAでStretchBltを使ったのは、転送元と転送先のサイズを別々に渡せるためで
> あって、縮小するためではありません。物理サイズは同じですよね。
> StretchBltを使おうとも、物理サイズが同じ場合と違う場合とでは、スピードも違うの
> ではないかと想像します。実験していないので確信はありませんが。

このような動作を一番理想としていました。

> ところで、どうしてそこまでこだわるのでしょう?
> 実験してみたら深刻なパフォーマンスの差が出たというのであれば、実験していない俺
> に言えることは何もありません。
> 別に俺の方法が一番パフォーマンスがいいというつもりはありませんが、そこまでこだ
> わるのであれば実験してみる価値はあるのでは?

本当に何度も何度もしつこく質問したにもかかわらず、
粘り強くご丁寧に回答いただきありがとうございました。

一番はじめに回答をいただいた時点で所望の動作が確認でき、
本来は自分でいろいろと実験をして、パフォーマンスを確認して
解決とするべきでした。

ついつい疑問に思ったことを実験もせずになんども質問を繰り返してしました。
申し訳ありませんでした。

私としましてはパターンAでいこうと思いますが、その他の動作も
実験して一番よいと思われる方法でいきたいと思います。

本当にありがとうございました。

古いスレッドには追加発言できません。