版权
本文为原创, 遵循 CC 4.0 BY-SA 版权协议, 转载需注明出处: https://blog.youkuaiyun.com/big_cheng/article/details/128417414.
前言
《Programming Windows》(Charles Petzold, 第五版) Bitmap 部分有一章专门讲述palette (调色板). 早期显卡只能支持256色, 无法支撑多个应用同时显示(例如2个应用加起来需要300种颜色). 所以Windows 提供了系统唯一的palette, 所有应用通过它来分配颜色. Palette 处理颜色映射、近似色等.
目前版本的Windows(例如Win10) 一般已经不支持256色模式了, 好在找到一台Vista (Home Basic, SP1, 32位) 还是可以设置为1280x800 256色模式, 运行书中相关程序(除了Image 相关的) 体验如下.
各程序
Grays1
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// fountain of grays
for (i = 0; i < 65; i++) {
rect.left = i * cxClient / 65;
rect.top = 0;
rect.right = (i + 1) * cxClient / 65;
rect.bottom = cyClient;
hBrush = CreateSolidBrush(RGB(min(255, 4 * i),
min(255, 4 * i),
min(255, 4 * i)) );
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
// 测试
SetTextColor(hdc, RGB(255, 0, 0));
TextOut(hdc, 300, 20, TEXT("Hello"), 5);
EndPaint(hwnd, &ps);
return 0;
此程序没有使用palette. 从左至右显示65个灰度条(gray shade, 即三原色值相等例如r=g=b=128) 从黑=>白 渐变. 但是只有四条实际是纯色 黑/128/192/白, 其余均使用纯色拟合(dither) 而成. 例如(黑=‘0’, 128=‘2’, 192=‘9’, 白=‘F’):
灰点整齐排列, 其余填充黑点:
0 2 0 2 0 2 0 2
0 0 0 0 0 0 0 0
0 2 0 2 0 2 0 2
0 0 0 0 0 0 0 0
马赛克:
2 0 2 0 2 0 2 0
0 2 0 2 0 2 0 2
2 0 2 0 2 0 2 0
0 2 0 2 0 2 0 2
黑点整齐排列, 其余填充灰点:
2 2 2 2 2 2 2 2
2 0 2 0 2 0 2 0
2 2 2 2 2 2 2 2
2 0 2 0 2 0 2 0
灰十字周围是白点:
F 2 F 2 F 2 F 2
2 2 2 F 2 2 2 F
F 2 F 2 F 2 F 2
2 F 2 2 2 F 2 2
F 2 F 2 F 2 F 2
测试"Hello" 是红色.
注: 将程序界面拷贝到小画家(mspaint), Windows 会自动调整小画家里的颜色例如128=>120, 192=>180.
Grays2
case WM_CREATE:
plp = (LOGPALETTE *) malloc(sizeof (LOGPALETTE) + 64 * sizeof (PALETTEENTRY));
plp->palVersion = 0x0300;
plp->palNumEntries = 65;
for (i = 0; i < 65; i++) {
plp->palPalEntry[i].peRed = (BYTE) min(255, 4 * i);
plp->palPalEntry[i].peGreen = (BYTE) min(255, 4 * i);
plp->palPalEntry[i].peBlue = (BYTE) min(255, 4 * i);
plp->palPalEntry[i].peFlags = 0;
}
hPalette = CreatePalette(plp);
free(plp);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SelectPalette(hdc, hPalette, FALSE);
RealizePalette(hdc);
// fountain of grays
for (i = 0; i < 65; i++) {
rect.left = i * cxClient / 65;
rect.top = 0;
rect.right = (i + 1) * cxClient / 65;
rect.bottom = cyClient;
hBrush = CreateSolidBrush(PALETTERGB(min(255, 4 * i),
min(255, 4 * i),
min(255, 4 * i)) );
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
// 测试
SetTextColor(hdc, PALETTERGB(255, 0, 0));
TextOut(hdc, 300, 20, TEXT("Hello"), 5);
EndPaint(hwnd, &ps);
return 0;
显示65个真正的灰度条, 不再是dither 而成.
测试"Hello" 用RGB 时显示红色, 用PALETTERGB 时显示70(纯色).
注: 程序界面拷贝到小画家, 小画家里改为显示6个灰度条(原来的多个灰度条合并了?).
Grays3
// WM_CREATE 同Grays2
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SelectPalette(hdc, hPalette, FALSE);
RealizePalette(hdc);
// fountain of grays
for (i = 0; i < 65; i++) {
rect.left = i * cxClient / 65;
rect.top = 0;
rect.right = (i + 1) * cxClient / 65;
rect.bottom = cyClient;
hBrush = CreateSolidBrush(PALETTEINDEX(i) );
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
// 测试
SetTextColor(hdc, PALETTERGB(255, 0, 0));
TextOut(hdc, 300, 20, TEXT("Hello"), 5);
EndPaint(hwnd, &ps);
return 0;
与Grays2 相比, Grays3 仅在WM_PAINT CreateSolidBrush 里改用了PALETTEINDEX. 显示效果一样.
Syspal1
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
GetSystemPaletteEntries(hdc, 0, 256, pe);
for (i = 0, x = 0, y = 0; i < 256; i++) {
wsprintf(szBuffer, TEXT("%02X-%02X-%02X"),
pe[i].peRed, pe[i].peGreen, pe[i].peBlue);
TextOut(hdc, x, y, szBuffer, lstrlen(szBuffer));
if ((x += sizeChar.cx) + sizeChar.cx > cxClient) {
x = 0;
if ((y += sizeChar.cy) > cyClient)
break;
}
}
EndPaint(hwnd, &ps);
return 0;
显示系统palette 全部(256个entry) 的内容(颜色值).
注意: 实测后10个entry 的第二个不是书中Figure 16−1 所说的"A0 A0 A4", 而是"0D 68 6B".
Syspal2
case WM_CREATE:
if ( !CheckDisplay(hwnd) )
return -1;
plp = (LOGPALETTE *) malloc(sizeof (LOGPALETTE) + 255 * sizeof (PALETTEENTRY));
plp->palVersion = 0x0300;
plp->palNumEntries = 256;
for (i = 0; i < 256; i++) {
plp->palPalEntry[i].peRed = i; // 255-i 则显示颜色为倒序(白->黑)
plp->palPalEntry[i].peGreen = 0;
plp->palPalEntry[i].peBlue = 0;
plp->palPalEntry[i].peFlags = PC_EXPLICIT;
}
hPalette = CreatePalette(plp);
free(plp);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
SelectPalette(hdc, hPalette, FALSE);
RealizePalette(hdc);
for (y = 0; y < 16; y++)
for (x = 0; x < 16; x++) {
hBrush = CreateSolidBrush(PALETTEINDEX(16 * y + x));
SetRect(&rect, x * cxClient / 16, y * cyClient / 16,
(x + 1) * cxClient / 16, (y + 1) * cyClient / 16);
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
EndPaint(hwnd, &ps);
return 0;
PC_EXPLICIT 说明该entry 的值 = 系统palette entry 的下标. 例如PC_EXPLICIT 3, 则PALETTEINDEX(3) 是取系统palette 第3项的颜色.
注: 将WM_CREATE 里"peRed = i" 修改为"peRed = 255-i" 后, 显示的颜色顺序颠倒(白=>黑).
注: 拷贝程序界面到小画家, 小画家里的颜色也出现一些差异. 例如"0D 68 6B" => “2F 36 99”, “FF 00 00” => “ED 1C 24”.
Syspal3
case WM_CREATE:
if ( !CheckDisplay(hwnd) )
return -1;
for (i = 0; i < 256; i++)
bits[i] = i;
hBitmap = CreateBitmap(16, 16, 1, 8, &bits);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);
StretchBlt(hdc, 0, 0, cxClient, cyClient,
hdcMem, 0, 0, 16, 16, SRCCOPY);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
return 0;
注: 拷到小画家里的界面颜色也发生变化.
动画 - Bounce
HPALETTE CreateRoutine(HWND hwnd) {
HPALETTE hPalette;
int i;
plp = (LOGPALETTE *) malloc(sizeof(LOGPALETTE) + 33 * sizeof (PALETTEENTRY) );
plp->palVersion = 0x0300;
plp->palNumEntries = 34;
for (i = 0; i < 34; i++) {
plp->palPalEntry[i].peRed = 255;
plp->palPalEntry[i].peGreen = (i == 0 ? 0 : 255);
plp->palPalEntry[i].peBlue = (i == 0 ? 0 : 255);
plp->palPalEntry[i].peFlags = (i == 33 ? 0 : PC_RESERVED);
}
hPalette = CreatePalette(plp);
SetTimer(hwnd, ID_TIMER, 50, NULL);
return hPalette;
}
34个entry, 前33个用于球, PC_RESERVED 代表在系统palette 里保留该entry (用于动画, 专用). 最后1个peFlags=0 可能会映射到系统palette 的末entry (白色, 不专用).
33个球初始第1个红色其他均白色.
void PaintRoutine(HDC hdc, int cxClient, int cyClient) {
HBRUSH hBrush;
int i, x1, x2, y1, y2;
RECT rect;
// bkgr: index 33
SetRect(&rect, 0, 0, cxClient, cyClient);
hBrush = CreateSolidBrush(PALETTEINDEX(33));
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
// 33 balls
SelectObject(hdc, GetStockObject(NULL_PEN));
for (i = 0; i < 33; i++) {
......
hBrush = CreateSolidBrush(PALETTEINDEX(i));
SelectObject(hdc, hBrush);
Ellipse(hdc, x1, y1, x2, y2);
DeleteObject(SelectObject(hdc, GetStockObject(WHITE_BRUSH)));
}
return;
}
绘制先用PALETTEINDEX(33) 即白色刷背景; 再从左至右按’W’ 型画33个球, 每个的颜色用PALETTEINDEX(0~32) (初值首球红其余白).
void TimerRoutine(HDC hdc, HPALETTE hPalette) {
static BOOL l2r = TRUE;
static int iBall;
// old ball => white
plp->palPalEntry[iBall].peGreen = 255;
plp->palPalEntry[iBall].peBlue = 255;
......
// new ball => red
plp->palPalEntry[iBall].peGreen = 0;
plp->palPalEntry[iBall].peBlue = 0;
// animate
AnimatePalette(hPalette, 0, 33, plp->palPalEntry);
return;
}
定期将原红球(的entry) 置为白色, 算出下一球的位置并将其entry 置为红色. 调用AnimatePalette 后将刚才的修改反映到系统palette 里, 则屏幕上程序界面的颜色就发生变化, 产生小球弹跳的效果, 不需要调用重绘(WM_PAINT).
即动画效果不是通过频繁重绘实现, 而是33个小球的位置不变, 但通过频繁调整每个小球指向的(PC_RESERVED) 系统palette entry 的颜色值来实现.
动画 - Fader
HPALETTE CreateRoutine(HWND hwnd) {
HPALETTE hPalette;
lp.palVersion = 0x0300;
lp.palNumEntries = 1;
lp.palPalEntry[0].peRed = 255;
lp.palPalEntry[0].peGreen = 255;
lp.palPalEntry[0].peBlue = 255;
lp.palPalEntry[0].peFlags = PC_RESERVED;
hPalette = CreatePalette(&lp);
SetTimer(hwnd, ID_TIMER, 50, NULL);
return hPalette;
}
Fader 只保留(PC_RESERVED) 一个系统palette entry, 初始白色.
void TimerRoutine(HDC hdc, HPALETTE hPalette) {
static BOOL fadeIn = TRUE;
if (fadeIn) {
lp.palPalEntry[0].peRed -= 4;
lp.palPalEntry[0].peGreen -= 4;
if (lp.palPalEntry[0].peRed == 3)
fadeIn = FALSE;
} else {
lp.palPalEntry[0].peRed += 4;
lp.palPalEntry[0].peGreen += 4;
if (lp.palPalEntry[0].peRed == 255)
fadeIn = TRUE;
}
AnimatePalette(hPalette, 0, 1, lp.palPalEntry);
return;
}
定期将该entry 的颜色值加深或变浅, 调用AnimatePalette 后屏幕显示的字体颜色就相应改变了.
动画 - Allcolor
HPALETTE CreateRoutine(HWND hwnd) {
HDC hdc;
HPALETTE hPalette;
LOGPALETTE lp;
// color resolution
hdc = GetDC(hwnd);
iIncr = 1 << (8 - GetDeviceCaps(hdc, COLORRES) / 3);
ReleaseDC(hwnd, hdc);
// lp
lp.palVersion = 0x0300;
lp.palNumEntries = 1;
lp.palPalEntry[0].peRed = 0;
lp.palPalEntry[0].peGreen = 0;
lp.palPalEntry[0].peBlue = 0;
lp.palPalEntry[0].peFlags = PC_RESERVED;
hPalette = CreatePalette(&lp);
//
pe = lp.palPalEntry[0];
SetTimer(hwnd, ID_TIMER, 10, NULL);
return hPalette;
}
Allcolor 也只保留一个系统palette entry. 如果COLORRES=18(三原色各占用6bit), 则iIncr = 1 << 2 = 4.
void DisplayRGB(HDC hdc, PALETTEENTRY * ppe) {
TCHAR szBuffer[16];
wsprintf(szBuffer, TEXT(" %02X-%02X-%02X "),
ppe->peRed, ppe->peGreen, ppe->peBlue);
TextOut(hdc, 0, 0, szBuffer, lstrlen(szBuffer));
}
void PaintRoutine(HDC hdc, int cxClient, int cyClient) {
HBRUSH hBrush;
RECT rect;
//
hBrush = CreateSolidBrush(PALETTEINDEX(0));
SetRect(&rect, 0, 0, cxClient, cyClient);
FillRect(hdc, &rect, hBrush);
DeleteObject(SelectObject(hdc, GetStockObject(WHITE_BRUSH)));
//
DisplayRGB(hdc, &pe);
return;
}
绘制使用保留entry 的当前颜色值.
void TimerRoutine(HDC hdc, HPALETTE hPalette) {
static BOOL br = TRUE, bg = TRUE, bb = TRUE;
pe.peBlue += (bb ? iIncr : -iIncr);
if (pe.peBlue == (BYTE) (bb ? 0 : 256 - iIncr)) {
......
}
//
AnimatePalette(hPalette, 0, 1, &pe);
DisplayRGB(hdc, &pe);
return;
}
定期用iIncr 值增减各原色值. iIncr 是最小增量.
实测窗口从黑色渐变为蓝色, 再渐变回黑色, 如此循环. 注: DisplayRGB 在左上角显示的当前颜色值并不会随时更新, 有时需10几秒, 不规律.
动画 - Pipes
HPALETTE CreateRoutine(HWND hwnd) {
HPALETTE hPalette;
int i;
plp = (LOGPALETTE *) malloc(sizeof (LOGPALETTE) + 32 * sizeof (PALETTEENTRY));
plp->palVersion = 0x300;
plp->palNumEntries = 16;
// 0~8, 8~0, 0~8, 8~0
for (i = 0; i <= 8; i++) {
plp->palPalEntry[i].peRed = (BYTE) min(255, 0x20 * i);
plp->palPalEntry[i].peGreen = 0;
plp->palPalEntry[i].peBlue = (BYTE) min(255, 0x20 * i);
plp->palPalEntry[i].peFlags = PC_RESERVED;
plp->palPalEntry[16 - i] = plp->palPalEntry[i];
plp->palPalEntry[16 + i] = plp->palPalEntry[i];
plp->palPalEntry[32 - i] = plp->palPalEntry[i];
}
hPalette = CreatePalette(plp);
SetTimer(hwnd, ID_TIMER, 100, NULL);
return hPalette;
}
33个entry: 0~8, 8~0, 0~8, 8~0 (4x9 - 3 = 33). 0~8 从黑色到紫色RGB(255,0,255).
void PaintRoutine(HDC hdc, int cxClient, int cyClient) {
HBRUSH hBrush;
int i;
RECT rect;
// background - 白色
......
// pipe interiors
for (i = 0; i < 128; i++) {
hBrush = CreateSolidBrush(PALETTEINDEX(i % 16));
SelectObject(hdc, hBrush);
rect.left = (127 - i) * cxClient / 128;
rect.right = (128 - i) * cxClient / 128;
rect.top = 4 * cyClient / 14;
rect.bottom = 5 * cyClient / 14;
FillRect(hdc, &rect, hBrush);
rect.left = i * cxClient /128;
rect.right = (i + 1) * cxClient / 128;
rect.top = 9 * cyClient / 14;
rect.bottom = 10 * cyClient / 14;
FillRect(hdc, &rect, hBrush);
DeleteObject(SelectObject(hdc, GetStockObject(WHITE_BRUSH)));
}
// pipe edges
MoveToEx(hdc, 0, 4 * cyClient / 14, NULL);
LineTo (hdc, cxClient, 4 * cyClient / 14);
......
return;
}
水平128等分, 从右往左绘制上方管道内容及从左往右绘制下方管道内容, 颜色从entry 0~15 循环.
void TimerRoutine(HDC hdc, HPALETTE hPalette) {
static int iIndex;
AnimatePalette(hPalette, 0, 16, plp->palPalEntry + iIndex);
iIndex = (iIndex + 1) % 16;
return;
}
定期从33个entry 轮换16个系统entry: 0~15, 1~16, …, 15~30.
实测效果上方管道往右流动, 下方管道往左流动.
Pipes + Syspal3
同时运行Syspal3: 系统palette 前10个是系统保留颜色, 紧跟着的就是Pipes 的16个entry, 且这16个entry 的颜色也在随时改变 - 对应管道内容的流动.
注: 关闭这2个程序后再运行Syspal3, 系统palette 的前10+16个entry 颜色保留上次离开时的.
Pipes + mspaint
Pipes 与小画家同时运行时(不论2者窗口有无重叠):
当焦点在Pipes (即Pipes active) 时, 小画家的颜色工具条里的2排色块(14x2, 2排分别以黑/白开头) 的第一排后13+第二排2,3,4 共16块的颜色对应Pipes 的颜色流动而变.
当焦点在小画家时, Pipes 管道内容静止, 且其颜色变成五颜六色(大概对应小画家颜色工具条里的颜色).
动画 - Tunnel
HPALETTE CreateRoutine(HWND hwnd) {
BYTE by;
HPALETTE hPalette;
int i;
plp = (LOGPALETTE *) malloc(sizeof (LOGPALETTE) + 255 * sizeof (PALETTEENTRY));
plp->palVersion = 0x0300;
plp->palNumEntries = 128;
for (i = 0; i < 128; i++) {
if (i < 64)
by = (BYTE) (4 * i); // 0~252
else
by = (BYTE) min(255, 4 * (128 - i)); // 255~4
plp->palPalEntry[i].peRed = by;
plp->palPalEntry[i].peGreen = by;
plp->palPalEntry[i].peBlue = by;
plp->palPalEntry[i].peFlags = PC_RESERVED;
plp->palPalEntry[i + 128].peRed = by;
plp->palPalEntry[i + 128].peGreen = by;
plp->palPalEntry[i + 128].peBlue = by;
plp->palPalEntry[i + 128].peFlags = PC_RESERVED;
}
hPalette = CreatePalette(plp);
SetTimer(hwnd, ID_TIMER, 50, NULL);
return hPalette;
}
Tunnel 使用256个entry, 前一半颜色黑=>白=>黑, 后一半重复前一半的. 前一半用作系统entry.
void PaintRoutine(HDC hdc, int cxClient, int cyClient) {
HBRUSH hBrush;
int i;
RECT rect;
for (i = 0; i < 127; i++) {
rect.left = i * cxClient / 255;
rect.top = i * cyClient / 255;
rect.right = cxClient - i * cxClient / 255;
rect.bottom = cyClient - i * cyClient / 255;
hBrush = CreateSolidBrush(PALETTEINDEX(i));
FillRect(hdc, &rect, hBrush);
DeleteObject(hBrush);
}
return;
}
Tunnel 从外向内绘制127个矩形, 依次用对应的系统entry 的颜色填充(从外到内黑=>白=>黑). 相邻矩形间隔1/255总宽, 最内层矩形宽约(255-126)-126 = 3/255总宽.
void TimerRoutine(HDC hdc, HPALETTE hPalette) {
static int iLevel;
iLevel = (iLevel + 1) % 128;
AnimatePalette(hPalette, 0, 128, plp->palPalEntry + iLevel);
return;
}
Tunnel 定期用256个entry 里从0开始、从1开始、…、从127开始、从0开始、… 的128个entry 来更换系统entry, 循环往复.
实测Tunnel 的效果最’惊艳’: 就好像在隧道里开车, 隧道每隔一段距离有光源. 随着车往前开, 视野逐渐变亮又逐渐变暗. 隧道似乎无穷无尽, 每当驶离光源车身就会陷入一片黑暗, 但是黑暗中前方又会出现朦胧的光, 那里是下一个光源.
Tunnel 窗口有2条较显眼(但不清晰) 的对角线, 因为PaintRoutine 里只画了矩形, 推测是颜色有细微差异的矩形由于间隔很近而(在尖角处)形成的视觉效果.
Tunnel + Syspal3
同时运行时, Syspal3 里从第11个开始的128个系统entry 的颜色也随着Tunnel 而持续对应变化.
Tunnel + mspaint
同时运行, 焦点在Tunnel 时, 小画家颜色工具条除了黑白2色外其他13x2块颜色均随着Tunnel 在持续变化.
焦点在小画家时, 小画家颜色工具条恢复正常, Tunnel 外围约1/7 厚度区域变成静止的五颜六色, 之内的区域保持原来的动画效果.
异常测试
系统palette 只有256个entry, 发现在需求超出256色时会有异常情况. 简单记录数例如下(未深入研究).
Grays3r + Grays3g
Grays3 只有65色, 尝试改为150色: 3r-偏红, 3g-偏绿, 同时运行时超过256色需求.
3r:
case WM_CREATE:
plp = (LOGPALETTE *) malloc(sizeof (LOGPALETTE) + 149 * sizeof (PALETTEENTRY));
plp->palVersion = 0x0300;
plp->palNumEntries = 150;
for (i = 0; i < 150; i++) {
plp->palPalEntry[i].peRed = (BYTE) 120;
plp->palPalEntry[i].peGreen = (BYTE) i;
plp->palPalEntry[i].peBlue = (BYTE) i;
plp->palPalEntry[i].peFlags = 0;
}
......
return 0;
case WM_PAINT:
// fountain of grays
for (i = 0; i < 150; i++) {
rect.left = i * cxClient / 150;
rect.top = 0;
rect.right = (i + 1) * cxClient / 150;
rect.bottom = cyClient;
......
}
......
return 0;
3g 类似只是改为偏绿:
case WM_CREATE:
......
for (i = 0; i < 150; i++) {
plp->palPalEntry[i].peRed = (BYTE) i;
plp->palPalEntry[i].peGreen = (BYTE) 120;
plp->palPalEntry[i].peBlue = (BYTE) i;
plp->palPalEntry[i].peFlags = 0;
}
......
return 0;
实测3r与3g同时运行时: 如焦点切到3r, 则3g 显示=>偏红; 如焦点再切到3g, 则3r 显示=>偏绿. 不论2个窗口是否有重叠.
焦点在3r 此时2窗口全偏红, 拖拽3r 窗口使之部分覆盖3g窗口, 则可发现3g 被覆盖的部分恢复其应有的偏绿.
Grays3r + Grays3g + Syspal3
焦点在3r时, Syspal3 的系统entry: 前10个(系统保留色) + 150个偏红各色对应3r + 86个偏绿各色对应3g(的前面部分) + 末10个(系统保留色).
焦点再切到3g, Syspal3: 前10个(系统保留色) + 150个偏绿各色对应3g + 86个偏红各色对应3r(从之前系统palette 的前面被’挤’下来) + 末10个(系统保留色).
可见150+150 = 300 > 256, 都是从系统palette 的开头(第11个) 开始分配.
Bounce + Syspal2
同时运行, 窗口不重叠. 此时Syspal2: 第11开始的33个系统entry 全是白色, 除了一个为红色对应Bounce 的红球, 且该红块的位置也对应红球在随时移动 - 红球在开头位置时红块也在开头(第12块), 红球在结尾位置时红块也在结尾.
焦点在Syspal2 时缓慢拖拽其窗口上下移动(不与Bounce 窗口重叠), 拖拽几秒后红球停止弹跳, 红块也停止移动.
此时继续拖拽Syspal2 窗口使之与Bounce 窗口(部分)重叠, 则重叠区域原来Bounce 因为白色而不可见的球变成黑色从而显现出来. 并且红球被覆盖后也变黑.
Bounce + Syspal3
Bounce 与Syspal3 同时运行时没有上述情况, 红球一直弹跳正常, 红块也一直在移动.
Bounce + Pipes
2者同时运行.
焦点在Pipes 时: Pipes 正常, 但Bounce 红球静止, 剩下大概一个’V’字范围内的球均显现出来, 且颜色对应为Pipes 的紫/黑色并且颜色也在’动’.
焦点再切到Bounce: 2个窗口均开始频闪. Bounce 'V’字范围内的球同前但会频繁变成白色所以频闪, 而红球在’W’字内正常弹跳. Pipes 的管道内容频繁变白色所以频闪, 其中定期会出现若干等分变红.
Bounce + Pipes + Syspal3
再同时运行Syspal3.
焦点在Pipes 时: (Syspal3显示)Pipes 的16个紫/黑色从第11个系统entry 开始分配. Bounce 的红块在系统palette 的后面区域移动.
焦点再切到Bounce: Bounce 和Pipes 都从第11个系统entry 开始分配颜色, 所以(这些entry)一会是紫/黑色一会是白/红色, 出现频闪.
(为什么要’抢’ 在相同的entry 分配导致频闪?)
Pipes + Tunnel
2者同时运行.
焦点在Pipes 时: Pipes 正常. Tunnel 最外围一小圈区域颜色变成Pipes 的紫/黑色(颜色也在变动).
焦点再切到Tunnel: 2个窗口均出现频闪. Pipes 的管道内容频繁闪现Tunnel 的黑灰白色. Tunnel 仅之前外围的那一小圈区域频闪.
Pipes + Tunnel + Syspal3
再同时运行Syspal3.
焦点在Pipes 时: (Syspal3显示)Pipes 的16色从第11个系统entry 开始分配, 紧跟着后面的128个分配给Tunnel.
焦点再切到Tunnel 时: Tunnel 的系统entry 往前挪动与原地未动的Pipes 的16个系统entry ‘争抢’ 导致频闪.
总结
Palette 比较复杂, 可能存在一些问题. 随着显示硬件的发展, 也许一般的编程不需要再考虑palette.
补充
Vista 下可以使用tcc (0.9.26) 编译. 例如:
tcc Grays1.c
tcc PalAnim.c Bounce.c