打開「工具 → 查看器 → 調色盤查看器」,可實時顯示 NES 畫面處理單元(PPU)的 32 種調色盤顏色:上 16 色為背景調色盤,下 16 色為精靈調色盤。每色佔 16×16 像素,整體位圖尺寸 256×32。
CPaletteView文件:PaletteView.h/cpp,對象 m_PaletteView 定義於 CMainFrame 中。
Create():窗口與位圖頭初始化BOOL CPaletteView::Create(HWND hWndParent) {
HWND hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, VIRTUANES_WNDCLASS,
"PaletteView", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
hWndParent, NULL, CApp::GetInstance(), (LPVOID)this);
if (!hWnd) return FALSE;
m_hWnd = hWnd;
// 8-bit 位圖頭:256×32,32 色調色盤
m_BitmapHdr.bih.biSize = sizeof(BITMAPINFOHEADER);
m_BitmapHdr.bih.biWidth = 256;
m_BitmapHdr.bih.biHeight = -32; // 負值表示自頂向下 DIB
m_BitmapHdr.bih.biPlanes = 1;
m_BitmapHdr.bih.biBitCount = 8;
m_BitmapHdr.bih.biCompression = BI_RGB;
m_BitmapHdr.bih.biClrUsed = 32;
// 從 DirectDraw 獲取當前 32 色調色盤數據
DirectDraw.GetPaletteData(m_Palette);
// 填充 m_lpPattern:每 16×16 區塊填入對應調色盤索引 0~31
for (INT y = 0; y < 32; y++) {
for (INT x = 0; x < 256; x++) {
m_lpPattern[x + y * 256] = (y >> 4) * 0x10 + (x >> 4);
}
}
return TRUE;
}
OnTimer():定時器驅動實時繪製WNDMSG CPaletteView::OnTimer(WNDMSGPARAM) {
if (!Emu.IsRunning()) return TRUE;
// 同步最新調色盤:前 16 色背景,後 16 色精靈
for (INT i = 0; i < 16; i++) {
m_BitmapHdr.rgb[i] = m_Palette[BGPAL[i]];
m_BitmapHdr.rgb[i + 16] = m_Palette[SPPAL[i]];
}
// 拉伸繪製到客戶區
RECT rc; GetClientRect(hWnd, &rc);
HDC hDC = GetDC(hWnd);
StretchDIBits(hDC, 0, 0, rc.right, rc.bottom,
0, 0, 256, 32, m_lpPattern,
(BITMAPINFO*)&m_BitmapHdr, DIB_RGB_COLORS, SRCCOPY);
ReleaseDC(hWnd, hDC);
return TRUE;
}
BITMAPINFOHEADER + RGBQUAD rgb[32] + 索引數據 m_lpPattern,屬經典 8-bit 設備無關位圖。biHeight = -32 使位圖數據按從上到下順序存儲,配合 StretchDIBits 直接繪製,無需翻轉。(y>>4)*0x10 + (x>>4) 將 256×32 畫布劃分為 16×16=256 個宏像素,每宏像素填單一索引值,構成 16×2 的調色盤矩陣。這系列文章雖屬小眾(NES 模擬器源碼分析),但堅持記錄是為了讓對復古遊戲技術感興趣的人有據可查,也為自己留下知識錨點。祝各位國慶快樂!
本文轉載自 “三人乘虎” 博客,原文地址:https://darhx.blog.51cto.com/7920146/1304059