256色モードでは、パレットを使わないと なかなか自分で表示したい色になってくれません。
ここでは、パレットの例を書きます。
この例では、パレットを作成する関数をCMyView::CreatePalette()としておきます。
実際には、CMyDocumentの中で作ってViewから読み込んでも構いませんし、
OnInitialUpdate()ハンドラの中で作っても構いません。
MyView.h:
class CMyView : public CView { 〜 protected: CPalette m_Palette; int CreatePalette(); 〜 };
MyView.cpp:
CMyView::CMyView() { // TODO: この場所に構築用のコードを追加してください。 CreatePalette(); //とりあえずコンストラクタで呼んでおく } 〜 int CMyView::CreatePalette() { if(m_Palette.m_hObject==0){ //未作成かどうか LOGPALETTE LogPal; LogPal.palVersion=0x300; LogPal.palNumEntries=1; m_Palette.CreatePalette(&LogPal); //パレットのエントリ数を256に BOOL b=m_Palette.ResizePalette(256); if(!b) return -1; } PALETTEENTRY PalEnt[256]; for(int i=0;i<256;i++){ PalEnt[i].peRed =((i>>5)&7)*255/7; PalEnt[i].peGreen=((i>>2)&7)*255/7; PalEnt[i].peBlue =((i>>0)&3)*255/3; PalEnt[i].peFlags=0; } UINT u=m_Palette.SetPaletteEntries(0,256,PalEnt); return u; }
この例ではパレットの個数を256個としていますが、必要に応じて16個にしたり、減らす事は可能です。
peRed,peGreen,peBlueには、自分の好きなように入れて下さい。
MyView.h:
class CMyView : public CView
{
〜
// オーバーライド
// ClassWizard は仮想関数を生成しオーバーライドします。
//{{AFX_VIRTUAL(CMyView)
protected:
virtual void OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView);
//}}AFX_VIRTUAL
〜
};
MyView.cpp:
void CMyView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
// TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください
CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
if(bActivate) OnDoRealizePalette((WPARAM)m_hWnd,0); //ここに追加
}
OnDoRealizePalette関数は、後で作ります。
MyView.h:
class CMyView : public CView
{
〜
// 生成されたメッセージ マップ関数
protected:
//{{AFX_MSG(CMyView)
// メモ - ClassWizard はこの位置にメンバ関数を追加または削除します。
// この位置に生成されるコードを編集しないでください。
//}}AFX_MSG
afx_msg LRESULT OnDoRealizePalette(WPARAM wPrm,LPARAM lPrm);
DECLARE_MESSAGE_MAP()
〜
};
MyView.cpp:
afx_msg LRESULT CMyView::OnDoRealizePalette(WPARAM wPrm,LPARAM lPrm)
{
CClientDC appDC(AfxGetApp()->m_pMainWnd);
CPalette *pOld=appDC.SelectPalette(&m_Palette,(HWND)wPrm!=m_hWnd);
UINT uiCount=appDC.RealizePalette();
appDC.SelectPalette(pOld,TRUE);
if(uiCount>0) GetDocument()->UpdateAllViews(NULL);
return uiCount;
}
MyView.cpp:
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: この場所にネイティブ データ用の描画コードを追加します。
CPalette *pOldPal=pDC->SelectPalette(&m_Palette,FALSE);
for(int i=0;i<256;i++){
int x=i%16;
int y=i/16;
int r=((i>>5)&7)*255/7;
int g=((i>>2)&7)*255/7;
int b=((i>>0)&3)*255/3;
// pDC->FillSolidRect(x*32,y*32,32,32,RGB(r,g,b));
pDC->FillSolidRect(x*32,y*32,32,32,PALETTERGB(r,g,b));
// pDC->FillSolidRect(x*32,y*32,32,32,PALETTEINDEX(i));
// pDC->SetPixel(x,y,RGB(r,g,b));
// pDC->SetPixel(x,y,PALETTERGB(r,g,b));
// pDC->SetPixel(x,y,PALETTEINDEX(i));
}
pDC->SelectPalette(pOldPal,TRUE);
}
描画前にpDCに対してパレットを割り当てる必要があります。
そして、RGBマクロではなくPALETTERGBマクロを使うというのがポイントです。
RGBマクロではシステムが用意したパレットから指定された色に近い色を探します。
それに対し、PALETTERGBマクロでは、割り当てられているパレット全体から近い色を探します。
PALETTEINDEXマクロは、パレット番号の色をそのまま使います。近い色を探すという手間が無い分
早い(と思う)ですが、どの番号にどの色が入っているかは
CPalette内の順番通りとは限らないらしいので注意が必要です。
ちょっと想像が大部分ですが、パレットの注意点について。[2002-08-03]
CPaletteで用意した自分のパレットは256色使っているわけですが、
これをSelectPaletteでDCに写した時に そのまま全部コピーされるわけではないようです。
DCの中では、コードが0〜15くらいの16色分くらい(具体的にいくつか、ということは忘れた)は
システムが使っているのです。CPaletteで256色フルに使っていても、DCに反映される時には
システムの個数分は別の似た色で置き換えられてしまうと思います。
この辺りが、「CPalette内の順番通りにならないらしい」の原因だと考えています。
MainFrm.h:
class CMainFrame : public CFrameWnd
{
〜
// 生成されたメッセージ マップ関数
protected:
//{{AFX_MSG(CMainFrame)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaletteChanged(CWnd* pFocusWnd);
afx_msg BOOL OnQueryNewPalette();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
〜
};
MainFrm.cpp:
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_CREATE() ON_WM_PALETTECHANGED() ON_WM_QUERYNEWPALETTE() //}}AFX_MSG_MAP END_MESSAGE_MAP() 〜 void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd) { // CFrameWnd::OnPaletteChanged(pFocusWnd); // TODO: この位置にメッセージ ハンドラ用のコードを追加してください CView *pView=GetActiveView(); if(pView!=NULL){ SendMessageToDescendants(WM_DOREALIZEPALETTE,(WPARAM)pView->m_hWnd); } } BOOL CMainFrame::OnQueryNewPalette() { // TODO: この位置にメッセージ ハンドラ用のコードを追加するかまたはデフォルトの処理を呼び出してください CView *pView=GetActiveView(); if(pView!=NULL){ pView->SendMessage(WM_DOREALIZEPALETTE,(WPARAM)pView->m_hWnd); } return TRUE; // return CFrameWnd::OnQueryNewPalette(); }
My.h
〜
#include "resource.h" // メイン シンボル
//独自のメッセージ:パレットを実体化する
#define WM_DOREALIZEPALETTE (WM_USER+0)
〜
MyView.h
BEGIN_MESSAGE_MAP(CMyView, CView)
//{{AFX_MSG_MAP(CMyView)
// メモ - ClassWizard はこの位置にマッピング用のマクロを追加または削除します。
// この位置に生成されるコードを編集しないでください。
//}}AFX_MSG_MAP
ON_MESSAGE(WM_DOREALIZEPALETTE, OnDoRealizePalette) //これを追加
END_MESSAGE_MAP()
WM_DOREALIZEPALETTEメッセージは、独自のものです。このメッセージを処理する関数は、 前に作ったOnDoRealizePalette関数です。
このあたりの詳しい事は、DeveloperStudioヘルプ内のMFCサンプル「DIBLOOK」を見て下さい。
この例では、ビットマップはメンバー変数にし、OnInitialUpdateメンバー関数の中で初期化します。
MyView.h
class CMyView : public CView { 〜 // オーバーライド // ClassWizard は仮想関数を生成しオーバーライドします。 //{{AFX_VIRTUAL(CMyView) public: virtual void OnDraw(CDC* pDC); // このビューを描画する際にオーバーライドされます。 virtual BOOL PreCreateWindow(CREATESTRUCT& cs); virtual void OnInitialUpdate(); //}}AFX_VIRTUAL 〜 protected: CBitmap m_Bitmap; 〜 };
MyView.cpp
void CMyView::OnInitialUpdate() { CView::OnInitialUpdate(); // TODO: この位置に固有の処理を追加するか、または基本クラスを呼び出してください CDC *pDC=GetDC(); CDC tmpDC; tmpDC.CreateCompatibleDC(pDC); m_Bitmap.CreateCompatibleBitmap(pDC,32*16,32*16); ReleaseDC(pDC); CBitmap *pOldTmpBmp=tmpDC.SelectObject(&m_Bitmap); CPalette *pOldTmpPal=tmpDC.SelectPalette(&m_Palette,FALSE); tmpDC.RealizePalette(); for(int i=0;i<256;i++){ int x=i%16; int y=i/16; int r=((i>>5)&7)*255/7; int g=((i>>2)&7)*255/7; int b=((i>>0)&3)*255/3; CBrush bsh(PALETTERGB(r,g,b)); tmpDC.FillRect(CRect(CPoint(x*32,y*32),CSize(32,32)),&bsh); // tmpDC.FillSolidRect(x*32,y*32,32,32,PALETTERGB(r,g,b)); // tmpDC.SetPixel(x,y,PALETTERGB(r,g,b)); } tmpDC.SelectPalette(pOldTmpPal,TRUE); tmpDC.SelectObject(pOldTmpBmp); } void CMyView::OnDraw(CDC* pDC) { CMyDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: この場所にネイティブ データ用の描画コードを追加します。 CDC tmpDC; tmpDC.CreateCompatibleDC(pDC); CBitmap *pOldBmp=tmpDC.SelectObject(&m_Bitmap); CPalette *pOldPal=pDC->SelectPalette(&m_Palette,FALSE); pDC->BitBlt(0,0,32*16,32*16,&tmpDC,0,0,SRCCOPY); pDC->SelectPalette(pOldPal,TRUE); tmpDC.SelectObject(pOldBmp); }ビットマップに描画する為のメモリDCに対してSelectPalette,RealizePaletteを行うのがポイントです。
ひとつ注意として、何故かFillSolidRectで描くとPALETTERGBマクロを使っていてもうまく表示されません。 FillRectとCBrushを使えば描けますが…。