解説
これまで使ってきた画像処理用のスケルトンはあくまでも描画テスト用で、 2つ以上の処理を組み合わせた画像処理を試そうと考えるとコード量が多くなり 読みづらいし、試しづらかった。またコーディング自体もミスだらけで、ちょ っと入力画像を誤ると破綻するものだった。そのためここではクラス化して少しはまともなスケルトンを作った。これ からはこのコードをベースにクラスCDnpImageを拡張しながら画像処理を実験 する予定。
実行例

ソースコード
以下のコードを「DnpImageBase.h」というファイル名で保存。 各種画像処理を施したいときはこのクラスCDnpImageにメンバー関数として加える。
#pragma once
#include "DnpImageBase.h"
class CDnpImage : public CDnpImageBase
{
public:
bool GrayScale(void)
{
if(IsValidImage() == false)
return false;
int nStrideBytes;
bool ret;
UINT x;
UINT y;
UINT nWidth;
UINT nHeight;
BYTE* pData;
RGBTRIPLE* pRGB;
nWidth = GetWidth();
nHeight = GetHeight();
ret = LockBits(&pData,&nStrideBytes);
if(ret == false)
return false;
for(y = 0; y < nHeight; y++)
{
pRGB = (RGBTRIPLE*)pData;
for(x = 0; x < nWidth; x++)
{
//グレースケール化
pRGB[x].rgbtRed = (BYTE)((77 * pRGB[x].rgbtRed + 150 * pRGB[x].rgbtGreen + 29 * pRGB[x].rgbtBlue) >> 8);
pRGB[x].rgbtGreen = pRGB[x].rgbtRed;
pRGB[x].rgbtBlue = pRGB[x].rgbtRed;
}
pData += nStrideBytes;
}
UnlockBits();
return true;
}
};
以下のソースコードを「DnpImageBase.h」というファイル名で保存する。 このクラスはGDI+を利用した画像の読み込み、表示を担い、画像処理は派生 クラスなどで行う。
#pragma once #include "atlstr.h" #includeusing namespace Gdiplus; #pragma comment(lib,"Gdiplus.lib") // // このクラスを利用するには、プログラム起動時にInitialize()を、 // 終了時にUninitialize()を呼び出さないとダメ // class CDnpImageBase { // // 表示用サムネイル管理クラス // //本当はImage::GetThumbnailImage()を使った方がいいのだが、 //画像処理をしてもその結果が反映されないので::StretchBltを //使っている。今後なんとかすべき? // class CThumbnail { HDC _hThumbnailDC; //縮小画像用の裏画面 HBITMAP _hThumbnailBitmap; //DC用のビットマップ UINT _nWidth; //縮小画像の幅 UINT _nHeight; //縮小画像の高さ public: // // コンストラクタ // CThumbnail() { _hThumbnailDC = NULL; _hThumbnailBitmap = NULL; Init(); } // // デストラクタ // ~CThumbnail() { Init(); } // // クラスの初期化 // //このクラスに格納されているサムネイル画像の破棄 // void Init(void) { if(_hThumbnailDC) ::DeleteDC(_hThumbnailDC); _hThumbnailDC = NULL; if(_hThumbnailBitmap) ::DeleteObject(_hThumbnailBitmap); _hThumbnailBitmap = NULL; _nWidth = 0; _nHeight = 0; } // // 画像の横幅取得 // UINT GetWidth(void) { return _nWidth; } // // 画像の縦幅取得 // UINT GetHeight(void) { return _nHeight; } // // 縮小画像が読み込まれているかチェック // bool IsValidImage(void) { if(_nWidth == 0 || _nHeight == 0 || _hThumbnailDC == NULL || _hThumbnailBitmap == NULL) return false; return true; } // // 縮小画像の作成 // //pImageを元にnWidth,nHeightサイズの縮小画像を作る //元画像と指定したサイズの縦横比が異なると縦長や横長の画像になるので注意 // bool CreateThumbnail(Bitmap* pImage,HDC hDC,UINT nWidth,UINT nHeight) { BOOL ret; HDC hDCTmp; HBITMAP hBitmap; Init(); if(pImage == NULL) return false; pImage->GetHBITMAP(RGB(0,0,0),&hBitmap); if(hBitmap == NULL) return false; hDCTmp = ::CreateCompatibleDC(hDC); ::SelectObject(hDCTmp,hBitmap); _hThumbnailDC = ::CreateCompatibleDC(hDC); _hThumbnailBitmap = ::CreateCompatibleBitmap(hDC,nWidth,nHeight); ::SelectObject(_hThumbnailDC,_hThumbnailBitmap); //ピクセル削除モードで拡大縮小 ::SetStretchBltMode(_hThumbnailDC,COLORONCOLOR); ret = ::StretchBlt(_hThumbnailDC,0,0,nWidth,nHeight,hDCTmp,0,0,pImage->GetWidth(),pImage->GetHeight(),SRCCOPY); ::DeleteDC(hDCTmp); ::DeleteObject(hBitmap); if(ret == FALSE) { Init(); return false; } _nWidth = nWidth; _nHeight = nHeight; return true; } // // 縮小画像の表示 // bool Draw(HDC hDC,int nX,int nY) { BOOL ret; if(IsValidImage() == false) return false; ret = ::BitBlt(hDC,nX,nY,_nWidth,_nHeight,_hThumbnailDC,0,0,SRCCOPY); return ret ? true : false; } }; CThumbnail _cThumbnail; //表示用の縮小画像管理クラス protected: Bitmap* _pMainImage; //読み込んだ画像オブジェクト //サムネイルの作成が必要かどうか //_pMainImageを画像処理したら_pMainImageと_pThumbnailで画像が異なってしまう。 //そのため_pMainImageを変更したら必ず_bNeedNewThumb=trueに変更すること! //例外!LockBits()とUnlockBits()を利用した画像処理の場合は自動的に処理さ //れるためこの変数をいじる必要はない bool _bNeedNewThumb; //現在読み込んでいる画像ファイル名とそのファイル更新日時 FILETIME _sFileTime; CAtlString _strFile; ULONG_PTR _nToken; //GDI+の管理値 public: // // コンストラクタ // CDnpImageBase() { _nToken = 0; _pMainImage = NULL; _bNeedNewThumb = true; _bLockBitmap = false; Init(); } // // デストラクタ // ~CDnpImageBase() { Init(); } // // 初期化 // //読み込んでいる画像がある場合はそれを破棄する // void Init() { if(_bLockBitmap) UnlockBits(); _bNeedNewThumb = true; ZeroMemory(&_sFileTime,sizeof(FILETIME)); _strFile = _T(""); if(_pMainImage) delete _pMainImage; _pMainImage = NULL; _cThumbnail.Init(); } // // GDI+の初期化 // //この関数はアプリケーション開始時に一度だけ呼ぶこと //呼ばないとこのクラス自体が使えません。 // void Initialize(void) { //GDI+開始 GdiplusStartupInput _sGdiplusStartupInput; GdiplusStartup(&_nToken,&_sGdiplusStartupInput,NULL); } // // GDI+の終了処理 // //この関数はアプリケーション終了時に一度だけ呼ぶこと! // void Uninitialize(void) { Init(); GdiplusShutdown(_nToken); } // // 画像ファイル読み込み // //pszFileで指定された画像を読み込む //もしも前回、同じファイルを読んでいた場合は読み込まない //bForce=trueを指定すれば強制的に読み込む // bool LoadFile(LPCTSTR pszFile,bool bForce=false) { ////////////////////////////// // ファイル更新時刻取得 // HANDLE hFind; WIN32_FIND_DATA FindFileData; hFind = ::FindFirstFile(pszFile,&FindFileData); if(hFind == INVALID_HANDLE_VALUE) return false; ::FindClose(hFind); if(bForce == false) { if(IsValidImage() && _strFile == pszFile) { if(::CompareFileTime(&_sFileTime,&FindFileData.ftLastWriteTime) == 0) return true; //既に同じ画像が読み込まれている } } ////////////////////////////// // 初期化 // Init(); ////////////////////////////// // 画像読込 // #ifdef _UNICODE _pMainImage = Bitmap::FromFile(pszFile,TRUE); #else _pMainImage = Bitmap::FromFile((CAtlStringW)pszFile,TRUE); #endif if(IsValidImage() == false) return false; //読み込み失敗! //読み込むファイル名と更新日時の保存 _strFile = pszFile; memcpy(&_sFileTime,&FindFileData.ftLastWriteTime,sizeof(FILETIME)); return true; } // // 画像の幅取得(単位ピクセル) // UINT GetWidth(void) { if(IsValidImage() == false) return 0; return _pMainImage->GetWidth(); } // // 画像の高さ取得(単位ピクセル) // UINT GetHeight(void) { if(IsValidImage() == false) return 0; return _pMainImage->GetHeight(); } // // 画像が読み込まれているかチェック // //このクラスに現在画像が読み込まれているかを調べる。 // bool IsValidImage(void) { if(_pMainImage == NULL || _pMainImage->GetWidth() == 0 || _pMainImage->GetHeight() == 0) { Init(); return false; } return true; } // // クラスの複製 // //このクラスに現在読み込まれている画像をcCopyToにコピーする //あくまでも"コピー"になることに注意。参照ではないのでcCopyTo //に画像処理を施してもこのクラス内の画像に影響はない。 // bool Clone(CDnpImageBase& cCopyTo) { bool ret; Bitmap* pBitmap; pBitmap = NULL; ret = CloneBitmap(pBitmap); if(ret == false) return false; return cCopyTo.SetBitmap(pBitmap); } // // ビットマップ指定 // //現在読み込まれている画像を破棄して、pBitmapのコピーを読み込む。 //関数内部でBitmapをコピーしていることに注意! // // CDnpImageBase cImage; // Bitmap* pSrc; // // pSrc = Bitmap::LoadFromFile(...) // cImage.SetBitmap(pSrc); //←ここで画像読み込み // delete pSrc; //←不要なら削除しなければならない! // // cImage.Draw(...) // // bool SetBitmap(Bitmap* pBitmap) { if(pBitmap == NULL || pBitmap->GetWidth() == 0 || pBitmap->GetHeight() == 0) return false; Init(); //Cloneしたものを渡す! _pMainImage = pBitmap->Clone(Rect(0,0,pBitmap->GetWidth(),pBitmap->GetHeight()),PixelFormatDontCare); return IsValidImage(); } // // ビットマップの複製取得 // //現在読み込まれている画像のBitmapを複製して渡す。 //取得したBitmapを画像処理しても、このクラス内の画像には影響しない。 // bool CloneBitmap(Bitmap*& pBitmap) { pBitmap = NULL; if(IsValidImage() == false) return false; pBitmap = _pMainImage->Clone(Rect(0,0,_pMainImage->GetWidth(),_pMainImage->GetHeight()),PixelFormatDontCare); if(pBitmap == NULL) return false; if(pBitmap->GetWidth() != _pMainImage->GetWidth() || pBitmap->GetHeight() != _pMainImage->GetHeight()) { delete pBitmap; pBitmap = NULL; return false; } return true; } // // 画像の描画 // //指定したDCに対して画像を描画する。 //幅と高さの比が元画像と異なる場合は...画像が縦長や横長になって表示される! // bool Draw(HDC hDC,int nX,int nY,int nWidth,int nHeight) { //サムネイルを作成してそれを表示する if(_bNeedNewThumb || _cThumbnail.IsValidImage() == false || _cThumbnail.GetWidth() != nWidth || _cThumbnail.GetHeight() != nHeight) { //サムネイル作成 bool ret; if(IsValidImage() == false) return false; ret = _cThumbnail.CreateThumbnail(_pMainImage,hDC,nWidth,nHeight); if(ret == false) return false; _bNeedNewThumb = false; } return _cThumbnail.Draw(hDC,nX,nY); } private: bool _bLockBitmap; BitmapData _sLockBitmapData; public: // // 画像編集用にRGB配列取得 // //ppcbRGBDataに読み込まれている画像の生データ、pnStrideBytesに画像の横一列に使われているバイト数が返る。 // //取り出した画像*ppcbRGBDataはRGBTRIPLE配列で並んでいる。ただし *pnStrideBytes != nWidth * sizeof(RGBTRIPLE) //のことがあるので注意!(*pnStrideBytesに負の値が入っても処理できるようにすること!) // //画像編集が終わったら必ずUnlockBits()を呼び出すこと! // bool LockBits(BYTE** ppcbRGBData,int* pnStrideBytes) { Status ret; if(ppcbRGBData) *ppcbRGBData = NULL; if(pnStrideBytes) *pnStrideBytes = 0; if(_bLockBitmap || IsValidImage() == false || ppcbRGBData == NULL || pnStrideBytes == NULL) return false; ret = _pMainImage->LockBits(&Rect(0,0,_pMainImage->GetWidth(),_pMainImage->GetHeight()),ImageLockModeRead | ImageLockModeWrite,PixelFormat24bppRGB,&_sLockBitmapData); if(ret != Ok) return false; _bLockBitmap = true; *ppcbRGBData = (BYTE*)_sLockBitmapData.Scan0; *pnStrideBytes = _sLockBitmapData.Stride; return true; } // // 画像編集終了 // //LockBits()と組みにして利用すること! // bool UnlockBits(void) { if(_bLockBitmap == false || IsValidImage() == false) return false; _pMainImage->UnlockBits(&_sLockBitmapData); _bLockBitmap = false; _bNeedNewThumb = true; return true; } };
WTLのSDI Applicationでプロジェクトを作成し、...View.hに太字で示したコードを 追加する。この例ではプロジェクト名を「ImageEffect114」にしている。
// ImageEffect114View.h : interface of the CImageEffect114View class // ///////////////////////////////////////////////////////////////////////////// #pragma once #include "DnpImage.h" class CImageEffect114View : public CWindowImpl{ CDnpImage _cImage1; CDnpImage _cImage2; public: DECLARE_WND_CLASS(NULL) BOOL PreTranslateMessage(MSG* pMsg) { pMsg; return FALSE; } BEGIN_MSG_MAP(CImageEffect114View) MESSAGE_HANDLER(WM_PAINT, OnPaint) END_MSG_MAP() // Handler prototypes (uncomment arguments if needed): // LRESULT MessageHandler(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) // LRESULT CommandHandler(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) // LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/) CImageEffect114View() { //GDI+初期化処理 //本当はアプリケーション起動時にすべき _cImage1.Initialize(); //画像ファイルの読み込みとコピー _cImage1.LoadFile(_T("c:\\test.jpg")); _cImage1.Clone(_cImage2); //画像処理 _cImage2.GrayScale(); } ~CImageEffect114View() { //画像の破棄 _cImage1.Init(); _cImage2.Init(); //GDI+終了処理 //本当はアプリケーション終了時にすべき _cImage1.Uninitialize(); } LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { CPaintDC dc(m_hWnd); _cImage1.Draw(dc.m_hDC,0,0,300,200); _cImage2.Draw(dc.m_hDC,302,0,300,200); return 0; } };
