Assertion Failed! -14ページ目

[BMP]ビットマップ色の反転2

以前 の色の反転処理は、ラスタオペレーションを利用したものであった。


今回はビット列を直接操作して反転を実現する。



Assertion Failed!-ビットマップ色の反転2


実際にやっていることは簡単であり、イメージデータのバイト列を

そのままビット反転するだけである。


StretchDIBits で使用したコードをほぼ流用して以下のように描画部を

書いた。

(ビットマップ読み込み部等は上記リンク参照)



void CStretchDIBitsDlg::OnPaint()
{
  if (IsIconic())
  {
    ・・・中略

  }
  else
  {
    CPaintDC dc(this);


    int nWidth = m_bih.biWidth;
    int nHeight = m_bih.biHeight;


    CBitmap bmp;

    BYTE *pImg;
    HBITMAP hbmp = (HBITMAP)::CreateDIBSection(dc.GetSafeHdc(), (BITMAPINFO*)&m_bih,
      DIB_RGB_COLORS,(void**)&pImg,NULL,0);


    memcpy(pImg, m_pImg, m_bih.biSizeImage);


    bmp.Attach(hbmp);


    // 反転処理

    int nImgLen = m_bih.biSizeImage;
    for (int i = 0; i < nImgLen; i++)
    {
      m_pImg[i] = ~m_pImg[i];
    }


    // 元画像と幅と高さは変更していないので等倍表示

    ::StretchDIBits(dc.GetSafeHdc(), 0, 0, nWidth, nHeight,
      0, 0, m_bih.biWidth, m_bih.biHeight, m_pImg,
      (BITMAPINFO*)&m_bih, DIB_RGB_COLORS, SRCCOPY);
  }
}


これだけ。以上。

[BMP]SetStretchBltMode

拡大/縮小した画像を綺麗に表示(補間)する


StretchDIBits で拡大/縮小した画像はビット情報が荒い状態である。


これを解消するためには、SetStretchBltModeでビットマップの伸縮モードを

設定すればよい。


追加するのは、StretchDIBitsをコールする直前でよい。


SetStretchBltModeには伸縮モードを引数として渡すが、

今回使用するのは「HALFTONE」である。


[HALFTONEについてのMSDNの説明]

コピー元長方形内のピクセルをコピー先長方形内のピクセルブロックに関連付けます。

コピー先ブロックの平均的な色は、コピー元のピクセルの色に近い色になります。



void CSampleDlg::OnPaint()
{
  if (IsIconic())
  {
    ・・・中略
  }
  else
  {
    CPaintDC dc(this);


    // 幅と高さを半分にする。
    int nWidth = m_bih.biWidth / 2;
    int nHeight = m_bih.biHeight / 2;


    CBitmap bmp;
    BYTE *pImg;


    HBITMAP hbmp = (HBITMAP)::CreateDIBSection(dc.GetSafeHdc(), (BITMAPINFO*)&m_bih,
      DIB_RGB_COLORS,(void**)&pImg,NULL,0);


    memcpy(pImg, m_pImg, m_bih.biSizeImage);


    bmp.Attach(hbmp);


    dc.SetStretchBltMode(HALFTONE);


    ::StretchDIBits(dc.GetSafeHdc(), 0, 0, nWidth, nHeight,
      0, 0, m_bih.biWidth, m_bih.biHeight, m_pImg,
      (BITMAPINFO*)&m_bih, DIB_RGB_COLORS, SRCCOPY);
  }
}


結果は以下のようになった。



Assertion Failed!-SetStretchBltMode

HALFTONE       未使用



画像が小さくて見難いが、結果は一目瞭然となる。

ただし、処理には多少(数ミリ秒くらい?未計測)遅くなる。

[BMP]StretchDIBits

DIBを拡大/縮小する。


ファイルから読み込んだファイルやCreateDIBSection で作成した

DIBを拡大/縮小するにはStretchDIBits()関数を使う。


DDBを拡大/縮小する関数としてStretchBlt()関数もあるが、汎用性を

考えるとStretchDIBitsを覚えたほうがよいと思う。


int StretchDIBits(
 HDC hdc,        // デバイスコンテキスト
 int XDest,        // 転送先開始位置x座標
 int YDest,        // 転送先開始位置y座標
 int nDestWidth,     // 転送先画像幅
 int nDestHeight,    // 転送先画像高さ
 int XSrc,         // 元画像開始位置x座標
 int YSrc,         // 元画像開始位置y座標
 int nSrcWidth,      // 元画像幅
 int nSrcHeight,     // 元画像高さ
 CONST VOID *lpBits, // 元画像イメージ
 CONST BITMAPINFO *lpBitsInfo, // 元画像BITMAPINFO構造体
 UINT iUsage,      // 色形式
 DWORD dwRop     // ラスタオペレーション
);


戻り値は、コピーした走査行(高さのこと?)が返される。



Assertion Failed!-StretchDIBits
50%に縮小


上図のように、50%縮小した際のソースはこちら。

(エラー処理などは一切考慮なし)


class CSampleDlg : public CDialog

{

private:

  // 元画像BIH

  BITMAPINFOHEADER m_bih;

  // 画像データ列格納

  BYTE *m_pImg;


  ・・・

}


BOOL CSampleDlg::OnInitDialog()
{
  ・・・中略
  CFile file(_T("C:\\Sample.bmp"), CFile::modeRead);


  BITMAPFILEHEADER bfh;
  file.Read(&bfh, sizeof(BITMAPFILEHEADER));


  file.Read(&m_bih, sizeof(BITMAPINFOHEADER));


  // イメージデータ取得

  m_pImg = new BYTE [m_bih.biSizeImage];

  file.Seek(bfh.bfOffBits, CFile::begin);
  file.Read(m_pImg, m_bih.biSizeImage);


  return TRUE;

}


void CSampleDlg::OnPaint()
{
  if (IsIconic())
  {
    ・・・中略
  }
  else
  {
    CPaintDC dc(this);


    // 幅と高さを半分にする。
    int nWidth = m_bih.biWidth / 2;
    int nHeight = m_bih.biHeight / 2;


    // 読み込んだ画像からDIBを作成する

    BYTE *pImg;
    HBITMAP hbmp = (HBITMAP)::CreateDIBSection(dc.GetSafeHdc(),

      (BITMAPINFO*)&m_bih, DIB_RGB_COLORS, (void**)&pImg, NULL, 0);

    memcpy(pImg, m_pImg, m_bih.biSizeImage);
    

    // ビットマップオブジェクトに割り当て
    CBitmap bmp;
    bmp.Attach(hbmp);


    // 拡大/縮小

    ::StretchDIBits(dc.GetSafeHdc(), 0, 0, nWidth, nHeight,
      0, 0, m_bih.biWidth, m_bih.biHeight, m_pImg,
      (BITMAPINFO*)&m_bih, DIB_RGB_COLORS, SRCCOPY);
  }
}


このように、拡大/縮小後の幅と高さを指定することで、自動的に関数内で

拡大/縮小を行ってくれる。(元画像のサイズへの比率で。)


しかし、現状のリサイズ後イメージは画像リンクを参照してもらえば

わかるように、色の割り当てが荒い。

回転の際に紹介したニアレストネイバー法 のような状態)


この画像を補間した状態で出力するためには、上記コードに

1行追加するだけでよいが、それはまた次回

[日記]くもとさいくつ


Assertion Failed!-スパイダ


やったった。


人生3回目の上級クリア。


久しぶりに休みっぽいことした。


満足。


こっちはおまけ。遅い。


Assertion Failed!-マインスイーパ

[MFC]マルチスレッド3

マルチスレッドその3


WaitForSingleObjectで同期処理を行おうとした場合、同期待ちをしようと

しているスレッドがすでに終了していた場合、関数はWAIT_FAILEDの

エラーを返す。


これは、デフォルトのスレッド生成では、スレッドの処理が完了した時点で

自身のオブジェクトを自動で破棄しているためである。


待つべきスレッドのハンドルが、すでに破棄されているため関数は失敗する。


[自動で破棄する形]

AfxBeginThread(ThreadFunc, NULL);


対処するためには、スレッドの起動方法を変更すればよい。


具体的には、スレッド生成したらすぐに処理を開始するのではなく

指示が出るまで待機させ、その待機している間にオブジェクト自動破棄の

フラグを書き換えてやる。


自動破棄のフラグを無効にした後、改めてスレッドを起動させれば

スレッド処理が完了してもオブジェクト自体は残り、同期待ち処理で

エラーが発生することもなくなる。


↓のように実装(スレッド起動時のみ抜粋)


[コード]

CWinThread *pThread =

   AfxBeginThread(ThreadFunc, NULL, 0, 0, CREATE_SUSPENDED);

pThread->m_bAutoDelete = FALSE;

pThread->ResumeThread();


Sleep(2000);


if (::WaitForSingleObject(pThread->m_hThread, INFINITE) == WAIT_FAILED)
{
  printf("wait error\n");
}

delete pThread;


AfxBeginThreadの第5引数にCREATE_SUSPENDEDを渡すことにより、

スレッドはサスペンド(待機)された状態で生成される。


この時点ではスレッド関数の処理は動いていない。


その間にCWinThreadのメンバ変数 m_bAutoDelete をFALSEにする

(デフォルトはTRUE)事により、自動破棄を無効にする。


それから、スレッド起動を再開するためのメンバ関数 ResumeThread()を呼ぶ。


今回はスレッドオブジェクトを自動破棄しないので、明示的にdeleteする

処理も追加してある。



スレッド生成を複数行う場合も同様の考え方で実現できる。

複数のスレッドを待つためにはWaitForMultipleObjects()といった関数も

あるが、基本的に使い方は同じである。