zoukankan      html  css  js  c++  java
  • Windows 调色板

    1调色板    1

    1.1 为什么要使用调色板    1

    1.2 使用调色板    2

    1.2.1 创建逻辑调色板    2

    1.2.2 使用    3

    1.2.3 销毁逻辑调色板    4

    1.3 系统调色板    5

    1.4 调色板消息    6

    1.4.1 WM_QUERYNEWPALETTE    6

    1.4.2 WM_PALETTECHANGED    7

    1.4.3 小心死循环    8

    1调色板

    1.1 为什么要使用调色板

    如果设置显示器的颜色为256色,如下图所示。那么就意味着:虽然显示器能显示的颜色数可能超过256个,但是显示一帧画面的颜色数最多只能达到256个。

    图1.1

    直接看下图的程序显示效果:

    图1.2

    上图有两个16×16的红色方阵。红色分量由0255逐渐加大。

    左边的方阵使用了调色板,颜色相对比较平滑;

    右边的方阵未使用调色板,颜色抖动得比较厉害。

    所以:在256色显示器上使用调色板是为了获得最好的显示效果。

    1.2 使用调色板

    使用调色板是很简单的。记住四个函数CreatePaletteSelectPaletteRealizePaletteDeleteObject,一个宏PALETTEINDEX就可以使用调色板了。

    上图(图1.2)是一个MFC对话框程序的显示截图。下面对其代码进行说明。

    首先给对话框类增加成员变量HPALETTE m_hPalette,如下所示。它表示一个逻辑调色板。

    class CDlgPalDlg : public CDialog

    {

    ... ... ...

    protected:

    HPALETTE m_hPalette;

    };

    1.2.1 创建逻辑调色板

    CDlgPalDlg的构造函数里,调用CreatePalette函数创建逻辑调色板。具体代码如下:

    CDlgPalDlg::CDlgPalDlg(CWnd* pParent /*=NULL*/)

    : CDialog(CDlgPalDlg::IDD, pParent)

    {

    LPLOGPALETTE pLogPal = (LPLOGPALETTE)malloc(

    sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256);

    pLogPal->palVersion        =    0x300;

    pLogPal->palNumEntries    =    256;

    for(int i = 0;i < 256;i++)

    {

    pLogPal->palPalEntry[i].peRed        =    i;

    pLogPal->palPalEntry[i].peGreen    =    0;

    pLogPal->palPalEntry[i].peBlue        =    0;

    pLogPal->palPalEntry[i].peFlags        =    0;

    }

    m_hPalette = CreatePalette(pLogPal);

    free(pLogPal);

    }

    说明:

    1、代码 pLogPal->palNumEntries = 256;说明逻辑调色板里有256个颜色。一个逻辑调色板最多也只能有256个颜色;

    2for循环给每个颜色设置RGB分量。可以代码里只设置了红色分量,绿、蓝分量都是零。

    1.2.2 使用

    在对话框窗口绘制函数CDlgPalDlg::OnPaint里用到了逻辑调色板。具体代码如下:

    void CDlgPalDlg::OnPaint()

    {

    CPaintDC    dc(this);

    HBRUSH    hBrush        =    NULL;

    HGDIOBJ    hBrushOld    =    NULL;

    int            x,y,i;

     

    dc.SelectStockObject(BLACK_PEN);

    {//使用调色板绘制左边的红色方阵

    SelectPalette(dc.m_hDC,m_hPalette,FALSE);

    RealizePalette(dc.m_hDC);

     

    for(i = 0;i < 256;i++)

    {

    x = (i % 16) * 16;

    y = (i / 16) * 16;

    hBrush = CreateSolidBrush(PALETTEINDEX(i));

    hBrushOld = SelectObject(dc.m_hDC,hBrush);

    dc.Rectangle(x,y,x+16,y+16);

    dc.SelectObject(hBrushOld);

    DeleteObject(hBrush);

    }

    }

    {/不使用调色板绘制右边的红色方阵

    for(i = 0;i < 256;i++)

    {

    x = (i % 16) * 16 + 300;

    y = (i / 16) * 16;

    hBrush = CreateSolidBrush(RGB(0,0,i));

    hBrushOld = SelectObject(dc.m_hDC,hBrush);

    dc.Rectangle(x,y,x+16,y+16);

    dc.SelectObject(hBrushOld);

    DeleteObject(hBrush);

    }

    }

    }

    说明:

    1SelectPalette(dc.m_hDC,m_hPalette,FALSE); 把逻辑调色板选入对话框客户区的dc。第三个参数非常重要,随后再做详细说明;

    2RealizePalette(dc.m_hDC);根据逻辑调色板中的颜色修改系统调色板。这个函数还有更多的技术细节,随后再做详细说明;

    3hBrush = CreateSolidBrush(PALETTEINDEX(i)); 创建一个刷子,注意它的颜色是PALETTEINDEX(i),表示使用调色板里的第i个颜色;

    4、绘制右边的红色方阵时,创建的刷子是hBrush = CreateSolidBrush(RGB(0,0,i));即直接使用RGB颜色,而非调色板中的颜色索引。

    1.2.3 销毁逻辑调色板

    CDlgPalDlg的析构函数里,调用DeleteObject函数销毁逻辑调色板。具体代码如下:

    CDlgPalDlg::~CDlgPalDlg()

    {

    if(m_hPalette)

    {

    DeleteObject(m_hPalette);

    m_hPalette = NULL;

    }

    }

    1.3 系统调色板

    上一节说到RealizePalette函数会根据逻辑调色板中的颜色修改系统调色板。假定有两个程序都调用了它,都去修改系统调色板,会出现什么情况?

    对上一节的程序稍加修改,就可以使用调色板绘制一个蓝色的方阵。现在同时运行这两个程序,看看什么效果。

    绘制红色方阵的程序(以下简称程序R)获得焦点时的显示如下。红色方阵显示非常好,蓝色方阵的显示似乎只用到了三种颜色。

    图1.3

    绘制蓝色方阵的程序(以下简称程序B)获得焦点时的显示如下。蓝色方阵显示非常好,红色方阵的显示似乎只用到了三种颜色。

    图1.4

    这些说明:

    1、系统调色板是独占资源。一个程序调用RealizePalette函数修改系统调色板后,其它程序都会受到影响;

    2、系统调色板里保留了20种颜色,这20种颜色无法被修改。所以程序R调用RealizePalette函数修改系统调色板时只修改了236种颜色,此时程序B显示的颜色其实是被保留的颜色;

    3RealizePalette函数修改系统调色板的条件:一是它的参数hdc必须是屏幕或窗口DC,而不能是打印机、内存DC;二是SelectPalette(hdc,m_hPalette,FALSE)的第三个参数必须为FALSE;三是程序的主窗口必须获得了焦点。

    程序R获得焦点的时候,它代码里的RealizePalette函数可以修改系统调色板,此时程序B未获得焦点,它代码里的RealizePalette函数就无法修改系统调色板。

    1.4 调色板消息

    因为系统调色板是独占资源,因此需要调色板消息协调各个程序对调色板的使用。需要注意:显示器的颜色为256色时,以下消息才会有效。

    1.4.1 WM_QUERYNEWPALETTE

    当程序主窗口获得焦点时,系统会给这个主窗口发送WM_QUERYNEWPALETTE消息。它的含义是询问程序是否修改了系统调色板?此时有两种回应:

    没有修改系统调色板,直接返回FALSE。代码如下:

    BOOL CDlgPalDlg::OnQueryNewPalette()

    {

    return FALSE;

    }

    修改了系统调色板,返回TRUE。代码如下:

    BOOL CDlgPalDlg::OnQueryNewPalette()

    {

    CClientDC dc(this);

    SelectPalette(dc.m_hDC,m_hPalette,FALSE);

    RealizePalette(dc.m_hDC);

    return TRUE;

    }

    1.4.2 WM_PALETTECHANGED

    当系统调色板被修改后,系统会给所有主窗口发送WM_PALETTECHANGED消息。它通知程序系统调色板被修改了,请做相应的处理。

    下面的处理很简单,调用Invalidate函数,重新绘制。

    void CDlgPalDlg::OnPaletteChanged(CWnd* pFocusWnd)

    {

    CDialog::OnPaletteChanged(pFocusWnd);

    Invalidate();

    }

    还可以使用WM_PALETTECHANGEDWPARAM参数(被MFC改成了CWnd*pFocusWndpFocusWnd->m_hWnd就是原始的WPARAM参数)。代码如下:

    void CDlgPalDlg::OnPaletteChanged(CWnd* pFocusWnd)

    {

    CDialog::OnPaletteChanged(pFocusWnd);

    if(pFocusWnd->m_hWnd != m_hWnd)

    {

    Invalidate();

    }

    }

    CDlgPalDlg接收到的WM_PALETTECHANGED消息有两种来源:一是其它程序修改了系统调色板;另一个是自身代码里的RealizePalette(dc.m_hDC);对于后一种情况pFocusWnd->m_hWnd 等于m_hWnd,上面代码的Invalidate();不会被执行,窗口也不会重绘。

    1.4.3 小心死循环

    调用RealizePalette函数会修改系统调色板,会触发WM_PALETTECHANGED消息。如果处理WM_PALETTECHANGED消息时再次调用RealizePalette函数修改系统调色板,就会再次触发WM_PALETTECHANGED消息……如此一来就陷入死循环了。

    分析下面的代码:

    void CDlgPalDlg::OnPaletteChanged(CWnd* pFocusWnd)

    {

    CDialog::OnPaletteChanged(pFocusWnd);

    Invalidate();

    }

     

    void CDlgPalDlg::OnPaint()

    {

    CPaintDC dc(this);

    SelectPalette(dc.m_hDC,m_hPalette,FALSE);

    RealizePalette(dc.m_hDC);

    } 

    窗口第一次绘制时会调用RealizePalette,它很可能会修改系统调色板,这样就会调用CDlgPalDlg::OnPaletteChanged里的Invalidate再次重绘。

    第二次绘制时仍会调用RealizePalette。因为第一次调用已经修改系统调色板了,系统调色板的颜色与逻辑调色板的颜色已经保持一致了。再次调用RealizePalette不会再修改系统调色板,也就不会再调用CDlgPalDlg::OnPaletteChanged里的Invalidate了。

    所以,上述只使用一个逻辑调色板的代码是不会陷入死循环的。

    再分析下面的代码:

    void CDlgPalDlg::OnPaletteChanged(CWnd* pFocusWnd)

    {

    CDialog::OnPaletteChanged(pFocusWnd);

    Invalidate();

    }

     

    void CDlgPalDlg::OnPaint()

    {

    CPaintDC dc(this);

    SelectPalette(dc.m_hDC,m_hPalRed,FALSE); //选用红色调色板

    RealizePalette(dc.m_hDC);

    {//绘制红色方阵

    ... ... ...

    }

    SelectPalette(dc.m_hDC,m_hPalBlue,FALSE); //选用蓝色调色板

    RealizePalette(dc.m_hDC);

    {//绘制蓝色方阵

    ... ... ...

    }

    } 

    CDlgPalDlg::OnPaint函数里首先选用红色调色板绘制红色方阵,然后选用蓝色调色板绘制蓝色方阵。结果就是两个RealizePalette都会修改系统调色板,都会触发WM_PALETTECHANGED消息,都会导致Invalidate被调用,都会导致CDlgPalDlg::OnPaint被再次执行……由此陷入了死循环。

    所以,程序里如果有多个逻辑调色板,必须保证只有一个是前景调色板,即调用SelectPalette函数选用这个调色板时第三个参数为FALSE;其它调色板必须为背景调色板,即调用SelectPalette函数选用其它调色板时第三个参数为TRUE

    系统调色板的独占使用是这样实现的:调用RealizePalette函数时,焦点所在程序的前景调色板可能会修改系统调色板,背景调色板以及未获得焦点的前景调色板不会修改系统调色板。

  • 相关阅读:
    IOS--UILabel的使用方法详细
    一个人不成熟的六大特征:
    UIView
    objective-c 错题
    洛谷P1039 侦探推理(模拟)
    洛谷P1038 神经网络(bfs,模拟,拓扑)
    FBI树-数据结构(二叉树)
    二叉树遍历(flist)(二叉树,已知中序层序,求先序)
    求先序排列(二叉树已知中序和后序,求先序)
    哈理工2015暑假集训 zoj 2975 Kinds of Fuwas
  • 原文地址:https://www.cnblogs.com/hanford/p/6164424.html
Copyright © 2011-2022 走看看