窗口绘制

Posted m-anonymous

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了窗口绘制相关的知识,希望对你有一定的参考价值。

WM_PAINT 消息:

    Windows通过发送 WM_PAINT 消息来通知窗口过程其客户区需要重绘。

    大多数 Windows 程序在 WinMain 函数初始化过程中会在进入消息循环之前调用 UpdateWindow() 函数。

     这将会向窗口过程函数( WndProc() )发送最初的 WM_PAINT消息,该消息通知窗口过程绘制客户区。

     在以下任何事件发生时,窗口过程都会收到一条 WM_PAINT 消息;

  • 用户移动一个窗口,导致原来被遮挡的部分窗口暴露出来。
  • 用户调整窗口大小。
  • 程序调用 ScrollWindow() 或 ScrollDC() 函数滚动客户区。
  • 程序调用 InvalidateRect() 或 InvalidateRgn() 函数显示生成 WM_PAINT 消息。
   某些情况下,当客户区的一部分被临时覆盖时,Windows 会试图保存被覆盖的这部分。
   以便将来恢复时使用。这并不是每次都可以成功。

   在以下情形,Windows 有时 会发生一条 WM_PAINT 消息。

  • Windows 关闭一个覆盖了部分窗口过程的对话框或消息框。
  • 下拉菜单被拉下然后收回。
  • 显示提示信息。
    少数情况下,Windows 总是会保存被覆盖部分的显示内容,然后再恢复。

    这些情况如下:

  • 鼠标指针在客户区内移动。
  • 在客户区内拖到图标。

 

有效矩形和无效矩形:

 尽管窗口过程必须能够在收到 WM_PAINT 消息时更新整个客户区,但通常它只需要更新其中的一部分。

最明显的例子是当一个对话框覆盖了客服区的一部分时:当对话框关闭时,只有先前被覆盖的那个矩形部分需要重新绘制。

 需要重新绘制的部分被称为“无效区域”或“更新区域”

在客户区中有一个无效区域将导致 Windows 在应用程序的消息队列中放置一条 WM_PAINT 消息。

只有当程序客户区的一部分失效时,窗口过程才会接受到 WM_PAINT 消息。

 Windows 内部为每一个窗口都保存了一个“绘制信息结构”(PAINTSTRUCT)。

该结构保存着一个可以覆盖该无效区域的最小矩形的坐标和一些其他的信息。这个最小区域称为“无效矩形”。

如果在窗口过程处理一条等候处理 WM_PAINT 消息之前,客户区的另外一部分也失效了,

那么 Windows 将计算出一个覆盖这两个失效部分的新的无效区域和无效矩形,并更新绘制信息结构中的数据。

也就是说将仍只有一条 WM_PAINT 消息。

 

窗口过程可以通过 InvalidateRect() 函数来强制使自己的客户区的一个矩形失效,也可以调用 ValidateRect() 函数来强制使客户区变的有效。

如果是让整个无效区域都有效,那么消息队列中的 WM_PAINT 消息就会被删除。这两个函数在有时候是很有用处的。

 窗口过程在处理 WM_PAINT 消息时,在调用BeginPaint() 函数时,整个客户区会变有效的 。

 

设备环境:

 当程序完成对客户区的绘制后,它必须释放设备环境句柄。释放后该句柄将不再有用。

 程序必须在处理同一条消息的过程中获取句柄和释放句柄。

 你不能在两条消息中间传递一个设备环境句柄,唯一的例外是通过调用 CreateDC() 函数创建设备环境。

 

获取设备环境句柄:方法一

 这种方法可以在处理 WM_PAINT 消息时使用。这关系到两个函数:BeginPaint() 和 EndPaint() 。

这两个函数都需要两个参数:一个是窗口的句柄,这是消息处理过程的参数:另一个是一个类型为 PAINTSTRUCT 结构的变量的地址。

程序通常将该结构体变量命名为 ps 。

 在处理 WM_PAINT 消息时,窗口过程首先调用BeginPaint() 函数,该函数通常会擦去无效区域的背景以便绘图。

 它同时还会填充 ps 结构的各个字段。函数的返回值就是设备环境句柄。通常会将它保存在一个名为 hdc 的变量中。

 窗口过程在处理 WM_PAINT 消息时必须成对的调用 BeginPaint() 和 EndPaint() 。

如果窗口过程不处理 WM_PAINT 消息,该消息就会被传送给 Windows 默认的窗口过程 DefWindowProc 。

 

获取设备环境句柄:方法二

 尽管最后在程序处理 WM_PAINT 消息时才更新整个客户区,

但有时也会发现在处理非 WM_PAINT 消息时绘制部分客户区也是很有用的。

这时你可以调用 GetDC() 函数来获得窗口客户区的设备环境句柄,使用完成后也必须调用 ReleaseDC() 函数将它释放:

hdc = GetDC(hwnd);
releaseDC(hwnd,hdc);

 这个函返回的句柄和 BeginPaint() 函数返回的有所不同,从 GetDC() 返回的设备环境句柄中的裁剪矩形是整个客户区。

这意味着你可以在客户区的任意部分绘制,而不局限于无效矩形。另外,GetDC() 函数将不会使无效区域有效化。

如果想使整个客户区有效,可以调用 ValidateRect() 函数。

 

TEXTOUT() 是显示文本的最重要的 GDI 函数,它的语法如下:

         TEXTOUT(hdc, x, y, psText, iLength);

         第一个参数是设备环境句柄:它既可以是从 GetDC() 返回的 hdc 值,也可以是处理 WM_PAINT 消息时

         从 BeginPaint() 返回的 hdc 值。

 

         设备环境中的属性决定了文本显示的特性。例如,设备环境中的一个属性决定了文本的颜色,默认颜色为黑色。

         设备环境同时默认文本的背景颜色为白色。当程序输出文本时,Windows 将用这个背景色填充每个字符周围的矩形区域 (我们称之为"字符框")。

         文本背景色和在窗口类中设定的背景色是不一样的。窗口类中的背景色 Windows 用来擦除客户区的刷子,

         它是一种图样,既可以是纯色也可以不是纯色的。它并不是设备环境结构的一部分。

         在定义窗口类时,大多数应用程序选择WHITE_BRUSH 作为背景,这样设备环境中默认的文本背景色 就和 Windows 用来擦除客户区的背景色相同了。

 

         参数 psText 是指向字符串的指针,而 iLength 是字符串中的字符数。如果 psText 指向一个 Unicode 字符串,

         则字符串占用的字节数是将是 iLength 的两倍。字符串中不应该有任何 ASCII 控制字符,如 回车键,换行符,制表符或退格键

 

          Windows 将这些控制字符显示为空心或实心方块。 Textout() 函数并不认为字符串尾部值为0字节

          (对Unicode 而言是值为0的双字节)表示字符串结束,它利用 iLength 参数来决定字符串的长度。

          参数 x 和 y 决定着输出字符串的在客户区的起始位置。 x 是水平位置,而 y 是垂直位置。

           第一个字符的左上角定位在坐标点 (x,y)。在默认的设备环境中,坐标原点位于客户区的左上角。

           如果传给 TEXTOUT() 函数的 x 和 y 都是 0,则字符串将紧靠客户区左上角。

 

 在类似于 TEXTOUT() 的 GDI 绘图函数的文档中,传给函数的坐标值通常被称为"逻辑坐标"。

     现在要注意的是,Windows 有各种"映射模式",来决定怎样将 GDI 函数中的逻辑坐标

     转换为显示器上的物理像素坐标。这些映射模式在设备环境中定义。

     默认的映射模式是 MM_TEXT(标识符定义在 WINGDI.H 头文件中)。

     在 WM_TEXT 映射模式下,逻辑单位和物理单位都是像素点。

     坐标是相对于客户区的左上角, x 的值从左往右增大,y 的值从上往下增大。

 

以上是关于窗口绘制的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 片段着色器未写入 fbo 颜色缓冲区

OpenGL:基本图形绘画二

GPU如何渲染到正确的窗口?

使用片段着色器在特定位置绘制完美的水平线

在画布上绘制片段视图

C++ GUI 窗口绘制了代码不应该绘制的形状