Windows DIB文件操作详解-5.DIB和调色板
Windows调色板是256色显卡时期的产物,现在显卡最少也是16bit的了,所以调色板基本上是用不到了的。
但是下面几种情况还是需要去使用和了解调色板:
1.在新显卡上保证256色兼容模式的正常运行
2.在256色显卡或低于256色显卡老机器上运行程序或在一些工业控制场合(为了节约成本可能采用256色显卡或低于256色显卡)运行程序
3.操作DIB的指定像素点数据
1.调色板是什么
同样采用下面一张图
如上图,我们可以调色板分为如下几种:
1.DIB文件中的调色板
2.内存中创建的逻辑调色板对象
3.显卡中硬件调色板
调色板顾名思义就是类似画家的调色板一样,以256色显卡来说,现在有256个小格子,每个格子中有一种颜色,每次绘图你可以用这256种颜色来绘图,如果你对当前的256种颜色不满意,可以更换其中的调色板颜色,但是每画一幅图只能用确认的256中颜色来。对应到电脑的调色板来说,显卡支持显示的颜色只有256种,存储在显卡硬件调色板中,你可以设置要采用的256种颜色,然后你要显示的所有颜色都会近似用256中颜色中的一种来显示,这就是所谓的调色板查表以取得最近似颜色。
可以看到,一幅图在电脑上显示的效果是什么样和当前显卡硬件调色板中的颜色是密不可分的,所以对于采用硬件调色板的的位图,保存它的最好做法就是保存位图数据的同时保存位图调色板。
为了改变硬件调色板中的颜色我们需要先创建逻辑调色板对象,然后再将逻辑调色板映射到硬件调色板上。
2.调色板的使用
对于带有调色板的DIB来说,显示DIB的一般步骤如下:
1.读入DIB分别为File Header、Info Header、Mask、Color Table、Bits各个区
2.利用DIB的调色板数据(Color Table区)创建逻辑调色板对象(CreatePalette),一般在WM_CREATE中
3.在WM_PAINT显示DIB之前,将基于DIB创建的逻辑调色板对象选进当前设备描述表(SelectPalette)并映射到硬件调色板上(RealizePalette)。
4.如果只是想保证指定窗口为活动窗口时显示的颜色,那么做完这三歩就行了。如果想保证指定窗口不是活动窗口时的颜色,那么还要处理WM_QUERYPALETTE和WM_PALETTECHANGED消息。这两个消息是为了帮助Windows组织系统调色板用的,这里不详述,直接按照固定模式使用,具体可以查看MSDN。
3.代码演示
演示程序为一个典型的Windows 带调色板DIB的显示过程。下面为部分代码,完整源代码最后会给出下载链接。
根据DIB Color Table创建逻辑调色板
//创建数据显示调色板 HPALETTE PackedDibCreatePalette(BITMAPINFO *pPackedDib, HWND hwnd) { HPALETTE hPalete; HDC hdc; int i, iNumColors; LOGPALETTE *plp = NULL; RGBQUAD *prgb = NULL; if (0 == PackedDibGetColorTableSize(pPackedDib) && PackedDibGetBitCount(pPackedDib) > 8)//没有调色板区且位深大于8,则没有必要创建调色板 { return NULL; } else if (0 == PackedDibGetColorTableSize(pPackedDib))//没有调色板区且位深小于等于8,则创建通用调色板 { hdc = GetDC(hwnd); hPalete = CreateHalftonePalette(hdc); ReleaseDC(hwnd, hdc); return hPalete; } else//使用位图自带的调色板区 { iNumColors = PackedDibGetNumColors(pPackedDib); plp = malloc(sizeof(LOGPALETTE) + (iNumColors-1)*sizeof(PALETTEENTRY)); if (NULL == plp) { return NULL; } plp->palVersion = 0x0300; plp->palNumEntries = iNumColors; for (i = 0; i < iNumColors; i++) { prgb = PackedDibGetColorTableEntry(pPackedDib, i); plp->palPalEntry[i].peRed = prgb->rgbRed; plp->palPalEntry[i].peGreen = prgb->rgbGreen; plp->palPalEntry[i].peBlue = prgb->rgbBlue; plp->palPalEntry[i].peFlags = 0; } hPalete = CreatePalette(plp); if (INVALID_HANDLE_VALUE == hPalete) { return NULL; } return hPalete; } }
WM_PAINT中显示DIB及WM_QUERYPALETTE和WM_PALETTECHANGED消息的处理
case WM_PAINT: hdc = BeginPaint (hwnd, &ps); if (hPalette) { SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); } if (pPackedDib) { SetDIBitsToDevice( hdc, 0, 0, PackedDibGetWidth(pPackedDib), PackedDibGetHeight(pPackedDib), 0, 0, 0, PackedDibGetHeight(pPackedDib), PackedDibGetBitsPtr(pPackedDib), pPackedDib, DIB_RGB_COLORS); } EndPaint (hwnd, &ps); return (0); case WM_QUERYNEWPALETTE: if(!hPalette) { return FALSE; } hdc = GetDC(hwnd); SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); InvalidateRect(hwnd, NULL, FALSE); ReleaseDC(hwnd, hdc); return TRUE; case WM_PALETTECHANGED: if(!hPalette || (HWND)wParam==hwnd) { break; } hdc = GetDC(hwnd); SelectPalette(hdc, hPalette, FALSE); RealizePalette(hdc); UpdateColors(hdc); ReleaseDC(hwnd, hdc); break;
4.DIB操作库
至此为止,关于DIB显示的所有知识点全部讲完。在给出的代码中实现了一个完整的DIB操作库,主要用于打开、显示和保存DIB。不同于实验DIB显示功能,一个完整性好的DIB操作库必须兼容各种版本DIB格式、不标准DIB位图。
1.DIB版本的兼容主要包括对OS/2格式位图的兼容(BITMAPCOREHEADER定义带来的各种差异,RGBTRIPLE带来的差异)。
2.不标准DIB位图主要指有的DIB位图不填充ClrUsed、SizeImage项,有的小于8bit的DIB位图没有调色板需要使用通用调色板,有的16bit位图不提供掩码等等。
总之,一个实现完整的DIB库需要考虑种种繁枝末节,如果是在商业程序中使用的话,最好使用已经开源的各种实现。该博文提供的DIB库在Pelzold的实现上修改而来,但仍然有不足,例如对压缩的DIB没有考虑等,仅供技术研究用。
完整源代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219