《Windows API每日一练》4.4 绘制填充区域

news2024/10/6 6:46:44

本节讲述如何填充由线条构建的封闭区域。当我们初始化一个窗口类时,往往已经指定了窗口的背景色画刷(WHITE_BRUSH),即默认的填充封闭区域背景的画刷。如果我们想更换背景颜色,需要选入其他系统预定义的画刷(BLACK_BRUSH、GRAY_BRUSH、NULL_BRUSH)。我们也可以选择创建并选入自定义的画刷。此外,我们还需要关注与填充背景相关的背景模式(透明模式和非透明模式)、填充模式(ALTERNATE模式和WINDING模式)和绘图模式。

本节必须掌握的知识点:

        多边形填充模式

        第26练:绘制填充区域

        画刷

4.4.1 多边形填充模式

Windows使用当前被选入设备环境的画笔来绘制图形的边框线。边框线使用当前的背 景模式、背景颜色和绘图模式,这与Windows绘制线条一样。我们所学的关于线条的所有 知识都适用于这些图形的边框线。下表列出了 Windows用于绘制带有边框的填充区域的7个函数。

函数名称

图形

Rectangle

直角矩形

Ellipse

椭圆

RoundRect

圆角矩形

Chord

弓形,由椭圆圆周上的弧和一根弦组成

Pie

椭圆上的一个扇形

Polygon

多边形

PolyPolygon

多个多边形

表4-2 带有边框的填充区域函数

Windows使用当前选入设备环境的画刷来填充图形。在默认情况下,使用的是备用对象WHITE_BRUSH,这就意味着使用白色来绘制图形内部。

Windows定义了 6种备用画刷: WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH 和NULL_BRUSH(又称为HOLLOW_BRUSH)。和选择备用画笔一样,可以将任何一种备用画刷选入设备环境。Windows定义画刷的句柄为HBRUSH类型,所以要先定义一个画 刷句柄变量:

HBRUSH hBrush;

获取GRAY_BRUSH句柄可以通过调用如下的GetStockObject函数实现:

hBrush = GetStockObject (GRAY_BRUSH);

然后通过调用SelectObject函数将它选进设备环境:

SelectObject (hdc, hBrush);

现在,当绘制上表中的任意图形时,内部都会变成灰色。

如果要绘制一个不含边框线的图形,则要把NULL_PEN画笔选入设备环境:

SelectObject (hdc, GetStockObject (NULL_PEN));

如果只想绘制图形的边框线,而不想填充图形的内部,可以将NULL_BRUSH选入设备环境:

SelectObject (hdc, GetStockobject (NULL_BRUSH);

也可以建立自定义的画刷,就像建立自定义的画笔一样。我们将在下一小节中介绍。

Polygon函数和多边形填充模式

Polygon函数用于绘制一个多边形形状。以下是Polygon函数的常见原型:

BOOL Polygon(

    HDC hdc,             // 设备上下文句柄

    const POINT* points,   // 多边形的顶点数组指针

    int count            // 顶点的数量

);

Polygon函数用于在设备上下文中绘制一个多边形形状。多边形由一系列顶点构成,通过指定顶点的坐标数组来定义。多边形的绘制遵循顺时针顺序连接顶点的规则。

举例

以下是一个示例代码,演示如何使用Polygon函数绘制一个三角形:

HDC hdc = GetDC(hwnd);  // 获取窗口的设备上下文

POINT points[3];  // 三角形的顶点数组

points[0].x = 100; points[0].y = 100;  // 第一个顶点

points[1].x = 200; points[1].y = 200;  // 第二个顶点

points[2].x = 300; points[2].y = 100;  // 第三个顶点

Polygon(hdc, points, 3);  // 绘制三角形

ReleaseDC(hwnd, hdc);  // 释放设备上下文

上述代码中,我们创建了一个包含三个顶点的POINT数组,并将顶点的坐标赋值。然后调用Polygon函数传递顶点数组和顶点数量来绘制三角形。

需要注意的是,绘制的多边形将使用当前设备上下文的绘图属性,如线条颜色、填充模式等。

PolyPolygon函数的调用形式如下:

PolyPolygon (hdc, apt, aiCounCs, iPolyCounC);

这个函数会绘制多个多边形。最后一个参数是绘制的多边形的个数。对每个多边形,数组 aiCounts给出了多边形顶点的个数。数组apt含有全部多边形的所有顶点。除了返回值外, PolyPolygon在功能上等同于下面的代码:

for (i = 0, iAccum = 0 ; i < iPolyCount ; i++)

{

       Polygon (hdc, apt + iAccum, aiCounts[i]) ;

       iAccum += aiCounts[i] ;

}

对Polygon和PolyPolygon函数,Windows都使用设备环境中的当前画刷来填充区域。 至于内部是如何填充的,要取决于多边形的填充模式,可以调用SetPolyFillMode函数来设置:

SetPolyFillMode (hdc, iMode);

在默认情况下,多边形的填充模式是ALTERNATE(交替)但是也可以将它设定为 WINDING(螺旋)。这两种方式的区别如图4-6所示。

●ALTERNATE和WINDING模式的区别

“ALTERNATE” 和 “WINDING”是用来处理复杂路径(即,路径中有交叉或重叠部分)的填充规则,本质上是决定哪些区域是需要被填充,哪些区域是应该被排除的。

1.ALTERNATE:也称为奇偶规则。该规则通过画直线从需要考虑填充的区域到区域外部,如果这个线穿过路径的交叉点次数是奇数,则算作在路径内部,该区域会被填充,如果是偶数,则不会被填充。

2.WINDING:也称为非零环绕数规则。按照这个规则,所有的路径都是有方向的。从一个区域向外画射线,路径从左到右穿过射线会加一,从右到左穿过射线会减一,如果最终的结果是非零,那么这个区域视为在路径内部,会被填充。如果结果是零,那么这个区域不会被填充。

图4-6用两种多边形填充模式绘制的图:ALTERNATE(左)和WINDING(右)

4.4.2 第26练:绘制填充区域

/*------------------------------------------------------------------

026  WIN32 API 每日一练

     第26个例子ALTWIND.C:绘制填充区域

     SetPolyFillMode函数

     Polygon函数

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT ("AltWind.C") ;

    (略)

     return msg.wParam ;

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

{

     static POINT apt[10];

     //多边形顶点坐标数组

     static POINT aptFigure[10] = { {10,70}, 50,70, 50,10, 90,10, 90,50,

      30,50, 30,90, 70,90, 70,30, 10,30 };

     static int cxClient,cyClient;

     HDC hdc ;

     PAINTSTRUCT ps

     switch (message)

     {

     case WM_SIZE:

          cxClient = LOWORD(lParam);

          cyClient = HIWORD(lParam);

          return 0 ;

     case WM_PAINT :

          hdc = BeginPaint(hwnd,&ps);

          SelectObject(hdc,GetStockObject(GRAY_BRUSH));

          //设置顶点坐标---左图

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

          {

               apt[i].x = cxClient *aptFigure[i].x/200;

               apt[i].y = cyClient *aptFigure[i].y/200;

          }

          //设置ALTERNATE填充模式

          //选择交替模式(填充每条扫描线上奇数和偶数多边形边之间的区域)

          SetPolyFillMode(hdc,ALTERNATE);

          Polygon(hdc,apt,10);//绘制多边形

          //重置顶点坐标---左图

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

          {

               apt[i].x += cxClient/2;

          }

          //设置WINDING填充模式

          //选择缠绕模式(用非零缠绕值填充任何区域)

          SetPolyFillMode(hdc,WINDING);

          Polygon(hdc,apt,10);//绘制多边形

          EndPaint(hwnd,&ps);

          return 0;

     case WM_DESTROY:

          PostQuitMessage(0);

          return 0;

     }

     return DefWindowProc(hwnd, message, wParam, lParam);

}

/******************************************************************************

SetPolyFillMode函数:设置填补多边形多边形填充模式。

int SetPolyFillMode(

  HDC hdc,

  int mode     //ALTERNATE:选择替代模式,只有该射线穿越奇数条边框线时,封闭区域才会被填充。

               //WINDING:选择绕线模式,穿过奇数条边框线或相同方向偶数条边时封闭区域才会被填充。

);

*******************************************************************************

Polygon函数:

绘制由以直线连接两个或更多个顶点的多边形。

使用当前的笔勾勒出多边形的轮廓,并使用当前的笔刷和多边形填充模式填充多边形。

BOOL Polygon(

  HDC         hdc,

  const POINT *apt, //指向POINT结构数组的指针,该数组以逻辑坐标形式指定多边形的顶点。

  int         cpt   //数组中的顶点数。该值必须大于或等于2。

);

*/

运行结果:

图4-7 填充模式

       总结

       上述实例非常简单,窗口过程中处理WM_PAINT消息时,先调用SelectObject函数选入一个灰色背景画刷,然后使用一个for循环语句初始化一个多边形顶点坐标数组,接着调用SetPolyFillMode函数和SetPolyFillMode函数设置填充模式并绘制多边形。

4.4.3 画刷

       在4.3节中我们讲述了如何创建新的自定义画笔。同样的道理,我们也可以创建新的自定义画刷。

Windows允许使用5种函数来建立逻辑画刷。调用SelectObject函数将画刷选入设备 环境。逻辑画刷也是GDI对象,这和逻辑画笔一样。所以所有你建立的画刷都必须最终被删除,但是如果它当前被选入了设备环境中,则不要删除它。

建立逻辑画刷的第一个函数

hBrush = CreateSolidBrush (crColor);

这个函数中的Solid(中文含义为实心)并不意味着画刷为纯色。将该画刷选入设备环境后, Windows可能会建立一个抖动位图,并且使用它作为画刷。

建立逻辑画刷的第二个函数

可以使用由水平、垂直或者对角线组成的“阴影线标记”(hatchmark)来建立一个画刷。这种样式的画刷对着色条形图的内部和在绘图机上绘图最常用。建立阴影线画刷的函数如下:

hBrush = CreateHatchBrush (iHatchSCyle, crColor);

参数iHatchStyle表示阴影线标记的外观。图4-8显示了 6种可用的明影线样式及其外观。

                                          图4-8 6种阴影线固刷样式

                           

CreateHatchBrush函数中的参数crClolor表示阴影线的颜色。把画刷选入设备环境后, Windows把这种颜色转换为显示器上可用的最近的纯色。阴影线之间的区域会使用设备环 境中定义的背景模式和背景颜色来着色。如果背景模式是OPAQUE,则背景颜色(也被转换 为纯色)用来填充线与线之间的空隙。如果背景的模式是TRANSPARENT,则Windows只画出阴影线,不填充它们之间的空隙。

建立逻辑画刷的第三和第四个函数

可以通过函数CreatePatternBrush和CreateDIBPatternBrushPt来建立自己的位图画刷。我们将在第十四章位图详细讲解。

建立逻辑画刷的第五个函数

建立逻辑画刷的第5个函数包含其他4个函数的所有功能:

hBrush = CreateBrushlndirect (&logbrush);

变量logbrush是一个类型为LOGBRUSH( “逻辑画刷”)的结构。这个结构有三个字段,lbStyle字段的值决定着Windows如何解释其他的两个字段。

LOGBRUSH结构体(三个字段,lbStyle 字段的值决定Windows如何解释其他两个字段)

lbStyle(UINT)

lbColor(COLORREF)

lbHatch(LONG)

BS_SOLID

画刷的颜色

被忽略

BS_HOLLOW

被忽略

被忽略

BS_HATCHED

阴影线的颜色

阴影线画刷的样式

BS_PATTERN

被忽略

位图的句柄

BS_DIBPATTERNPT

被忽略

指向DIB的指针

选入画刷和删除画刷

画刷作为GDI对象,同样必须遵循GDI对象三原则。在前面我们调用SelectObject函数将逻辑画笔选入设备环境,调用DeleteObject函数来 删除这个逻辑画笔,调用GetObject函数来获取逻辑画笔的信息。对于画刷,同样可以使用 这三个函数。一旦拥有一个画刷句柄,就可以调用SelectObject函数将其选入设备环境:

SelectObject (hdc, hBrush);

然后,调用DeleteObject函数删除已建立的画刷:

DeleteObject (hBrush);

但是不要删除当前被选入设备环境的画刷。

如果需要获取画刷的信息,可以调用GetObject函数:

GetObject (hBrush, sizeof (logbrush), (LPVOID) slogbrush);

其中,logbrush是一个类型为LOGBRUSH的结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1830212.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

助力樱桃智能自动化采摘,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建果园种植采摘场景下樱桃成熟度智能检测识别系统

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;再到医疗健康&#xff0c;其影响力无处不在。然而&#xff0c;当我们把目光转向中国的农业领域时&#xff0c;一个令人惊讶的…

几何公差的设计和选用

保证轴承的旋转精度&#xff0c;提出圆柱度&#xff1b; 这里的轴肩部面 为了测量方便&#xff0c;使用圆跳动代替垂直度公差方便一些。

胡说八道(24.6.15)——数字电子技术及Multisim仿真

上回书说到数电中的两种基本存储电路——锁存器和触发器以及时序逻辑电路的设计和分析。今天来看看触发器的几个应用&#xff0c;主要实践实践&#xff0c;不能只说概念。 首先&#xff0c;首当其冲的就是计数器&#xff0c;如果大家看过我之前记的笔记的话&#xff0c;应该不会…

vue3轮播图怎么做

先看效果 实现代码 <n-carouseleffect"card"dot-type"line"draggable:autoplay"!isHovered":current-index"currentIndex"prev-slide-style"transform: translateX(-150%) translateZ(-450px);opacity:1"next-slide-st…

SE语法总结博文(附思维导图)

Java中的规范 注释 //单行注释 /*多行注释 */ /**文档注释 */命名规范 命名时可以包含&#xff1a;字母、数字以及 下划线和 $ 符号等等。 但是不能以数字开头&#xff0c;也不能是关键字&#xff0c;且严格区分大小写。 类名&#xff1a;每个单词的首字母大写(大驼峰)&…

springboot优雅shutdown时异步线程安全优化

前面针对graceful shutdown写了两篇文章 第一篇&#xff1a; https://blog.csdn.net/chenshm/article/details/139640775 只考虑了阻塞线程&#xff0c;没有考虑异步线程 第二篇&#xff1a; https://blog.csdn.net/chenshm/article/details/139702105 第二篇考虑了多线程的安全…

Rewrite the Stars

文章目录 摘要1、引言2、相关工作3、重写星操作3.1、单层中的星操作3.2、扩展到多层3.3、特殊情况3.4、实证研究3.4.1、星操作的实证优越性3.4.2、决策边界对比3.4.3、扩展到无激活函数的网络 3.5、开放讨论与更广泛的影响 4、概念验证&#xff1a;StarNet4.1、StarNet架构4.2、…

HDU - 5651 xiaoxin juju needs help(Java JS Python C C++)

题目来源 Problem - 5651 (hdu.edu.cn) 题目描述 众所周知&#xff0c;小新是一位才华横溢的程序员。当他还是小学六年级的学生时&#xff0c;他就知道回文字符串了。 今年夏天&#xff0c;他在腾讯实习。一天&#xff0c;他的领导来找小新帮忙。他的领导给了他一个字符串&a…

LabVIEW程序的常见加密方式

LabVIEW程序的加密对于保护知识产权和敏感数据至关重要。本文将详细介绍LabVIEW程序常用的加密方式&#xff0c;包括VI加密、代码保护、文件加密和通信加密等&#xff0c;帮助开发者选择合适的加密方法来确保程序的安全性和完整性。 LabVIEW程序的常见加密方式 VI加密&#xf…

Fiddler抓包工具介绍

下载 下载:Web Debugging Proxy and Troubleshooting Tools|Fiddler 进去要填一个表 汉化版 百度网盘 请输入提取码 提取码&#xff1a;xq9t 下载过附件之后分别把两个文件 点开fiddler就ok了 配置https fiddler要想抓到https包(解密的),点击tools->options勾选三个对…

做好程序前设计

不要小看任何一道编程题目&#xff01;一定一定一定要想好之后再动手&#xff01;&#xff01;&#xff01; 带上你的草稿本&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xf…

DP:01背包问题

一、背包问题的概述 背包问题是⼀种组合优化的NP完全问题。 本质上是为了找出“带有限制条件的组合最优解” 1、根据物品的个数&#xff0c;分为如下几类&#xff1a; • 01背包问题&#xff1a;每个物品只有⼀个&#xff08;重点掌握&#xff09;• 完全背包问题&#xff1…

牛客热题:最长上升子序列(一)

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;最长上升子序列(一)题目链接方法…

用PHP来调用API给自己定制一个“每日新闻”

头条新闻汇聚了互联网上的时事动态&#xff0c;提供最新新闻动态、网络热门话题和视频更新等&#xff0c;覆盖社会、政治、体育、经济、娱乐、科技等多个领域&#xff0c;并不断刷新内容。企业应用这一接口后&#xff0c;可以快速吸引更多的用户访问自己的平台。即使是非新闻类…

WINUI——CommunityToolkit.Mvvm Messenger接收消息时报错:Cannot access a disposed object.

背景 WINUI开发时使用CommunityToolkit.Mvvm的Messemger让UI展示一些信息时出现错误&#xff1a; System.ObjectDisposedException:“Cannot access a disposed object. ObjectDisposed_ObjectName_Name” 详细见下述截图&#xff1a; 开发环境 WIN11 WINUI&#xff13; …

微信小程序开发系列(三十五)·自定义组件的属性properties

微信小程序开发系列&#xff08;三十四&#xff09;自定义组件的创建、注册以及使用&#xff08;数据和方法事件的使用&#xff09;_wx小程序组件开发-CSDN博客 目录 1. 组件的属性 2. 组件的使用 3. 细节描述 1. 组件的属性 Properties是指组件的对外属性&#xff0c;主…

Nginx之静态文件服务器的搭建

1.概述 静态文件服务器是指提供HTML文件访问或客户端 可直接从中下载文件的Web服务器。对于图片、 JavaScript或CSS文件等渲染页面外观的、不会动态改 变内容的文件&#xff0c;大多数网站会单独提供以静态文件服 务器的方式对其进行访问&#xff0c;实现动静分离的架构。 HTML…

ReactNative和Android通信

初始化一个RN项目以后&#xff0c;接下来想要让Android与React Native通信 写一个继承自ReactContextBaseJavaModule类的子类&#xff0c;重写getName方法 package com.awesomeprojectimport android.util.Log import android.widget.Toast import com.facebook.react.bridge.…

Java并发自测题

文章目录 一、什么是线程和进程?线程与进程的关系,区别及优缺点&#xff1f;二、为什么要使用多线程呢?三、说说线程的生命周期和状态?四、什么是线程死锁?如何预防和避免线程死锁?五、synchronized 关键字六、并发编程的三个重要特性七、JMM &#xff08;Java Memory Mod…

Android 自定义View

我们所有的试图都是起源于自定义View&#xff0c;包括ViewGroup也是继承于它&#xff0c;可以说它是视图组件之父。 我们可以从它的大致流程来分为四个部分&#xff1a; 构造方法&#xff0c;onMeasure&#xff0c;onLayout&#xff0c;onDraw 构造方法&#xff1a; 它主要有…