デバイスコンテキストに描画できる大きさの制限について

[戻る]
コロ 2009/10/11(日) 14:43:06
開発環境 Windows XP Visual C++6.0  MFC、SDIです。

画像処理ソフトを作成しています。
前回高速化のためにメモリデバイスコンテキストの使用についてアドバイスを頂きまし
たが、今度は別件で質問があります。


画像の拡大表示時にDIB画像をOnDraw(CDC* pDC)関数内で

StretchDIBits(hdc,0,0,m_SizeX*m_zoom, 
m_SizeY*m_zoom ,0,0,m_SizeX,m_SizeY,m_Bitdata,&bmpInfo,DIB_RGB_COLORS,SRCCOPY);

m_SizeX、m_SizeY:画像サイズ
m_Bitdata:DIBデータ
m_zoom:ズーム倍率

で表示していますが、拡大率が大きくなると画像が表示されなくなってしまいます。
デバイスコンテキストに非常に大きな領域(元画像2400*1300でm_zoom=15倍とか)を描画
しているためにこのような問題が発生していると推測しています。
高速化のためにメモリデバイスコンテキストを用いるコードに変更(OnDraw内ではBitBlt
を使用する)の際にも同様の問題が発生しています。
メモリデバイスコンテキストにも同様に非常に大きな領域(元画像2400*1300でm_zoom=15
倍とか)を描画することができません。


質問@:デバイスコンテキストまたはメモリデバイスコンテキストへの描画できる領域
の制限はどこで決まるのでしょうか?
質問の意図は現在作成しているビューワーでは拡大率をx12倍(元画像2400*1300)まで
はOKでx15倍からNGですが、ハードスペックによる制限であるなら、拡大表示のコ
ードを書き直す必要があると感じているからです。


質問A
ビューワーで拡大表示する時の一般的な手法等があったらアドバイス願います。
ウインドウ領域の大きさから必要領域を計算してその部分のみをデバイスコンテキスト
に描画し、スクロールに合わせて再度描画する等の手法が一般的なのでしょうか?



<前回の質問>
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200910/09100004.txt



質問の連投で大変恐縮ですが、よろしくお願いいたします。
このラウンジの「デバイスコンテキスト」で検索した質問は全て読みましたが解決できな
かったためアドバイスをお願いいたします。

FlatWest 2009/10/11(日) 21:50:17
回答 1: 描画できないのはメモリが不足しているため。ゆえに環境依存。
    試しに、元画像2400*1300でm_zoom=15倍とかの大きさのビットマップを
    CreateCompatibleBitmap() してみるとよろし。戻り値はおそらく FALSE に
    なるでしょう。

回答 2: その手法でよろしいかと。

しま 2009/10/11(日) 22:27:16
DDB(普通の意味でのビットマップ)は印刷のことも含めて考えると
使わない方がいいですし、あまり大きなビットマップが作れない
場合もありました
(もっとも、これは10年以上前の経験からのことなので今は改善しているかも
しれません)

DIB か DIBセクションかなら DDB 程の制限は経験しませんでしたが、
当時は1Gバイトもの大きさの画像データーを扱うこと自体が特殊
な時代でしたから(そもそも身近にそんなに大きな画像データーが
なかったし、8 ビットカラーが当たり前の時代でしたので 24bitカラー
や 32bit カラーのデーターがあっても使えるグラフィックカードが
8bit カラーしか扱えないといった時代でした)

ところで色深度について(32ビットカラーとか16ビットカラーとか
8ビットカラーとか)触れていないので、画像データーの大きさ(
バイト数)が判りませんが、 XP が扱えるユーザー領域の制限以内に
収まることは間違いないのですね?
(一つのアプリケーション全体で2Gバイトより1Mバイト少ない)

コロ 2009/10/11(日) 23:10:33
FlatWest様、しま様 ご回答ありがとうございます。

色深度についてですが32bitを使用しているので15倍では2Gバイトを超えていました。
これが原因のようですね。

>試しに、元画像2400*1300でm_zoom=15倍とかの大きさのビットマップを
>CreateCompatibleBitmap() してみるとよろし。戻り値はおそらく FALSE に
>なるでしょう。

確かにFALSEが返ってきました。2G以下でもFALSEが返ってきます。
DDBは大きなビットマップを扱えないようですね。

疑問が解決してすっきりしました。
早速修正の作業に取り掛かります。

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

コロ 2009/10/11(日) 23:11:03
[[解決]]
すみません、解決欄のチェックをし忘れました。

ロマ 2009/10/13(火) 10:31:22
CreateDIBSectionのhSection引数(handle to a file mapping object)を使えば
大きなビットマップを作れそうです。

1)(2400 * 15) * (1300 * 15) * 4 = 2.8GByte以上の file mapping objectを作る
2)転送先をY軸方向で分割して細長いDIBSectionを作り、ビットマップビットを書き込む
 つまり、CreateDIBSectionのBITMAPINFOHEADERのbiHeightを 1/nに、
 dwOffset size * i / nにして、ループしながらビットを書き込んでいく
3)描画時は、画面高さのDIBSectionを作る
4)file mapping objectは終了時まで保持する

こんな流れです。

# file mapping objectを丸ごと使ったDIBSectionは失敗しました。
# 私の環境ではCreateFileMappingのhFile引数を0xffffffffにすると
 スワップが始まって大変でしたが、描画は早いようです。
# 速度が実用的かどうかは分かりません。

仲澤@失業者 2009/10/13(火) 12:49:43
もういっちょアドバイスすると、StretchDIBits()の「コピー先・・・」は
せいぜい対象Viewの大きさ程度で十分ですね。デスクトップサイズにしてる
コードも見かけますが、マルチモニター時に破綻します。
また、それに対応する元画像の「コピー元・・・」は「コピー先・・・」サイズ
から勘案して、小さくできるわけです。そうすると
 m_SizeX * m_zoom
の様なややなげやりなコードにはなりません(笑)。
もちろんdoubleで計算して、四捨五入などの誤差まるめを行ってください。

まだ、工夫の余地があるようですね。

ロマ 2009/10/13(火) 15:11:16
> 3)描画時は、画面高さのDIBSectionを作る
「画面」は方言ですねw。描画先のウィンドウのクライアントエリアの高さです。

> また、それに対応する元画像の「コピー元・・・」は「コピー先・・・」サイズ
> から勘案して、小さくできるわけです。
これは異論あり。元画像が実寸(たとえば[mm]単位)の可能性もあります。

前回書き漏れたこと
CreateDIBSectionのdwOffset引数の下位2bitはゼロにしないとまずいようです。
(こういうのは何と書けばいいのか、要はDWORD単位ということです)

仲澤@失業者 2009/10/13(火) 18:42:49
> これは異論あり。元画像が実寸(たとえば[mm]単位)の可能性もあります。

若干誤解されたかもしれませんね。詳しく解説すると、拡大を

(1)拡大した全幅(Xz) = 元BMPの全幅(X) × 拡大率(Z)

の様に定義し、Viewの実際の幅を(Xv)とするならば、

(2)Xz = X * Z より Xv = Xpart * Zが導かれます。

つまり、Viewの幅全てにZで拡大したBMPを表示するときに
必要になる元のBMPの幅(Xpart)は

(3)Xpart = Xv / Z

で求められます。つまり以降の(4)と(5)は数学的に等価ですが
中間で算定される数値が小さいため、(5)の方が
「早く」「少ないメモリ」で動作するわけです。

(4) StretchDIBit(
    hdc_ura,
      0,                    // コピー先長方形の左上隅の x 座標
      0,                    // コピー先長方形の左上隅の y 座標
      (現画像の幅) * Z,     // コピー先長方形の幅
      (現画像の高) * Z,     // コピー先長方形の高さ
      0,                    // コピー元長方形の左上隅の x 座標
      0,                    // コピー元長方形の左上隅の x 座標
      (現画像の幅),         // コピー元長方形の幅
      (現画像の幅),         // コピー元長方形の高さ
     ・・・);


(5) StretchDIBit(
    hdc_ura,
      0,                    // コピー先長方形の左上隅の x 座標
      0,                    // コピー先長方形の左上隅の y 座標
      (Viewの幅),           // コピー先長方形の幅
      (Viewの高),           // コピー先長方形の高さ
      0,                     // コピー元長方形の左上隅の x 座標
      0,                     // コピー元長方形の左上隅の x 座標
      (Viewの幅) / Z,        // コピー元長方形の幅
      (Viewの高) / Z,        // コピー元長方形の高さ
     ・・・);

いかがでしょう。
もちろん細かい例外が必要ですが。

仲澤@失業者 2009/10/13(火) 18:51:46
> で求められます。つまり以降の(4)と(5)は数学的に等価ですが

違いますね(笑)。

  で求められます。つまり以降の(4)と(5)は表示上は同等の結果になりますが

に訂正します。

ロマ 2009/10/13(火) 21:31:24
スクロールでページup/downする毎に
元ビットマップから拡大ビットマップを作り直すという理解で良いですか。

もしそうならば、どっちが使い勝手がいいんでしょうか。
私の方法では読み込み時に時間がかかります。
スクロールは早いんじゃないかなぁと期待していますが、
多くの場合、実験は期待を裏切りますからなんともいえません。

仲澤@失業者 2009/10/14(水) 10:34:56
>スクロールでページup/downする毎に
>元ビットマップから拡大ビットマップを作り直すという理解で良いですか。

そうですね。
表示のみ(含む拡大縮小オフセット)やトリミング程度の編集を目的とした場合は
 1.編集対象全体(又は部分)を選択した裏DC
 2.表DC
の2段式でも十分かもしれませんが、より複雑な画像処理の場合は

 1.編集対象全体を選択しているDC
 2.インターフェース専用DC(拡大縮小、オフセット、他)
 3.表DC

の少なくとも3段式になると思います。この場合

 (1)ラバーバンド、編集用ハンドル、等は2.又は3.へ描画
 (2)編集操作確定後の描画処理は当然1.へ描画

みたいな感じ。
実際には効率を上げるため、さらに複雑になってると思われます。
要は2.の表示インターフェースDCの仕組みを工夫して、
いかに早く、効率的に表示して見せるかにかかっていると思われます。

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