[BMP]ビットマップ色の反転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);
}
}
結果は以下のようになった。
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 // ラスタオペレーション
);
戻り値は、コピーした走査行(高さのこと?)が返される。
上図のように、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行追加するだけでよいが、それはまた次回 。
[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()といった関数も
あるが、基本的に使い方は同じである。