CBitmapのコピーをするには

[戻る]
shogo 2004/07/26(月) 10:32:57
おはようございます,shogoと申します.

開発環境はWinXP,VC7,MFCです.

あるクラスにあるCBitmapを関数の呼び出し側のCBitmapにコピーしたいので,
CBitmapを返す関数CBitmap GetBitmap(void)を以下のように定義したのですが,
(やりたいことは,CBitmap mBitmap = GetBitmap();という感じでコピーして,
この受け取ったCBitmapを表示したい.)
CBitmap CXXX::GetBitmap(void)
{
    return bitmap;
}
「class 'CBitmap':コピーコンストラクタが使用できないか,
'explicit'として宣言されています.」
というエラーが出ます.

このエラーの意味と,これの対処方法を教えていただけないでしょうか?

よろしくお願いします.

dairygoods 2004/07/26(月) 10:56:12
コピーコンストラクタが定義されていないため、
コピーできないという意味です。

実際、ビットマップをコピーする必要はあるのでしょうか?
(たとえば、同じ元絵を複製して加工したいなど)

shogo 2004/07/26(月) 11:05:10
shogoです.

> 実際、ビットマップをコピーする必要はあるのでしょうか?
とのことですが,必要があるとは思っているんですが.
やりたいことは,「同じ元絵を複製して加工」に近いことです.

よろしくお願いします.

dairygoods 2004/07/26(月) 11:19:30
でしたら、必要になるたびにロードする方法はいかがでしょう。

void CXXX::GetBitmap(CBitmap& bitmap)
{
  bitmap.LoadBitmap(...);
}

shogo 2004/07/26(月) 11:25:32
shogoです.

dairygoodsさん,
CBitmapはファイルから読み込んで作っているのではなく,
動的に自分で作っています(動画の1フレームをキャプチャして).
ですから,
bitmap.LoadBitmap(...);
は使えないと思います.

dairygoods 2004/07/26(月) 11:49:00
では、
GetBitmapBitでビット情報を取得し、
CreateBitmapで新しいビットマップを作るという方法はどうでしょう。

Ban 2004/07/26(月) 11:53:26
新しい CBitmap を DC にマッピングして BitBlt するとか、
割と普通な気がします。

# リソースのコピーは通常重いから、わざと noncopyable なんですよね。

shogo 2004/07/26(月) 12:10:06
dairygoodsさん,Banさん,レスありがとうございます.

> リソースのコピーは通常重いから
とのことですので,
例えば,
GetCDC(CDC* pDC) {
    pDC->SelectObject(&bitmap); //CBitmap bitmap;
}
といったような関数を作ってやれば,よいのでしょうか?

よろしくお願いいたします.

Ban 2004/07/26(月) 12:48:27
> この受け取ったCBitmapを表示したい.

だけで、特に変更などしないならこれでいいのでは?

const CBitmap& CXXX::GetBitmap(void)
{
    return bitmap;
}
const CBitmap& mBitmap = GetgetBitmap();
// 後はこの mBitmap を表示する


もし、ディープコピーしたいのであれば、例えばこんな感じ?

void  // エラーチェックはご随意に
CXXX::GetBitmap(CBitmap& dst)
{
    // 作業用にコンパチブル辺りの DC を確保(もしくはクラスメンバに保持しておく)
    // DC に dst を SelectObject
    // DC に bitmap を BitBlt
    // DC から dst を切り離し
}
で、
GetBitmap(mBitmap); // mBitmap を作成して渡し、内容を複製してもらう。

# 後は、CBitmap を継承して、上記処理を
# public なコピーコンストラクタとして書けば、
# 複製可能ビットマップクラスも作れる...かも?

shogo 2004/07/26(月) 13:18:07
shogoです.

> const CBitmap& CXXX::GetBitmap(void)
> {
>     return bitmap;
> }
> const CBitmap& mBitmap = GetgetBitmap();
> // 後はこの mBitmap を表示する
こちらの場合,CXXXの方でフレームキャプチャしなおしたら,
呼び出した側のビットマップ(ここでいうmBitmap)
も変わるように思えるのですが,そういうことはないですか?
(OnDrawが呼ばれると,表示が変わるということです.)

ちょっと具体的に,目標を言いますと,
キャプチャしたら(bitmapにフレームキャプチャ),
mBitmapの方の表示は常に変えるのではなくて,
こちらが指示をしたらmBitmapの方を変更という形にしたいと思っています.
(bitmapにキャプチャするごとに,mBitmapも変化しては困る.)

よろしくお願いします.

Ban 2004/07/26(月) 14:19:57
> こちらの場合,CXXXの方でフレームキャプチャしなおしたら,
> 呼び出した側のビットマップ(ここでいうmBitmap)
> も変わるように思えるのですが,そういうことはないですか?

CXXX が情報を保持しつづけないのであれば、(勿論)変わります。
上の記述からすると CXXX は"画像のキャプチャ"が責務のようですが、
CXXX という名前からはそこまで察することは出来ませんでしたので...。
メンバのビットマップは更新されるんですね。


もっとも、OnDraw との関連性は分かりませんので、それが
> (OnDrawが呼ばれると,表示が変わるということです.)
に繋がるかどうかは、shogo さんの構成によるかと思います。

GetBitmap で取れる参照を常にそのまま OnDraw の中で BitBlt
していると OnDraw の度に更新されてしまうでしょうし、
更新制御なりしていればそうならないように使うことも可能です。

>(bitmapにキャプチャするごとに,mBitmapも変化しては困る.)

私の先の例だと、mBitmap が const CBitmap& になっていたので
誤解させてしまったようですが、更新されたくないのであれば、
取得した CBitmap の参照をそのまま保持/表示するのではなく、
更新時に、表示用DC にマッピングした mBitmap にでも BitBlt
するようにすればいいのではないかと思います。


もしくは、下記のように明示的に Bitmap に複製しておいても構いませんが。

>> void  // エラーチェックはご随意に
>> CXXX::GetBitmap(CBitmap& dst)
    -- snip --
>> GetBitmap(mBitmap); // mBitmap 
>> を作成して渡し、内容を複製してもらう。


いずれにしても、Bitmap から Bitmap に手軽に転送するのは
DC に割り付けて BitBlt だと思います。
前者と後者の違いは呼び元と呼び先のどちらがコピーを行うかだけで、
作業用の DC が必要ない(描画用の DC をそのまま利用可能)分、
前者の方が若干いいのではないかとは思います。

shogo 2004/07/26(月) 14:47:45
shogoです.

> DC に割り付けて BitBlt だと思います。
の方法のほうがよさそうということはわかりますが,
考えた方法があっているか,よくわかりません.

私は,以下のように考えました.
まず,呼び出される側CXXXの方で,次の関数を定義する.
CBitmap* CXXX::GetCDC(CDC* pDC) {
    return pDC->SelectObject(&bitmap); //CBitmap bitmap;
}
呼び出し側は,なんかの関数内で,
CDC* pDC = GetDC();
CDC dc;
dc.CreateCompatibleDC(pDC);
CBitmap *pOld = GetDC(&dc); //コピー関数
pDC->BitBlt(0,0,320,240,&dc,0,0,SRCCOPY);
dc.SelectObject(pOld);
とする,と考えました.
(CDC dc;はメンバ変数にしておく?)

いかかでしょう?こういうことでしょうか?
よろしくお願いいたします.

Ban 2004/07/26(月) 15:18:47
shogo さんの元のコードが GetBitmap でしたので、
それを踏襲して CXXX からは CBitmap を返し、
> pDC->SelectObject(&bitmap); //CBitmap bitmap;
に相当する処理をクライアント側で...という方向で書いていましたが、
コピー処理自体は shogo さんの認識された方法とほぼ一緒です。

# コピー禁止のオブジェクトはポインタか参照でアクセスするのが一般的な
# セオリーですが、Bitmap の場合は、DC へのマッピングでも
# 同種のことが実現可能なので、shogo さんの方法も有効だと思います。

当初の目的通り CBitmap が欲しいのであれば、それを割り当ててから
BitBlt すればいいでしょうし、そのいずれかを保存しておけば、
CXXX::bitmap が更新されても、OnDraw で表示が更新されてしまう
ことはないはずです。

shogo 2004/07/26(月) 15:51:21
[[解決]]
dairygoodsさん,Banさん,ありがとうございました.

まとめとしましては,
CBitmapのコピーは重いため,あまり推奨された方法ではなく,
表示だけの問題であれば,
CBitmapをDCにマッピングしてBitBltするなどの処置をしたほうがよい.

私の考えた方法は以下の通りです.
まず,呼び出される側CXXXの方で,次の関数を定義する.
CBitmap* CXXX::GetDC(CDC* pDC) {
    return pDC->SelectObject(&bitmap); //CBitmap bitmap;
}
呼び出し側は,なんかの関数内で,
CDC* pDC = GetDC();
CDC dc;
dc.CreateCompatibleDC(pDC);
CBitmap *pOld = GetDC(&dc); //定義した関数
pDC->BitBlt(0,0,320,240,&dc,0,0,SRCCOPY);
dc.SelectObject(pOld);
(CDC dc;はメンバ変数にしておく?)
というようにすれば表示可能.

やすい 2004/07/29(木) 14:51:59
私もクリップボードからHBITMAPを取り出す為に以下の様な関数を作りました。

HBITMAP CopyHBITMAP(const HBITMAP bmp )
{
   HBITMAP srcBmp ;
   DIBSECTION sec ={0,};
   void* data = 0 ;
   CDC* pDC = AfxGetMainWnd()->GetDC() ;
   // この時点ではパレットは不明
   ::GetObject(bmp,sizeof(DIBSECTION),&sec) ;
   BITMAPINFO* pInfo = (BITMAPINFO*) ::GlobalAlloc( GPTR ,
        sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256 ) ;
   if( sec.dsBmih.biSize == sizeof(BITMAPINFOHEADER) && 
    sec.dsBm.bmWidth == sec.dsBmih.biWidth &&
         sec.dsBm.bmHeight == sec.dsBmih.biHeight )
   {
      memcpy(pInfo,&sec.dsBmih,sizeof(BITMAPINFOHEADER) ) ;
      pInfo->bmiHeader.biCompression = 0 ;
 
    //作る時にはパレットは空
      VERIFY( srcBmp = CreateDIBSection(pDC->GetSafeHdc(),pInfo,
    sec.dsBmih.biBitCount > 8 ? DIB_RGB_COLORS : DIB_PAL_COLORS,
    &data,0,0) ) ;    }
   else
   {
      pInfo->bmiHeader.biPlanes = sec.dsBm.bmPlanes ;
      pInfo->bmiHeader.biBitCount = sec.dsBm.bmBitsPixel ;
      pInfo->bmiHeader.biWidth = sec.dsBm.bmWidth ;
      pInfo->bmiHeader.biHeight = sec.dsBm.bmHeight ;
      pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER) ;
      int pal_size = sec.dsBm.bmBitsPixel > 8 ? 0 : ( 1 << 
           sec.dsBm.bmBitsPixel ) ;
      pInfo->bmiHeader.biSizeImage = sizeof(BITMAPINFOHEADER) +
          pal_size    + sec.dsBm.bmWidthBytes * sec.dsBm.bmHeight ;

    //作る時にはパレットは空
    VERIFY( srcBmp = ::CreateDIBitmap(pDC->GetSafeHdc(),
         (BITMAPINFOHEADER*)pInfo,CBM_INIT,&data,pInfo,
    sec.dsBm.bmBitsPixel > 8 ? DIB_RGB_COLORS : DIB_PAL_COLORS) );
    }
   VERIFY(::GetDIBits(pDC->GetSafeHdc(),bmp,0,sec.dsBm.bmHeight,data,pInfo, 
    sec.dsBm.bmBitsPixel > 8 ? DIB_RGB_COLORS : DIB_PAL_COLORS)) ;
    // ここでパレットを取得できるが、secBmpにセットする方法が見つかりません。
   ::GlobalFree(pInfo) ;
   AfxGetMainWnd()->ReleaseDC(pDC) ;
   return srcBmp ;
}
フルカラーはきちんと取り込めるのですが、パレットの内容が取り込めません。
ソースの中でpInfoにパレットを取得できるのですが、それをHBITMAPにセットする
方法が分りません。どのような方法が考えられるでしょうか。

あとbiCompressionが16bitカラーの場合、3になってしまいますが、それではきちんと
表示されないので、0に変えてしまっているのですが、問題になるケースがあるのでしょうか。

Gak 2004/07/29(木) 15:30:09
HBITMAP(CBitmap)のコピーは ::CopyImage() を使用すれば簡単かも

やすい 2004/07/29(木) 23:19:35
変事が遅れてすみません。ルーターが"ATMレイヤエラー"とかで接続がずっと切れていました。
>HBITMAP(CBitmap)のコピーは ::CopyImage() を使用すれば簡単かも
おっしゃる通りでした。わざわざ関数作るまでもありませんでした。
半日間の苦労は何だったんだか。(T_T)

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