PW系列 | palette 体验

版权

本文为原创, 遵循 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值